-- Author : Mike Wilson (mike.wilson@rockstarnorth.com) -- cutfileLoader plugin filein "pipeline/util/lighteditor_ng.ms" global string g_lastDir global string g_lastCutFile global string g_lastLightFile global g_cutFileLastWrite global g_lightFileLastWrite global int g_perforce = true global g_DateTime = dotNetClass "System.DateTime" fn SetPerforceIntegration p4 = ( g_perforce = p4 RsProjectSetPerforceIntegration g_perforce ) fn CleanUpScene = ( local objsDeleted = true while objsDeleted do ( objsDeleted = false for scene_node in rootScene.world.children do ( if scene_node != undefined then ( if (getuserprop scene_node "persistant_geo") == undefined then ( delete scene_node objsDeleted = true ) ) ) ) ) fn LoadCutXMLFile dir = ( gRsPerforce.sync dir silent:true g_lastCutFile = dir local cutFileInfo = (dotNetObject "System.IO.FileInfo" g_lastCutFile) g_cutFileLastWrite = cutFileInfo.LastWriteTime CleanUpScene() Reset() if doesFileExist dir then ( if LoadCutFile dir == true then ( -- set the range of the animation time line to the range of the cut file animationRange = interval 0 (GetCutFileEndRange() as Integer) offsetNode = getNodeByName "Scene_Offset" if (offsetNode != undefined) do ( sceneTranslation = GetSceneOffsetTranslation() sceneRotation = GetSceneOffsetRotation() euler = eulerAngles sceneRotation.x sceneRotation.y sceneRotation.z offsetNode.rotation = eulerToQuat euler offsetNode.position = sceneTranslation ) if RsContentTree.DoesDLCProjectExist dir then ( RsContentTree.SetupProjectAndBranchGlobals dlcProjectName:(RsContentTree.GetDLCProject dir).name ) else ( RsContentTree.SetupProjectAndBranchGlobals() ) return true ) ) return false ) fn ImportLight dir = ( gRsPerforce.sync dir silent:true g_lastLightFile = dir local lightFileInfo = (dotNetObject "System.IO.FileInfo" g_lastLightFile) g_lightFileLastWrite = lightFileInfo.LastWriteTime outOfDateError = false outOfDateString = "" --if doesFileExist dir then ( if ImportLightXML dir == true then ( for i=0 to GetLightCount()-1 do ( myLight = getNodeByName (GetLightName i) if myLight == undefined then ( myLight = RageLight lightType:1 lightIntensity:3.0 if RotationIsRelative i then ( format "Light node '%' has relative rotation\n" myLight.name myLight.rotation = quat (GetLightRotationQuat i).x (GetLightRotationQuat i).y (GetLightRotationQuat i).z (GetLightRotationQuat i).w ) else ( format "Light node '%' has absolute rotation\n" myLight.name euler = eulerAngles (GetLightRotation i).x (GetLightRotation i).y (GetLightRotation i).z myLight.rotation = eulerToQuat euler ) myLight.position = GetLightPosition i myLight.name = GetLightName i myLight.lightColour = Color ((GetLightColour i).x*255) ((GetLightColour i).y*255) ((GetLightColour i).z*255) myLight.lightIntensity = GetLightIntensity i myLight.lightAttenEnd = GetLightFallOff i myLight.lightFalloff = GetLightConeAngle i myLight.lightType = GetLightType i myLight.lightHotspot = GetLightInnerConeAngle i myLight.lightExpFalloff = GetLightExpFallOff i SetLightAttributes i myLight SetLightKeyFrames i myLight if GetLightCameraAnimatedFlag i == false then ( setUserProp myLight "animated" "true" ) if GetLightAttachedName i != "" then ( myAttached = GetNodeByName (GetLightAttachedName i) if myAttached == undefined then ( myAttached = Dummy boxsize:[15,15,15] myAttached.name = (GetLightAttachedName i) myAttached.rotation = quat (GetLightAttachedRotation i).x (GetLightAttachedRotation i).y (GetLightAttachedRotation i).z (GetLightAttachedRotation i).w myAttached.position = GetLightAttachedPosition i ) setUserProp myLight "attached" (GetLightAttachedName i) ) ) -- re-parent the camera / light -- this solely relys on the camera names, if these change the mapping will be wrong cameraname = GetLightCamera i cameraNode = getNodeByName cameraname --HACK: To convert existing asset data, see if there's a node that begins with the name of the light's camera. --This is a best-match scenario, which can cause bugs but it's better than lights being removed entirely. if cameraNode == undefined then ( format "Unable to find camera named '%' for node '%'\n" cameraname myLight.name for child in rootnode.children do ( if ( findString child.name cameraname != 0) then ( cameraname = child.name cameranode = getNodeByName cameraName exit ) ) ) if cameraNode != undefined then ( myLight.parent = cameraNode ) else ( outOfDateString = outOfDateString + ("Unable to find camera \"" + cameraname + "\" in scene to be associated with light " + (GetLightName i) as string) + ".\n" outOfDateError = true ) ) if outOfDateError == true then ( messagebox outOfDateString title:"Out of Date" ) return true ) ) return false ) fn GetAnimatedLightNodes = ( animatedLights = #() for node in $objects do ( if classof node == Dummy then ( if node.children.count > 0 then ( -- iterate every light which is a child of this camera for child in node.children do ( if classof child == RageLight then ( if IsAnimatable child == true then ( append animatedLights child ) ) ) ) ) ) animatedLights ) fn ValidateLightNames rename:false = ( local lightList = #() local invalidNames = #() local promptForRename = true for node in $objects do ( if classOf node != Dummy then ( if not (appendIfUnique lightList node.name) then ( if rename then ( local firstName = node.name node.name = uniqueName node.name append lightList node.name print "---LIGHT RENAMED---" format "% renamed to: %\n" firstName node.name ) else ( if promptForRename then ( local msg = ("Light name " + node.name + " is a duplicate, would you like to auto-rename?") local ret = RsQueryBoxMultiBtn msg title:"Erro: Duplicate Light Names" labels:#("Yes to All","Yes","No","No to All") defaultBtn:2 timeout:0 if ret==1 or ret==2 then ( if ret==1 do rename=true local firstName = node.name node.name = uniqueName node.name append lightList node.name print "---LIGHT RENAMED---" format "% renamed to: %\n" firstName node.name ) else ( if ret==4 do promptForRename=false appendIfUnique invalidNames node.name ) ) else appendIfUnique invalidNames node.name ) ) ) ) if (not rename) and invalidNames.count != 0 then ( ss = stringStream "" format "The following light names are not unique: " to:ss for iName in invalidNames do ( format "%" iName to:ss if (iName != invalidNames[invalidNames.Count]) do format " , " to:ss ) messagebox ss title:"Error: Duplicate Names" return false ) else return true ) fn ValidateUniqueLights dir = ( --rename local scene lights, then check concats ValidateLightNames rename:true gRsPerforce.sync ( RsConfigGetAssetsDir() + "cuts/!!Cutlists/...") silent:true -- iterate every camera and find the lights attached - camera cut objects are Dummy objects in the scene for node in $objects do ( if classof node == Dummy then ( if node.children.count > 0 then ( -- iterate every light which is a child of this camera for child in node.children do ( if classof child == RageLight then ( -- Pre-validate light names while ( (strMessage = ValidateUniqueLightName (RsMakeSafeSlashes dir) child.name) != undefined ) do ( if firstMessage == undefined do firstMessage = strMessage child.name = uniqueName child.name ) if firstMessage != undefined do ( print "---LIGHT RENAMED---" format "%\nrenamed to: %\n" firstMessage child.name ) ) ) ) ) ) ) fn ExportLight dir = ( if IsCutFileLoaded() == 0 or ValidateLightNames() == false then ( return false ) CreateLightXML() containsAnimatedLights = false animatedLightNodes = GetAnimatedLightNodes() Reset() gRsPerforce.sync ( RsConfigGetAssetsDir() + "cuts/!!Cutlists/...") silent:true -- iterate every camera and find the lights attached - camera cut objects are Dummy objects in the scene for node in $objects do ( if classof node == Dummy then ( if node.children.count > 0 then ( -- iterate every light which is a child of this camera for child in node.children do ( if classof child == RageLight then ( sliderTime = 0 strMessage = undefined -- Pre-validate light names while ( (strMessage = ValidateUniqueLightName dir child.name) != undefined ) do ( ss = stringStream "" format "% \n\nDo you want to automatically rename this light?" strMessage to:ss if queryBox ss title:"Rename Light?" then ( child.name = uniqueName child.name ) else ( return false ) ) euler = (quatToEuler child.rotation) if AddLightToLightXML child child.position (point3 euler.x euler.y euler.z) child.transform child.lightType child.lightColour child.lightIntensity child.lightAttenEnd child.lightFalloff child.lightHotspot child.lightExpFalloff == false then ( continue ) ) ) ) ) ) for i = 1 to animatedLightNodes.Count do ( containsAnimatedLights = true CreateAnim (animationRange.start/30) ((animationRange.end + 1)/30) (animationRange.end + 1) animatedLightNodes[i].name dir animatedLightNodes[i].lightType ) if containsAnimatedLights == True then ( for i=0 to animationRange.end do ( for j = 1 to animatedLightNodes.Count do ( child = animatedLightNodes[j] AddFrameToAnim (j-1) child i ) ) ) for i = 1 to animatedLightNodes.Count do ( child = animatedLightNodes[i] if g_perforce == true then ( sceneDir = dir if sceneDir[sceneDir.count] == "/" or sceneDir[sceneDir.count] == "\\" then ( sceneDir = substring sceneDir 1 (sceneDir.count-1) ) p4Dir = sceneDir + "\\" + child.name + ".anim" gRsPerforce.sync p4Dir silent:true if gRsPerforce.edit p4Dir silent:true == false then ( gRsPerforce.add #( "-t", "binary+m", (p4Dir) ) silent:true ) p4Dir = sceneDir + "\\" + child.name + ".clip" gRsPerforce.sync p4Dir silent:true if gRsPerforce.edit p4Dir silent:true == false then ( gRsPerforce.add #( "-t", "binary+m", (p4Dir) ) silent:true ) ) SaveAnim(i-1) ) sceneDir = dir if sceneDir[sceneDir.count] == "/" or sceneDir[sceneDir.count] == "\\" then ( sceneDir = substring sceneDir 1 (sceneDir.count-1) ) assetFileName = sceneDir + "/data.lightxml" cutFileName = sceneDir + "/data.cutxml" if g_perforce == true then ( gRsPerforce.sync assetFileName silent:true if gRsPerforce.edit assetFileName silent:true == false then ( gRsPerforce.add assetFileName silent:true ) ) result = SaveLightXML assetFileName false g_lastLightFile = assetFileName lightFileInfo = dotNetObject "System.IO.FileInfo" g_lastLightFile g_lightFileLastWrite = lightFileInfo.LastWriteTime --Although we wont be updating the cutxml, if they have exported to a different folder, --We need to keep track of the new cutxml g_lastCutFile = cutFileName cutFileInfo = dotNetObject "System.IO.FileInfo" g_lastCutFile g_cutFileLastWrite = cutFileInfo.LastWriteTime return result ) fn BuildCutscene dir preview = ( if g_perforce == true then ( syncDir = RsConfigGetAssetsDir() + "export/anim/cutscene/..." gRsPerforce.sync syncDir silent:false ) if BuildZip dir true preview == false then ( false ) return true ) --Takes in dummy node with name 'AutoClip...' and returns a new unique light name --Function a bit dirty due to naming conventions of lights fn GetLightNameFromParent light = ( if light == undefined or light.parent == undefined do return undefined local cam = light.parent local index = findString cam.Name "AutoClip" if index == undefined do return undefined local newName = replace cam.name index 8 "AC" local camNames = filterString newName "_" local lightNames = filterString light.Name "_" local notFound = true for camName in camNames while notFound do ( if matchPattern camName pattern:"AC*" do ( for i = 1 to lightNames.count while notFound do ( if matchPattern lightNames[i] pattern:"AC*" do ( notFound = false lightNames[i] = camName ) ) ) ) newName = RsArrayJoin lightNames token:"_" uniqueName newName numDigits:3 ) fn CopyLightProps objsFrom objsTo = ( if objsFrom.count != objsTo.count do return false for i=1 to objsFrom.count do ( local fromLight = objsFrom[i] local toLight = objsTo[i] copyattrs fromLight pasteattrs toLight if GetAttrClass fromLight != "Gta LightPhoto" do return false if GetAttrClass toLight != "Gta LightPhoto" do return false local fromLightType = lightEditorInstanceNG.GetLightType fromLight local toLightType = lightEditorInstanceNG.GetLightType toLight --get properties of the source light local excluded = #(#lightType, #Omni_Light, #Free_Spot, #Target_Spot, #Free_Directional_Light, #Target_Directional_Light) --if its capsule to capsule then copy the capsule width, otherwise. skip it! if toLightType == #Capsule and fromLightType != #Capsule then ( join excluded #(#lightWidthAttenEnd, #lightAttenEnd, #lightExpFalloff) ) for prop in (getPropNames fromLight) where findItem excluded prop == 0 do ( local val = getProperty fromLight prop lightEditorInstanceNG.CopyProperty fromLight toLight prop ) --copy the wirecolor setProperty toLight #wireColor (getProperty fromLight #wireColor) ) return true ) fn DuplicateLights lights targets: rename:false copyStyle:#copy = ( if lights == undefined or copyStyle == undefined do ( return false ) if classOf lights != Array do lights = #(lights) if targets == unsupplied or targets == undefined or targets.count == 0 then ( if (maxOps.cloneNodes lights cloneType:copyStyle actualNodeList:&onl newNodes:&nnl) then #nodialog ( --Copy properties across using light editor CopyLightProps onl nnl if rename then ( for i = 1 to nnl.count do ( newName = GetLightNameFromParent nnl[i] if newName != undefined then nnl[i].name = newName ) ) ) ) else ( for target in targets do ( --Clone the selected objects if (maxOps.cloneNodes lights cloneType:copyStyle actualNodeList:&onl newNodes:&nnl) then #nodialog ( --Copy properties across using light editor CopyLightProps onl nnl for i = 1 to nnl.count do ( nnl.parent = target if rename then ( newName = GetLightNameFromParent nnl[i] if newName != undefined then nnl[i].name = newName ) ) ) ) ) --Max adds a redundant userProp onto any copied objects, doesn't even ensure a new line, so remove it for scene_obj in objects do ( ori_prop= getUserPropBuffer scene_obj bad_prop="LastPose = undefined" if matchPattern ori_prop pattern:"*LastPose = undefined*"==true do ( bad_start=findString ori_prop bad_prop new_prop=substring ori_prop 1 (bad_start-1) setUserPropBuffer scene_obj new_prop ) ) ) fn CreateExportBuildScenes scenes numberOfLights = ( for scene in scenes do ( resetMaxFile #noPrompt msg = "\n*" + scene print msg datastream = scene + "/data_stream.cutxml" if LoadCutXMLFile datastream == true then ( for node in $objects do ( if classof node == Dummy then ( if node.name == "OnAllTime" or node.name == "Scene_Offset" then ( continue ) for i = 1 to numberOfLights do ( r = RageLight() r.lightType = 2 r.parent = node -- set attributes to be default castDynamicObjectShadowIndex = getattrindex "Gta LightPhoto" "Cast Dynamic Object Shadow" setattr r castDynamicObjectShadowIndex true softLightIndex = getattrindex "Gta LightPhoto" "Soft Light" setattr r softLightIndex true dayIndex = getattrindex "Gta LightPhoto" "Day" setattr r dayIndex true nightIndex = getattrindex "Gta LightPhoto" "Night" setattr r nightIndex true coronaSizeIndex = getattrindex "Gta LightPhoto" "Corona size" setattr r coronaSizeIndex 0 coronaHDRMultiplier = getattrindex "Gta LightPhoto" "Corona HDR multiplier" setattr r coronaHDRMultiplier 0 ) ) ) if ExportLight scene == true then ( BuildCutscene scene false ) ) ) ) fn ExportCutsceneLightingScenes scenes = ( print "--START--" for scene in scenes do ( resetMaxFile #noPrompt msg = "\n*" + scene print msg datastream = scene + "data.cutxml" if LoadCutXMLFile datastream == true then ( datalight = scene + "data.lightxml" if ImportLight datalight == true then ( ExportLight scene ) ) ) print "\n--END--" ) fn GetDirFromFilename fileName = ( newFileName = RsMakeSafeSlashes fileName dirTokens = filterString newFileName "/" newFileName = "" for i = 1 to dirTokens.count-1 do ( newFileName += (dirTokens[i] + "/") ) newFileName )