SHARE
TWEET

fgshooter_uri.py

oracio Feb 15th, 2012 168 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # written by Brenton Rayner at The Mill (www.themill.com)
  2. # fgshooter.py
  3. # version 1.0.1
  4. # released 1/7/12
  5. # Edited by Uri T.: 2/10/12
  6.  
  7. import pymel.core as pm
  8. import logging
  9.  
  10. log = logging.getLogger("fgshooter")
  11.  
  12.  
  13. def currentCamera(render_cam):
  14.     '''
  15.    Create an fgshooter camera at same location as the render camera.
  16.    '''
  17.     # Duplicate the render camera and make sure the duplicate is not renderable.
  18.     fg_cam = pm.duplicate(render_cam, rr=True, name="fgshooterCamera")[0]
  19.     fg_cam_shape = fg_cam.getShape()
  20.     fg_cam_shape.renderable.set(False)
  21.     # Change the fgshooter camera's wireframe color.
  22.     pm.color(fg_cam, ud=2)
  23.  
  24.     # Find the render camera's transfrom and parent the fgshooter to it.
  25.     render_cam_transform = render_cam.listRelatives(p=True, type="transform")[0]
  26.     pm.parent(fg_cam, render_cam_transform)
  27.  
  28.     # Pass aperture and aspect information with the fgshooter camera's scale.
  29.     aperture = render_cam.horizontalFilmAperture.get()
  30.     aspect = aperture / pm.Attribute("defaultResolution.deviceAspectRatio").get()
  31.     fg_cam.scaleX.set(aperture)
  32.     fg_cam.scaleY.set(aspect)
  33.  
  34.     # Connect the render camera's focal length to the fgshooter since it could be animated.
  35.     multiply_divide = pm.createNode("multiplyDivide")
  36.     multiply_divide.input2X.set(0.03937)
  37.     render_cam.focalLength >> multiply_divide.input1X
  38.     multiply_divide.outputX >> fg_cam.scaleZ
  39.  
  40.     return fg_cam_shape
  41.  
  42. def fixedCamera(render_cam, frame):
  43.     '''
  44.    Create an fgshooter camera fixed at the render camera's position for a spcified frame.
  45.    '''
  46.     # Not the best way to get the world position and rotation values out of the render_camera... but it works.
  47.     # Pymel's matrix.rotate and matrix.getRotation() seem to be broken.
  48.     tmp_node = pm.createNode("decomposeMatrix")
  49.     render_cam.worldMatrix >> tmp_node.inputMatrix
  50.     frame_position = [tmp_node.outputTranslateX.get(time = frame),
  51.                       tmp_node.outputTranslateY.get(time = frame),
  52.                       tmp_node.outputTranslateZ.get(time = frame)]
  53.     frame_rotation = [tmp_node.outputRotateX.get(time = frame),
  54.                       tmp_node.outputRotateY.get(time = frame),
  55.                       tmp_node.outputRotateZ.get(time = frame)]
  56.     pm.delete(tmp_node)
  57.  
  58.     # Create the fixed camera. Change it's wireframe color.
  59.     fg_cam, fg_cam_shape = pm.camera(name="fgshooterCamera", position=frame_position, rotation=frame_rotation)
  60.     pm.color(fg_cam, ud=2)
  61.  
  62.     # Adjust the fgshooter camera's scale to pass aperture, aspect, and focal length information.
  63.     aperture = render_cam.horizontalFilmAperture.get(time = frame)
  64.     aspect = aperture / pm.Attribute("defaultResolution.deviceAspectRatio").get(time = frame)
  65.     focal = 0.03937 * render_cam.focalLength.get(time = frame)
  66.  
  67.     fg_cam.scaleX.set(aperture)
  68.     fg_cam.scaleY.set(aspect)
  69.     fg_cam.scaleZ.set(focal)
  70.  
  71.     return fg_cam_shape
  72.  
  73. def offsetCamera(render_cam, offset):
  74.     '''
  75.    Create an fgshooter camera offset from the render camera by a few frames.
  76.    '''
  77.     # Create camera and change wireframe color.
  78.     fg_cam, fg_cam_shape = pm.camera(name = "fgshooterCamera")
  79.     pm.color(fg_cam, ud=2)
  80.  
  81.     # Create all the connection nodes we are going to need.
  82.     decompose_matrix = pm.createNode("decomposeMatrix")
  83.     multiply_divide = pm.createNode("multiplyDivide")
  84.     frame_cache_tx = pm.createNode("frameCache")
  85.     frame_cache_ty = pm.createNode("frameCache")
  86.     frame_cache_tz = pm.createNode("frameCache")
  87.     frame_cache_rx = pm.createNode("frameCache")
  88.     frame_cache_ry = pm.createNode("frameCache")
  89.     frame_cache_rz = pm.createNode("frameCache")
  90.     frame_cache_fl = pm.createNode("frameCache")
  91.  
  92.     # Connect all of those nodes to the render camera.
  93.     render_cam.worldMatrix >> decompose_matrix.inputMatrix
  94.     decompose_matrix.outputTranslateX >> frame_cache_tx.stream
  95.     decompose_matrix.outputTranslateY >> frame_cache_ty.stream
  96.     decompose_matrix.outputTranslateZ >> frame_cache_tz.stream
  97.     decompose_matrix.outputRotateX >> frame_cache_rx.stream
  98.     decompose_matrix.outputRotateY >> frame_cache_ry.stream
  99.     decompose_matrix.outputRotateZ >> frame_cache_rz.stream
  100.     render_cam.focalLength >> frame_cache_fl.stream
  101.  
  102.     # If the offset is positive, use the future attribute.
  103.     if offset > 0:
  104.         # Workaround for a pymel bug
  105.         offset = ".future[" + str(offset) +"]"
  106.         pm.Attribute(frame_cache_tx + offset) >> fg_cam.translateX
  107.         pm.Attribute(frame_cache_ty + offset) >> fg_cam.translateY
  108.         pm.Attribute(frame_cache_tz + offset) >> fg_cam.translateZ
  109.         pm.Attribute(frame_cache_rx + offset) >> fg_cam.rotateX
  110.         pm.Attribute(frame_cache_ry + offset) >> fg_cam.rotateY
  111.         pm.Attribute(frame_cache_rz + offset) >> fg_cam.rotateZ
  112.         pm.Attribute(frame_cache_fl + offset) >> multiply_divide.input1X
  113.  
  114.     # If the offset is negative, use the past attribute.
  115.     else:
  116.         offset = -offset
  117.         frame_cache_tx.past[offset] >> fg_cam.translateX
  118.         frame_cache_ty.past[offset] >> fg_cam.translateY
  119.         frame_cache_tz.past[offset] >> fg_cam.translateZ
  120.         frame_cache_rx.past[offset] >> fg_cam.rotateX
  121.         frame_cache_ry.past[offset] >> fg_cam.rotateY
  122.         frame_cache_rz.past[offset] >> fg_cam.rotateZ
  123.         frame_cache_fl.past[offset] >> multiply_divide.input1X
  124.  
  125.     # Pass aperture, aspect, and focal length infromation with the fgshooter camera's scale.
  126.     # Focal length is connected because it could be animated.
  127.     aperture = render_cam.horizontalFilmAperture.get()
  128.     aspect = aperture / pm.Attribute("defaultResolution.deviceAspectRatio").get()
  129.     multiply_divide.input2X.set(0.03937)
  130.  
  131.     fg_cam.scaleX.set(aperture)
  132.     fg_cam.scaleY.set(aspect)
  133.     multiply_divide.outputX >> fg_cam.scaleZ
  134.  
  135.     return fg_cam_shape
  136.  
  137. def resetFgShooter(fg_shooter):
  138.     '''
  139.    Deletes the fgshooter cameras and node trees connected to the mip_fgshooter shader.
  140.    '''
  141.     # The hip bone is connected to the thigh bone...
  142.     nodes = set()
  143.     for cam in fg_shooter.listConnections(type="camera"):
  144.         for md in cam.listConnections(type="multiplyDivide"):
  145.             for fc in md.listConnections(type="frameCache"):
  146.                 nodes.add(fc)
  147.             nodes.add(md)
  148.         for fc in cam.listConnections(type="frameCache"):
  149.             for dm in fc.listConnections(type="decomposeMatrix"):
  150.                 nodes.add(dm)
  151.             nodes.add(fc)
  152.         for uc in cam.listConnections(type="unitConversion"):
  153.             for fc in uc.listConnections(type="frameCache"):
  154.                 for dm in fc.listConnections(type="decomposeMatrix"):
  155.                     nodes.add(dm)
  156.                 nodes.add(fc)
  157.             nodes.add(uc)
  158.         nodes.add(cam)
  159.     pm.delete(nodes)
  160.  
  161. def getFgShooter(render_cam):
  162.     '''
  163.    Finds or creates a mip_fgshooter shader on the render camera.
  164.    '''
  165.     removeFgShooters()
  166.  
  167.     # We have not found a mip_fgshooter. Create one and connect it to the utilities.
  168.     mip_fgshooter = pm.createNode("mip_fgshooter")
  169.     mip_fgshooter.message.connect("defaultRenderUtilityList1.utilities", na=True)
  170.  
  171.     # We also need to connect the mip_fgshooter to our camera.
  172.     if render_cam.miLensShader.isConnected() or render_cam.miLensShaderList.listConnections():
  173.         if render_cam.miLensShaderList.get(mi=True) is not None:
  174.             for index in render_cam.miLensShaderList.get(mi=True):
  175.                 if not render_cam.miLensShaderList[index].listConnections():
  176.                     pm.mel.removeMultiInstance(render_cam.miLensShaderList[index])
  177.         new_index = 0
  178.         if render_cam.miLensShaderList.get(mi=True):
  179.             new_index = render_cam.miLensShaderList.get(mi=True)[-1] + 1
  180.         mip_fgshooter.message >> render_cam.miLensShaderList[new_index]
  181.     else:
  182.         mip_fgshooter.message >> render_cam.miLensShader
  183.     return mip_fgshooter
  184.  
  185. def getRenderCamera():
  186.     '''
  187.    Find the render camera. If the render camera is a default camera, return None.
  188.    '''
  189.     render_cam = None
  190.    
  191.     '''
  192.    # Do not include the default cameras.
  193.    default_cameras = ["frontShape", "perspShape", "sideShape", "topShape"]
  194.    cameras = pm.ls(cameras=True)
  195.    for cam in cameras:
  196.        if cam.renderable.get() and cam.name() not in default_cameras:
  197.            log.info(cam)
  198.            render_cam = cam
  199.            break
  200.    '''
  201.     # Uri: Get the render camera from selection. You need to select only one camera
  202.     selected = pm.ls(sl=True)
  203.     selected_length = len(selected)
  204.     if selected_length == 1:
  205.         try:
  206.             selected_obj = pm.nt.Camera(selected[0].getShape())
  207.             default_cameras = ["frontShape", "perspShape", "sideShape", "topShape"]
  208.             if selected_obj not in default_cameras:
  209.                 cam_check = pm.ls( 'fgshooter*', sl=True )
  210.                 if len(cam_check) == 0:
  211.                     render_cam = selected_obj
  212.         except:
  213.             pass
  214.     return render_cam
  215.  
  216. def removeFgShooters():
  217.     '''
  218.    Remove all of the mip_fgshooter shaders as well as the fgshooter cameras and node trees.
  219.    '''
  220.     fg_shooters = pm.ls(type = "mip_fgshooter")
  221.     # Uri: get the render camera
  222.     render_cam = getRenderCamera()
  223.     for fg_shooter in fg_shooters:
  224.         # Uri: check if the selected camera is connected to the mip_fgshooter
  225.         if render_cam is None:
  226.             log.warning("Please select one render camera.")
  227.             break    
  228.         listCon = pm.listConnections(fg_shooter, d=True, s=False)
  229.         for aCon in listCon:
  230.            
  231.             if pm.nodeType(aCon) == 'transform':
  232.                 if render_cam == aCon.getShape():
  233.                     transform = fg_shooter.trans
  234.  
  235.                     # Remove all the fgshooter cameras and node trees!
  236.                     resetFgShooter(transform)
  237.  
  238.                     # Delete the mip_fgshooter shader!
  239.                     pm.delete(fg_shooter)
  240.                 break
  241.  
  242. def createFgShooters(frames=[], offsets=[], current_camera=True):
  243.     '''
  244.    Create fgshooter cameras for the render camera. Cameras can be fixed, current, or offset.
  245.    '''
  246.     # Ensure that decomposeMatrix is loaded.
  247.     if not pm.pluginInfo("decomposeMatrix", q=True, loaded=True):
  248.         log.info("Loading \"decomposeMatrix\" plugin.")
  249.         pm.loadPlugin("decomposeMatrix")
  250.  
  251.     # Make sure there are no duplicate frames or offsets.
  252.     frames = set(frames)
  253.     offsets = set(offsets)
  254.    
  255.     # Get the render Camera.
  256.     render_cam = getRenderCamera()
  257.     if render_cam is None:
  258.         log.warning("Please select one render camera.")
  259.         return None
  260.    
  261.     # Get the mip_fgshooter shader connected to the render camera.
  262.     fg_shooter = getFgShooter(render_cam)
  263.     transform = fg_shooter.trans
  264.    
  265.     index = 0
  266.  
  267.     # Create current fgshooter camera.
  268.     if current_camera:
  269.         fg_cam = currentCamera(render_cam)
  270.         fg_cam.worldMatrix >> transform[index]
  271.         index += 1
  272.  
  273.     # Create fixed fgshooter cameras.
  274.     for frame in frames:
  275.         fg_cam = fixedCamera(render_cam, frame)
  276.         fg_cam.worldMatrix >> transform[index]
  277.         index += 1
  278.  
  279.     # Create offset fgshooter cameras.
  280.     for offset in offsets:
  281.         if not isinstance(offset, int):
  282.             log.warning("Offsets must be intergers. Skipping: %s", offset)
  283.             continue
  284.         if offset:
  285.             fg_cam = offsetCamera(render_cam, offset)
  286.             fg_cam.worldMatrix >> transform[index]
  287.             index += 1
  288. def getMultiAttr(attribute):
  289.     '''
  290.    Returns the multi attribtues for a multi attribute.
  291.    '''
  292.     multi = []
  293.     count = attribute.evaluateNumElements()
  294.     for i in range(count):
  295.         multi.append(attribute.elementByPhysicalIndex(i))
  296.     return multi
  297.  
  298.  
  299. class ui():
  300.     def __init__(self):
  301.         self.window_title = "fgshooter window"
  302.         self.window_name = "fgshooter_window"
  303.        
  304.         # Default fgshooter values.
  305.         self.render_camera = True
  306.         self.stationary_frames = [0.0, 12.0, 24.0]
  307.         self.offset_frames = []
  308.         self.create(3, 0)
  309.    
  310.     def create(self, stationary_count, offset_count):
  311.         '''
  312.        Create the fgshooter window.
  313.        '''
  314.         if pm.window(self.window_name, exists=True):
  315.             pm.deleteUI(self.window_name)
  316.         pm.window(self.window_name, title=self.window_title)
  317.        
  318.         main_form = pm.formLayout(numberOfDivisions=2)
  319.         self.column = pm.columnLayout(adjustableColumn=True)
  320.        
  321.         # Render Camera
  322.         self.render_camera_field = pm.checkBoxGrp(label="Include Render Camera", value1=self.render_camera, changeCommand=self.updateRenderCamera)
  323.                
  324.         # Stationary Cameras
  325.         pm.separator(height=20, style="in")
  326.        
  327.         pm.rowLayout(numberOfColumns=3, columnWidth3=(140, 80, 80), columnAlign=(1, 'right'), columnAttach3=("right", "both", "both"))
  328.         pm.text("Stationary Cameras")
  329.         self.stationary_field = pm.intField(value=stationary_count)
  330.         pm.button(label="Update", height=22, command=self.update)
  331.         pm.setParent('..')
  332.        
  333.         self.stationary = []
  334.         i = 0
  335.         while i < stationary_count:
  336.             self.stationary.append(pm.floatFieldGrp(value1=self.stationary_frames[i], label="frame"))
  337.             i += 1
  338.        
  339.         # Offset Cameras
  340.         pm.separator(height=20, style="in")
  341.        
  342.         pm.rowLayout(numberOfColumns=3, columnWidth3=(140, 80, 80), columnAlign=(1, 'right'), columnAttach3=("right", "both", "both"))
  343.         pm.text("Offset Cameras")
  344.         self.offset_field = pm.intField(value=offset_count)
  345.         pm.button(label="Update", height=22, command=self.update)
  346.         pm.setParent('..')
  347.        
  348.         self.offset = []
  349.         i = 0
  350.         while i < offset_count:
  351.             self.offset.append(pm.intFieldGrp(value1=self.offset_frames[i], label="frame offset"))
  352.             i += 1
  353.        
  354.         pm.setParent('..')
  355.        
  356.         # remove/apply buttons        
  357.         self.remove_button = pm.button(label="Remove from Selected", height=30, command=self.remove)
  358.         self.apply_button = pm.button(label="Apply / Refresh", height=30, command=self.apply)
  359.        
  360.         pm.formLayout(main_form, edit=True, attachForm=[(self.column, "top", 2),(self.column, "left", 2),(self.column, "right", 2), (self.remove_button, "bottom", 2), (self.remove_button, "left", 2), (self.apply_button, "bottom", 2), (self.apply_button, "right", 2)], attachControl=(self.remove_button, "right", 1, self.apply_button), attachPosition=[ (self.remove_button, "right", 0, 1), (self.apply_button, "left", 1, 1)] )
  361.        
  362.         pm.setParent('..')
  363.         pm.showWindow()
  364.    
  365.     def updateRenderCamera(self, ignore):
  366.         self.render_camera = bool(self.render_camera_field.getValue1())
  367.    
  368.     def update(self, ignore):
  369.         # Update stationary.
  370.         self.find_stationary()
  371.         stationary_count = self.stationary_field.getValue()
  372.        
  373.         stationary_length = len(self.stationary_frames)
  374.         while stationary_length < stationary_count:
  375.             default_stationary = 0.0 + stationary_length * 12.0
  376.             self.stationary_frames.append(default_stationary)
  377.             stationary_length = len(self.stationary_frames)
  378.         self.stationary_frames = self.stationary_frames[:stationary_count]
  379.  
  380.         # Update offset
  381.         self.find_offset()
  382.         offset_count = self.offset_field.getValue()
  383.        
  384.         offset_length = len(self.offset_frames)
  385.         while offset_length < offset_count:
  386.             default_offset = offset_length / 2 + 1
  387.             if offset_length % 2:
  388.                 default_offset *= -1
  389.             self.offset_frames.append(default_offset)
  390.             offset_length = len(self.offset_frames)
  391.         self.offset_frames = self.offset_frames[:offset_count]
  392.        
  393.         # Refresh the UI
  394.         self.create(stationary_count, offset_count)
  395.    
  396.     def find_stationary(self):
  397.         self.stationary_frames = []
  398.         for stationary_camera in self.stationary:
  399.             self.stationary_frames.append(stationary_camera.getValue1())
  400.    
  401.     def find_offset(self):
  402.         self.offset_frames = []
  403.         for offset_camera in self.offset:
  404.             self.offset_frames.append(offset_camera.getValue1())
  405.    
  406.     def remove(self, ignore):
  407.         '''
  408.        Delete fgshooter cameras and all associated nodes.
  409.        '''
  410.         removeFgShooters()
  411.    
  412.     def apply(self, ignore):
  413.         '''
  414.        Create/refresh the fgshooter cameras and connectons.
  415.        '''
  416.         self.find_stationary()
  417.         self.find_offset()
  418.         createFgShooters(frames=self.stationary_frames, offsets=self.offset_frames, current_camera=self.render_camera)
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top