##########################################################################
#
#  Copyright (c) 2018, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import IECore
import IECoreScene

import Gaffer
import GafferScene
import GafferArnold
import GafferDispatch
import GafferImage

import imath
import inspect

class ArnoldTextureBake( GafferDispatch.TaskNode ) :

	class __CameraSetup( GafferScene.FilteredSceneProcessor ) :

		def __init__( self, name = "__CameraSetup" ) :

			GafferScene.FilteredSceneProcessor.__init__( self, name )

			# Public plugs
			self["cameraGroup"] = Gaffer.StringPlug( "cameraGroup", Gaffer.Plug.Direction.In, "__TEXTUREBAKE_CAMERAS" )
			self["bakeDirectory"] = Gaffer.StringPlug( "bakeDirectory", Gaffer.Plug.Direction.In, "" )
			self["defaultFileName"] = Gaffer.StringPlug( "defaultFileName", Gaffer.Plug.Direction.In, "${bakeDirectory}/<AOV>/<AOV>.<UDIM>.exr" )
			self["defaultResolution"] = Gaffer.IntPlug( "defaultResolution", Gaffer.Plug.Direction.In, 512 )
			self["uvSet"] = Gaffer.StringPlug( "uvSet", Gaffer.Plug.Direction.In, "uv" )
			self["udims"] = Gaffer.StringPlug( "udims", Gaffer.Plug.Direction.In, "" )
			self["normalOffset"] = Gaffer.FloatPlug( "normalOffset", Gaffer.Plug.Direction.In, 0.1 )
			self["aovs"] = Gaffer.StringPlug( "aovs", Gaffer.Plug.Direction.In, "beauty:rgba" )
			self["tasks"] = Gaffer.IntPlug( "tasks", Gaffer.Plug.Direction.In, 1 )
			self["taskIndex"] = Gaffer.IntPlug( "taskIndex", Gaffer.Plug.Direction.In, 0 )

			# Output
			self["renderFileList"] = Gaffer.StringVectorDataPlug( "renderFileList", Gaffer.Plug.Direction.Out, defaultValue = IECore.StringVectorData() )
			self["renderFileList"].setFlags( Gaffer.Plug.Flags.Serialisable, False )

			# Private internal network

			self["__udimQuery"] = GafferScene.UDIMQuery()
			self["__udimQuery"]["in"].setInput(  self["in"] )
			self["__udimQuery"]["uvSet"].setInput(  self["uvSet"] )
			self["__udimQuery"]["attributes"].setValue( "bake:resolution bake:fileName" )
			self["__udimQuery"]["filter"].setInput( self["filter"] )

			self["__chunkedBakeInfo"] = Gaffer.CompoundObjectPlug( "__chunkedBakeInfo", Gaffer.Plug.Direction.In, IECore.CompoundObject() )
			self["__chunkedBakeInfo"].setFlags( Gaffer.Plug.Flags.Serialisable, False )

			self["__chunkExpression"] = Gaffer.Expression()
			self["__chunkExpression"].setExpression( inspect.cleandoc(
				"""
				import collections
				import re

				rawInfo = parent["__udimQuery"]["out"]

				defaultFileName = parent["defaultFileName"]
				defaultResolution = parent["defaultResolution"]

				selectUdimsStr = parent["udims"]

				# FrameList really ought to take care of this check, instead of just doing
				# something obviously wrong
				if re.match( ".*[0-9] +[0-9].*", selectUdimsStr ):
					raise RuntimeError( "ArnoldTextureBake : Udim list must be comma separated." )

				selectUdims = set( IECore.FrameList.parse( selectUdimsStr ).asList() )

				allMeshes = collections.defaultdict( lambda : [] )
				for udim, meshes in rawInfo.items():
					if selectUdims and not int( udim ) in selectUdims:
						continue

					for mesh, extraAttributes in meshes.items():
						resolution = defaultResolution
						if "bake:resolution" in extraAttributes:
							resolution = extraAttributes["bake:resolution"].value

						fileName = defaultFileName
						if "bake:fileName" in extraAttributes:
							fileName = extraAttributes["bake:fileName"].value

						allMeshes[ (fileName, udim) ].append( { "mesh" : mesh, "resolution" : resolution } )

				fileList = sorted( allMeshes.keys() )

				info = IECore.CompoundObject()

				numTasks = min( parent["tasks"], len( fileList ) )
				taskIndex = parent["taskIndex"]

				if taskIndex < numTasks:

					chunkStart = ( taskIndex * len( fileList ) ) / numTasks
					chunkEnd = ( ( taskIndex + 1 ) * len( fileList ) ) / numTasks

					dupeCount = 0
					prevFileName = ""
					for fileNameTemplate, udim in fileList[chunkStart:chunkEnd]:
						for meshData in allMeshes[(fileNameTemplate, udim)]:
							o = IECore.CompoundObject()
							o["mesh"] = IECore.StringData( meshData["mesh"] )
							o["udim"] = IECore.IntData( int( udim ) )
							o["resolution"] = IECore.IntData( meshData["resolution"] )

							udimStr = str( udim )
							fileName = fileNameTemplate.replace( "<UDIM>", udimStr )

							if fileName == prevFileName:
								dupeCount += 1
								fileName = fileName + ".layer" + str( dupeCount )
							else:
								prevFileName = fileName
								dupeCount = 0

							o["fileName"] = IECore.StringData( fileName )

							name = o["mesh"].value.replace( "/", "_" ) + "." + udimStr
							info[ name ] = o
				parent["__chunkedBakeInfo"] = info

				fileList = []
				for name, i in info.items():
					fileName = i["fileName"].value
					for nameAndAov in parent["aovs"].strip( " " ).split( " " ):
						fileList.append( i["fileName"].value.replace( "<AOV>", nameAndAov.split(":")[0] ) )
				parent["renderFileList"] = IECore.StringVectorData( fileList )
				"""
			), "python" )



			self["__parent"] = GafferScene.Parent()
			self["__parent"]["parent"].setValue( "/" )
			for c in ['bound', 'transform', 'attributes', 'object', 'childNames', 'setNames', 'set']:
				self["__parent"]["in"][c].setInput( self["in"][c] )

			self["__outputExpression"] = Gaffer.Expression()
			self["__outputExpression"].setExpression( inspect.cleandoc(
				"""
				import IECoreScene

				# Transfer all input globals except for outputs
				inGlobals = parent["in"]["globals"]
				outGlobals = IECore.CompoundObject()
				for key, value in inGlobals.items():
					if not key.startswith( "output:" ):
						outGlobals[key] = value

				# Make our own outputs
				info = parent["__chunkedBakeInfo"]
				for cameraName, i in info.items():
					params = IECore.CompoundData()
					fileName = i["fileName"].value
					params["camera"] = IECore.StringData( "/" + parent["cameraGroup"] + "/" + cameraName )
					for nameAndAov in parent["aovs"].strip( " " ).split( " " ):
						tokens = nameAndAov.split( ":" )
						if len( tokens ) != 2:
							raise RuntimeError( "Invalid bake aov specification: %s It should contain a : between name and data." )
						( aovName, aov ) = tokens
						aovFileName = fileName.replace( "<AOV>", aovName )
						outGlobals["output:" + cameraName + "." +  aov] = IECoreScene.Output( aovFileName, "exr", aov + " RGBA", params )
				parent["__parent"]["in"]["globals"] = outGlobals
				"""
			), "python" )

			self["__camera"] = GafferScene.Camera()
			self["__camera"]["projection"].setValue( "orthographic" )

			self["__cameraTweaks"] = GafferScene.CameraTweaks()
			self["__cameraTweaks"]["in"].setInput( self["__camera"]["out"] )
			self["__cameraTweaks"]["tweaks"]["projection"] = GafferScene.TweakPlug( "projection", "uv_camera" )
			self["__cameraTweaks"]["tweaks"]["resolution"] = GafferScene.TweakPlug( "resolution", imath.V2i( 0 ) )
			self["__cameraTweaks"]["tweaks"]["u_offset"] = GafferScene.TweakPlug( "u_offset", 0.0 )
			self["__cameraTweaks"]["tweaks"]["v_offset"] = GafferScene.TweakPlug( "v_offset", 0.0 )
			self["__cameraTweaks"]["tweaks"]["mesh"] = GafferScene.TweakPlug( "mesh", "" )
			self["__cameraTweaks"]["tweaks"]["uv_set"] = GafferScene.TweakPlug( "uv_set", "" )
			self["__cameraTweaks"]["tweaks"]["extend_edges"] = GafferScene.TweakPlug( "extend_edges", False )
			self["__cameraTweaks"]["tweaks"]["offset"] = GafferScene.TweakPlug( "offset", 0.1 )

			self["__cameraTweaks"]["tweaks"]["offset"]["value"].setInput( self["normalOffset"] )

			self["__cameraTweaksFilter"] = GafferScene.PathFilter()
			self["__cameraTweaksFilter"]["paths"].setValue( IECore.StringVectorData( [ '/camera' ] ) )
			self["__cameraTweaks"]["filter"].setInput( self["__cameraTweaksFilter"]["out"] )


			self["__collectScenes"] = GafferScene.CollectScenes()
			self["__collectScenes"]["sourceRoot"].setValue( "/camera" )
			self["__collectScenes"]["rootNameVariable"].setValue( "collect:cameraName" )
			self["__collectScenes"]["in"].setInput( self["__cameraTweaks"]["out"] )

			self["__group"] = GafferScene.Group()
			self["__group"]["in"][0].setInput( self["__collectScenes"]["out"] )
			self["__group"]["name"].setInput( self["cameraGroup"] )

			self["__parent"]["children"][0].setInput( self["__group"]["out"] )

			self["__collectSceneRootsExpression"] = Gaffer.Expression()
			self["__collectSceneRootsExpression"].setExpression( inspect.cleandoc(
				"""
				info = parent["__chunkedBakeInfo"]
				parent["__collectScenes"]["rootNames"] = IECore.StringVectorData( info.keys() )
				"""
			), "python" )

			self["__cameraSetupExpression"] = Gaffer.Expression()
			self["__cameraSetupExpression"].setExpression( inspect.cleandoc(
				"""
				cameraName = context["collect:cameraName"]
				info = parent["__chunkedBakeInfo"]
				i = info[cameraName]
				udimOffset = i["udim"].value - 1001
				parent["__cameraTweaks"]["tweaks"]["resolution"]["value"] = imath.V2i( i["resolution"].value )
				parent["__cameraTweaks"]["tweaks"]["u_offset"]["value"] = -( udimOffset % 10 )
				parent["__cameraTweaks"]["tweaks"]["v_offset"]["value"] = -( udimOffset / 10 )
				parent["__cameraTweaks"]["tweaks"]["mesh"]["value"] = i["mesh"].value
				parent["__cameraTweaks"]["tweaks"]["uv_set"]["value"] = parent["uvSet"] if parent["uvSet"] != "uv" else ""
				"""
			), "python" )

			self["out"].setFlags( Gaffer.Plug.Flags.Serialisable, False )
			self["out"].setInput( self["__parent"]["out"] )

	def __init__( self, name = "ArnoldTextureBake" ) :

		GafferDispatch.TaskNode.__init__( self, name )

		self["in"] = GafferScene.ScenePlug()
		self["filter"] = GafferScene.FilterPlug()
		self["bakeDirectory"] = Gaffer.StringPlug( "bakeDirectory", defaultValue = "" )
		self["defaultFileName"] = Gaffer.StringPlug( "defaultFileName", defaultValue = "${bakeDirectory}/<AOV>/<AOV>.<UDIM>.exr" )
		self["defaultResolution"] = Gaffer.IntPlug( "defaultResolution", defaultValue = 512 )
		self["uvSet"] = Gaffer.StringPlug( "uvSet", defaultValue = 'uv' )
		self["udims"] = Gaffer.StringPlug( "udims", defaultValue = "" )
		self["normalOffset"] = Gaffer.FloatPlug( "offset", defaultValue = 0.1 )
		self["aovs"] = Gaffer.StringPlug( "aovs", defaultValue = 'beauty:RGBA' )
		self["tasks"] = Gaffer.IntPlug( "tasks", defaultValue = 1 )
		self["cleanupIntermediateFiles"] = Gaffer.BoolPlug( "cleanupIntermediateFiles", defaultValue = True )


		self["applyMedianFilter"] = Gaffer.BoolPlug( "applyMedianFilter", Gaffer.Plug.Direction.In, False )
		self["medianRadius"] = Gaffer.IntPlug( "medianRadius", Gaffer.Plug.Direction.In, 1 )

		# Set up connection to preTasks beforehand
		self["__PreTaskList"] = GafferDispatch.TaskList()
		self["__PreTaskList"]["preTasks"].setInput( self["preTasks"] )

		self["__CleanPreTasks"] = Gaffer.DeleteContextVariables()
		self["__CleanPreTasks"].setup( GafferDispatch.TaskNode.TaskPlug() )
		self["__CleanPreTasks"]["in"].setInput( self["__PreTaskList"]["task"] )
		self["__CleanPreTasks"]["variables"].setValue( "BAKE_WEDGE:index BAKE_WEDGE:value_unused" )

		# First, setup python commands which will dispatch a chunk of a render or image tasks as
		# immediate execution once they reach the farm - this allows us to run multiple tasks in
		# one farm process.
		self["__RenderDispatcher"] = GafferDispatch.PythonCommand()
		self["__RenderDispatcher"]["preTasks"][0].setInput( self["__CleanPreTasks"]["out"] )
		self["__RenderDispatcher"]["command"].setValue( inspect.cleandoc(
			"""
			import GafferDispatch
			# We need to access frame and "BAKE_WEDGE:index" so that the hash of render varies with the wedge index,
			# so we might as well print what we're doing
			IECore.msg( IECore.MessageHandler.Level.Info, "Bake Process", "Dispatching render task index %i for frame %i" % ( context["BAKE_WEDGE:index"], context.getFrame() ) )
			d = GafferDispatch.LocalDispatcher()
			d.dispatch( [ self.parent()["__bakeDirectoryContext"] ] )
			"""
		) )
		self["__ImageDispatcher"] = GafferDispatch.PythonCommand()
		self["__ImageDispatcher"]["preTasks"][0].setInput( self["__RenderDispatcher"]["task"] )
		self["__ImageDispatcher"]["command"].setValue( inspect.cleandoc(
			"""
			import GafferDispatch
			# We need to access frame and "BAKE_WEDGE:index" so that the hash of render varies with the wedge index,
			# so we might as well print what we're doing
			IECore.msg( IECore.MessageHandler.Level.Info, "Bake Process", "Dispatching image task index %i for frame %i" % ( context["BAKE_WEDGE:index"], context.getFrame() ) )
			d = GafferDispatch.LocalDispatcher()
			d.dispatch( [ self.parent()["__CleanUpSwitch"] ] )
			"""
		) )
		# Connect through the dispatch settings to the render dispatcher
		# ( The image dispatcher runs much quicker, and should be OK using default settings )
		self["__RenderDispatcher"]["dispatcher"].setInput( self["dispatcher"] )

		# Set up variables so the dispatcher knows that the render and image dispatches depend on
		# the file paths ( in case they are varying in a wedge )
		for redispatch in [ self["__RenderDispatcher"], self["__ImageDispatcher"] ]:
			redispatch["variables"].addChild( Gaffer.NameValuePlug( "bakeDirectory", "", "bakeDirectoryVar" ) )
			redispatch["variables"].addChild( Gaffer.NameValuePlug( "defaultFileName", "", "defaultFileNameVar" ) )

		# Connect the variables via an expression so that get expanded ( this also means that
		# if you put #### in a filename you will get per frame tasks, because the hash will depend
		# on frame number )
		self["__DispatchVariableExpression"] = Gaffer.Expression()
		self["__DispatchVariableExpression"].setExpression( inspect.cleandoc(
			"""
			parent["__RenderDispatcher"]["variables"]["bakeDirectoryVar"]["value"] = parent["bakeDirectory"]
			parent["__RenderDispatcher"]["variables"]["defaultFileNameVar"]["value"] = parent["defaultFileName"]
			parent["__ImageDispatcher"]["variables"]["bakeDirectoryVar"]["value"] = parent["bakeDirectory"]
			parent["__ImageDispatcher"]["variables"]["defaultFileNameVar"]["value"] = parent["defaultFileName"]
			"""
		), "python" )

		# Wedge based on tasks into the overall number of tasks to run.  Note that we don't know how
		# much work each task will do until we actually run the render tasks ( this is when scene
		# expansion happens ).  Because we must group all tasks that write to the same file into the
		# same task batch, if tasks is a large number, some tasks batches could end up empty
		self["__MainWedge"] = GafferDispatch.Wedge()
		self["__MainWedge"]["preTasks"][0].setInput( self["__ImageDispatcher"]["task"] )
		self["__MainWedge"]["variable"].setValue( "BAKE_WEDGE:value_unused" )
		self["__MainWedge"]["indexVariable"].setValue( "BAKE_WEDGE:index" )
		self["__MainWedge"]["mode"].setValue( 1 )
		self["__MainWedge"]["intMin"].setValue( 1 )
		self["__MainWedge"]["intMax"].setInput( self["tasks"] )

		self["task"].setInput( self["__MainWedge"]["task"] )
		self["task"].setFlags( Gaffer.Plug.Flags.Serialisable, False )

		# Now set up the render tasks.  This involves doing the actual rendering, and triggering the
		# output of the file list index file.

		# First get rid of options from the upstream scene that could mess up the bake
		self["__OptionOverrides"] = GafferScene.StandardOptions()
		self["__OptionOverrides"]["in"].setInput( self["in"] )
		self["__OptionOverrides"]["options"]["pixelAspectRatio"]["enabled"].setValue( True )
		self["__OptionOverrides"]["options"]["resolutionMultiplier"]["enabled"].setValue( True )
		self["__OptionOverrides"]["options"]["overscan"]["enabled"].setValue( True )
		self["__OptionOverrides"]["options"]["renderCropWindow"]["enabled"].setValue( True )
		self["__OptionOverrides"]["options"]["cameraBlur"]["enabled"].setValue( True )
		self["__OptionOverrides"]["options"]["transformBlur"]["enabled"].setValue( True )
		self["__OptionOverrides"]["options"]["deformationBlur"]["enabled"].setValue( True )

		self["__CameraSetup"] = self.__CameraSetup()
		self["__CameraSetup"]["in"].setInput( self["__OptionOverrides"]["out"] )
		self["__CameraSetup"]["filter"].setInput( self["filter"] )
		self["__CameraSetup"]["defaultFileName"].setInput( self["defaultFileName"] )
		self["__CameraSetup"]["defaultResolution"].setInput( self["defaultResolution"] )
		self["__CameraSetup"]["uvSet"].setInput( self["uvSet"] )
		self["__CameraSetup"]["aovs"].setInput( self["aovs"] )
		self["__CameraSetup"]["normalOffset"].setInput( self["normalOffset"] )
		self["__CameraSetup"]["tasks"].setInput( self["tasks"] )
		self["__CameraSetup"]["udims"].setInput( self["udims"] )

		self["__Expression"] = Gaffer.Expression()
		self["__Expression"].setExpression( 'parent["__CameraSetup"]["taskIndex"] = context.get( "BAKE_WEDGE:index", 0 )', "python" )

		self["__indexFilePath"] = Gaffer.StringPlug()
		self["__indexFilePath"].setFlags( Gaffer.Plug.Flags.Serialisable, False )
		self["__IndexFileExpression"] = Gaffer.Expression()
		self["__IndexFileExpression"].setExpression( inspect.cleandoc(
			"""
			import os
			parent["__indexFilePath"] = os.path.join( parent["bakeDirectory"], "BAKE_FILE_INDEX_" +
				str( context.get("BAKE_WEDGE:index", 0 ) ) + ".####.txt" )
			"""
		), "python" )

		self["__outputIndexCommand"] = GafferDispatch.PythonCommand()
		self["__outputIndexCommand"]["variables"].addChild( Gaffer.NameValuePlug( "bakeDirectory", Gaffer.StringPlug() ) )
		self["__outputIndexCommand"]["variables"][0]["value"].setInput( self["bakeDirectory"] )
		self["__outputIndexCommand"]["variables"].addChild( Gaffer.NameValuePlug( "indexFilePath", Gaffer.StringPlug() ) )
		self["__outputIndexCommand"]["variables"][1]["value"].setInput( self["__indexFilePath"] )
		self["__outputIndexCommand"]["variables"].addChild( Gaffer.NameValuePlug( "fileList", Gaffer.StringVectorDataPlug( defaultValue = IECore.StringVectorData() ) ) )
		self["__outputIndexCommand"]["variables"][2]["value"].setInput( self["__CameraSetup"]["renderFileList"] )
		self["__outputIndexCommand"]["command"].setValue( inspect.cleandoc(
			"""
			import os
			import distutils.dir_util

			# Ensure path exists
			distutils.dir_util.mkpath( variables["bakeDirectory"] )

			f = open( variables["indexFilePath"], "w" )

			f.writelines( [ i + "\\n" for i in sorted( variables["fileList"] ) ] )
			f.close()
			IECore.msg( IECore.MessageHandler.Level.Info, "Bake Process", "Wrote list of bake files for this chunk to " + variables["indexFilePath"] )
			"""
		) )

		self["__arnoldRender"] = GafferArnold.ArnoldRender()
		self["__arnoldRender"]["preTasks"][0].setInput( self["__outputIndexCommand"]["task"] )
		self["__arnoldRender"]["dispatcher"]["immediate"].setValue( True )
		self["__arnoldRender"]["in"].setInput( self["__CameraSetup"]["out"] )

		self["__bakeDirectoryContext"] = GafferDispatch.TaskContextVariables()
		self["__bakeDirectoryContext"]["variables"].addChild( Gaffer.NameValuePlug( "bakeDirectory", Gaffer.StringPlug() ) )
		self["__bakeDirectoryContext"]["variables"][0]["value"].setInput( self["bakeDirectory"] )
		self["__bakeDirectoryContext"]["preTasks"][0].setInput( self["__arnoldRender"]["task"] )

		# Now set up the image tasks.  This involves merging all layers for a UDIM, filling in the
		# background, writing out this image, converting it to tx, and optionally deleting all the exrs

		self["__imageList"] = Gaffer.CompoundObjectPlug( "__imageList", defaultValue = IECore.CompoundObject() )
		self["__imageList"].setFlags( Gaffer.Plug.Flags.Serialisable, False )

		self["__ImageReader"] = GafferImage.ImageReader()
		self["__CurInputFileExpression"] = Gaffer.Expression()
		self["__CurInputFileExpression"].setExpression( inspect.cleandoc(
			"""
			l = parent["__imageList"]
			outFile = context["wedge:outFile"]
			loopIndex = context[ "loop:index" ]
			parent["__ImageReader"]["fileName"] = l[outFile][ loopIndex ]
			"""
		), "python" )

		# Find the max size of any input file
		self["__SizeLoop"] = Gaffer.LoopComputeNode()
		self["__SizeLoop"].setup( Gaffer.IntPlug() )

		self["__SizeMaxExpression"] = Gaffer.Expression()
		self["__SizeMaxExpression"].setExpression( inspect.cleandoc(
			"""
			f = parent["__ImageReader"]["out"]["format"]
			parent["__SizeLoop"]["next"] = max( f.width(), parent["__SizeLoop"]["previous"] )
			"""
		), "python" )

		# Loop over all input files for this output file, and merge them all together
		self["__ImageLoop"] = Gaffer.LoopComputeNode()
		self["__ImageLoop"].setup( GafferImage.ImagePlug() )

		self["__NumInputsForCurOutputExpression"] = Gaffer.Expression()
		self["__NumInputsForCurOutputExpression"].setExpression( inspect.cleandoc(
			"""
			l = parent["__imageList"]
			outFile = context["wedge:outFile"]
			numInputs = len( l[outFile] )
			parent["__ImageLoop"]["iterations"] = numInputs
			parent["__SizeLoop"]["iterations"] = numInputs
			"""
		), "python" )

		self["__Resize"] = GafferImage.Resize()
		self["__Resize"]["format"]["displayWindow"]["min"].setValue( imath.V2i( 0, 0 ) )
		self["__Resize"]['format']["displayWindow"]["max"]["x"].setInput( self["__SizeLoop"]["out"] )
		self["__Resize"]['format']["displayWindow"]["max"]["y"].setInput( self["__SizeLoop"]["out"] )
		self["__Resize"]['in'].setInput( self["__ImageReader"]["out"] )

		self["__Merge"] = GafferImage.Merge()
		self["__Merge"]["in"][0].setInput( self["__Resize"]["out"] )
		self["__Merge"]["in"][1].setInput( self["__ImageLoop"]["previous"] )
		self["__Merge"]["operation"].setValue( GafferImage.Merge.Operation.Add )

		self["__ImageLoop"]["next"].setInput( self["__Merge"]["out"] )

		# Write out the combined image, so we can immediately read it back in
		# This is just because we're doing enough image processing that we
		# could saturate the cache, and Gaffer wouldn't know that this is
		# the important result to keep
		self["__ImageIntermediateWriter"] = GafferImage.ImageWriter()
		self["__ImageIntermediateWriter"]["in"].setInput( self["__ImageLoop"]["out"] )

		self["__ImageIntermediateReader"] = GafferImage.ImageReader()

		# Now that we've merged everything together, we can use a BleedFill to fill in the background,
		# so that texture filtering across the edges will pull in colors that are at least reasonable.
		self["__BleedFill"] = GafferImage.BleedFill()
		self["__BleedFill"]["in"].setInput( self["__ImageIntermediateReader"]["out"] )

		self["__Median"] = GafferImage.Median()
		self["__Median"]["in"].setInput( self["__BleedFill"]["out"] )
		self["__Median"]["enabled"].setInput( self["applyMedianFilter"] )
		self["__Median"]["radius"]["x"].setInput( self["medianRadius"] )
		self["__Median"]["radius"]["y"].setInput( self["medianRadius"] )

		# Write out the result
		self["__ImageWriter"] = GafferImage.ImageWriter()
		self["__ImageWriter"]["in"].setInput( self["__Median"]["out"] )
		self["__ImageWriter"]["preTasks"][0].setInput( self["__ImageIntermediateWriter"]["task"] )

		# Convert result to texture
		self["__ConvertCommand"] = GafferDispatch.SystemCommand()
		# We shouldn't need a sub-shell and this prevents S.I.P on the Mac from
		# blocking the dylibs loaded by maketx.
		self["__ConvertCommand"]["shell"].setValue( False )
		self["__ConvertCommand"]["substitutions"].addChild( Gaffer.NameValuePlug( "inFile", IECore.StringData(), "member1" ) )
		self["__ConvertCommand"]["substitutions"].addChild( Gaffer.NameValuePlug( "outFile", IECore.StringData(), "member1" ) )
		self["__ConvertCommand"]["preTasks"][0].setInput( self["__ImageWriter"]["task"] )
		self["__ConvertCommand"]["command"].setValue( 'maketx --wrap clamp {inFile} -o {outFile}' )


		self["__CommandSetupExpression"] = Gaffer.Expression()
		self["__CommandSetupExpression"].setExpression( inspect.cleandoc(
			"""

			outFileBase = context["wedge:outFile"]
			intermediateExr = outFileBase + ".intermediate.exr"
			parent["__ImageIntermediateWriter"]["fileName"] = intermediateExr
			parent["__ImageIntermediateReader"]["fileName"] = intermediateExr
			tmpExr = outFileBase + ".tmp.exr"
			parent["__ImageWriter"]["fileName"] = tmpExr
			parent["__ConvertCommand"]["substitutions"]["member1"]["value"] = tmpExr
			parent["__ConvertCommand"]["substitutions"]["member2"]["value"] = outFileBase + ".tx"
			"""
		), "python" )

		self["__ImageWedge"] = GafferDispatch.Wedge()
		self["__ImageWedge"]["preTasks"][0].setInput( self["__ConvertCommand"]["task"] )
		self["__ImageWedge"]["variable"].setValue( 'wedge:outFile' )
		self["__ImageWedge"]["indexVariable"].setValue( 'wedge:outFileIndex' )
		self["__ImageWedge"]["mode"].setValue( int( GafferDispatch.Wedge.Mode.StringList ) )

		self["__CleanUpCommand"] = GafferDispatch.PythonCommand()
		self["__CleanUpCommand"]["preTasks"][0].setInput( self["__ImageWedge"]["task"] )
		self["__CleanUpCommand"]["variables"].addChild( Gaffer.NameValuePlug( "filesToDelete", Gaffer.StringVectorDataPlug( defaultValue = IECore.StringVectorData() ), "member1" ) )
		self["__CleanUpCommand"]["command"].setValue( inspect.cleandoc(
			"""
			import os
			for tmpFile in variables["filesToDelete"]:
				os.remove( tmpFile )
			"""
		) )

		self["__CleanUpExpression"] = Gaffer.Expression()
		self["__CleanUpExpression"].setExpression( inspect.cleandoc(
			"""

			imageList = parent["__imageList"]

			toDelete = []
			for outFileBase, inputExrs in imageList.items():
				tmpExr = outFileBase + ".tmp.exr"
				intermediateExr = outFileBase + ".intermediate.exr"
				toDelete.extend( inputExrs )
				toDelete.append( tmpExr )
				toDelete.append( intermediateExr )
			toDelete.append( parent["__indexFilePath"] )

			parent["__CleanUpCommand"]["variables"]["member1"]["value"] = IECore.StringVectorData( toDelete )
			"""
		), "python" )

		self["__CleanUpSwitch"] = GafferDispatch.TaskSwitch()
		self["__CleanUpSwitch"]["preTasks"][0].setInput( self["__ImageWedge"]["task"] )
		self["__CleanUpSwitch"]["preTasks"][1].setInput( self["__CleanUpCommand"]["task"] )
		self["__CleanUpSwitch"]["index"].setInput( self["cleanupIntermediateFiles"] )

		# Set up the list of input image files to process, and the corresponding list of
		# output files to wedge over
		self["__ImageSetupExpression"] = Gaffer.Expression()
		self["__ImageSetupExpression"].setExpression( inspect.cleandoc(
			"""
			f = open( parent["__indexFilePath"], "r" )

			fileList = f.read().splitlines()
			fileDict = {}
			for i in fileList:
				rootName = i.rsplit( ".exr", 1 )[0]
				if rootName in fileDict:
					fileDict[ rootName ].append( i )
				else:
					fileDict[ rootName ] = IECore.StringVectorData( [i] )
			parent["__imageList"] = IECore.CompoundObject( fileDict )

			parent["__ImageWedge"]["strings"] = IECore.StringVectorData( fileDict.keys() )
			"""
		), "python" )




IECore.registerRunTimeTyped( ArnoldTextureBake, typeName = "GafferArnold::ArnoldTextureBake" )


[(599, 'IECore.registerRunTimeTyped', 'IECore.registerRunTimeTyped', (['ArnoldTextureBake'], {'typeName': '"""GafferArnold::ArnoldTextureBake"""'}), False, 'import IECore\n'), (262, 'GafferDispatch.TaskNode.__init__', 'GafferDispatch.TaskNode.__init__', (['self', 'name'], {}), False, 'import GafferDispatch\n'), (264, 'GafferScene.ScenePlug', 'GafferScene.ScenePlug', ([], {}), False, 'import GafferScene\n'), (265, 'GafferScene.FilterPlug', 'GafferScene.FilterPlug', ([], {}), False, 'import GafferScene\n'), (266, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""bakeDirectory"""'], {'defaultValue': '""""""'}), False, 'import Gaffer\n'), (267, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""defaultFileName"""'], {'defaultValue': '"""${bakeDirectory}/<AOV>/<AOV>.<UDIM>.exr"""'}), False, 'import Gaffer\n'), (268, 'Gaffer.IntPlug', 'Gaffer.IntPlug', (['"""defaultResolution"""'], {'defaultValue': '(512)'}), False, 'import Gaffer\n'), (269, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""uvSet"""'], {'defaultValue': '"""uv"""'}), False, 'import Gaffer\n'), (270, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""udims"""'], {'defaultValue': '""""""'}), False, 'import Gaffer\n'), (271, 'Gaffer.FloatPlug', 'Gaffer.FloatPlug', (['"""offset"""'], {'defaultValue': '(0.1)'}), False, 'import Gaffer\n'), (272, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""aovs"""'], {'defaultValue': '"""beauty:RGBA"""'}), False, 'import Gaffer\n'), (273, 'Gaffer.IntPlug', 'Gaffer.IntPlug', (['"""tasks"""'], {'defaultValue': '(1)'}), False, 'import Gaffer\n'), (274, 'Gaffer.BoolPlug', 'Gaffer.BoolPlug', (['"""cleanupIntermediateFiles"""'], {'defaultValue': '(True)'}), False, 'import Gaffer\n'), (277, 'Gaffer.BoolPlug', 'Gaffer.BoolPlug', (['"""applyMedianFilter"""', 'Gaffer.Plug.Direction.In', '(False)'], {}), False, 'import Gaffer\n'), (278, 'Gaffer.IntPlug', 'Gaffer.IntPlug', (['"""medianRadius"""', 'Gaffer.Plug.Direction.In', '(1)'], {}), False, 'import Gaffer\n'), (281, 'GafferDispatch.TaskList', 'GafferDispatch.TaskList', ([], {}), False, 'import GafferDispatch\n'), (284, 'Gaffer.DeleteContextVariables', 'Gaffer.DeleteContextVariables', ([], {}), False, 'import Gaffer\n'), (292, 'GafferDispatch.PythonCommand', 'GafferDispatch.PythonCommand', ([], {}), False, 'import GafferDispatch\n'), (304, 'GafferDispatch.PythonCommand', 'GafferDispatch.PythonCommand', ([], {}), False, 'import GafferDispatch\n'), (329, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (343, 'GafferDispatch.Wedge', 'GafferDispatch.Wedge', ([], {}), False, 'import GafferDispatch\n'), (358, 'GafferScene.StandardOptions', 'GafferScene.StandardOptions', ([], {}), False, 'import GafferScene\n'), (379, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (382, 'Gaffer.StringPlug', 'Gaffer.StringPlug', ([], {}), False, 'import Gaffer\n'), (384, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (393, 'GafferDispatch.PythonCommand', 'GafferDispatch.PythonCommand', ([], {}), False, 'import GafferDispatch\n'), (416, 'GafferArnold.ArnoldRender', 'GafferArnold.ArnoldRender', ([], {}), False, 'import GafferArnold\n'), (421, 'GafferDispatch.TaskContextVariables', 'GafferDispatch.TaskContextVariables', ([], {}), False, 'import GafferDispatch\n'), (432, 'GafferImage.ImageReader', 'GafferImage.ImageReader', ([], {}), False, 'import GafferImage\n'), (433, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (444, 'Gaffer.LoopComputeNode', 'Gaffer.LoopComputeNode', ([], {}), False, 'import Gaffer\n'), (447, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (456, 'Gaffer.LoopComputeNode', 'Gaffer.LoopComputeNode', ([], {}), False, 'import Gaffer\n'), (459, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (470, 'GafferImage.Resize', 'GafferImage.Resize', ([], {}), False, 'import GafferImage\n'), (476, 'GafferImage.Merge', 'GafferImage.Merge', ([], {}), False, 'import GafferImage\n'), (487, 'GafferImage.ImageWriter', 'GafferImage.ImageWriter', ([], {}), False, 'import GafferImage\n'), (490, 'GafferImage.ImageReader', 'GafferImage.ImageReader', ([], {}), False, 'import GafferImage\n'), (494, 'GafferImage.BleedFill', 'GafferImage.BleedFill', ([], {}), False, 'import GafferImage\n'), (497, 'GafferImage.Median', 'GafferImage.Median', ([], {}), False, 'import GafferImage\n'), (504, 'GafferImage.ImageWriter', 'GafferImage.ImageWriter', ([], {}), False, 'import GafferImage\n'), (509, 'GafferDispatch.SystemCommand', 'GafferDispatch.SystemCommand', ([], {}), False, 'import GafferDispatch\n'), (519, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (534, 'GafferDispatch.Wedge', 'GafferDispatch.Wedge', ([], {}), False, 'import GafferDispatch\n'), (540, 'GafferDispatch.PythonCommand', 'GafferDispatch.PythonCommand', ([], {}), False, 'import GafferDispatch\n'), (551, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (570, 'GafferDispatch.TaskSwitch', 'GafferDispatch.TaskSwitch', ([], {}), False, 'import GafferDispatch\n'), (577, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (55, 'GafferScene.FilteredSceneProcessor.__init__', 'GafferScene.FilteredSceneProcessor.__init__', (['self', 'name'], {}), False, 'import GafferScene\n'), (58, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""cameraGroup"""', 'Gaffer.Plug.Direction.In', '"""__TEXTUREBAKE_CAMERAS"""'], {}), False, 'import Gaffer\n'), (59, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""bakeDirectory"""', 'Gaffer.Plug.Direction.In', '""""""'], {}), False, 'import Gaffer\n'), (60, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""defaultFileName"""', 'Gaffer.Plug.Direction.In', '"""${bakeDirectory}/<AOV>/<AOV>.<UDIM>.exr"""'], {}), False, 'import Gaffer\n'), (61, 'Gaffer.IntPlug', 'Gaffer.IntPlug', (['"""defaultResolution"""', 'Gaffer.Plug.Direction.In', '(512)'], {}), False, 'import Gaffer\n'), (62, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""uvSet"""', 'Gaffer.Plug.Direction.In', '"""uv"""'], {}), False, 'import Gaffer\n'), (63, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""udims"""', 'Gaffer.Plug.Direction.In', '""""""'], {}), False, 'import Gaffer\n'), (64, 'Gaffer.FloatPlug', 'Gaffer.FloatPlug', (['"""normalOffset"""', 'Gaffer.Plug.Direction.In', '(0.1)'], {}), False, 'import Gaffer\n'), (65, 'Gaffer.StringPlug', 'Gaffer.StringPlug', (['"""aovs"""', 'Gaffer.Plug.Direction.In', '"""beauty:rgba"""'], {}), False, 'import Gaffer\n'), (66, 'Gaffer.IntPlug', 'Gaffer.IntPlug', (['"""tasks"""', 'Gaffer.Plug.Direction.In', '(1)'], {}), False, 'import Gaffer\n'), (67, 'Gaffer.IntPlug', 'Gaffer.IntPlug', (['"""taskIndex"""', 'Gaffer.Plug.Direction.In', '(0)'], {}), False, 'import Gaffer\n'), (75, 'GafferScene.UDIMQuery', 'GafferScene.UDIMQuery', ([], {}), False, 'import GafferScene\n'), (84, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (168, 'GafferScene.Parent', 'GafferScene.Parent', ([], {}), False, 'import GafferScene\n'), (173, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (202, 'GafferScene.Camera', 'GafferScene.Camera', ([], {}), False, 'import GafferScene\n'), (205, 'GafferScene.CameraTweaks', 'GafferScene.CameraTweaks', ([], {}), False, 'import GafferScene\n'), (207, 'GafferScene.TweakPlug', 'GafferScene.TweakPlug', (['"""projection"""', '"""uv_camera"""'], {}), False, 'import GafferScene\n'), (209, 'GafferScene.TweakPlug', 'GafferScene.TweakPlug', (['"""u_offset"""', '(0.0)'], {}), False, 'import GafferScene\n'), (210, 'GafferScene.TweakPlug', 'GafferScene.TweakPlug', (['"""v_offset"""', '(0.0)'], {}), False, 'import GafferScene\n'), (211, 'GafferScene.TweakPlug', 'GafferScene.TweakPlug', (['"""mesh"""', '""""""'], {}), False, 'import GafferScene\n'), (212, 'GafferScene.TweakPlug', 'GafferScene.TweakPlug', (['"""uv_set"""', '""""""'], {}), False, 'import GafferScene\n'), (213, 'GafferScene.TweakPlug', 'GafferScene.TweakPlug', (['"""extend_edges"""', '(False)'], {}), False, 'import GafferScene\n'), (214, 'GafferScene.TweakPlug', 'GafferScene.TweakPlug', (['"""offset"""', '(0.1)'], {}), False, 'import GafferScene\n'), (218, 'GafferScene.PathFilter', 'GafferScene.PathFilter', ([], {}), False, 'import GafferScene\n'), (223, 'GafferScene.CollectScenes', 'GafferScene.CollectScenes', ([], {}), False, 'import GafferScene\n'), (228, 'GafferScene.Group', 'GafferScene.Group', ([], {}), False, 'import GafferScene\n'), (234, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (242, 'Gaffer.Expression', 'Gaffer.Expression', ([], {}), False, 'import Gaffer\n'), (285, 'GafferDispatch.TaskNode.TaskPlug', 'GafferDispatch.TaskNode.TaskPlug', ([], {}), False, 'import GafferDispatch\n'), (294, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\timport GafferDispatch\n\t\t\t# We need to access frame and "BAKE_WEDGE:index" so that the hash of render varies with the wedge index,\n\t\t\t# so we might as well print what we\'re doing\n\t\t\tIECore.msg( IECore.MessageHandler.Level.Info, "Bake Process", "Dispatching render task index %i for frame %i" % ( context["BAKE_WEDGE:index"], context.getFrame() ) )\n\t\t\td = GafferDispatch.LocalDispatcher()\n\t\t\td.dispatch( [ self.parent()["__bakeDirectoryContext"] ] )\n\t\t\t"""'], {}), False, 'import inspect\n'), (306, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\timport GafferDispatch\n\t\t\t# We need to access frame and "BAKE_WEDGE:index" so that the hash of render varies with the wedge index,\n\t\t\t# so we might as well print what we\'re doing\n\t\t\tIECore.msg( IECore.MessageHandler.Level.Info, "Bake Process", "Dispatching image task index %i for frame %i" % ( context["BAKE_WEDGE:index"], context.getFrame() ) )\n\t\t\td = GafferDispatch.LocalDispatcher()\n\t\t\td.dispatch( [ self.parent()["__CleanUpSwitch"] ] )\n\t\t\t"""'], {}), False, 'import inspect\n'), (330, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\tparent["__RenderDispatcher"]["variables"]["bakeDirectoryVar"]["value"] = parent["bakeDirectory"]\n\t\t\tparent["__RenderDispatcher"]["variables"]["defaultFileNameVar"]["value"] = parent["defaultFileName"]\n\t\t\tparent["__ImageDispatcher"]["variables"]["bakeDirectoryVar"]["value"] = parent["bakeDirectory"]\n\t\t\tparent["__ImageDispatcher"]["variables"]["defaultFileNameVar"]["value"] = parent["defaultFileName"]\n\t\t\t"""'], {}), False, 'import inspect\n'), (385, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\timport os\n\t\t\tparent["__indexFilePath"] = os.path.join( parent["bakeDirectory"], "BAKE_FILE_INDEX_" +\n\t\t\t\tstr( context.get("BAKE_WEDGE:index", 0 ) ) + ".####.txt" )\n\t\t\t"""'], {}), False, 'import inspect\n'), (400, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\timport os\n\t\t\timport distutils.dir_util\n\n\t\t\t# Ensure path exists\n\t\t\tdistutils.dir_util.mkpath( variables["bakeDirectory"] )\n\n\t\t\tf = open( variables["indexFilePath"], "w" )\n\n\t\t\tf.writelines( [ i + "\\\\n" for i in sorted( variables["fileList"] ) ] )\n\t\t\tf.close()\n\t\t\tIECore.msg( IECore.MessageHandler.Level.Info, "Bake Process", "Wrote list of bake files for this chunk to " + variables["indexFilePath"] )\n\t\t\t"""'], {}), False, 'import inspect\n'), (434, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\tl = parent["__imageList"]\n\t\t\toutFile = context["wedge:outFile"]\n\t\t\tloopIndex = context[ "loop:index" ]\n\t\t\tparent["__ImageReader"]["fileName"] = l[outFile][ loopIndex ]\n\t\t\t"""'], {}), False, 'import inspect\n'), (445, 'Gaffer.IntPlug', 'Gaffer.IntPlug', ([], {}), False, 'import Gaffer\n'), (448, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\tf = parent["__ImageReader"]["out"]["format"]\n\t\t\tparent["__SizeLoop"]["next"] = max( f.width(), parent["__SizeLoop"]["previous"] )\n\t\t\t"""'], {}), False, 'import inspect\n'), (457, 'GafferImage.ImagePlug', 'GafferImage.ImagePlug', ([], {}), False, 'import GafferImage\n'), (460, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\tl = parent["__imageList"]\n\t\t\toutFile = context["wedge:outFile"]\n\t\t\tnumInputs = len( l[outFile] )\n\t\t\tparent["__ImageLoop"]["iterations"] = numInputs\n\t\t\tparent["__SizeLoop"]["iterations"] = numInputs\n\t\t\t"""'], {}), False, 'import inspect\n'), (471, 'imath.V2i', 'imath.V2i', (['(0)', '(0)'], {}), False, 'import imath\n'), (520, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\n\t\t\toutFileBase = context["wedge:outFile"]\n\t\t\tintermediateExr = outFileBase + ".intermediate.exr"\n\t\t\tparent["__ImageIntermediateWriter"]["fileName"] = intermediateExr\n\t\t\tparent["__ImageIntermediateReader"]["fileName"] = intermediateExr\n\t\t\ttmpExr = outFileBase + ".tmp.exr"\n\t\t\tparent["__ImageWriter"]["fileName"] = tmpExr\n\t\t\tparent["__ConvertCommand"]["substitutions"]["member1"]["value"] = tmpExr\n\t\t\tparent["__ConvertCommand"]["substitutions"]["member2"]["value"] = outFileBase + ".tx"\n\t\t\t"""'], {}), False, 'import inspect\n'), (543, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\timport os\n\t\t\tfor tmpFile in variables["filesToDelete"]:\n\t\t\t\tos.remove( tmpFile )\n\t\t\t"""'], {}), False, 'import inspect\n'), (552, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\n\t\t\timageList = parent["__imageList"]\n\n\t\t\ttoDelete = []\n\t\t\tfor outFileBase, inputExrs in imageList.items():\n\t\t\t\ttmpExr = outFileBase + ".tmp.exr"\n\t\t\t\tintermediateExr = outFileBase + ".intermediate.exr"\n\t\t\t\ttoDelete.extend( inputExrs )\n\t\t\t\ttoDelete.append( tmpExr )\n\t\t\t\ttoDelete.append( intermediateExr )\n\t\t\ttoDelete.append( parent["__indexFilePath"] )\n\n\t\t\tparent["__CleanUpCommand"]["variables"]["member1"]["value"] = IECore.StringVectorData( toDelete )\n\t\t\t"""'], {}), False, 'import inspect\n'), (578, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\tf = open( parent["__indexFilePath"], "r" )\n\n\t\t\tfileList = f.read().splitlines()\n\t\t\tfileDict = {}\n\t\t\tfor i in fileList:\n\t\t\t\trootName = i.rsplit( ".exr", 1 )[0]\n\t\t\t\tif rootName in fileDict:\n\t\t\t\t\tfileDict[ rootName ].append( i )\n\t\t\t\telse:\n\t\t\t\t\tfileDict[ rootName ] = IECore.StringVectorData( [i] )\n\t\t\tparent["__imageList"] = IECore.CompoundObject( fileDict )\n\n\t\t\tparent["__ImageWedge"]["strings"] = IECore.StringVectorData( fileDict.keys() )\n\t\t\t"""'], {}), False, 'import inspect\n'), (81, 'IECore.CompoundObject', 'IECore.CompoundObject', ([], {}), False, 'import IECore\n'), (85, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\t\timport collections\n\t\t\t\timport re\n\n\t\t\t\trawInfo = parent["__udimQuery"]["out"]\n\n\t\t\t\tdefaultFileName = parent["defaultFileName"]\n\t\t\t\tdefaultResolution = parent["defaultResolution"]\n\n\t\t\t\tselectUdimsStr = parent["udims"]\n\n\t\t\t\t# FrameList really ought to take care of this check, instead of just doing\n\t\t\t\t# something obviously wrong\n\t\t\t\tif re.match( ".*[0-9] +[0-9].*", selectUdimsStr ):\n\t\t\t\t\traise RuntimeError( "ArnoldTextureBake : Udim list must be comma separated." )\n\n\t\t\t\tselectUdims = set( IECore.FrameList.parse( selectUdimsStr ).asList() )\n\n\t\t\t\tallMeshes = collections.defaultdict( lambda : [] )\n\t\t\t\tfor udim, meshes in rawInfo.items():\n\t\t\t\t\tif selectUdims and not int( udim ) in selectUdims:\n\t\t\t\t\t\tcontinue\n\n\t\t\t\t\tfor mesh, extraAttributes in meshes.items():\n\t\t\t\t\t\tresolution = defaultResolution\n\t\t\t\t\t\tif "bake:resolution" in extraAttributes:\n\t\t\t\t\t\t\tresolution = extraAttributes["bake:resolution"].value\n\n\t\t\t\t\t\tfileName = defaultFileName\n\t\t\t\t\t\tif "bake:fileName" in extraAttributes:\n\t\t\t\t\t\t\tfileName = extraAttributes["bake:fileName"].value\n\n\t\t\t\t\t\tallMeshes[ (fileName, udim) ].append( { "mesh" : mesh, "resolution" : resolution } )\n\n\t\t\t\tfileList = sorted( allMeshes.keys() )\n\n\t\t\t\tinfo = IECore.CompoundObject()\n\n\t\t\t\tnumTasks = min( parent["tasks"], len( fileList ) )\n\t\t\t\ttaskIndex = parent["taskIndex"]\n\n\t\t\t\tif taskIndex < numTasks:\n\n\t\t\t\t\tchunkStart = ( taskIndex * len( fileList ) ) / numTasks\n\t\t\t\t\tchunkEnd = ( ( taskIndex + 1 ) * len( fileList ) ) / numTasks\n\n\t\t\t\t\tdupeCount = 0\n\t\t\t\t\tprevFileName = ""\n\t\t\t\t\tfor fileNameTemplate, udim in fileList[chunkStart:chunkEnd]:\n\t\t\t\t\t\tfor meshData in allMeshes[(fileNameTemplate, udim)]:\n\t\t\t\t\t\t\to = IECore.CompoundObject()\n\t\t\t\t\t\t\to["mesh"] = IECore.StringData( meshData["mesh"] )\n\t\t\t\t\t\t\to["udim"] = IECore.IntData( int( udim ) )\n\t\t\t\t\t\t\to["resolution"] = IECore.IntData( meshData["resolution"] )\n\n\t\t\t\t\t\t\tudimStr = str( udim )\n\t\t\t\t\t\t\tfileName = fileNameTemplate.replace( "<UDIM>", udimStr )\n\n\t\t\t\t\t\t\tif fileName == prevFileName:\n\t\t\t\t\t\t\t\tdupeCount += 1\n\t\t\t\t\t\t\t\tfileName = fileName + ".layer" + str( dupeCount )\n\t\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t\tprevFileName = fileName\n\t\t\t\t\t\t\t\tdupeCount = 0\n\n\t\t\t\t\t\t\to["fileName"] = IECore.StringData( fileName )\n\n\t\t\t\t\t\t\tname = o["mesh"].value.replace( "/", "_" ) + "." + udimStr\n\t\t\t\t\t\t\tinfo[ name ] = o\n\t\t\t\tparent["__chunkedBakeInfo"] = info\n\n\t\t\t\tfileList = []\n\t\t\t\tfor name, i in info.items():\n\t\t\t\t\tfileName = i["fileName"].value\n\t\t\t\t\tfor nameAndAov in parent["aovs"].strip( " " ).split( " " ):\n\t\t\t\t\t\tfileList.append( i["fileName"].value.replace( "<AOV>", nameAndAov.split(":")[0] ) )\n\t\t\t\tparent["renderFileList"] = IECore.StringVectorData( fileList )\n\t\t\t\t"""'], {}), False, 'import inspect\n'), (174, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\t\timport IECoreScene\n\n\t\t\t\t# Transfer all input globals except for outputs\n\t\t\t\tinGlobals = parent["in"]["globals"]\n\t\t\t\toutGlobals = IECore.CompoundObject()\n\t\t\t\tfor key, value in inGlobals.items():\n\t\t\t\t\tif not key.startswith( "output:" ):\n\t\t\t\t\t\toutGlobals[key] = value\n\n\t\t\t\t# Make our own outputs\n\t\t\t\tinfo = parent["__chunkedBakeInfo"]\n\t\t\t\tfor cameraName, i in info.items():\n\t\t\t\t\tparams = IECore.CompoundData()\n\t\t\t\t\tfileName = i["fileName"].value\n\t\t\t\t\tparams["camera"] = IECore.StringData( "/" + parent["cameraGroup"] + "/" + cameraName )\n\t\t\t\t\tfor nameAndAov in parent["aovs"].strip( " " ).split( " " ):\n\t\t\t\t\t\ttokens = nameAndAov.split( ":" )\n\t\t\t\t\t\tif len( tokens ) != 2:\n\t\t\t\t\t\t\traise RuntimeError( "Invalid bake aov specification: %s It should contain a : between name and data." )\n\t\t\t\t\t\t( aovName, aov ) = tokens\n\t\t\t\t\t\taovFileName = fileName.replace( "<AOV>", aovName )\n\t\t\t\t\t\toutGlobals["output:" + cameraName + "." +  aov] = IECoreScene.Output( aovFileName, "exr", aov + " RGBA", params )\n\t\t\t\tparent["__parent"]["in"]["globals"] = outGlobals\n\t\t\t\t"""'], {}), False, 'import inspect\n'), (208, 'imath.V2i', 'imath.V2i', (['(0)'], {}), False, 'import imath\n'), (219, 'IECore.StringVectorData', 'IECore.StringVectorData', (["['/camera']"], {}), False, 'import IECore\n'), (235, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\t\tinfo = parent["__chunkedBakeInfo"]\n\t\t\t\tparent["__collectScenes"]["rootNames"] = IECore.StringVectorData( info.keys() )\n\t\t\t\t"""'], {}), False, 'import inspect\n'), (243, 'inspect.cleandoc', 'inspect.cleandoc', (['"""\n\t\t\t\tcameraName = context["collect:cameraName"]\n\t\t\t\tinfo = parent["__chunkedBakeInfo"]\n\t\t\t\ti = info[cameraName]\n\t\t\t\tudimOffset = i["udim"].value - 1001\n\t\t\t\tparent["__cameraTweaks"]["tweaks"]["resolution"]["value"] = imath.V2i( i["resolution"].value )\n\t\t\t\tparent["__cameraTweaks"]["tweaks"]["u_offset"]["value"] = -( udimOffset % 10 )\n\t\t\t\tparent["__cameraTweaks"]["tweaks"]["v_offset"]["value"] = -( udimOffset / 10 )\n\t\t\t\tparent["__cameraTweaks"]["tweaks"]["mesh"]["value"] = i["mesh"].value\n\t\t\t\tparent["__cameraTweaks"]["tweaks"]["uv_set"]["value"] = parent["uvSet"] if parent["uvSet"] != "uv" else ""\n\t\t\t\t"""'], {}), False, 'import inspect\n'), (323, 'Gaffer.NameValuePlug', 'Gaffer.NameValuePlug', (['"""bakeDirectory"""', '""""""', '"""bakeDirectoryVar"""'], {}), False, 'import Gaffer\n'), (324, 'Gaffer.NameValuePlug', 'Gaffer.NameValuePlug', (['"""defaultFileName"""', '""""""', '"""defaultFileNameVar"""'], {}), False, 'import Gaffer\n'), (394, 'Gaffer.StringPlug', 'Gaffer.StringPlug', ([], {}), False, 'import Gaffer\n'), (396, 'Gaffer.StringPlug', 'Gaffer.StringPlug', ([], {}), False, 'import Gaffer\n'), (422, 'Gaffer.StringPlug', 'Gaffer.StringPlug', ([], {}), False, 'import Gaffer\n'), (429, 'IECore.CompoundObject', 'IECore.CompoundObject', ([], {}), False, 'import IECore\n'), (513, 'IECore.StringData', 'IECore.StringData', ([], {}), False, 'import IECore\n'), (514, 'IECore.StringData', 'IECore.StringData', ([], {}), False, 'import IECore\n'), (70, 'IECore.StringVectorData', 'IECore.StringVectorData', ([], {}), False, 'import IECore\n'), (398, 'IECore.StringVectorData', 'IECore.StringVectorData', ([], {}), False, 'import IECore\n'), (542, 'IECore.StringVectorData', 'IECore.StringVectorData', ([], {}), False, 'import IECore\n')]