macroScript SpriteMakerScript category:"Mikes tools" buttonText:"Sprite" tooltip:"Create Sprite Sheet" Icon:#("Gamebryo", 3) ( SequenceNames = #("Invalid") SequenceStartFrames = #(0) SequenceEndFrames = #(100) SelectedSequence = 1 EditSequenceRollout rollout SpriteMakerRollout "Sprite Maker" width:200 height:448 ( listBox SequenceList "Sequences" pos:[8,8] width:184 height:6 button AddSeqButton "Add..." pos:[8,120] width:56 height:16 button RemoveSeqButton "Remove" pos:[136,120] width:56 height:16 button EditSeqButton "Edit..." pos:[72,120] width:56 height:16 spinner FrameSkipSpinner "Every nth frame" pos:[16,152] width:104 height:16 fieldwidth:24 range:[1,100,1] type:#integer scale:1 spinner CellWidthSpinner "Cell Width" pos:[16,184] width:80 height:16 fieldwidth:32 range:[1,256,32] type:#integer scale:1 spinner CellHeightSpinner "Cell Height" pos:[112,184] width:80 height:16 fieldwidth:32 range:[1,256,32] type:#integer scale:1 checkbox POTCheck "Force sheet to power of two" pos:[8,208] width:152 height:16 enabled:true checked:true checkbox AntialiasingCheck "Antialiasing" pos:[8, 232] width:80 height:16 spinner CameraPOVSpinner "Angle Count" pos:[104, 232] width:88 height:16 fieldwidth:24 range:[1,100,1] type:#integer scale:1 spinner ObjectIDSpinner "ObjectID" pos:[8, 256] width: 80 height: 16 fieldwidth:24 range:[0,100,0] type:#integer scale:1 groupBox StatsGroup "Post-Processing" pos:[8, 280] width:184 height:128 checkBox PPEnabledCheck "Post Processing Enabled" pos:[16, 304] editText TempFolderEdit "Temp:" pos:[16, 328] width:144 button TempFolderPick "..." pos:[168, 328] width:16 editText PPMEdit "PPM:" pos:[16, 352] width:144 button PPMPick "..." pos:[168, 352] width:16 editText ConfigEdit "Config:" pos:[16, 376] width:144 button ConfigPick "..." pos:[168, 376] width:16 button CreateButton "Create Sheet" pos:[8,416] width:88 height:24 button CancelButton "Close" pos:[103,416] width:89 height:24 function LoadSetting settingsObject settingName defaultValue = ( storedSetting = getUserProp settingsObject settingName if (storedSetting != undefined) then return storedSetting else return defaultValue ) function LoadSequences = ( settingsObject = $SpriteMakerDummy if settingsObject == undefined then ( SequenceNames = #("Default") SequenceStartFrames = #(0) SequenceEndFrames = #(30) return() ) SequenceNames = #() SequenceStartFrames = #() SequenceEndFrames = #() -- load sequence data SequenceString = getUserPropBuffer settingsObject SequenceStartIndex = findString SequenceString "!StartSequences!" sequenceEndIndex = findString SequenceString "!EndSequences!" SequenceString = subString SequenceString (SequenceStartIndex + ("!StartSequences!").count) (SequenceEndIndex - ("!StartSequences!").count - 1) SequenceStream = SequenceString as stringstream nextToken = peekToken SequenceStream while (nextToken != undefined) do ( nextName = readToken SequenceStream nextStart = readToken SequenceStream nextEnd = readToken SequenceStream if nextName != undefined and nextStart != undefined and nextEnd != undefined then ( append SequenceNames nextName append SequenceStartFrames (nextStart as integer) append SequenceEndFrames (nextEnd as integer) ) nextToken = peekToken SequenceStream ) -- now load common params FrameSkipSpinner.value = LoadSetting settingsObject "FrameSkipValue" 1 CellWidthSpinner.value = LoadSetting settingsObject "CellWidth" 32 CellHeightSpinner.value = LoadSetting settingsObject "CellHeight" 32 AntialiasingCheck.checked = LoadSetting settingsObject "AntialiasingOn" false CameraPOVSpinner.value = LoadSetting settingsObject "AngleCount" 1 PPEnabledCheck.checked = LoadSetting settingsObject "PPEnabled" false TempFolderEdit.text = LoadSetting settingsObject "TempFolder" "C:\\Temp" PPMEdit.text = LoadSetting settingsObject "PPM" "C:\\PostProcessManager.exe" ConfigEdit.text = LoadSetting settingsObject "Config" "C:\\config.xml" POTCheck.checked = LoadSetting settingsObject "ForcePOT" true ObjectIDSpinner.value = LoadSetting settingsObject "ObjectID" 0 ) function SaveSequences = ( settingsObject = $SpriteMakerDummy if settingsObject == undefined then ( settingsObject = dummy name:"SpriteMakerDummy" ) SequenceString = "!StartSequences!\n" for i = 1 to SequenceNames.count do ( SequenceString += SequenceNames[i] + " " SequenceString += SequenceStartFrames[i] as string + " " SequenceString += SequenceEndFrames[i] as string + "\n" ) SequenceString += "!EndSequences!\n" setUserPropBuffer settingsObject SequenceString setUserProp settingsObject "FrameSkipValue" FrameSkipSpinner.value setUserProp settingsObject "CellWidth" CellWidthSpinner.value setUserProp settingsObject "CellHeight" CellHeightSpinner.value setUserProp settingsObject "AntialiasingOn" AntialiasingCheck.checked setUserProp settingsObject "AngleCount" CameraPOVSpinner.value setUserProp settingsObject "PPEnabled" PPEnabledCheck.checked setUserProp settingsObject "TempFolder" TempFolderEdit.text setUserProp settingsObject "PPM" PPMEdit.text setUserProp settingsObject "Config" ConfigEdit.text setUserProp settingsObject "ForcePOT" POTCheck.checked setUserProp settingsObject "ObjectID" ObjectIDSpinner.value ) function UpdateList = ( SequenceList.items = #() for i = 1 to SequenceNames.count do ( newItem = SequenceNames[i] + " " newItem += (SequenceStartFrames[i] as string) + " " newItem += (SequenceEndFrames[i] as string) newArray = SequenceList.items append newArray newItem SequenceList.items = newArray ) ) function SequenceEditCallback newName newStart newEnd = ( -- make sure a valid name is given if newName == "" or newName == undefined then ( newName = "Sequence" + (SelectedSequence as string) ) SequenceNames[SelectedSequence] = newName SequenceStartFrames[SelectedSequence] = newStart SequenceEndFrames[SelectedSequence] = newEnd UpdateList() SaveSequences() ) function RepositionTargetCamera camera angleIndex angleCount = ( if not isKindOf camera Targetcamera then return() currentTarget = camera.target if currentTarget == undefined then return() deltaX = camera.Position.x - currentTarget.Position.x deltaY = camera.Position.y - currentTarget.Position.y flatDistance = sqrt (deltaX * deltaX + deltaY * deltaY) actualAngle = ((angleIndex - 1) as float / angleCount as float) * 360.0 camera.Position.x = cos(actualAngle) * flatDistance * -1.0 camera.Position.y = sin(actualAngle) * flatDistance * -1.0 ) on AntialiasingCheck changed newValue do ( SaveSequences() ) on CameraPOVSpinner changed newValue do ( SaveSequences() ) on CellWidthSpinner changed newValue do ( SaveSequences() ) on CellHeightSpinner changed newValue do ( SaveSequences() ) on FrameSkipSpinner changed newValue do ( SaveSequences() ) on ObjectIDSpinner changed newValue do ( SaveSequences() ) on AddSeqButton pressed do ( SelectedSequence = SequenceNames.count + 1 createDialog EditSequenceRollout modal:true ) on EditSeqButton pressed do ( SelectedSequence = SequenceList.selection if SelectedSequence == 0 then SelectedSequence = 1 EditSequenceRollout.NameEdit.text = SequenceNames[SelectedSequence] EditSequenceRollout.StartSpinner.value = SequenceStartFrames[SelectedSequence] as integer EditSequenceRollout.EndSpinner.value = SequenceEndFrames[SelectedSequence] as integer createDialog EditSequenceRollout modal:true ) on RemoveSeqButton pressed do ( sequenceToRemove = SequenceList.selection if sequenceToRemove == 0 then return() deleteItem SequenceNames sequenceToRemove deleteItem SequenceStartFrames sequenceToRemove deleteItem SequenceEndFrames sequenceToRemove UpdateList() SaveSequences() ) on PPEnabledCheck changed newValue do ( SaveSequences() ) on TempFolderEdit entered newValue do ( SaveSequences() ) on TempFolderPick pressed do ( tempPath = getSavePath caption:"Choose Temp Folder" initialDir:TempFolderEdit.text if tempPath != undefined then ( TempFolderEdit.text = tempPath SaveSequences() ) ) on PPMEdit entered newValue do ( SaveSequences() ) on PPMPick pressed do ( tempPath = getOpenFilename caption:"Set Post Process Manager path" filename:PPMEdit.text types:"executable(*.exe)|*.exe|" if tempPath != undefined then ( PPMEdit.text = tempPath SaveSequences() ) ) on ConfigEdit entered newValue do ( SaveSequences() ) on ConfigPick pressed do ( tempPath = getOpenFilename caption:"Set Configuration file" filename:ConfigEdit.text types:"XML(*.xml)|*.xml|" if tempPath != undefined then ( ConfigEdit.text = tempPath SaveSequences() ) ) on CreateButton pressed do ( -- here is where the magic happens -- collect the UI params angleCount = cameraPOVSpinner.value as integer frameSkip = FrameSkipSpinner.value as integer cellWidth = CellWidthSpinner.value as integer cellHeight = CellHeightSpinner.value as integer antialiasingOn = AntialiasingCheck.checked pPEnabled = PPEnabledCheck.checked tempFolder = TempFolderEdit.text pPM = PPMEdit.text config = ConfigEdit.text objectID = ObjectIDSpinner.value -- validate post processing options if pPEnabled then ( -- does the temp directory exist? if not doesFileExist tempFolder then ( messageBox "Temp folder doesn't exist. Select new folder and try again." return() ) -- does the post process manager file exist? if not doesFileExist pPM then ( messageBox "Post process manager file doesn't exist. Select new file and try again." return() ) -- does the config file exist? if not doesFileExist config then ( messageBox "Chosen config file doesn't exist. Select new file and try again." return() ) ) -- determine camera turnaround parameters activeCam = getActiveCamera() if not isKindOf activeCam Targetcamera then activeCam = undefined if activeCam == undefined then angleCount = 1 oldCamPos = [0, 0, 0] if activeCam != undefined then oldCamPos = activeCam.Position -- determine how many cells we will need cellsWide = 1 cellsHigh = SequenceNames.count * angleCount for i = 1 to SequenceNames.count do ( sequenceCells = (SequenceEndFrames[i] - SequenceStartFrames[i]) / frameSkip sequenceCells = sequenceCells as integer sequenceCells += 1 if sequenceCells > cellsWide then ( cellsWide = sequenceCells ) ) -- compute the final sheet size sheetWidth = cellsWide * cellWidth sheetHeight = cellsHigh * cellHeight -- round up to the nearest power of two, if necessary if POTCheck.checked then ( counter = 1 while counter < sheetWidth do counter = counter * 2 sheetWidth = counter counter = 1 while counter < sheetHeight do counter = counter * 2 sheetHeight = counter ) -- create our bitmaps sheetBitmap = bitmap sheetWidth sheetHeight color:(color 255 0 255) renderTarget = bitmap cellWidth cellHeight -- loop over sequences for i = 1 to SequenceNames.count do ( -- loop over angles for a = 1 to angleCount do ( -- re-orient the camera if (activeCam != undefined) then RepositionTargetCamera activeCam a angleCount -- loop over frames sequenceCells = (SequenceEndFrames[i] - SequenceStartFrames[i]) / frameSkip sequenceCells = sequenceCells as integer sequenceCells += 1 for j = 1 to sequenceCells do ( -- determine the time frameTime = SequenceStartFrames[i] + (j - 1) * frameSkip if frameTime > SequenceEndFrames[i] then frameTime = SequenceEndFrames[i] sliderTime = frameTime -- do the actual render if (isKindOf renderers.current Default_Scanline_Renderer) then render to:renderTarget antiAliasing:antialiasingOn channels:#(#objectID) else render to:renderTarget targetX = (j - 1) * cellWidth -- copy the pixel data over for sourceY = 0 to (cellHeight - 1) do ( targetY = ((i - 1) * angleCount + (a - 1)) * cellHeight + sourceY if (objectID == 0) then ( rowColors = GetPixels renderTarget [0, sourceY] cellWidth SetPixels sheetBitmap [targetX, targetY] rowColors ) else ( -- if we are filtering to a specifc objectID, only copy over the pixels that match for sourceX = 0 to (cellheight - 1) do ( channelValues = getChannel renderTarget [sourceX, sourceY] #objectID currentColor = getPixels renderTarget [sourceX, sourceY] 1 targetX = (j - 1) * cellWidth + sourceX if (channelValues[1] == objectID) then SetPixels sheetBitmap [targetX, targetY] currentColor else SetPixels sheetBitmap [targetX, targetY] #(color 0 0 0 0) ) ) ) ) -- end frames loop ) -- end angles loop ) -- end sequences loop -- return the original camera position if activeCam != undefined then activeCam.Position = oldCamPos -- if we don't post process, just pop up the results if not pPEnabled then ( -- display the result so it can be saved unDisplay renderTarget display sheetBitmap ) else ( -- if post processing is enabled (we have already validated settings) unDisplay renderTarget -- first, save our bitmap out to a temp file tempFilename = tempFolder + "\\tempSpriteSheet.png" sheetBitmap.filename = tempFilename save sheetBitmap -- delete a previous copy of our output file outputFilename = tempFolder + "\\Output.png" deleteFile outputFilename -- construct a string to send to a DOS window command = ("\"" + pPM + "\"") args = ("\"" + config + "\" \"" + tempFilename + "\" \"" + outputFilename + "\"") -- write that string to a temporary batch file (so that we aren't limited in arg length batFilename = tempFolder + "\\temp.bat" deleteFile batFilename batFile = createFile batFilename format "% %" command args to:batFile close batFile -- execute our bat file DOSCommand (batFilename) -- give the other process a while to write the output file if ((getFiles outputFilename).count != 0) then ( shellLaunch outputFilename "" ) ) ) on CancelButton pressed do ( destroyDialog SpriteMakerRollout ) on SpriteMakerRollout open do ( LoadSequences() UpdateList() ) ) -- end rollout rollout EditSequenceRollout "Edit Sequence" width:216 height:80 ( editText NameEdit "Name:" pos:[8,8] width:144 height:16 spinner StartSpinner "Start Frame" pos:[16,32] width:136 height:16 range:[0,10000,0] type:#integer scale:1 spinner EndSpinner "End Frame" pos:[16,56] width:136 height:16 range:[0,10000,30] type:#integer scale:1 button OkButton "OK" pos:[160,8] width:48 height:64 on OkButton pressed do ( SpriteMakerRollout.SequenceEditCallback NameEdit.text StartSpinner.value EndSpinner.value destroyDialog EditSequenceRollout ) ) -- end rollout rollout RenderOptionsRollout "Render Options" width:216 height:80 ( ) on execute do ( createDialog SpriteMakerRollout ) ) -- end macroscript