2D TO 3DSMAX AUTOMATION
I’d like to explain the Art Pipeline I crafted for bringing 2D non-modular environment art into a 3D engine.
The first two projects I was assigned on at Way Forward were 2D games running on a 3D engine. Each level had unique, hand-painted background art; meaning that we couldn’t simply use tile sets or modular pieces to get the background art in-game; every level needed to have its unique background art assets imported wholesale. The problem was…the level art was ginormous; averaging about 20,000 pixels wide 4,000 pixels tall. Even with our engine’s TGA compression, that was much too large for a single file. Since the background art process was already fairly cemented, we couldn’t reasonably switch over to tilesets without throwing away a lot of resources. The only solution was to cut up the art into several TGA files instead of one giant texture. And so, the first major iteration of my Art Pipeline featured automation in Photoshop and 3DSMax to bring the art into 3DSMax for exporting to the engine.
In Photoshop, the user would create layers of rectangles that would overlay the art; with a handful hopefully being able to cover the whole level art. These rectangle layers are essentially the jigsaw pieces that the level art will be cut up into.
In addition to the CutUp script, I also developed several more minor scripts that would perform other batch tasks necessary for the level art Pipeline. This included an Extend script that would add a bleeding pixel border to prevent seams in-engine. There was also a script that would generate a txt file of every rectangle layer XY coordinate in the level art PSD for 3DSMax purposes that will be explained in the next paragraph.
With the generated TGA files, it was time to bring the level art into 3DSMax. Being a 2D game in 3D space, the level art pieces would essentially be textures on various-sized planes. I created a maxscript that would batch through every TGA file in a folder, create a new Plane, and save it out as its own max file. It would also resize itself based on the size of the texture and move the pivot point to the bottom left corner for lining up purposes.
Once all the plane files were created, the user would import them into the proper Level max file. Unfortunately, the planes would all be at the origin point.
I devised another maxscript that read XY coordinates from an ini file generated in Photoshop (mentioned in above paragraph). The script name matches the plane to the proper coordinates found in the ini file and then moves each plane into its proper position.
The end result would be that all the planes would line up perfectly, recreating the same image as was in the photoshop file originally. The user will then use Way Forward’s proprietary tools to export level data and assets directly from 3DSMax. Lastly, the user would run a build of the game to view how the level art appears in-engine.
The second major iteration of this Art Pipeline process greatly improved the Photoshop portion of the process. Another major Tech Art challenge came when our project was trying condense the file size of the entire game into 1 GB. Since level art was not being resued throughout all the levels, it was quite large; ballooning the game to three times the required size of the cartridge. The solution was to get rid of as much alpha as possible. Previously, our pipeline had users create relatively big rectangle layers to cut up the levels into several large TGA files. However, minimizing unneeded alpha space required much more rectangles to cut around the alpha; rectangles in the hundreds per level. Going from about 5 to 10 rectangle layers to 150 to 300 meant a lot more user time. So, the next major iteration was further automating the CutUp script to generate the rectangles as well as cut them! Even though it was exponentially more TGA files, they were each smaller, plus the lack of alpha on many of them meant that each level would average out three times less MBs.
We created a script that would divide the photoshop file into N number of grid squares. The script would then store each grid square into an array, throwing out any empty grid squares. It would also combine like-sized neighboring grid squares to minimize the number of final TGAs. Lastly, the script would push in the sides of the grid squares to further minimize the alpha.