Package nukescripts :: Module cameratracker
[hide private]
[frames] | no frames]

Source Code for Module nukescripts.cameratracker

  1  """Functions used by the CameraTracker node""" 
  2   
  3  import nuke 
  4  import nukescripts 
  5  import os 
  6   
  7  import camerapresets 
  8   
  9  # 
 10  # Initialisation 
 11  # 
 12   
13 -def populateExportMenu(cameraTracker):
14 """Populate the export menu on a CameraTracker node.""" 15 # These are the entries that will appear in the CameraTracker's export menu. 16 # You can extend this list with your own entries if you like. 17 entries = [ 18 # Label # Script to execute when the bake button is pressed. # Tooltip for this entry. 19 ( 'Camera', 'nukescripts.cameratracker.createCamera(nuke.thisNode())', "Create a Camera linked to the calculated projection."), 20 ( 'Camera rig', 'nukescripts.cameratracker.createCameraRig(nuke.thisNode())', "Create a multi-view rig with a Camera per view."), 21 ( 'Camera set', 'nukescripts.cameratracker.createCameraSet(nuke.thisNode())', "Create a set of Cameras, one Camera per solved frame."), 22 ( 'Scene', 'nukescripts.cameratracker.createScene(nuke.thisNode())', "Create a Scene with a Camera and PointCloud for the camera solve."), 23 ( 'Scene+', 'nukescripts.cameratracker.createEverything(nuke.thisNode())', "Create a Scene with a Camera, PointCloud, ScanlineRender, and LensDistortion (Undistort) node."), 24 ( 'Point cloud', 'nukescripts.cameratracker.createPointCloud(nuke.thisNode())', "Create a PointCloud for the camera solve."), 25 ( 'Distortion', 'nukescripts.cameratracker.createLensDistortion(nuke.thisNode())', 26 "Create a LensDistortion node preconfigured for distortion using the same settings as the CameraTracker. " + 27 "You can use this node to distort your CG or other elements before comping them back over the input footage."), 28 ( 'Undistortion', 'nukescripts.cameratracker.createUndistortion(nuke.thisNode())', 29 "Create a LensDistortion node preconfigured for undistortion using the same settings as the CameraTracker. " + 30 "You can apply this node to your input footage to make it match CG elements rendered using the calculated camera."), 31 ( 'Cards', 'nukescripts.cameratracker.createCards(nuke.thisNode())', "Create a group of cards.") 32 # Add your own entries here, if desired. 33 ] 34 35 k = cameraTracker['exportMenu'] 36 k.setValues([ "%s\t%s" % (script, label) for label, script, _ in entries ]) 37 38 # Calculate a tooltip which describes all of the available export options. 39 tooltipLines = [ "Create new camera and point cloud nodes based on the results of the solve. The available options are:", "" ] 40 for label, _, tooltip in entries: 41 tooltipLines.append("<b>%s:</b> %s" % (label, tooltip)) 42 43 k.setTooltip(os.linesep.join(tooltipLines))
44 45
46 -def populateFilmBackPresets(cameraTracker):
47 """Populate the film back presets menu on a CameraTracker node.""" 48 # These are the entries that will appear in the CameraTracker's film back presets menu. 49 # You can extend this list with your own entries by modifying camerapresets.py 50 51 k = cameraTracker['filmBackSizePresets'] 52 k.setValues(["%s\t%s" % (idx, label) for idx, label in enumerate(nukescripts.camerapresets.getLabels())])
53 54 # 55 # Export functions 56 # 57
58 -def createCamera(solver):
59 """Create a camera node based on the projection calculated by the solver.""" 60 x = solver.xpos() 61 y = solver.ypos() 62 w = solver.screenWidth() 63 h = solver.screenHeight() 64 m = int(x + w/2) 65 numviews = len( nuke.views() ) 66 link = False 67 linkKnob = solver.knob("linkOutput") 68 if linkKnob: 69 link = bool(linkKnob.getValue()) 70 71 camera = nuke.createNode('Camera', '', False) 72 camera.setInput(0,None) 73 camera.setXYpos(m - int(camera.screenWidth()/2), y + w) 74 if link: 75 camera.knob("focal").setExpression(solver.name() + ".focalLength") 76 camera.knob("haperture").setExpression(solver.name() + ".aperture.x") 77 camera.knob("vaperture").setExpression(solver.name() + ".aperture.y") 78 camera.knob("translate").setExpression(solver.name() + ".camTranslate") 79 camera.knob("rotate").setExpression(solver.name() + ".camRotate") 80 camera.knob("win_translate").setExpression(solver.name() + ".windowTranslate") 81 camera.knob("win_scale").setExpression(solver.name() + ".windowScale") 82 else: 83 camera.knob("focal").fromScript(solver.knob("focalLength").toScript(False)) 84 camera.knob("translate").fromScript(solver.knob("camTranslate").toScript(False)) 85 camera.knob("rotate").fromScript(solver.knob("camRotate").toScript(False)) 86 camera.knob("win_translate").fromScript(solver.knob("windowTranslate").toScript(False)) 87 camera.knob("win_scale").fromScript(solver.knob("windowScale").toScript(False)) 88 for i in xrange(numviews): 89 camera.knob("haperture").setValue(solver.knob("aperture").getValue(0,i+1),0,0,i+1) 90 camera.knob("vaperture").setValue(solver.knob("aperture").getValue(1,i+1),0,0,i+1)
91 92
93 -def createCameraRig(solver):
94 """Create a multi-view rig with a camera per view.""" 95 numviews = len( nuke.views() ) 96 if numviews < 2: 97 nuke.message("Creating a camera rig requires multiple views.\n" + 98 "You can add additional views in your <em>Project Settings</em>, on the <em>Views</em> tab.") 99 return 100 101 x = solver.xpos() 102 y = solver.ypos() 103 w = solver.screenWidth() 104 h = solver.screenHeight() 105 m = int(x + w/2) 106 link = False 107 linkKnob = solver.knob("linkOutput") 108 if linkKnob: 109 link = bool(linkKnob.getValue()) 110 111 join = nuke.nodes.JoinViews() 112 join.setInput(0,None) 113 join.setXYpos(m + int(w*1.5) + int(w * numviews/2), y + w) 114 115 for i in xrange(numviews): 116 viewStr = nuke.views()[i] 117 camera = nuke.nodes.Camera() 118 camera.setInput(0,None) 119 if link: 120 camera.knob("focal").setExpression(solver.name() + ".focalLength." + viewStr) 121 camera.knob("haperture").setExpression(solver.name() + ".aperture." + viewStr + ".x") 122 camera.knob("vaperture").setExpression(solver.name() + ".aperture." + viewStr + ".y") 123 camera.knob("translate").setExpression(solver.name() + ".camTranslate." + viewStr) 124 camera.knob("rotate").setExpression(solver.name() + ".camRotate." + viewStr) 125 camera.knob("win_translate").setExpression(solver.name() + ".windowTranslate." + viewStr) 126 camera.knob("win_scale").setExpression(solver.name() + ".windowScale." + viewStr) 127 else: 128 if solver.knob("focalLength").isAnimated(): 129 camera.knob("focal").copyAnimation(0,solver.knob("focalLength").animation(0,i+1)) 130 else: 131 camera.knob("focal").setValue(solver.knob("focalLength").getValue(0,i+1),0,0,i+1) 132 if solver.knob("camTranslate").isAnimated(): 133 camera.knob("translate").copyAnimation(0,solver.knob("camTranslate").animation(0,i+1)) 134 camera.knob("translate").copyAnimation(1,solver.knob("camTranslate").animation(1,i+1)) 135 camera.knob("translate").copyAnimation(2,solver.knob("camTranslate").animation(2,i+1)) 136 else: 137 camera.knob("translate").setValue(solver.knob("camTranslate").getValue(0,i+1),0,0,i+1) 138 camera.knob("translate").setValue(solver.knob("camTranslate").getValue(1,i+1),1,0,i+1) 139 camera.knob("translate").setValue(solver.knob("camTranslate").getValue(2,i+1),2,0,i+1) 140 if solver.knob("camRotate").isAnimated(): 141 camera.knob("rotate").copyAnimation(0,solver.knob("camRotate").animation(0,i+1)) 142 camera.knob("rotate").copyAnimation(1,solver.knob("camRotate").animation(1,i+1)) 143 camera.knob("rotate").copyAnimation(2,solver.knob("camRotate").animation(2,i+1)) 144 else: 145 camera.knob("rotate").setValue(solver.knob("camRotate").getValue(0,i+1),0,0,i+1) 146 camera.knob("rotate").setValue(solver.knob("camRotate").getValue(1,i+1),1,0,i+1) 147 camera.knob("rotate").setValue(solver.knob("camRotate").getValue(2,i+1),2,0,i+1) 148 camera.knob("win_translate").setValue(solver.knob("windowTranslate").getValue(0,i+1),0,0,i+1) 149 camera.knob("win_scale").setValue(solver.knob("windowScale").getValue(0,i+1),0,0,i+1) 150 camera.knob("haperture").setValue(solver.knob("aperture").getValue(0,i+1),0,0,i+1) 151 camera.knob("vaperture").setValue(solver.knob("aperture").getValue(1,i+1),0,0,i+1) 152 camera.setXYpos(m + 2*w + w*i, y) 153 if numviews==2: 154 if i==0: 155 camera.knob("gl_color").setValue(0xFF0000FF) 156 camera.knob("tile_color").setValue(0xFF0000FF) 157 if i==1: 158 camera.knob("gl_color").setValue(0x00FF00FF) 159 camera.knob("tile_color").setValue(0x00FF00FF) 160 camera.setName( viewStr ) 161 join.setInput(i,camera)
162 163
164 -def createCameraSet(solver):
165 """Create a set of Cameras, one Camera per solved frame.""" 166 numCameras = solver["camTranslate"].getNumKeys() 167 168 if numCameras >= 100: 169 ok = nuke.ask("This will create %d cards, which may take some time.\nAre you sure?" % numCameras) 170 if not ok: 171 return 172 173 if numCameras == 0: 174 nuke.message("You can only create a camera set when you have solved cameras.") 175 return 176 177 x = solver.xpos() 178 y = solver.ypos() 179 w = solver.screenWidth() 180 h = solver.screenHeight() 181 m = int(x + w*2) 182 link = False 183 linkKnob = solver.knob("linkOutput") 184 if linkKnob: 185 link = bool(linkKnob.getValue()) 186 exprStr = "[python {nuke.toNode('" + solver.fullName() +"')" 187 188 group = nuke.createNode("Group", '', False) 189 group.begin() 190 group.setName("Cameras") 191 group.setXYpos(m + w, y + w) 192 if numCameras>0: 193 scene = nuke.createNode("Scene", '', False) 194 sw = scene.screenWidth() 195 sh = scene.screenHeight() 196 inImg = nuke.createNode("Input", '', False) 197 inImg.setName("img"); 198 inImg.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y) 199 out = nuke.createNode("Output", '', False) 200 out.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y + 4*w) 201 out.setInput(0, scene) 202 scene.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y + 3*w) 203 for i in xrange(numCameras): 204 frame = solver.knob("camTranslate").getKeyTime(i) 205 camera = nuke.createNode("Camera", '', False) 206 camera.setSelected(False) 207 camera.setInput(0,inImg) 208 camera.setXYpos(m + w*i - int(sw/2), y + 2*w) 209 scene.setInput(i, camera) 210 if link: 211 camera.knob("focal").setExpression( exprStr + ".knob('focalLength').getValueAt(" + str(frame) + ")}]" ) 212 camera.knob("haperture").setExpression( exprStr + ".knob('aperture').getValueAt(" + str(frame) + ",0)}]" ) 213 camera.knob("vaperture").setExpression( exprStr + ".knob('aperture').getValueAt(" + str(frame) + ",1)}]" ) 214 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",0)}]",0 ) 215 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",1)}]",1 ) 216 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",2)}]",2 ) 217 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",0)}]",0 ) 218 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",1)}]",1 ) 219 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",2)}]",2 ) 220 else: 221 camera.knob("focal").setValue( solver.knob("focalLength").getValueAt(frame) ) 222 camera.knob("haperture").setValue( solver.knob("aperture").getValueAt(frame,0) ) 223 camera.knob("vaperture").setValue( solver.knob("aperture").getValueAt(frame,1) ) 224 camera.knob("translate").setValue( solver.knob("camTranslate").getValueAt(frame) ) 225 camera.knob("rotate").setValue( solver.knob("camRotate").getValueAt(frame) ) 226 group.end() 227 group.setInput(0,solver) 228 return group
229
230 -def createScene(cameraTracker):
231 """Create a Scene with a Camera and PointCloud for the camera solve.""" 232 scene = nuke.createNode('Scene', '', False) 233 camera = nuke.createNode('Camera', '', False) 234 pointCloud = nuke.createNode('CameraTrackerPointCloud', '', False) 235 sw = scene.screenWidth() 236 sh = scene.screenHeight() 237 x = cameraTracker.xpos() 238 y = cameraTracker.ypos() 239 w = cameraTracker.screenWidth() 240 h = cameraTracker.screenHeight() 241 m = int(x + w/2) 242 camera.setXYpos(m + w, y + w + int((h-sh)/2)) 243 pointCloud.setXYpos(m - int(pointCloud.screenWidth()/2), y + w) 244 scene.setXYpos(m - int(sw/2), y + w*2 - int((sh-h)/2)) 245 camera.setInput(0,None) 246 pointCloud.setInput(0,cameraTracker) 247 scene.setInput(0,camera) 248 scene.setInput(1,pointCloud) 249 numviews = len( nuke.views() ) 250 link = False 251 linkKnob = cameraTracker.knob("linkOutput") 252 if linkKnob: 253 link = bool(linkKnob.getValue()) 254 if link: 255 camera.knob("focal").setExpression(cameraTracker.name() + ".focalLength") 256 camera.knob("haperture").setExpression(cameraTracker.name() + ".aperture.x") 257 camera.knob("vaperture").setExpression(cameraTracker.name() + ".aperture.y") 258 camera.knob("translate").setExpression(cameraTracker.name() + ".camTranslate") 259 camera.knob("rotate").setExpression(cameraTracker.name() + ".camRotate") 260 camera.knob("win_translate").setExpression(cameraTracker.name() + ".windowTranslate") 261 camera.knob("win_scale").setExpression(cameraTracker.name() + ".windowScale") 262 else: 263 camera.knob("focal").fromScript(cameraTracker.knob("focalLength").toScript(False)) 264 camera.knob("translate").fromScript(cameraTracker.knob("camTranslate").toScript(False)) 265 camera.knob("rotate").fromScript(cameraTracker.knob("camRotate").toScript(False)) 266 camera.knob("win_translate").fromScript(cameraTracker.knob("windowTranslate").toScript(False)) 267 camera.knob("win_scale").fromScript(cameraTracker.knob("windowScale").toScript(False)) 268 for i in xrange(numviews): 269 camera.knob("haperture").setValue(cameraTracker.knob("aperture").getValue(0,i+1),0,0,i+1) 270 camera.knob("vaperture").setValue(cameraTracker.knob("aperture").getValue(1,i+1),0,0,i+1) 271 return [scene, camera, pointCloud]
272 273
274 -def createEverything(cameraTracker):
275 """Create a Scene with a Camera, PointCloud, ScanlineRender, and LensDistortion (Undistort) node.""" 276 [scene, camera, pointCloud] = createScene(cameraTracker); 277 lensDistort = createUndistortion(cameraTracker) 278 scanline = nuke.createNode('ScanlineRender', '', False) 279 # need to create a dummy camera here, as there seems to be a bug where calling 280 # .screenWidth() on the scene or camera objects always returns 0. 281 dummyCamera = nuke.createNode('Camera', '', False) 282 # creating our 4 dots 283 cameraToSceneDot = nuke.createNode('Dot', '', False) 284 cameraToScanlineRenderDot = nuke.createNode('Dot', '', False) 285 lensToScanlineDot = nuke.createNode('Dot', '', False) 286 # setting up widths & heights 287 sw = dummyCamera.screenWidth() 288 sh = dummyCamera.screenHeight() 289 x = cameraTracker.xpos() 290 y = cameraTracker.ypos() 291 w = cameraTracker.screenWidth() 292 h = cameraTracker.screenHeight() 293 # have to hard-code our dot size because of the the 294 # .screenWidth()/Height() always returning 0 bug. 295 dw = 12 296 dh = 12 297 m = int(x + w/2) 298 hspacing = int(w*1.5) 299 vspacing = w 300 # deleting our dummy camera 301 nuke.delete(dummyCamera); 302 # setting the node positions 303 camera.setXYpos( m - hspacing - int(sw/2), y + vspacing + int((h-sh)/2)) 304 pointCloud.setXYpos( m - int(w/2), y + vspacing) 305 lensDistort.setXYpos( m + hspacing - int(w/2), y + vspacing) 306 scene.setXYpos( m - int(sw/2), y + 2*vspacing + int((h-sh)/2)) 307 scanline.setXYpos( m - int(w/2), y + 3*vspacing) 308 # setting dot positions 309 cameraToSceneDot.setXYpos( m - hspacing - int(dw/2), y + 2*vspacing + int((h-dh)/2) ) 310 cameraToScanlineRenderDot.setXYpos( m - hspacing - int(dw/2), y + 3*vspacing + int((h-dh)/2) ) 311 lensToScanlineDot.setXYpos( m + hspacing - int(dw/2), y + 3*vspacing + int((h-dh)/2) ) 312 # setting dot connections 313 cameraToSceneDot.setInput(0, camera) 314 cameraToScanlineRenderDot.setInput(0, cameraToSceneDot) 315 lensToScanlineDot.setInput(0, lensDistort) 316 # setting node connections 317 camera.setInput(0,None) 318 pointCloud.setInput(0,cameraTracker) 319 # try set the lens distortion input to the CT's input if possible, otherwise ensure it's 320 # disconnected. 321 if cameraTracker.inputs() > 0 and cameraTracker.input(0) != None: 322 lensDistort.setInput(0, cameraTracker.input(0)) 323 else: 324 lensDistort.setInput(0, None) 325 scene.setInput(0,cameraToSceneDot) 326 scene.setInput(1,pointCloud) 327 scanline.setInput(0, lensToScanlineDot) 328 scanline.setInput(1, scene) 329 scanline.setInput(2, cameraToScanlineRenderDot)
330 331
332 -def createPointCloud(cameraTracker):
333 """Create a CameraTrackerPointCloud node.""" 334 _clearSelection() 335 cameraTracker.setSelected(True) 336 pointCloud = nuke.createNode('CameraTrackerPointCloud', '', False)
337 338
339 -def createLensDistortion(cameraTracker):
340 return _createLensDistortionNode(cameraTracker, False)
341 342
343 -def createUndistortion(cameraTracker):
344 return _createLensDistortionNode(cameraTracker, True)
345 346
347 -def _createLensDistortionNode(cameraTracker, invertDistortion):
348 """Create a LensDistortion node which matches the settings calculated by the CameraTracker.""" 349 _clearSelection() 350 351 lensDistort = nuke.createNode('LensDistortion2', '', False) 352 lensDistort.setInput(0, cameraTracker.input(0)) 353 link = cameraTracker["linkOutput"].getValue() 354 _copyKnob(cameraTracker, "lensType", lensDistort, "lensType", link) 355 _copyKnob(cameraTracker, "distortion1", lensDistort, "distortionDenominator0", link) 356 _copyKnob(cameraTracker, "distortion2", lensDistort, "distortionDenominator1", link) 357 _copyKnob(cameraTracker, "distortionCenter", lensDistort, "centre", link) 358 _copyKnob(cameraTracker, "anamorphicSqueeze", lensDistort, "anamorphicSqueeze", link) 359 _copyKnobIdx(cameraTracker, "asymmetricDistortion", 0, lensDistort, "distortionDenominatorX00", 0, link) 360 _copyKnobIdx(cameraTracker, "asymmetricDistortion", 1, lensDistort, "distortionDenominatorY00", 0, link) 361 362 if invertDistortion: 363 lensDistort['output'].setValue('Undistort') 364 else: 365 lensDistort['output'].setValue('Redistort') 366 367 lensDistort['projection'].setValue('Rectilinear') 368 lensDistort['distortionModelPreset'].setValue('NukeX Classic') 369 370 lensDistort.selectOnly() 371 372 return lensDistort
373 374
375 -def createCards(solver):
376 numCameras = solver["camTranslate"].getNumKeys() 377 378 if numCameras >= 100: 379 ok = nuke.ask("This will create %d cards, which may take some time.\nAre you sure?" % numCameras) 380 if not ok: 381 return 382 383 if numCameras == 0: 384 nuke.message("You can only create a card set when you have solved cameras.") 385 return 386 387 x = solver.xpos() 388 y = solver.ypos() 389 w = solver.screenWidth() 390 h = solver.screenHeight() 391 m = int(x + w*2) 392 link = False 393 linkKnob = solver.knob("linkOutput") 394 if linkKnob: 395 link = bool(linkKnob.getValue()) 396 exprStr = "[python {nuke.toNode('" + solver.fullName() +"')" 397 398 group = nuke.createNode("Group", '', False) 399 group.begin() 400 group.setName("Cards") 401 group.setXYpos(m + w, y + w) 402 if numCameras>0: 403 scene = nuke.createNode("Scene", '', False) 404 sw = scene.screenWidth() 405 sh = scene.screenHeight() 406 inImg = nuke.createNode("Input", '', False) 407 inImg.setName("img"); 408 inImg.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y) 409 out = nuke.createNode("Output", '', False) 410 out.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y + 5*w) 411 out.setInput(0, scene) 412 group.addKnob(nuke.Tab_Knob('cards','Cards')) 413 zDistKnob = nuke.Double_Knob('z','z') 414 zDistKnob.setRange(0,100) 415 zDistKnob.setTooltip("Cards are placed this far from origin. Use this to make a pan & tile dome of this radius.") 416 zDistKnob.setDefaultValue([1]) 417 group.addKnob(zDistKnob) 418 for i in xrange(numCameras): 419 frame = solver.knob("camTranslate").getKeyTime(i) 420 hold = nuke.createNode("FrameHold", '', False) 421 hold.setInput(0, inImg) 422 hold.knob("first_frame").setValue(frame) 423 hold.setXYpos(m + w*i - int(w/2), y + w) 424 camera = nuke.createNode("Camera", '', False) 425 camera.setInput(0,None) 426 if link: 427 camera.knob("focal").setExpression( exprStr + ".knob('focalLength').getValueAt(" + str(frame) + ")}]" ) 428 camera.knob("haperture").setExpression( exprStr + ".knob('aperture').getValueAt(" + str(frame) + ",0)}]" ) 429 camera.knob("vaperture").setExpression( exprStr + ".knob('aperture').getValueAt(" + str(frame) + ",1)}]" ) 430 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",0)}]",0 ) 431 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",1)}]",1 ) 432 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",2)}]",2 ) 433 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",0)}]",0 ) 434 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",1)}]",1 ) 435 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",2)}]",2 ) 436 else: 437 camera.knob("focal").setValue( solver.knob("focalLength").getValueAt(frame) ) 438 camera.knob("haperture").setValue( solver.knob("aperture").getValueAt(frame,0) ) 439 camera.knob("vaperture").setValue( solver.knob("aperture").getValueAt(frame,1) ) 440 camera.knob("translate").setValue( solver.knob("camTranslate").getValueAt(frame) ) 441 camera.knob("rotate").setValue( solver.knob("camRotate").getValueAt(frame) ) 442 camera.setXYpos(m + w*i - int(sw/2), y + 3*w) 443 card = nuke.createNode("Card", '', False) 444 card.setSelected(False) 445 card.knob("lens_in_focal").setExpression( camera.name() + ".focal" ) 446 card.knob("lens_in_haperture").setExpression( camera.name() + ".haperture" ) 447 card.knob("translate").setExpression( camera.name() + ".translate" ) 448 card.knob("rotate").setExpression( camera.name() + ".rotate" ) 449 card.knob("z").setExpression( "parent.z" ) 450 card.setInput(0, hold) 451 card.setXYpos(m + w*i - int(w/2), y + 2*w) 452 scene.setInput(i*2,camera) 453 scene.setInput(i*2+1,card) 454 scene.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y + 4*w) 455 group.end() 456 group.setInput(0,solver) 457 return group
458 459
460 -def _copyKnob(fromNode, fromKnobName, toNode, toKnobName, link):
461 fromKnob = fromNode[fromKnobName] 462 toKnob = toNode[toKnobName] 463 if link: 464 toKnob.setExpression(fromKnob.fullyQualifiedName()) 465 else: 466 toKnob.fromScript(fromKnob.toScript(False))
467
468 -def _copyKnobIdx(fromNode, fromKnobName, fromKnobIdx, toNode, toKnobName, toKnobIdx, link):
469 fromKnob = fromNode[fromKnobName] 470 toKnob = toNode[toKnobName] 471 if link: 472 toKnob.setExpression(fromKnob.fullyQualifiedName() + "." + str(fromKnobIdx), toKnobIdx) 473 else: 474 if fromKnob.isAnimated(fromKnobIdx): 475 if fromKnob.hasExpression(fromKnobIdx): 476 toKnob.setExpression(fromKnob.animation(fromKnobIdx).expression(), toKnobIdx) 477 else: 478 toKnob.copyAnimation(toKnobIdx, fromKnob.animation(fromKnobIdx)) 479 else: 480 toKnob.setValue(fromKnob.getValue(fromKnobIdx), toKnobIdx)
481 482
483 -def _clearSelection():
484 nuke.selectAll() 485 nuke.invertSelection()
486 487 488 # 489 # Camera Film Back Preset Functions 490 # 491
492 -def setKnobToPreset(cameraTracker, selectedPresetIdx):
493 """Finds the index of the preset knob and sets the film back size knob accordingly.""" 494 filmbackSizeKnob = cameraTracker['filmBackSize'] 495 selectedFilmbackSize = nukescripts.camerapresets.getFilmBackSize(selectedPresetIdx) 496 filmbackSizeKnob.setValue( selectedFilmbackSize[0], 0 ) 497 filmbackSizeKnob.setValue( selectedFilmbackSize[1], 1 ) 498 499 # Making sure the film back units are set to 'mm' 500 filmBackUnits = cameraTracker['filmBackUnits'] 501 filmBackUnits.setValue(0)
502 503 504 # 505 # Callbacks 506 # 507
508 -def cameratrackerCreateCallback():
509 populateExportMenu(nuke.thisNode()) 510 populateFilmBackPresets(nuke.thisNode())
511 512 513 if not nuke.env['assist']: 514 nuke.addOnCreate(cameratrackerCreateCallback, nodeClass='CameraTracker') 515 nuke.addOnCreate(cameratrackerCreateCallback, nodeClass='CameraTracker1_0') 516 517 # 518 # Track import and export functions 519 # 520
521 -class LinkableImportPanel( nukescripts.PythonPanel ):
522 """ 523 Modal dialog for selecting a Linkable node in the script. 524 525 The following class creates a modal dialog with one UI element: an enum of nodes 526 that derive from the LinkableI class. (The LinkableI interface allows us to easily query and import 527 2D data from a variety of sources, particularly the Tracker node.) The user then selects their source node 528 to import the data from. Once Ok'ed, the code below creates a new user track for each LinkableInfo object 529 returned in the node.linkableKnobs() function with an XY co-ordinate, and then copies each animation curve 530 entry. 531 """ 532
533 - def __init__( self ):
534 nukescripts.PythonPanel.__init__( self, "Select Tracker to Import From", "uk.co.thefoundry.FramePanel" ) 535 # Show a list of trackers in the project. 536 self._trackers = [n.name() for n in nuke.allNodes() if n.linkableKnobs(nuke.KnobType.eXYKnob)] 537 self._tracker = nuke.Enumeration_Knob( "trackers", "tracker node", self._trackers ) 538 self._tracker.setTooltip( "The Tracker node to import from." ) 539 self.addKnob( self._tracker )
540 541
542 - def showModalDialog( self, dstNode ):
543 result = nukescripts.PythonPanel.showModalDialog( self ) 544 if result: 545 srcNode = self._tracker.value() 546 return srcNode 547 return None
548
549 -def _copyLinkableXYAnimCurve(linkableSrc, linkableDst):
550 """Copies the x, y animation curves from one XYKnob linkable object to another.""" 551 linkKnobSrc = linkableSrc.knob() 552 linkKnobDst = linkableDst.knob() 553 linkableSrcIndices = linkableSrc.indices() 554 linkableDstIndices = linkableDst.indices() 555 556 # Setting enabled 557 linkKnobDst.setValue(linkableSrc.enabled(), 0) 558 559 # Clearing the animation curves of the locatior x and y curve 560 linkKnobDst.clearAnimated(int(linkableDstIndices[0])) 561 linkKnobDst.clearAnimated(int(linkableDstIndices[1])) 562 563 # Now setting x and y positions 564 for i in xrange(0, linkKnobSrc.getNumKeys(int(linkableSrcIndices[0]))): 565 t = linkKnobSrc.getKeyTime(i, int(linkableSrcIndices[0])) 566 567 # Because of a bug in the tableknob, before setting the first key/value, we need to re-enable setAnimated. 568 # See below for more details. 569 if i == 0: 570 linkKnobDst.setAnimated(int(linkableDstIndices[0])) 571 linkKnobDst.setAnimated(int(linkableDstIndices[1])) 572 573 # Finally setting the key/values 574 linkKnobDst.setValueAt( linkKnobSrc.getValueAt(t, int(linkableSrcIndices[0])), t, int(linkableDstIndices[0]) ) 575 linkKnobDst.setValueAt( linkKnobSrc.getValueAt(t, int(linkableSrcIndices[1])), t, int(linkableDstIndices[1]) ) 576 577 # Because of the setAnimation bug which always creates a key at 0, we need to explicitly check and remove it. 578 if i == 0 and t != 0: 579 linkKnobDst.removeKeyAt(0, int(linkableDstIndices[0])) 580 linkKnobDst.removeKeyAt(0, int(linkableDstIndices[1]))
581 582
583 -def importTracker(cameraTracker):
584 """Import data from a Tracker node into the CameraTracker node.""" 585 # This is the entry point where we bring up our modal dialog. 586 strSrcNode = LinkableImportPanel().showModalDialog(cameraTracker) 587 if not strSrcNode: 588 return 589 590 # First we start by getting the nodes. 591 srcNode = nuke.toNode(strSrcNode) 592 # And from the nodes, we get our LinkableInfo objects. 593 linkablesSrc = srcNode.linkableKnobs(nuke.KnobType.eXYKnob) 594 linkablesDst = cameraTracker.linkableKnobs(nuke.KnobType.eXYKnob) 595 596 # The following 'startIndex' is used to determine the index of the newly 597 # created user track below. For example, if there are already 10 user tracks, the 598 # next index of the user track we create will be at 10. 599 startIndex = len(linkablesDst) 600 601 # First create new user track for each valid track 602 numValidTracks = 0 603 604 for linkableSrc in linkablesSrc: 605 linkableSrcIndices = linkableSrc.indices() 606 607 # Make sure our source linkable has two indices, one for each co-ordinate. 608 if len(linkableSrcIndices) != 2: 609 continue 610 611 # Here we're manually executing a button, when really 612 # we should be using the 'createLinkable' function in the NDK. 613 # When this code is made into a NukeScript file, we should really just add 614 # 'createLinkable' to PythonObject. 615 cameraTracker['addUserTrack'].execute() 616 617 # We now grab our destination LinkableInfo, our new user track and copy the anims. 618 linkablesDst = cameraTracker.linkableKnobs(nuke.KnobType.eXYKnob) 619 620 if startIndex >= len(linkablesDst): 621 continue; 622 623 linkableDst = linkablesDst[startIndex] 624 625 _copyLinkableXYAnimCurve(linkableSrc, linkableDst) 626 627 # Incrementing the startIndex to say where to find the next user track. 628 startIndex = startIndex + 1 629 630 # Lastly, copying the 'enabled' status of the track. 631 trackEnabled = linkableSrc.enabled() 632 cameraTrackKnob = linkableDst.knob() 633 cameraTrackIndices = linkableDst.indices() 634 if len(cameraTrackIndices) != 2: 635 continue 636 637 # The following line derives the 'enabled' column index. Until the TableKnob 638 # has proper Python bindings we must hardcode these index offsets. 639 cameraTrackEnabledIdx = cameraTrackIndices[0] - 2 640 cameraTrackKnob.setValue( trackEnabled, int(cameraTrackEnabledIdx) )
641
642 -def exportTracker(cameraTracker):
643 """Export data from the CameraTracker node into a Tracker node.""" 644 tracker = nuke.createNode('Tracker4', '', True) 645 x = cameraTracker.xpos() 646 y = cameraTracker.ypos() 647 w = cameraTracker.screenWidth() 648 h = cameraTracker.screenHeight() 649 m = int(x + w/2) 650 tracker.setXYpos(m - int(tracker.screenWidth()/2), y + w) 651 652 # And from the nodes, we get our LinkableInfo objects. 653 linkablesSrc = cameraTracker.linkableKnobs(nuke.KnobType.eXYKnob) 654 linkablesDst = tracker.linkableKnobs(nuke.KnobType.eXYKnob) 655 656 # The following 'startIndex' is used to determine the index of the newly 657 # created user track below. For example, if there are already 10 user tracks, the 658 # next index of the user track we create will be at 10. 659 startIndex = len(linkablesDst) 660 # First create new track for each valid user track 661 numValidTracks = 0 662 # Create an empty dict 663 trackEnabledDict = {} 664 for linkableSrc in linkablesSrc: 665 linkableSrcIndices = linkableSrc.indices() 666 667 # Make sure our source linkable has two indices, one for each co-ordinate. 668 if len(linkableSrcIndices) < 2: 669 continue 670 671 # Here we're manually executing a button, when really 672 # we should be using the 'createLinkable' function in the NDK. 673 # When this code is made into a NukeScript file, we should really just add 674 # 'createLinkable' to PythonObject. 675 tracker['add_track'].execute() 676 # We now grab our destination LinkableInfo, our new user track and copy the anims. 677 linkablesDst = tracker.linkableKnobs(nuke.KnobType.eXYKnob) 678 679 if startIndex >= len(linkablesDst): 680 continue; 681 682 linkableDst = linkablesDst[startIndex] 683 684 _copyLinkableXYAnimCurve(linkableSrc, linkableDst) 685 686 # Adding to the dict for later table manipulation. 687 trackEnabledDict[linkableDst] = linkableSrc.enabled() 688 689 # Incrementing the startIndex to say where to find the next user track. 690 startIndex = startIndex + 1 691 692 # We need to do a bit more to make the exported tracks nicer. In particular, 693 # the enabled flags between user and exported tracks must be respected, 694 # and the target offset_x and offset_y fields must not be keyed. This has 695 # to be done as a separate loop as there may be sync issues. 696 for linkableDst, userTrackEnabled in trackEnabledDict.iteritems(): 697 trackKnob = linkableDst.knob() 698 trackIndices = linkableDst.indices() 699 if len(trackIndices) != 2: 700 continue 701 702 # The following three lines are deriving the indices of the 'enabled' and 703 # the 'offset_x' and 'offset_y' columns in the Tracker. Until the TableKnob 704 # has proper Python bindings we must hardcode these index offsets. 705 trackEnabledIdx = trackIndices[0] - 2 706 trackOffsetXIdx = trackIndices[1] + 1 707 trackOffsetYIdx = trackIndices[1] + 2 708 trackKnob.clearAnimated( int(trackOffsetXIdx) ) 709 trackKnob.clearAnimated( int(trackOffsetYIdx) ) 710 trackKnob.clearAnimated( int(trackEnabledIdx) ) 711 trackKnob.setValue( userTrackEnabled, int(trackEnabledIdx) ) 712 # A second clearAnimated is needed as the above setValue seems to create a key. 713 trackKnob.clearAnimated( int(trackEnabledIdx) )
714 715
716 -def importTracks(cameraTracker):
717 """Utility function that collects the filename to be used in importing tracks from a Shake formatted file.""" 718 filename = nuke.getClipname("Import File") 719 if filename is None: 720 filename = "" 721 722 cameraTracker.knob("tracksFile").fromScript( filename )
723 724
725 -def exportTracks(cameraTracker):
726 """Utility function that collects the filename to be used in exporting tracks from a Shake formatted file.""" 727 filename = nuke.getClipname("Export File") 728 if filename is None: 729 filename = "" 730 731 cameraTracker.knob("tracksFile").fromScript( filename )
732