def createRawObject(name, faces, verts, uvFaces, uvs): mm = mxs.mesh(numverts=len(verts)) oldUndo = mxs.execute("set undo off") try: mm.name = name for i in xrange(len(verts)): mxs.meshop.setvert( mm, i + 1, mxs.Point3(float(verts[i][0]), float(verts[i][1]), float(verts[i][2]))) mxs.convertTo(mm, mxs.PolyMeshObject) for face in faces: fPlus = [i + 1 for i in face] mxs.polyop.createPolygon(mm, fPlus) if uvs is not None: mxs.polyop.defaultMapFaces(mm, 1) mxs.polyop.setNumMapVerts(mm, 1, len(uvs), keep=False) mxs.polyop.setNumMapFaces(mm, 1, len(uvFaces), keep=False) for i in xrange(len(uvs)): mxs.polyop.setMapVert( mm, 1, i + 1, mxs.Point3(float(uvs[i][0]), float(uvs[i][1]), 0.0)) mxs.convertTo(mm, mxs.PolyMeshObject) for f, face in enumerate(uvFaces): fPlus = [i + 1 for i in face] mxs.polyop.setMapFace(mm, 1, f + 1, fPlus) finally: mxs.execute("set undo {}".format('on' if oldUndo else 'off')) return mm
def _addNativeController(self, name, group='', tpe=float, default=0.0): if not group: group = 'Custom_Attributes' types = {float: 'float', int: 'integer'} if tpe in [float, int] and isinstance(default, tpe): maxScript = """fn addAttributeToObject obj = ( attribute = attributes {group} ( parameters main ( {name} type:#{tpe} default:{default} ) ) CustAttributes.add obj attribute #Unique return obj.{name}.controller )""" mxs.execute( maxScript.format(name=name, group=group, tpe=types[tpe], default=default)) return mxs.addAttributeToObject(self._nativePointer) else: raise Exception('This method only support ints ') return None
def addProceduralShake(self): listController = mxs.getPropertyController( self._nativePointer.controller, 'rotation') print mxs.classOf(listController) if not mxs.classOf(listController) == mxs.rotation_list: listController = mxs.rotation_list() mxs.setPropertyController(self._nativePointer.controller, 'rotation', listController) noise = mxs.Noise_rotation() noise.frequency = 0.05 noise.fractal = False noise.seed = random.randint(0, 10000000) # Got lazy here, did not have time to find the Python way to do it. maxScript = """fn setNoiseControllerStrength noiseController x y z = ( noiseController.noise_strength = [x, y, z] )""" mxs.execute(maxScript) weirdFactor = 57.296 mxs.setNoiseControllerStrength(noise, 0.4 / weirdFactor, 0.4 / weirdFactor, 0.1 / weirdFactor) mxs.setPropertyController(listController, 'Available', noise) return True
def _connectStudiomaxSignal(self, connDef, cross3dSignal): """ \remarks Responsible for connecting a signal to studiomax """ # store the maxscript methods needed if connDef.callbackType == _ConnectionType.Viewport: signal = _STUDIOMAX_VIEWPORT_TEMPLATE % { 'cls': 'dispatch', 'function': connDef.function, 'signal': cross3dSignal } # Ensure that if the old signal existed it is removed before redefining it. # If function is undefined it will do nothing mxs.unregisterRedrawViewsCallback( getattr(mxs, 'blurfn_%s' % cross3dSignal)) mxs.execute(signal) mxs.registerRedrawViewsCallback( getattr(mxs, 'blurfn_%s' % cross3dSignal)) else: # Connect the callback self._addCallback(connDef, cross3dSignal) # Connect any associated callbacks using a diffrent ID name allows us to disconnect # this signal without affecting any direct connections to the associated callbacks for reqDef in connDef.associated: self._addCallback( reqDef, reqDef.signal, 'cross3dcallbacks_{}'.format(connDef.callback))
def _addNativeController(self, name, group='', tpe=float, default=0.0): if not group: group = 'Custom_Attributes' types = {float: 'float', int:'integer'} if tpe in [float, int] and isinstance(default, tpe): maxScript = """fn addAttributeToObject obj = ( attribute = attributes {group} ( parameters main ( {name} type:#{tpe} default:{default} ) ) CustAttributes.add obj attribute #Unique return obj.{name}.controller )""" mxs.execute(maxScript.format(name=name, group=group, tpe=types[tpe], default=default)) return mxs.addAttributeToObject(self._nativePointer) else: raise Exception('This method only support ints ') return None
def applyCache(self, path, transformIdentifier, propertiesIdentifier): # Applying the controller for transform. self._nativePointer.controller = mxs.Alembic_Xform(path=path, identifier=transformIdentifier) # Creating our own little MAXScript function for things unsupported in Py3dsMax. mxs.execute('fn setTimeController obj controller = (obj.controller.time.controller = controller)') # Creating the time controller. timeController = mxs.Float_Script(script='S') # Adding a script controller to alembic time. mxs.setTimeController(self._nativePointer, timeController) # Creating our own little MAXScript function things unsupported in Py3dsMax. maxScript = """fn setAlembicFloatController cam timeController = ( cam.{prop}.controller = Alembic_Float_Controller() cam.{prop}.controller.path = @"{path}" cam.{prop}.controller.identifier = @"{identifier}" cam.{prop}.controller.property = @"{propertyName}" cam.{prop}.controller.time.controller = timeController return cam.{prop}.controller.time.controller )""" # These are the properties we are going to want to load alembic data on. cameraType = self.cameraType() if cameraType == CameraType.Physical: # We need to create a dedicated radians FOV contoller as the regular FOV controller expects degrees. self._addNativeController('RadianFOV', group='Alembic') properties = {'Alembic.RadianFOV': 'horizontalFOV', 'clip_near': 'NearClippingPlane', 'clip_far': 'FarClippingPlane', 'focus_distance': 'FocusDistance'} else: properties = {'fov': 'horizontalFOV', 'nearclip': 'NearClippingPlane', 'farclip': 'FarClippingPlane', 'mpassEffect.focalDepth': 'FocusDistance'} for prop in properties: mxs.execute(maxScript.format(prop=prop, path=path, identifier=propertiesIdentifier, propertyName=properties[prop])) mxs.setAlembicFloatController(self._nativePointer, timeController) # FOV is going to be special for V-Ray camera. if cameraType == CameraType.Physical: # Building a script controller for the FOV in degrees. scriptController = mxs.float_script() scriptController.addtarget('RadianFOV', mxs.getPropertyController(self._nativePointer, "RadianFOV")) # Using the radians FOV controller reading the Alembic and converting to degrees. scriptController.script = 'radToDeg(RadianFOV)' mxs.setPropertyController(self._nativePointer, 'fov', scriptController) return True
def _nativeController( self, name ): # Checking inputs. if not isinstance(name, basestring): return None # Handling nested attributes. The try is here to avoid crashes. # If you do not believe it, try for yourself. maxScript = """fn attributeValue obj = ( value = undefined try value = obj.{path} catch() value )""" mxs.execute(maxScript.format(path=name)) if mxs.attributeValue(self._nativePointer) is not None: maxScript = """fn controller obj = ( obj.{path}.controller )""" mxs.execute(maxScript.format(path=name)) controller = mxs.controller(self._nativePointer) if controller is not None: return controller # Strip out controller references. name = name.replace('.controller', '') parent = self._nativePointer # look up the parent splt = name.split('.') controller = None parent = self._nativePointer # Start with the transform controller if necessary. if (splt[0] == 'transform'): parent = parent.controller controller = parent splt = splt[1:] for section in splt: controller = mxs.getPropertyController( parent, section ) if ( not controller ): return None parent = controller return controller
def _nativeController(self, name): # Checking inputs. if not isinstance(name, basestring): return None # Handling nested attributes. The try is here to avoid crashes. # If you do not believe it, try for yourself. maxScript = """fn attributeValue obj = ( value = undefined try value = obj.{path} catch() value )""" mxs.execute(maxScript.format(path=name)) if mxs.attributeValue(self._nativePointer) is not None: maxScript = """fn controller obj = ( obj.{path}.controller )""" mxs.execute(maxScript.format(path=name)) controller = mxs.controller(self._nativePointer) if controller is not None: return controller # Strip out controller references. name = name.replace('.controller', '') parent = self._nativePointer # look up the parent splt = name.split('.') controller = None parent = self._nativePointer # Start with the transform controller if necessary. if (splt[0] == 'transform'): parent = parent.controller controller = parent splt = splt[1:] for section in splt: controller = mxs.getPropertyController(parent, section) if (not controller): return None parent = controller return controller
def register( cls ): # clear the old defs cls.clear() # define the class cls.define() # create the maxscript for this class and run it data = { 'class': cls.__name__, 'name': cls.attrName(), 'classVersion': cls.__version__, 'parameters': '', 'controls': '' } # create the parameters paramstr = [] for param in cls._paramDefs.values(): paramstr.append( param.defString() ) data[ 'parameters' ] = '\n'.join( paramstr ) mscript = maxscript % data mxs.execute( mscript )
def _setNativeController( self, name, nativeController ): # strip out controller references name = name.replace( '.controller', '' ) # Handling nested attributes. try: maxScript = """fn attributeValue obj = ( return obj.{path} )""" mxs.execute(maxScript.format(path=name)) if mxs.attributeValue(self._nativePointer) is not None: maxScript = """fn setController obj ctrl = ( obj.{path}.controller = ctrl )""" mxs.execute(maxScript.format(path=name)) mxs.setController(self._nativePointer, nativeController) return True except RuntimeError: pass # look up the parent splt = name.split( '.' ) parent = self._nativePointer # start with the transform controller if necessary if ( splt[0] == 'transform' ): parent = parent.controller splt = splt[1:] for section in splt: parent = mxs.getPropertyController(parent, section) if ( not parent ): return False # set the property propname = splt[-1] try: mxs.setPropertyController( parent, propname, nativeController ) return True except: return False
def _setNativeController(self, name, nativeController): # strip out controller references name = name.replace('.controller', '') # Handling nested attributes. try: maxScript = """fn attributeValue obj = ( return obj.{path} )""" mxs.execute(maxScript.format(path=name)) if mxs.attributeValue(self._nativePointer) is not None: maxScript = """fn setController obj ctrl = ( obj.{path}.controller = ctrl )""" mxs.execute(maxScript.format(path=name)) mxs.setController(self._nativePointer, nativeController) return True except RuntimeError: pass # look up the parent splt = name.split('.') parent = self._nativePointer # start with the transform controller if necessary if (splt[0] == 'transform'): parent = parent.controller splt = splt[1:] for section in splt: parent = mxs.getPropertyController(parent, section) if (not parent): return False # set the property propname = splt[-1] try: mxs.setPropertyController(parent, propname, nativeController) return True except: return False
def getViewData(): """" Gets current viewport transform matrix and returns a list of position and rotation values """ data = [] vp = mxs.activeViewport viewTM = mxs.viewport.getTM() # We need to invert inverseViewTM = mxs.Inverse(viewTM) # MAKE THIS MORE EFFECTIVE; DONT CONVERT TO STRING AND BACK # get rotation data rot = inverseViewTM.rotationPart ea_rot_str = str( mxs.execute(str(rot) + " as EulerAngles")) # converting to euler angles ea_rot_xyz = ea_rot_str.split(" ")[ 1:] # split by space and skip "eulerAngles" ea_rot_xyz[2] = ea_rot_xyz[2][:-1] # then skip ")" at last element # order data to match persp view axes # default order of data 0-5 is: posX, posY, posZ, rotX, rotY, rotZ # http://forums.epicgames.com/threads/712799-Convert-3dsmax-rotation-to-Unreal-Rotation # UDK rotation order: pitch(rotY in Max), yaw(rotZ in Max), roll(rotX in Max) # pitch and roll are swapedp, and yaw is inverted # this works for PERSPECTIVE and CAMERA views, but only when we subtract 90 degrees from the x rotation later # the following order can be used for reordering from the default order (see above), # deprecated now as we built up data this way from the start now # return [ data[1], data[0], data[2], data[3], -data[5], data[4] ] # append position to data list # data.append(inverseViewTM.position.y) # data.append(inverseViewTM.position.x) # data.append(inverseViewTM.position.z) data.append(inverseViewTM.position.y) data.append(inverseViewTM.position.x) data.append(inverseViewTM.position.z) # append rotation to data list # TEMP CONVERSION TO UDK ROTATION VALUES HERE (MOVE TO UDKTRANSLATOR THEN) # data.append( (((float( ea_rot_xyz[0] )) - 90 ) * DegToUnrRot) % 65536 ) # data.append( (float( ea_rot_xyz[2] ) * -1 * DegToUnrRot) % 65536 ) # data.append( (float( ea_rot_xyz[1] ) * DegToUnrRot) % 65536 ) data.append((float(ea_rot_xyz[0])) - 90) data.append(float(ea_rot_xyz[2]) * -1) data.append(float(ea_rot_xyz[1])) # print data return data
def selectAdjacentEdges(obj, centers): mxs.execute(''' function meshCralwer_toBitArray thing = ( try ( return (thing as bitArray) ) catch () ) ''') selectModifier = mxs.edit_poly() mxs.addmodifier(obj, selectModifier) mcenters = sorted([i + 1 for i in centers]) vertBA = mxs.meshCralwer_toBitArray(mcenters) edgeBA = mxs.polyop.getEdgesUsingVert(obj, vertBA) emptyBA = mxs.meshCralwer_toBitArray([]) edge = mxs.execute("#edge") selectModifier.setEPolySelLevel(edge) selectModifier.setSelection(edge, emptyBA) selectModifier.select(edge, edgeBA)
def addChangeHandler(): """ Adds changehandler to the current selection in 3ds Max """ # first, reset list from max import objectWatcher objectWatcher.removeChangeHandler() # # add one changehandler for the whole selection to detect transform changes # # changeHandler = mxs.execute( "when transform selection changes handleAt:#redrawViews obj do ( python.exec(\"objectWatcher.onChanged( obj )\"))" ) if len(mxs.selection) > 0: global changeHandler changeHandler = mxs.execute( "when transform selection changes handleAt:#redrawViews do ( python.exec(\"objectWatcher.onChanged()\"))" ) print "OW: Adding change handler to selection" else: print "OW: Nothing selected, no changehandler added"
def setAllVerts(obj, newVerts): if mxs.classOf(obj) == mxs.XRefObject: obj = obj.actualBaseObject if mxs.classOf(obj) in [mxs.Editable_Poly, mxs.PolyMeshObject]: maxAll = mxs.execute('#all') maxPos = [ mxs.point3(float(i[0]), float(i[1]), float(i[2])) for i in newVerts ] mxs.polyop.setVert(obj, maxAll, maxPos) else: for i, v in enumerate(newVerts): mxs.setVert(obj, i + 1, *v) return True
def _connectStudiomaxSignal(self, connDef, cross3dSignal): """ \remarks Responsible for connecting a signal to studiomax """ # store the maxscript methods needed if connDef.callbackType == _ConnectionType.Viewport: signal = _STUDIOMAX_VIEWPORT_TEMPLATE % { 'cls':'dispatch', 'function':connDef.function, 'signal':cross3dSignal } # Ensure that if the old signal existed it is removed before redefining it. # If function is undefined it will do nothing mxs.unregisterRedrawViewsCallback(getattr(mxs, 'blurfn_%s' % cross3dSignal)) mxs.execute(signal) mxs.registerRedrawViewsCallback(getattr(mxs, 'blurfn_%s' % cross3dSignal)) else: # Connect the callback self._addCallback(connDef, cross3dSignal) # Connect any associated callbacks using a diffrent ID name allows us to disconnect # this signal without affecting any direct connections to the associated callbacks for reqDef in connDef.associated: self._addCallback(reqDef, reqDef.signal, 'cross3dcallbacks_{}'.format(connDef.callback))
def getViewData(): """" Gets current viewport transform matrix and returns a list of position and rotation values """ data = [] vp = mxs.activeViewport viewTM = mxs.viewport.getTM() # We need to invert inverseViewTM = mxs.Inverse(viewTM) # MAKE THIS MORE EFFECTIVE; DONT CONVERT TO STRING AND BACK # get rotation data rot = inverseViewTM.rotationPart ea_rot_str = str(mxs.execute(str(rot) + " as EulerAngles")) # converting to euler angles ea_rot_xyz = ea_rot_str.split(" ")[1:] # split by space and skip "eulerAngles" ea_rot_xyz[2] = ea_rot_xyz[2][:-1] # then skip ")" at last element # order data to match persp view axes # default order of data 0-5 is: posX, posY, posZ, rotX, rotY, rotZ # http://forums.epicgames.com/threads/712799-Convert-3dsmax-rotation-to-Unreal-Rotation # UDK rotation order: pitch(rotY in Max), yaw(rotZ in Max), roll(rotX in Max) # pitch and roll are swapedp, and yaw is inverted # this works for PERSPECTIVE and CAMERA views, but only when we subtract 90 degrees from the x rotation later # the following order can be used for reordering from the default order (see above), # deprecated now as we built up data this way from the start now # return [ data[1], data[0], data[2], data[3], -data[5], data[4] ] # append position to data list # data.append(inverseViewTM.position.y) # data.append(inverseViewTM.position.x) # data.append(inverseViewTM.position.z) data.append(inverseViewTM.position.y) data.append(inverseViewTM.position.x) data.append(inverseViewTM.position.z) # append rotation to data list # TEMP CONVERSION TO UDK ROTATION VALUES HERE (MOVE TO UDKTRANSLATOR THEN) # data.append( (((float( ea_rot_xyz[0] )) - 90 ) * DegToUnrRot) % 65536 ) # data.append( (float( ea_rot_xyz[2] ) * -1 * DegToUnrRot) % 65536 ) # data.append( (float( ea_rot_xyz[1] ) * DegToUnrRot) % 65536 ) data.append((float(ea_rot_xyz[0]))-90) data.append(float(ea_rot_xyz[2])*-1) data.append(float(ea_rot_xyz[1])) # print data return data
def addProceduralShake(self): listController = mxs.getPropertyController(self._nativePointer.controller, 'rotation') print mxs.classOf(listController) if not mxs.classOf(listController) == mxs.rotation_list: listController = mxs.rotation_list() mxs.setPropertyController(self._nativePointer.controller, 'rotation', listController) noise = mxs.Noise_rotation() noise.frequency = 0.05 noise.fractal = False noise.seed = random.randint(0, 10000000) # Got lazy here, did not have time to find the Python way to do it. maxScript = """fn setNoiseControllerStrength noiseController x y z = ( noiseController.noise_strength = [x, y, z] )""" mxs.execute(maxScript) weirdFactor = 57.296 mxs.setNoiseControllerStrength(noise, 0.4/weirdFactor, 0.4/weirdFactor, 0.1/weirdFactor) mxs.setPropertyController(listController, 'Available', noise) return True
def valueAtFrame(self, frame): mxs.execute("""fn getControllerValueAtFrame controller frame = ( at time frame return controller.value )""") return mxs.getControllerValueAtFrame(self._nativePointer, frame)
def applyCache(self, path, transformIdentifier, propertiesIdentifier): # Applying the controller for transform. self._nativePointer.controller = mxs.Alembic_Xform( path=path, identifier=transformIdentifier) # Creating our own little MAXScript function for things unsupported in Py3dsMax. mxs.execute( 'fn setTimeController obj controller = (obj.controller.time.controller = controller)' ) # Creating the time controller. timeController = mxs.Float_Script(script='S') # Adding a script controller to alembic time. mxs.setTimeController(self._nativePointer, timeController) # Creating our own little MAXScript function things unsupported in Py3dsMax. maxScript = """fn setAlembicFloatController cam timeController = ( cam.{prop}.controller = Alembic_Float_Controller() cam.{prop}.controller.path = @"{path}" cam.{prop}.controller.identifier = @"{identifier}" cam.{prop}.controller.property = @"{propertyName}" cam.{prop}.controller.time.controller = timeController return cam.{prop}.controller.time.controller )""" # These are the properties we are going to want to load alembic data on. cameraType = self.cameraType() if cameraType == CameraType.Physical: # We need to create a dedicated radians FOV contoller as the regular FOV controller expects degrees. self._addNativeController('RadianFOV', group='Alembic') properties = { 'Alembic.RadianFOV': 'horizontalFOV', 'clip_near': 'NearClippingPlane', 'clip_far': 'FarClippingPlane', 'focus_distance': 'FocusDistance' } else: properties = { 'fov': 'horizontalFOV', 'nearclip': 'NearClippingPlane', 'farclip': 'FarClippingPlane', 'mpassEffect.focalDepth': 'FocusDistance' } for prop in properties: mxs.execute( maxScript.format(prop=prop, path=path, identifier=propertiesIdentifier, propertyName=properties[prop])) mxs.setAlembicFloatController(self._nativePointer, timeController) # FOV is going to be special for V-Ray camera. if cameraType == CameraType.Physical: # Building a script controller for the FOV in degrees. scriptController = mxs.float_script() scriptController.addtarget( 'RadianFOV', mxs.getPropertyController(self._nativePointer, "RadianFOV")) # Using the radians FOV controller reading the Alembic and converting to degrees. scriptController.script = 'radToDeg(RadianFOV)' mxs.setPropertyController(self._nativePointer, 'fov', scriptController) return True
def generatePlayblast( self, path, frameRange=None, resolution=None, slate='', effects=None, geometryOnly=True, pathFormat=r'{basePath}\{fileName}.{frame}.{ext}'): ''' /option <bool> effects ''' # Treating inputs. if isinstance(frameRange, int): frameRange = FrameRange([frameRange, frameRange]) # collecting what we need scene = self._scene basePath, fn = os.path.split(path) fileSplit = fn.split( '.' ) fileName = '.'.join( fileSplit[:-1] ) initialRange = scene.animationRange() fileExtension = fileSplit[-1] # Creating folder if does not exist. dirName = os.path.dirname(path) if not os.path.exists(dirName): os.makedirs(dirName) # Checking inputs. if not frameRange: frameRange = initialRange if not resolution: resolution = scene.renderSize() # Setting slates. if slate: self.setSlateText(slate) self.setSlateIsActive(True) # storing infornation initialGeometryVisibility = mxs.hideByCategory.geometry initialShapesVisibility = mxs.hideByCategory.shapes initialLightsVisibility = mxs.hideByCategory.lights initialCamerasVisibility = mxs.hideByCategory.cameras initialHelpersVisibility = mxs.hideByCategory.helpers initialSpaceWarpsVisibility = mxs.hideByCategory.spacewarps initialParticleSystemsVisibility = mxs.hideByCategory.particles initialBoneObjectsVisibility = mxs.hideByCategory.bones initialGridVisibility = mxs.viewport.getGridVisibility( self._name ) initialFrame = scene.currentFrame() initialSelection = scene.selection() initialSafeFrame = mxs.displaySafeFrames initialViewNumber = mxs.viewport.numViews # Getting the camera. camera = self.camera() # setting the scene scene.setAnimationRange( frameRange ) # Setting the viewport. if geometryOnly: mxs.hideByCategory.geometry = False mxs.hideByCategory.shapes = True mxs.hideByCategory.lights = True mxs.hideByCategory.cameras = True mxs.hideByCategory.helpers = True mxs.hideByCategory.spacewarps = True mxs.hideByCategory.particles = False mxs.hideByCategory.bones = True scene.clearSelection() mxs.displaySafeFrames = False mxs.viewport.setGridVisibility( self._name, False ) if initialViewNumber > 1: mxs.execute( 'max tool maximize' ) # getting the viewport size information viewSize = self.size() completed = True count = 0 mxs.pyhelper.setViewportQuadSize( resolution.width(), resolution.height() ) # Should we compute multi-pass effects. effects = camera.hasMultiPassEffects() and effects in [None, True] # This is my crappy way of figuring out if we are using Nitrous. nitrous = not mxs.gw.GetDriverString() and application.version() >= 15 # We are going to use progressive rendering if the mode is set to Depth of Field (Mental Ray). physical = mxs.classof(camera.nativePointer()) in (mxs.VRayPhysicalCamera, mxs.Physical) progressive = nitrous and (physical or mxs.classof(camera.nativePointer().mpassEffect) == mxs.Depth_of_Field__mental_ray) # If the viewport is using Nitrous. if camera and effects and progressive: # Storing and setting up Nitrous options. nitrousSettings = mxs.NitrousGraphicsManager.GetActiveViewportSetting() initialFadingFactor = nitrousSettings.ProgressiveFadingFactor nitrousSettings.ProgressiveFadingFactor = 0 # For each frame. for frame in range( frameRange[0], frameRange[1] + 1 ): image = None count = count + 1 # Watching for Esc key. if mxs.keyboard.escPressed: completed = False break scene.setCurrentFrame(frame) if camera: if effects: # If we use a Nitrous viewport, we compute the depth of field the new way. passes = 0 if progressive: while not mxs.NitrousGraphicsManager.isProgressiveRenderingFinished(): mxs.NitrousGraphicsManager.progressiveRendering() passes += 1 if passes == 32: break # Otherwise we compute it the old way by using the API method. else: camera.renderMultiPassEffects() # Text overlays are only supported until Max 2011. if slate and application.version() <= 13: self.slateDraw() # For Max 2012 and above only the viewport object allows to save the picture with multipass effects. if application.version() >= 14 and effects: image = mxs.viewport.getViewportDib() if not image: image = mxs.gw.getViewportDib() imagePath = pathFormat.format(basePath=basePath, fileName=fileName, frame=frame, ext=fileExtension) image.filename = imagePath mxs.save(image) # Updating count. if count == 100: mxs.gc() count = 0 # Restoring scene settings. scene.setAnimationRange(initialRange) # Restoring viewport settings. self._name = mxs.viewport.activeViewport self.slateClear() scene.setSelection( initialSelection ) scene.setCurrentFrame( initialFrame ) mxs.displaySafeFrames = initialSafeFrame mxs.hideByCategory.geometry = initialGeometryVisibility mxs.hideByCategory.shapes = initialShapesVisibility mxs.hideByCategory.lights = initialLightsVisibility mxs.hideByCategory.cameras = initialCamerasVisibility mxs.hideByCategory.helpers = initialHelpersVisibility mxs.hideByCategory.spacewarps = initialSpaceWarpsVisibility mxs.hideByCategory.particles = initialParticleSystemsVisibility mxs.hideByCategory.bones = initialBoneObjectsVisibility mxs.viewport.setGridVisibility( self._name, initialGridVisibility ) mxs.pyhelper.setViewportQuadSize( viewSize[0], viewSize[1] ) self.setSlateIsActive( False ) # Restoring Nitrous settings. if camera and progressive and camera.hasMultiPassEffects() and effects in [None, True]: # Restoring Nitrous settings. nitrousSettings.ProgressiveFadingFactor = initialFadingFactor if initialViewNumber != 1: mxs.execute( 'max tool maximize' ) # After storing all these bitmaps. We make sure to flush Max's memory. mxs.gc() return completed