# This is a general render script that uses ffmpeg.exe. It requires that you create a # "Render" folder in your My Documents/FaceFX Studio 20XX folder, and place ffmpeg.exe # in there. You can set console variables to control how the script will behave (what # camera to use, if you want to render out one animation, or an entire group, etc.) # See the comments below for more info. from FxStudio import * from FxAnimation import * import sys, os, time, math def getRenderSetting(cvarName, defaultValue): retVal = getConsoleVariableImpl(cvarName); if None == retVal: retVal = defaultValue FxStudio.echo("setting console variable " + cvarName + " to " + str(retVal) ) return retVal def getRenderSettingOptions(cvarName, defaultValue, options): FxStudio.echo("Options for console variable " + cvarName + " include: " + str(options)); return getRenderSetting(cvarName, defaultValue) def renderFaceFX(): # This is the output directory and where ffmpeg.exe is found. # You need to create the Render folder manually. # This should look something like: My Documents/FaceFX Studio 2010/Render # Make sure you have write access to this folder! userRenderDir = getConsoleVariable( "g_userdirectory") + "Render\\" # ---------------------------------------------------------------------- # Settings. These are retrieved from console variables for easy access # If no console variable is set, we provide default values. # The getRenderSetting function takes the variable name as the first # parameter and the default value you want to use if the variable is not # yet set as the second parameter. # ---------------------------------------------------------------------- outputDirectory = getRenderSetting( "r_outputdir", userRenderDir) if not os.path.exists(outputDirectory): outputDirectoryReturn = FxStudio.displayDirectorySelectionDialog('The default render folder ' + outputDirectory + ' did not exist. Select a Folder for temporary render files.') outputDirectory = str(outputDirectoryReturn) + "\\" if None == outputDirectoryReturn: raise RuntimeError, "No render folder"; ffmpegdir = getRenderSetting( "r_ffmpegdir", userRenderDir) FFMPEGFilePath = ffmpegdir + "ffmpeg.exe" if not os.path.exists(FFMPEGFilePath): FFMPEGFilePathReturn = FxStudio.displayFileOpenDialog("Locate FFMPEG.exe. It is not in the default location.", ffmpegdir, default_filename="ffmpeg.exe", file_must_exist=True) FFMPEGFilePath = str(FFMPEGFilePathReturn) if None == FFMPEGFilePathReturn: raise RuntimeError, "No ffmpeg.exe"; # we can only render movies if ffmpeg is found. renderMode = getRenderSettingOptions( "r_rendermode", "anim", ["anim", "group", "all"]) FPS = int(getRenderSetting( "r_fps", 20)) pixelW = int(getRenderSetting( "r_pixelw", 640)) pixelH = int(getRenderSetting( "r_pixelh", 480)) # Change these numbers to increase or decrease the quality of the movie. Higher quality is bigger file size. # low quality - qmin = 10, qmax = 51 # high quality - qmin = 1, qmax = 51 qmin_setting = float(getRenderSetting( "r_compression_min", 7)) qmax_setting = float(getRenderSetting( "r_compression_max", 51)) imageFormat = getRenderSetting( "r_imageformat", "BMP") # Change the extension here to render out a different type of movie (AVI, MPG, FLV, WMV, etc.) movieFormat = getRenderSetting( "r_movieformat", "FLV") # The default camera is used by not passing in an argument for the camera. The default is the # camera selected when you initially open the scene. Use "Default_Render_Cam" to specify # That nothing should be passed in. camera = getRenderSetting( "r_camera", "Default_Render_Cam") if camera == "Default_Render_Cam" and len(getCameraNames()) > 0: camera = getSingleChoiceFromUser("Choose a camera from which to render:", "Camera Selection", getCameraNames()) if camera == "": camera = "Default_Render_Cam" facefxFilename = os.path.basename(FxStudio.getActorPath()).replace(".facefx", "") # Create the output directory. os.system("mkdir " + "\"" + outputDirectory + "\"") screenshotFilename = outputDirectory + facefxFilename + "." + imageFormat screenshot_folder = outputDirectory + "__temp__screenshots\\" os.system("mkdir " + "\"" + screenshot_folder + "\"") # Add quotes to the exe path FFMPEGFilePath = "\"" + FFMPEGFilePath + "\"" filebase = "fx_render" screenshot_file = screenshot_folder + filebase selectedGroup = getSelectedAnimGroupName() selectedAnim = getSelectedAnimName() animationNames = getAnimationNames() if getSelectedAnimName() == "" and renderMode == "anim": raise RuntimeError, "No animation selected!" for group in animationNames: if (group[0] == selectedGroup) or renderMode == "all": for animName in group[1]: if (animName == selectedAnim) or renderMode == "all" or renderMode == "group": outputFileName = facefxFilename + "_" + group[0] + "_" + animName + "." + movieFormat outputMovie = "\"" + outputDirectory + outputFileName + "\"" selectAnimation(group[0], animName); selectedAnim = Animation(group[0], animName); # Unfortunately, ffmpg's itsoffset isn't working properly by letting us delay the audio by the amount # that the animation starts before time 0. As a result, we need to start at 0, and "chop off" negative # keys. #animStartTime = selectedAnim.startTime animStartTime = 0 animEndTime = selectedAnim.endTime offsetTime = -animStartTime; timestep = 1.0/FPS; i = 0 t = animStartTime paddedZeros = "000" while t < animEndTime: setCurrentTime(t) t += timestep if i > 9: paddedZeros = "00" if i > 99: paddedZeros = "0" if i > 999: paddedZeros = "" if camera != "Default_Render_Cam": issueCommand('render -w "%s" -h "%s" -f "%s" -camera "%s" -fsaa 1;'%(pixelW, pixelH, screenshot_file + paddedZeros + str(i) + "." + imageFormat, camera)) else: issueCommand('render -w "%s" -h "%s" -f "%s" -fsaa 1;'%(pixelW, pixelH, screenshot_file + paddedZeros + str(i) + "." + imageFormat)) i += 1 i -= 1 # replaceing relevant sections with this should work but it doesn't: imageFormat + "\" -itsoffset " + str(offsetTime) + " -i \"" # we use "call" to get around a problem with having spaces in the EXE name and argument names discussed here: http://bugs.python.org/issue1524 command = "call " + FFMPEGFilePath + " -r " + str(FPS) + " -y -f image2 -i " + "\"" + screenshot_file + "%04d." + imageFormat + "\" "; if os.path.exists(selectedAnim.absoluteAudioAssetPath): # we resample the audio with -ar 22050 because it is required for mp3-based output formates like FLV (11025 and 44100 work as well) command += "-i \"" + selectedAnim.absoluteAudioAssetPath + "\" -ar 22050 " command += "-qmin " + str(qmin_setting) + " -qmax " + str(qmax_setting) + " -r " + str(FPS) + " " + outputMovie ; if os.system(command) == 1: print "ffmpeg failed to convert the file. Make sure the file is not in use or write protected. The following is the command that failed:\n" + command print "warning: some versions of ffmpeg report audio failure, but actually succeed in converting the audio." #raise RuntimeError, "ffmpeg failed to convert the file." else: FxStudio.echo("Saved file: " + outputMovie); #delete screenshot files. Only match the exact file pattern to avoid deleting important files command = "del \"" +screenshot_folder + filebase + "*." + imageFormat + "\""; os.system(command) if renderMode == "anim": retVal = getConsoleVariableImpl("r_saveFileName"); if None == retVal: browseDialogFileReturn = FxStudio.displayFileSaveDialog("Move the file to a different location if you prefer.",outputDirectory, default_filename=outputFileName, wildcard="*."+movieFormat) if None != browseDialogFileReturn: browseDialogFile = str(browseDialogFileReturn) command = "move " + outputMovie + " \"" + browseDialogFile + "\""; os.system(command) #this will fail if there are any files in it, so it is relatively safe regardless of how careless you get with this script. os.rmdir(screenshot_folder) if getSelectedAnimation()[1] == "": errorBox("No animation selected! This script requires a selected animation to run.") raise RuntimeError, "No animation is selected. Can not run script." if isNoSave(): errorBox("This script can not be run from the no-save version of FaceFx Studio. It relies on the render command which saves files.") raise RuntimeError, "No-save version can't use render command." #Do the Render renderFaceFX();