Thursday, 3 February 2022

Sequence 2 Video

This handy little script will take any image sequence, exr, png, jpg etc, and combine it into a h264 mp4 video. 

There is a bat file in the repo below that needs to be added to a menu in windows. Open an explorer window and type "shell:sendto" in the address bar. Place the seq2Vid_CG.bat file in here and edit it so that it points to the locaiton of the downloaded Seq2Vid_v03.py

Once this has been done and FFMPEG has been installed (in C:/Dev/FFMPEG/bin....) you can now RMB on any image from a sequence and click "Send to" and then Seq2Vid_v03.py. The script will then run in a command line and create a video preview, which will be in the correct locaiton based off our previosuly created folder structure (having used the FolderGeneratoin_v001.py script)


Wednesday, 26 January 2022

Max File Automation

As part of my plan to overhaul the pipeline at work I've created a few scripts to manage file creation and saving. The idea is to standardize file naming, render outputs and render passes with the plan to automate later stages of the pipeline such as auto creating previews and nuke projects.

-------------------------------------------------------------------------------------
As these scripts are getting larger and larger I've decided to share my public Git repo rather than pasting the entire code block here.
https://github.com/eoin-personal/Public-Repo
-------------------------------------------------------------------------------------


What this main script will do: 

  • Search the target drive for available project folders
  • Create max file in the correct location based of the project selected in the UI.
  • Set render outputs to the correct location with the correct naming convention.
  • Set all render passes to the studio standard.
  • Set 3D Studio Max settings (fps, render size) based of selection of a 16:9 or 360 project.
  • Allow for scene or asset generation with some different settings based on this choice.
  • Allow for the Version and Take up tools to work, maintaining the file naming convention and updating the render outputs when necessary.

The UI in action:


There are 3 additional scripts that I've created for the pipeline overhaul.

The first is a folder generation script. This contains 2 parts, a bat file and a python file. The bat file can be placed in any location on any drive and once the path inside points to the python file it will create a folder structure in the root of the drive the bat file is currently stored on. The name of this project will fit our current format  <Date>_<JobCode>_<JobName> with the user providing the job code and name and the script auto formatting the current date into a usable format.

The second and third files are for versioning up the max files created using the Max Project Generator script. These will maintain the correct naming conventions and update with new render paths if the prompt is answered with yes.

VersionUp_v001.ms

TakeUp_v001.ms

Wednesday, 27 October 2021

I fixed it.......for now

After putting the original script through the production pipeline I noticed a few errors. These had been fine when working off one max file, but once I used the loop to go through multiple max files the issues started to creep in. 
3DS Max stores variables once they are assigned and this is something I need to get used to when debugging. If I ran the script once and stepped through to solve an issue, any variables I had manually set during this would be locked in place till overwritten or Max was closed. This led to a variable not being set correctly in the loops and this only became apparent once I got the final results back, the files had rendered out correctly but not in the right folders. My first clue was that each image sequence was begin stored in the previous folder according o the order of the main loop.

The issue was with the render output filename and path, with the variable "verShotFolder" This variable was being set inside of an 'if' loop and not being stored outside of it afterwards. 
The solution was to create this variable at the beginning of the whole script as verShotFolder = ""

I also split some of that loop up as the...
if (doesfileexist verShotFolder) != true do
(makeDir verShotFolder)
...was being called the same no matter the result of the first loop.

----------------------------------------------------------------------------------------------------------------------

Debugging this code was a bit of a mission, but I feel I've learned a significant flag to watch out for in future coding exercises. If a variable from inside a loop is being called outside of it, that variable must be declared first, outside of the loop, so that the loop can fill it in, otherwise the variable from the loop will only hold the value assigned to it for the duration of the loop.

----------------------------------------------------------------------------------------------------------------------

maxFiles = getfiles @"O:\VirtualTour_VR\3D\Projects\Scenes\ScriptBackup\*.max"
::filein @"O:VirtualTour_VR\Scripts\DeadlineSubmitter_v01.ms"
mergeFileName = @"O:\VirtualTour_VR\3D\Projects\Scenes\ScriptTest\NovartisUpdatedHead01.max"
verShotFolder = ""
for i = 1 to maxFiles.count do
(
loadMaxFile (maxfiles[i])
verShotFolder = ""
---------------------------------------------------------------------------
----------- Update geo and texture

oldHeadGeo = $Char_Head
headParent = $Char_CTRL_Main
bandRemove = $Char_HeadStrip
innerHead = $Char_InnerHead

mergeMAXFile mergeFileName  #useMergedMtlDups
newHeadGeo = $NovartisUpdatedHead01
newHeadGeo.transform = oldHeadGeo.transform
newHeadGeo.parent = headParent
innerHead.parent = headParent
delete oldHeadGeo
delete bandRemove
viewport.setCamera $PhysCamera001
meditMaterials[1] = sceneMaterials["Head_Body"]
$Char_Body.material = meditMaterials[1]

---------------------------------------------------------------------------
----------- Make new folder for v2.2

renderSceneDialog.close()
rc=renderers.current
rc.output_saveRawFile=true
rendTimeType = 2
ogOutput = rc.output_rawFileName
verFolder = pathConfig.removePathLeaf ogOutput
verFolderTest = (filterstring (verFolder) "\\")[(filterstring (verFolder) "\\").count]

rendFileOuputSplit = (filterstring (ogOutput) "\\")[(filterstring (ogOutput) "\\").count]
versionUp =  (filterstring (rendFileOuputSplit) ".")
versionPt2 = versionUp[2]
versionPt2Solo = versionPt2[1] as integer
versionUpSolo = (versionPt2Solo + 1) as string
UppedVerFilename = versionUp[1] + "." + versionUpSolo + "_" + "." + versionUp[3]
--outputPath = verShotFolder + "\\" + UppedVerFilename

ogFileName = (filterstring (ogOutput) "\\")[(filterstring (ogOutput) "\\").count]
ogFileVerMain =  (filterstring (ogFileName) ".")[1]
mainVer = ogFileVerMain[ogFileVerMain.count]
targetVersion =  "v" + mainVer + "." + versionUpSolo
shotFolder = pathConfig.removePathLeaf verFolder
if (verFolderTest == "Intro") then
(
verShotFolder = (verFolder + "\\" + targetVersion)
)
else if (verFolderTest == "Outro") then
(
verShotFolder = (verFolder + "\\" + targetVersion)
)
else
(
verShotFolder = (shotFolder + "\\" + targetVersion)
)
if doesfileexist(verShotFolder) != true do
(
makeDir verShotFolder
)

---------- Set Vray render output to new version

outputPath = verShotFolder + "\\" + UppedVerFilename
rc.output_rawFileName = outputPath

---------- Save new max file version
maxFilePathSplit = (filterstring (maxfilepath) "\\")
parentMaxFolder = pathConfig.removePathLeaf maxfilepath
newMaxFileSave = parentMaxFolder
maxFileNameSplit =  (filterstring (maxfilename) ".")
maxFileVersion = maxFileNameSplit[2] as integer
maxFileVersioningUp = (maxFileVersion + 1) as string
newMaxFileName = maxFileNameSplit[1] + "." + maxFileVersioningUp + "." + maxFileNameSplit[3]
newMaxFileNameFullPath = maxfilepath + newMaxFileName
if (doesfileexist newMaxFileNameFullPath) != true then
(
saveMaxFile newMaxFileNameFullPath
)
else
(
messagebox "!!!Warning!!!\nA more recent max file already exists\nJob has been submitted anyway"
)

----------Send to deadline

_DlJobName = maxfilename
_Priority = 30
_Comment = "-- Submitted Via DLSubmitJob"
_UserName = "Eoin"
_Group = "singles"
DLRenSubmit _DlJobName _Priority _Comment _UserName _Group 
)



Wednesday, 13 October 2021

Pipeline Beginnings

 I've been learning to code over the past few years but have recently started a course "TD Certification" by Allan Mckay. I've decided to track my progress here so will be posting up any scripts I work on for work and anything else I can think to script.

----------------------------------------------------------------------------------------------------------------------

I've found in the past that I have struggled to find applications for the coding I was learning, but as a result of this course I feel I now have enough knowledge, and the ability to plan out a script. I had always found blank page syndrome difficult to deal with in relation to coding, never really knowing what could be done and how to go about it. Through the course I have found out that the easiest way is to break any job up into single steps and then work on each one of those one by one, combining them where needed. The course began focusing on Maxscript but towards the end we have moved on to Python, where I would have a small amount of experience which I am now finding I have a greater understanding than i thought I did beforehand.

----------------------------------------------------------------------------------------------------------------------

At the bottom of this post is the code for the first situation I have found where I can apply what I have learned to a task at work - I needed to replace geometry and update a texture for 15 files and then submit each one to Deadline with a versioned up folder and file name. Below is the plan I came up with to get myself started.

1.Import new head
2.Extract new texture
3.Apply new texture to body
4.Align new head to old one
5.Attach head to 'Char_CTRL_Main'
6.Remove old head and band
7.Version up render output
8.Turn render on
9.Make sure correct viewport is rendering
10.Save new max file
        11.Send to farm

While going through these steps I realised I could skip step 2 by bringing the new texture and setting a flag <#useMergedMtlDups> on the import function to specify to use the updated merged material instead of the scene one. EDIT: this unfortunately did not work as planned at which point I figured out how to transfer a texture from the imported geo to the rest of the geo in the scene that needed it to be applied on.

At the beginning of this exercise I had to pipe certain text items directly in, mainly around the version number for the max files and render outputs, but as I progressed I figured out some ways to extract these with code, thus allowing for the script to be used beyond 'v2.1'.
At present the only things that need to be entered manually are the location of the max files folder, and the assets that need to be replaced and/or deleted.

I really enjoyed this exercise, it was a great excuse to test out what I've learned over the last 2 months, both in terms of knowledge and how to go about building a script. Plenty of Google searching and testing single lines of code, but the satisfaction of getting something like this to work is hard to put to words.

----------------------------------------------------------------------------------------------------------------------

I am hoping I will get some more jobs that will allow me to push myself further, and I have some plans for a studio pipeline overhaul, to change the folder and file naming structure to allow for much greater automation of simple tasks.

----------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------

maxFiles = getfiles @"O:\VirtualTour_VR\3D\Projects\Scenes\*.max"
for i = 1 to maxFiles.count do
(
loadMaxFile (maxfiles[i])

::filein @"O:\VirtualTour_VR\Scripts\DeadlineSubmitter_v01.ms"
mergeFileName =  
        @"O:\VirtualTour_VR\3D\Projects\Scenes\ScriptTest\CharUpdatedHead01.max"

---------------------------------------------------------------------------
----------- Update geo and texture

oldHeadGeo = $Char_Head
headParent = $Char_CTRL_Main
bandRemove = $Char_HeadStrip
innerHead = $Char_InnerHead


mergeMAXFile mergeFileName  #useMergedMtlDups
newHeadGeo = $UpdatedHead01
newHeadGeo.transform = oldHeadGeo.transform
newHeadGeo.parent = headParent
innerHead.parent = headParent

delete oldHeadGeo
delete bandRemove

        meditMaterials[1] = sceneMaterials["Head_Body"]
        $Char_Body.material = meditMaterials[1]

viewport.setCamera $PhysCamera001

---------------------------------------------------------------------------
----------- Make new folder and rename output path

        renderSceneDialog.close() rc=renderers.current rc.output_saveRawFile=true rendTimeType = 2 ogOutput = rc.output_rawFileName verFolder = pathConfig.removePathLeaf ogOutput verFolderTest = (filterstring (verFolder) "\\")[(filterstring (verFolder) "\\").count] rendFileOuputSplit = (filterstring (ogOutput) "\\")[(filterstring (ogOutput) "\\").count] versionUp = (filterstring (rendFileOuputSplit) ".") versionPt2 = versionUp[2] versionPt2Solo = versionPt2[1] as integer versionUpSolo = (versionPt2Solo + 1) as string UppedVerFilename = versionUp[1] + "." + versionUpSolo + "_" + "." + versionUp[3] outputPath = verShotFolder + "\\" + UppedVerFilename ogFileName = (filterstring (ogOutput) "\\")[(filterstring (ogOutput) "\\").count] ogFileVerMain = (filterstring (ogFileName) ".")[1] mainVer = ogFileVerMain[ogFileVerMain.count] targetVersion = "v" + mainVer + "." + versionUpSolo if (verFolderTest == "Intro") or (verFolderTest == "Outro") then ( verShotFolder = (verFolder + "\\" + targetVersion) if doesfileexist(verShotFolder) != true do ( makeDir verShotFolder ) ) else ( shotFolder = pathConfig.removePathLeaf verFolder verShotFolder = (shotFolder + "\\" + targetVersion) if doesfileexist(verShotFolder) != true do ( makeDir verShotFolder ) ) 
rc.output_rawFileName = outputPath


---------- Save new max file version

maxFilePathSplit = (filterstring (maxfilepath) "\\")
parentMaxFolder = pathConfig.removePathLeaf maxfilepath
newMaxFileSave = parentMaxFolder

maxFileNameSplit =  (filterstring (maxfilename) ".")
maxFileVersion = maxFileNameSplit[2] as integer
maxFileVersioningUp = (maxFileVersion + 1) as string
newMaxFileName = maxFileNameSplit[1] + "." + maxFileVersioningUp + "." +                                    maxFileNameSplit[3]
newMaxFileNameFullPath = maxfilepath + newMaxFileName
if (doesfileexist newMaxFileNameFullPath) != true then
(
saveMaxFile newMaxFileNameFullPath
)
else
(
messagebox "!!!Warning!!!\nA more recent max file already exists\nJob has been submitted            anyway"
)
----------Send to deadline

DLRenSubmit _JobName _Priority _Comment _UserName _Group 

)


Friday, 5 June 2015

Unity

So I've started looking into some games studios recently and I decided it would be a good idea to try and get to grips with Unity, the software that most of them use.

I had no experience with game engines so it took a lot of tinkering and problem solving but I managed to string together a simple idle, walk and run cycle.

I started out creating a basic idle cycle with a character from my time on iAnimate. While I was happy with the cycle, whenever I tried to import it into Unity the mesh broke, with the head detatching from the body, and none of my animations were transferred across.


I tried several solutions to get the character into Unity but I reached the conclusion that the characters I had were not built to go into it. I searched online and through all my rigs to see if one would be suitable to transfer across while also allowing me to animate with it in Maya but could not find any. So I took the Max rig, removed the mesh from the rig and re-rigged it myself using abAutoRig, found at http://www.supercrumbly.com/3d.php?sid=173#.VXGcm89VhBc


With this rig I was able to animate all my cycles and import them into Unity. On each cycle I had to bake the animation into the bones and then export the bones and mesh as an FBX.
I imported the base rig, mesh and bones, into unity and then imported my 3 cycles. Using the animate tab I created transitions between the idle and walk and the walk and run cycles.


I found this exercise really interesting and rewarding. I always enjoy trying out a new piece of software and I found most of the issues I encountered helped me out with figuring out how unity works at a basic level, at least as far as creating animations that will work inside of it.

Tuesday, 27 January 2015

Parkour 2.0

My Final project as part of the Ianimate course. The brief was to have a character run from one location to another and to incorporate some acting and camera movement and cuts. I feel a lot of what I have learned over the previous two shots came through in this one and I tried to push the 2D aspect of the finished piece instead of focusing on the 3D world space of the scene so much.



And that's it for Ianimate for now, I got a huge amount from the course, learning a proper workflow which was something I had struggled with before, really pushing my poses and getting them locked down before moving on to breakdowns and knowing at what point I'm ready to start polishing to avoid confusion at a later stage. I may return to complete another semester at some stage but for now I'm gonna take what I've learned and apply it to the work I'm currently involved with.