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('LensDistortion', '', 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, "distortion1", link) 356 _copyKnob(cameraTracker, "distortion2", lensDistort, "distortion2", link) 357 _copyKnob(cameraTracker, "distortionCenter", lensDistort, "distortionCenter", link) 358 _copyKnob(cameraTracker, "anamorphicSqueeze", lensDistort, "anamorphicSqueeze", link) 359 _copyKnob(cameraTracker, "asymmetricDistortion", lensDistort, "asymmetricDistortion", link) 360 lensDistort['invertDistortion'].setValue(invertDistortion) 361 _copyKnob(cameraTracker, "filter", lensDistort, "filter", False) 362 _copyKnob(cameraTracker, "cardScale", lensDistort, "cardScale", False) 363 _copyKnob(cameraTracker, "a", lensDistort, "a", False) 364 _copyKnob(cameraTracker, "b", lensDistort, "b", False) 365 _copyKnob(cameraTracker, "c", lensDistort, "c", False) 366 367 lensDistort.selectOnly() 368 369 return lensDistort
370 371
372 -def createCards(solver):
373 numCameras = solver["camTranslate"].getNumKeys() 374 375 if numCameras >= 100: 376 ok = nuke.ask("This will create %d cards, which may take some time.\nAre you sure?" % numCameras) 377 if not ok: 378 return 379 380 if numCameras == 0: 381 nuke.message("You can only create a card set when you have solved cameras.") 382 return 383 384 x = solver.xpos() 385 y = solver.ypos() 386 w = solver.screenWidth() 387 h = solver.screenHeight() 388 m = int(x + w*2) 389 link = False 390 linkKnob = solver.knob("linkOutput") 391 if linkKnob: 392 link = bool(linkKnob.getValue()) 393 exprStr = "[python {nuke.toNode('" + solver.fullName() +"')" 394 395 group = nuke.createNode("Group", '', False) 396 group.begin() 397 group.setName("Cards") 398 group.setXYpos(m + w, y + w) 399 if numCameras>0: 400 scene = nuke.createNode("Scene", '', False) 401 sw = scene.screenWidth() 402 sh = scene.screenHeight() 403 inImg = nuke.createNode("Input", '', False) 404 inImg.setName("img"); 405 inImg.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y) 406 out = nuke.createNode("Output", '', False) 407 out.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y + 5*w) 408 out.setInput(0, scene) 409 group.addKnob(nuke.Tab_Knob('cards','Cards')) 410 zDistKnob = nuke.Double_Knob('z','z') 411 zDistKnob.setRange(0,100) 412 zDistKnob.setTooltip("Cards are placed this far from origin. Use this to make a pan & tile dome of this radius.") 413 zDistKnob.setDefaultValue([1]) 414 group.addKnob(zDistKnob) 415 for i in xrange(numCameras): 416 frame = solver.knob("camTranslate").getKeyTime(i) 417 hold = nuke.createNode("FrameHold", '', False) 418 hold.setInput(0, inImg) 419 hold.knob("first_frame").setValue(frame) 420 hold.setXYpos(m + w*i - int(w/2), y + w) 421 camera = nuke.createNode("Camera", '', False) 422 camera.setInput(0,None) 423 if link: 424 camera.knob("focal").setExpression( exprStr + ".knob('focalLength').getValueAt(" + str(frame) + ")}]" ) 425 camera.knob("haperture").setExpression( exprStr + ".knob('aperture').getValueAt(" + str(frame) + ",0)}]" ) 426 camera.knob("vaperture").setExpression( exprStr + ".knob('aperture').getValueAt(" + str(frame) + ",1)}]" ) 427 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",0)}]",0 ) 428 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",1)}]",1 ) 429 camera.knob("translate").setExpression( exprStr + ".knob('camTranslate').getValueAt(" + str(frame) + ",2)}]",2 ) 430 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",0)}]",0 ) 431 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",1)}]",1 ) 432 camera.knob("rotate").setExpression( exprStr + ".knob('camRotate').getValueAt(" + str(frame) + ",2)}]",2 ) 433 else: 434 camera.knob("focal").setValue( solver.knob("focalLength").getValueAt(frame) ) 435 camera.knob("haperture").setValue( solver.knob("aperture").getValueAt(frame,0) ) 436 camera.knob("vaperture").setValue( solver.knob("aperture").getValueAt(frame,1) ) 437 camera.knob("translate").setValue( solver.knob("camTranslate").getValueAt(frame) ) 438 camera.knob("rotate").setValue( solver.knob("camRotate").getValueAt(frame) ) 439 camera.setXYpos(m + w*i - int(sw/2), y + 3*w) 440 card = nuke.createNode("Card", '', False) 441 card.setSelected(False) 442 card.knob("lens_in_focal").setExpression( camera.name() + ".focal" ) 443 card.knob("lens_in_haperture").setExpression( camera.name() + ".haperture" ) 444 card.knob("translate").setExpression( camera.name() + ".translate" ) 445 card.knob("rotate").setExpression( camera.name() + ".rotate" ) 446 card.knob("z").setExpression( "parent.z" ) 447 card.setInput(0, hold) 448 card.setXYpos(m + w*i - int(w/2), y + 2*w) 449 scene.setInput(i*2,camera) 450 scene.setInput(i*2+1,card) 451 scene.setXYpos(m + int(w*(numCameras-1)/2) - int(sw/2), y + 4*w) 452 group.end() 453 group.setInput(0,solver) 454 return group
455 456
457 -def _copyKnob(fromNode, fromKnobName, toNode, toKnobName, link):
458 fromKnob = fromNode[fromKnobName] 459 toKnob = toNode[toKnobName] 460 if link: 461 toKnob.setExpression(fromKnob.fullyQualifiedName()) 462 else: 463 toKnob.fromScript(fromKnob.toScript(False))
464 465
466 -def _clearSelection():
467 nuke.selectAll() 468 nuke.invertSelection()
469 470 471 # 472 # Camera Film Back Preset Functions 473 # 474
475 -def setKnobToPreset(cameraTracker, selectedPresetIdx):
476 """Finds the index of the preset knob and sets the film back size knob accordingly.""" 477 filmbackSizeKnob = cameraTracker['filmBackSize'] 478 selectedFilmbackSize = nukescripts.camerapresets.getFilmBackSize(selectedPresetIdx) 479 filmbackSizeKnob.setValue( selectedFilmbackSize[0], 0 ) 480 filmbackSizeKnob.setValue( selectedFilmbackSize[1], 1 ) 481 482 # Making sure the film back units are set to 'mm' 483 filmBackUnits = cameraTracker['filmBackUnits'] 484 filmBackUnits.setValue(0)
485 486 487 # 488 # Callbacks 489 # 490
491 -def cameratrackerCreateCallback():
492 populateExportMenu(nuke.thisNode()) 493 populateFilmBackPresets(nuke.thisNode())
494 495 496 if not nuke.env['assist']: 497 nuke.addOnCreate(cameratrackerCreateCallback, nodeClass='CameraTracker') 498 nuke.addOnCreate(cameratrackerCreateCallback, nodeClass='CameraTracker1_0') 499 500 # 501 # Track import and export functions 502 # 503
504 -class LinkableImportPanel( nukescripts.PythonPanel ):
505 """ 506 Modal dialog for selecting a Linkable node in the script. 507 508 The following class creates a modal dialog with one UI element: an enum of nodes 509 that derive from the LinkableI class. (The LinkableI interface allows us to easily query and import 510 2D data from a variety of sources, particularly the Tracker node.) The user then selects their source node 511 to import the data from. Once Ok'ed, the code below creates a new user track for each LinkableInfo object 512 returned in the node.linkableKnobs() function with an XY co-ordinate, and then copies each animation curve 513 entry. 514 """ 515
516 - def __init__( self ):
517 nukescripts.PythonPanel.__init__( self, "Select Tracker to Import From", "uk.co.thefoundry.FramePanel" ) 518 # Show a list of trackers in the project. 519 self._trackers = [n.name() for n in nuke.allNodes() if n.linkableKnobs(nuke.KnobType.eXYKnob)] 520 self._tracker = nuke.Enumeration_Knob( "trackers", "tracker node", self._trackers ) 521 self._tracker.setTooltip( "The Tracker node to import from." ) 522 self.addKnob( self._tracker )
523 524
525 - def showModalDialog( self, dstNode ):
526 result = nukescripts.PythonPanel.showModalDialog( self ) 527 if result: 528 srcNode = self._tracker.value() 529 return srcNode 530 return None
531
532 -def _copyLinkableXYAnimCurve(linkableSrc, linkableDst):
533 """Copies the x, y animation curves from one XYKnob linkable object to another.""" 534 linkKnobSrc = linkableSrc.knob() 535 linkKnobDst = linkableDst.knob() 536 linkableSrcIndices = linkableSrc.indices() 537 linkableDstIndices = linkableDst.indices() 538 539 # Setting enabled 540 linkKnobDst.setValue(linkableSrc.enabled(), 0) 541 542 # Clearing the animation curves of the locatior x and y curve 543 linkKnobDst.clearAnimated(int(linkableDstIndices[0])) 544 linkKnobDst.clearAnimated(int(linkableDstIndices[1])) 545 546 # Now setting x and y positions 547 for i in xrange(0, linkKnobSrc.getNumKeys(int(linkableSrcIndices[0]))): 548 t = linkKnobSrc.getKeyTime(i, int(linkableSrcIndices[0])) 549 550 # Because of a bug in the tableknob, before setting the first key/value, we need to re-enable setAnimated. 551 # See below for more details. 552 if i == 0: 553 linkKnobDst.setAnimated(int(linkableDstIndices[0])) 554 linkKnobDst.setAnimated(int(linkableDstIndices[1])) 555 556 # Finally setting the key/values 557 linkKnobDst.setValueAt( linkKnobSrc.getValueAt(t, int(linkableSrcIndices[0])), t, int(linkableDstIndices[0]) ) 558 linkKnobDst.setValueAt( linkKnobSrc.getValueAt(t, int(linkableSrcIndices[1])), t, int(linkableDstIndices[1]) ) 559 560 # Because of the setAnimation bug which always creates a key at 0, we need to explicitly check and remove it. 561 if i == 0 and t != 0: 562 linkKnobDst.removeKeyAt(0, int(linkableDstIndices[0])) 563 linkKnobDst.removeKeyAt(0, int(linkableDstIndices[1]))
564 565
566 -def importTracker(cameraTracker):
567 """Import data from a Tracker node into the CameraTracker node.""" 568 # This is the entry point where we bring up our modal dialog. 569 strSrcNode = LinkableImportPanel().showModalDialog(cameraTracker) 570 if not strSrcNode: 571 return 572 573 # First we start by getting the nodes. 574 srcNode = nuke.toNode(strSrcNode) 575 # And from the nodes, we get our LinkableInfo objects. 576 linkablesSrc = srcNode.linkableKnobs(nuke.KnobType.eXYKnob) 577 linkablesDst = cameraTracker.linkableKnobs(nuke.KnobType.eXYKnob) 578 579 # The following 'startIndex' is used to determine the index of the newly 580 # created user track below. For example, if there are already 10 user tracks, the 581 # next index of the user track we create will be at 10. 582 startIndex = len(linkablesDst) 583 584 # First create new user track for each valid track 585 numValidTracks = 0 586 587 for linkableSrc in linkablesSrc: 588 linkableSrcIndices = linkableSrc.indices() 589 590 # Make sure our source linkable has two indices, one for each co-ordinate. 591 if len(linkableSrcIndices) != 2: 592 continue 593 594 # Here we're manually executing a button, when really 595 # we should be using the 'createLinkable' function in the NDK. 596 # When this code is made into a NukeScript file, we should really just add 597 # 'createLinkable' to PythonObject. 598 cameraTracker['addUserTrack'].execute() 599 600 # We now grab our destination LinkableInfo, our new user track and copy the anims. 601 linkablesDst = cameraTracker.linkableKnobs(nuke.KnobType.eXYKnob) 602 603 if startIndex >= len(linkablesDst): 604 continue; 605 606 linkableDst = linkablesDst[startIndex] 607 608 _copyLinkableXYAnimCurve(linkableSrc, linkableDst) 609 610 # Incrementing the startIndex to say where to find the next user track. 611 startIndex = startIndex + 1 612 613 # Lastly, copying the 'enabled' status of the track. 614 trackEnabled = linkableSrc.enabled() 615 cameraTrackKnob = linkableDst.knob() 616 cameraTrackIndices = linkableDst.indices() 617 if len(cameraTrackIndices) != 2: 618 continue 619 620 # The following line derives the 'enabled' column index. Until the TableKnob 621 # has proper Python bindings we must hardcode these index offsets. 622 cameraTrackEnabledIdx = cameraTrackIndices[0] - 2 623 cameraTrackKnob.setValue( trackEnabled, int(cameraTrackEnabledIdx) )
624
625 -def exportTracker(cameraTracker):
626 """Export data from the CameraTracker node into a Tracker node.""" 627 tracker = nuke.createNode('Tracker4', '', True) 628 x = cameraTracker.xpos() 629 y = cameraTracker.ypos() 630 w = cameraTracker.screenWidth() 631 h = cameraTracker.screenHeight() 632 m = int(x + w/2) 633 tracker.setXYpos(m - int(tracker.screenWidth()/2), y + w) 634 635 # And from the nodes, we get our LinkableInfo objects. 636 linkablesSrc = cameraTracker.linkableKnobs(nuke.KnobType.eXYKnob) 637 linkablesDst = tracker.linkableKnobs(nuke.KnobType.eXYKnob) 638 639 # The following 'startIndex' is used to determine the index of the newly 640 # created user track below. For example, if there are already 10 user tracks, the 641 # next index of the user track we create will be at 10. 642 startIndex = len(linkablesDst) 643 # First create new track for each valid user track 644 numValidTracks = 0 645 # Create an empty dict 646 trackEnabledDict = {} 647 for linkableSrc in linkablesSrc: 648 linkableSrcIndices = linkableSrc.indices() 649 650 # Make sure our source linkable has two indices, one for each co-ordinate. 651 if len(linkableSrcIndices) < 2: 652 continue 653 654 # Here we're manually executing a button, when really 655 # we should be using the 'createLinkable' function in the NDK. 656 # When this code is made into a NukeScript file, we should really just add 657 # 'createLinkable' to PythonObject. 658 tracker['add_track'].execute() 659 # We now grab our destination LinkableInfo, our new user track and copy the anims. 660 linkablesDst = tracker.linkableKnobs(nuke.KnobType.eXYKnob) 661 662 if startIndex >= len(linkablesDst): 663 continue; 664 665 linkableDst = linkablesDst[startIndex] 666 667 _copyLinkableXYAnimCurve(linkableSrc, linkableDst) 668 669 # Adding to the dict for later table manipulation. 670 trackEnabledDict[linkableDst] = linkableSrc.enabled() 671 672 # Incrementing the startIndex to say where to find the next user track. 673 startIndex = startIndex + 1 674 675 # We need to do a bit more to make the exported tracks nicer. In particular, 676 # the enabled flags between user and exported tracks must be respected, 677 # and the target offset_x and offset_y fields must not be keyed. This has 678 # to be done as a separate loop as there may be sync issues. 679 for linkableDst, userTrackEnabled in trackEnabledDict.iteritems(): 680 trackKnob = linkableDst.knob() 681 trackIndices = linkableDst.indices() 682 if len(trackIndices) != 2: 683 continue 684 685 # The following three lines are deriving the indices of the 'enabled' and 686 # the 'offset_x' and 'offset_y' columns in the Tracker. Until the TableKnob 687 # has proper Python bindings we must hardcode these index offsets. 688 trackEnabledIdx = trackIndices[0] - 2 689 trackOffsetXIdx = trackIndices[1] + 1 690 trackOffsetYIdx = trackIndices[1] + 2 691 trackKnob.clearAnimated( int(trackOffsetXIdx) ) 692 trackKnob.clearAnimated( int(trackOffsetYIdx) ) 693 trackKnob.clearAnimated( int(trackEnabledIdx) ) 694 trackKnob.setValue( userTrackEnabled, int(trackEnabledIdx) ) 695 # A second clearAnimated is needed as the above setValue seems to create a key. 696 trackKnob.clearAnimated( int(trackEnabledIdx) )
697 698
699 -def importTracks(cameraTracker):
700 """Utility function that collects the filename to be used in importing tracks from a Shake formatted file.""" 701 filename = nuke.getClipname("Import File") 702 if filename is None: 703 filename = "" 704 705 cameraTracker.knob("tracksFile").fromScript( filename )
706 707
708 -def exportTracks(cameraTracker):
709 """Utility function that collects the filename to be used in exporting tracks from a Shake formatted file.""" 710 filename = nuke.getClipname("Export File") 711 if filename is None: 712 filename = "" 713 714 cameraTracker.knob("tracksFile").fromScript( filename )
715