-- MaxScript interface to Access database for CMX exporter. -- Don Hopkins, Maxis. -- Check for the plug-in. if (exportCmxFile == undefined) do ( format "*** MAXISCRP.DLX MaxScript Plug-In Not Found!!! ***\n" exit ) -- Close the database in case this code is reloaded. try ( closeDatabase() accessApp = undefined ) catch () -- Globals. fn doExportAll = () -- forward reference redefined later fn loadCmxExporterCache = () -- forward reference redefined later fn keepBlowingChunks = () -- forward reference redefined later fn doDosCommandInDir = () -- forward reference redefined later -- global tempDir = getEnv "TEMP" global tempDir = undefined if (tempDir == undefined) do ( tempDir = "c:\\temp" ) global maxInstallDir = getCurrentDirectory() global accessApp, db, recordSet1, recordSet2 global logFile = stdout global databaseName = "\\\\elmo\\ctg\\projects\\tds\\database\\object.mdb" global databaseCacheName1 = tempDir + "\\MaxAnimationDatabaseCache.ms" global databaseCacheName2 = tempDir + "\\Max3DRTDatabaseCache.ms" global tableName1 = "AnimationMaxExport" global tableName2 = "3DRT" global userName = getUserName() if (userName == undefined) do (userName = "UNKNOWN") global homeDir = getEnv "HOME" if (homeDir == undefined) do (homeDir = "d:\\Users\\" + userName) global sourceSafeProgDir = "S:\\win32" global sourceSafeCmd = "ss.exe" global sourceSafeDir = homeDir global sourceSafeServerDir = "\\\\panther\\ctg72" global accessCmd = "C:\\Program Files\\Microsoft Office\\Office\\Msaccess.exe" global gameDataPath = "\\SimsBuild\\Runtime\\GameData" -- More globals. global cmxExporter global cmxExporterLoader global cmxExporterRollout global cmxExporterFloater global cmxExporterWidth = 500 global cmxExporterHeight = 800 global exporterRecords = #() global curExporterRecord = undefined global exportRecords = #() global exportResult = undefined global exportResults = #() global exportLoadedFiles = #() global exportMissingFiles = #() global blowingChunks = false global chunkSize = 50 -- global chunkSize = 100 global skipToRecord = 0 global processRecords = 999999 global selectedAnimationIndex = 1 global sourceSafeIgnoreInputFlag = " -I-" global sourceSafeNoCommentFlag = " -CNoComment" global poseDynamicTolerance = 0.5 / 12.0 -- half inch global poseStaticTolerance = 1.0 / 12.0 -- 1 inch global verbose = false global poseTable = #() global unexpectedSkills = #() global missingSkills = #() global interestingKeys = #( "Name", "Filename", "MaxPath", "CMX Directory", "Export", "Keywords") -- Environment. global exporterEnvironmentFile = "Scripts\\cmx-exporter-environment.ms" global exporterEnvironmentVariables = #( "accessCmd", "sourceSafeDir", "sourceSafeProgDir", "sourceSafeServerDir", "tempDir", "blowingChunks", "chunkSize", "skipToRecord", "processRecords") global blowChunksFile = "chunks-blowing.txt" global blowChunksLog = "chunk-log" fn writeStringToFile str filename = ( local f = createFile fileName format "%" str to: f close f ) fn readStringFromFile filename = ( local result = "" try ( local f = openFile fileName local done = false while (not done) do ( if (eof f) then ( done = true ) else ( local line = readLine f result = result + line + "\n" ) ) close f ) catch () return result ) fn loadEnvironment = ( local fileName = (maxInstallDir + "\\" + exporterEnvironmentFile) try ( fileIn fileName format "\n\nLoaded exporter environment from \"%\".\n\n" fileName ) catch ( format "\n\nNo exporter environment was defined in \"%\". Using defaults.\n\n" fileName ) for var in exporterEnvironmentVariables do ( local val = execute var format " % = \"%\"\n" var val ) format "\n\n" ) fn saveEnvironment = ( local fileName = (maxInstallDir + "\\" + exporterEnvironmentFile) local f = createFile fileName local var format "\n\nSaving environment to %.\n\n" fileName for var in exporterEnvironmentVariables do ( local val = (execute var) if (isKindOf val String) then ( local strval = (execute var) as String local i local result = "" for i = 1 to strval.count do ( local ch = strval[i] if (ch == "\\") do ( ch = "\\\\" ) -- if result += ch ) -- for strval = result format "% = \"%\"\n" var strval format "% = \"%\"\n" var strval to: f ) else if ((isKindOf val Integer) or (isKindOf val Float) or (isKindOf val BooleanClass)) then ( format "% = %\n" var val format "% = %\n" var val to: f ) else ( format "Unknown environment variable type: % % %\n" var val (classOf val) ) ) format "\n\n" close f ) fn blowChunks size = ( if (size == 0) then ( blowingChunks = false skipToRecord = 0 processRecords = 999999 writeStringToFile "false" (maxInstallDir + "\\" + blowChunksFile) saveEnvironment() ) else ( blowingChunks = true skipToRecord = 1 chunkSize = size processRecords = chunkSize writeStringToFile "true" (maxInstallDir + "\\" + blowChunksFile) saveEnvironment() keepBlowingChunks() ) ) fn blowNextChunk = ( if (blowingChunks) do ( local fileNames = CMXExporter.exportList.items local fileCount = fileNames.count skipToRecord = skipToRecord + chunkSize saveEnvironment() if (skipToRecord >= fileCount) do ( format "========== DONE BLOWING CHUNKS ==========" blowChunks 0 ) ) if (blowingChunks) do ( doDosCommandInDir "KeepBlowingChunks.lnk" maxInstallDir ) quitMax #noPrompt ) fn keepBlowingChunks = ( if (blowingChunks) do ( local logName = maxInstallDir + "\\" + blowChunksLog + "-" + (skipToRecord as String) + "-" + (chunkSize as String) + ".txt" openLog logName format "========== BLOWING CHUNKS % % ==========\n\n" skipToRecord chunkSize addRollout cmxExporterLoader format "Loading exporter cache...\n" loadCmxExporterCache() -- SAFETY CMXExporter.writeEnableCheck.checked = false format "Exporting chunk of % starting at %\n" chunkSize skipToRecord doExportAll() format "========== DONE BLOWING CHUNKS % % ==========\n\n" skipToRecord chunkSize closeLog() blowNextChunk() ) ) LoadEnvironment() -- Utilities. -- Motion capture ping pong ball transplant utilities -- Map bone names global csmBoneNameMap = #( "Bip02", "Bip01", "Bip02 Pelvis", "Bip01 HIPS", "Bip02 Spine", "Bip01 LOW_TORSO", "Bip02 Spine1", "Bip01 UP_TORSO", "Bip02 Spine2", "Bip01 UP_TORSO", "Bip02 Neck", "Bip01 NECK", "Bip02 Head", "Bip01 HEAD", "Bip02 L Clavicle", "Bip01 LCLAVICLE", "Bip02 L UpperArm", "Bip01 LBICEP", "Bip02 L Forearm", "Bip01 LFOREARM", "Bip02 L Hand", "Bip01 LHAND", "Bip02 L Finger2", "Bip01 endLHAND", "Bip02 R Clavicle", "Bip01 RCLAVICLE", "Bip02 R UpperArm", "Bip01 RBICEP", "Bip02 R Forearm", "Bip01 RFOREARM", "Bip02 R Hand", "Bip01 RHAND", "Bip02 R Finger2", "Bip01 endRHAND", "Bip02 L Thigh", "Bip01 LTHIGH", "Bip02 L Calf", "Bip01 LCALF", "Bip02 L Foot", "Bip01 LFOOT", "Bip02 L Toe4", "Bip01 endLFOOT", "Bip02 R Thigh", "Bip01 RTHIGH", "Bip02 R Calf", "Bip01 RCALF", "Bip02 R Foot", "Bip01 RFOOT", "Bip02 R Toe4", "Bip01 endRFOOT") fn mapCsmBoneName name = ( local mapCount = (csmBoneNameMap.count / 2) for i = 1 to mapCount do ( if (csmBoneNameMap[1 + (2 * (i - 1))] == name) do ( local newname = csmBoneNameMap[2 + (2 * (i - 1))] return newname ) ) return name ) -- Select the balls before calling this function. fn transplantSelectedBalls = ( for i in selection do ( local iname = i.name local pname = i.parent.name local newpname = mapCsmBoneName pname coordsys i.parent ( local cmd = "$'" + iname + "'.parent = $'" + newpname + "'" execute cmd local lpos = i.pos * (inverse (scalematrix (i.parent.transform.scalepart))) cmd = "coordsys $'" + i.parent.name + "' (local i = $'" + i.name + "'; i.parent = $'" + newpname + "'; i.pos.x = " + (lpos.x as string) + " ; i.pos.y = " + (lpos.y as string) + " ; i.pos.z = " + (lpos.z as string) + ")" format "%\n" cmd ) ) ) fn positionBalls = ( coordsys $'Bip01 UP_TORSO' (local i = $'SACR'; i.parent = $'Bip01 UP_TORSO'; i.pos.x = 0.00481195 ; i.pos.y = -0.0198247 ; i.pos.z = 0.000301042) coordsys $'Bip01 HEAD' (local i = $'RBHD'; i.parent = $'Bip01 HEAD'; i.pos.x = 0.030386 ; i.pos.y = -0.0187251 ; i.pos.z = -0.0130215) coordsys $'Bip01 HEAD' (local i = $'LBHD'; i.parent = $'Bip01 HEAD'; i.pos.x = 0.0308579 ; i.pos.y = -0.0187683 ; i.pos.z = 0.014225) coordsys $'Bip01 HEAD' (local i = $'LFHD'; i.parent = $'Bip01 HEAD'; i.pos.x = 0.0398321 ; i.pos.y = 0.0149992 ; i.pos.z = 0.0127958) coordsys $'Bip01 HEAD' (local i = $'RFHD'; i.parent = $'Bip01 HEAD'; i.pos.x = 0.0393602 ; i.pos.y = 0.0150423 ; i.pos.z = -0.0144507) coordsys $'Bip01 NECK' (local i = $'C7'; i.parent = $'Bip01 NECK'; i.pos.x = 0.0237345 ; i.pos.y = -0.0184942 ; i.pos.z = -9.90197e-005) coordsys $'Bip01 endLHAND' (local i = $'LFIN'; i.parent = $'Bip01 endLHAND'; i.pos.x = 0.00904921 ; i.pos.y = -0.0239149 ; i.pos.z = 0.00962157) coordsys $'Bip01 LHAND' (local i = $'LWRB'; i.parent = $'Bip01 LHAND'; i.pos.x = -0.00883686 ; i.pos.y = -0.0106959 ; i.pos.z = 0.018266) coordsys $'Bip01 LHAND' (local i = $'LWRA'; i.parent = $'Bip01 LHAND'; i.pos.x = -0.00635483 ; i.pos.y = -0.0102082 ; i.pos.z = -0.0177065) coordsys $'Bip01 LBICEP' (local i = $'LILB'; i.parent = $'Bip01 LBICEP'; i.pos.x = 0.0327735 ; i.pos.y = 3.58991e-007 ; i.pos.z = -0.00570468) coordsys $'Bip01 LBICEP' (local i = $'LELB'; i.parent = $'Bip01 LBICEP'; i.pos.x = 0.0363025 ; i.pos.y = -0.000615776 ; i.pos.z = 0.00439109) coordsys $'Bip01 LCLAVICLE' (local i = $'LSHO'; i.parent = $'Bip01 LCLAVICLE'; i.pos.x = 0.032552 ; i.pos.y = 0.0035651 ; i.pos.z = 0.00537376) coordsys $'Bip01 endRHAND' (local i = $'RFIN'; i.parent = $'Bip01 endRHAND'; i.pos.x = 0.00805891 ; i.pos.y = -0.0266339 ; i.pos.z = -0.00821503) coordsys $'Bip01 RHAND' (local i = $'RWRB'; i.parent = $'Bip01 RHAND'; i.pos.x = -0.00947516 ; i.pos.y = -0.0113927 ; i.pos.z = -0.0173682) coordsys $'Bip01 RHAND' (local i = $'RWRA'; i.parent = $'Bip01 RHAND'; i.pos.x = -0.00600188 ; i.pos.y = -0.0109836 ; i.pos.z = 0.0172175) coordsys $'Bip01 RBICEP' (local i = $'RILB'; i.parent = $'Bip01 RBICEP'; i.pos.x = 0.0343592 ; i.pos.y = -0.000342505 ; i.pos.z = 0.00607472) coordsys $'Bip01 RBICEP' (local i = $'RELB'; i.parent = $'Bip01 RBICEP'; i.pos.x = 0.0363857 ; i.pos.y = -0.000615606 ; i.pos.z = -0.00439439) coordsys $'Bip01 RCLAVICLE' (local i = $'RSHO'; i.parent = $'Bip01 RCLAVICLE'; i.pos.x = 0.0325354 ; i.pos.y = 0.00356516 ; i.pos.z = -0.00537394) coordsys $'Bip01 UP_TORSO' (local i = $'STRN'; i.parent = $'Bip01 UP_TORSO'; i.pos.x = 0.0029934 ; i.pos.y = 0.0195829 ; i.pos.z = 0.000301284) coordsys $'Bip01 UP_TORSO' (local i = $'T10'; i.parent = $'Bip01 UP_TORSO'; i.pos.x = 0.00296704 ; i.pos.y = -0.0198156 ; i.pos.z = 0.000301229) coordsys $'Bip01 UP_TORSO' (local i = $'CLAV'; i.parent = $'Bip01 UP_TORSO'; i.pos.x = 0.022955 ; i.pos.y = 0.0122574 ; i.pos.z = 0.000428891) coordsys $'Bip01 endLFOOT' (local i = $'LTOE'; i.parent = $'Bip01 endLFOOT'; i.pos.x = 0.0373488 ; i.pos.y = 0.0887725 ; i.pos.z = -0.247719) coordsys $'Bip01 LFOOT' (local i = $'LHEL'; i.parent = $'Bip01 LFOOT'; i.pos.x = 0.0182901 ; i.pos.y = -0.0124445 ; i.pos.z = -0.001115) coordsys $'Bip01 LFOOT' (local i = $'LMT5'; i.parent = $'Bip01 LFOOT'; i.pos.x = 0.0138659 ; i.pos.y = 0.0304036 ; i.pos.z = -0.0187962) coordsys $'Bip01 LFOOT' (local i = $'LMTI'; i.parent = $'Bip01 LFOOT'; i.pos.x = 0.0129633 ; i.pos.y = 0.024857 ; i.pos.z = 0.0147998) coordsys $'Bip01 LFOOT' (local i = $'LANK'; i.parent = $'Bip01 LFOOT'; i.pos.x = -0.00861955 ; i.pos.y = -0.000900826 ; i.pos.z = -0.0321137) coordsys $'Bip01 LCALF' (local i = $'LKNI'; i.parent = $'Bip01 LCALF'; i.pos.x = 0.00229929 ; i.pos.y = -0.000825234 ; i.pos.z = 0.0045046) coordsys $'Bip01 LCALF' (local i = $'LKNE'; i.parent = $'Bip01 LCALF'; i.pos.x = 5.81811e-005 ; i.pos.y = 4.95417e-005 ; i.pos.z = -0.00563543) coordsys $'Bip01 endRFOOT' (local i = $'RTOE'; i.parent = $'Bip01 endRFOOT'; i.pos.x = 0.0258126 ; i.pos.y = 0.0995815 ; i.pos.z = 0.245193) coordsys $'Bip01 RFOOT' (local i = $'RHEL'; i.parent = $'Bip01 RFOOT'; i.pos.x = 0.0170535 ; i.pos.y = -0.0130715 ; i.pos.z = 0.00099974) coordsys $'Bip01 RFOOT' (local i = $'RMTI'; i.parent = $'Bip01 RFOOT'; i.pos.x = 0.0138109 ; i.pos.y = 0.0262304 ; i.pos.z = -0.0172072) coordsys $'Bip01 RFOOT' (local i = $'RMT5'; i.parent = $'Bip01 RFOOT'; i.pos.x = 0.0131544 ; i.pos.y = 0.0319249 ; i.pos.z = 0.021099) coordsys $'Bip01 RFOOT' (local i = $'RANK'; i.parent = $'Bip01 RFOOT'; i.pos.x = -0.0107982 ; i.pos.y = -0.00178294 ; i.pos.z = 0.0321191) coordsys $'Bip01 RCALF' (local i = $'RKNE'; i.parent = $'Bip01 RCALF'; i.pos.x = -5.35838e-005 ; i.pos.y = 1.76138e-005 ; i.pos.z = 0.00581271) coordsys $'Bip01 RCALF' (local i = $'RKNI'; i.parent = $'Bip01 RCALF'; i.pos.x = 0.00270632 ; i.pos.y = -0.00274075 ; i.pos.z = -0.00447235) coordsys $'Bip01 HIPS' (local i = $'LBWT'; i.parent = $'Bip01 HIPS'; i.pos.x = 0.0129768 ; i.pos.y = -0.015792 ; i.pos.z = 0.0219691) coordsys $'Bip01 HIPS' (local i = $'LFWT'; i.parent = $'Bip01 HIPS'; i.pos.x = 0.00707942 ; i.pos.y = 0.0186615 ; i.pos.z = 0.0218974) coordsys $'Bip01 HIPS' (local i = $'RBWT'; i.parent = $'Bip01 HIPS'; i.pos.x = 0.0128306 ; i.pos.y = -0.0155076 ; i.pos.z = -0.0221799) coordsys $'Bip01 HIPS' (local i = $'RFWT'; i.parent = $'Bip01 HIPS'; i.pos.x = 0.00693443 ; i.pos.y = 0.0188483 ; i.pos.z = -0.0222517) ) -- Database utilities fn openDatabase = ( if accessApp == undefined do ( accessApp = createOleObject "Access.Application" ) accessApp.openCurrentDatabase databaseName db = accessApp.currentDB() CMXExporter.messageText.text = "Preparing export tables..." db.execute "AnimationExport" CMXExporter.messageText.text = "Opening export tables..." -- recordSet1 = db.OpenRecordset tableName1 0 0 recordSet1 = db.openTable tableName1 recordSet2 = db.openTable tableName2 ) fn closeDatabase = ( if accessApp != undefined do ( try ( recordSet1.close() ) catch () try ( releaseOleObject recordSet1 ) catch () recordSet1 = undefined try ( recordSet2.close() ) catch () try ( releaseOleObject recordSet2 ) catch () recordSet2 = undefined try ( db.close() ) catch () try ( releaseOleObject db ) catch () db = undefined try ( accessApp.closeCurrentDatabase() ) catch () try ( accessApp.quit() ) catch () try ( releaseOleObject accessApp ) catch () accessApp = undefined ) ) -- This function takes the name of a record set, and a record set, -- and defines a MaxScript structure Record, with the keys of the record, -- a global array of strings RecordKeys, with the names of the keys, -- and a function getRecord, that takes a record set, makes a new -- Record, and reads the record set's corresponding field values into it. -- -- There is a bug with Access, in that if we are not TOTALLY anal about calling -- releaseOleObject on every ole object created, it never exits, while busy waiting -- and eating up the cpu. This code is very careful to release all ole objects, -- even in the event of mysterious, unexplainable errors, which do happen. -- fn defRecordStruct name keys = ( local str1 = "struct DB" + name + "Record (Type, " local str2 = "global DB" + name + "RecordKeys = #(\"Type\", " local str3 = "fn getDB" + name + "Record recordSet = (\n" + " local fields = recordSet.fields\n" + " local rec = (DB" + name + "Record " local i local n = 0 for dbKey in keys do ( -- Suck the spaces out of the Access dbKey name to get the Max key name. local key = dbKey local spaceIndex while ((spaceIndex = findString key " ") != undefined) do ( key = (subString key 1 (spaceIndex - 1)) + (subString key (spaceIndex + 1) (key.count - spaceIndex)) ) if (n != 0) do ( str1 += ", " str2 += ", " str3 += " " ) n += 1 str1 += key str2 += "\"" + dbKey + "\"" str3 += "(local field = fields.item \"" + dbKey + "\";" + " local val = (try (field.value) catch (\"\"));" + " releaseOleObject field;"+ " val)" ) str1 += ")" str2 += ")" str3 += " )\n" + " releaseOleObject fields\n" + " rec\n" + ")\n" execute str1 execute str2 execute str3 ) fn GetDBAnimationRecord recordSet = ( -- dummy placeholder ) fn GetDB3DRTRecord recordSet = ( -- dummy placeholder ) -- Read all the Animation records out of the database, -- into the global array exporterRecords. -- fn readAnimationRecords recordSet = ( local record = 0 local records = recordSet.recordCount progressStart "Reading Animation Database..." recordSet.MoveFirst() while (recordSet.EOF() == false) do ( progressUpdate ((100 * record) / records) record += 1 local rec = getDBAnimationRecord recordSet -- Ignore all records that aren't to be exported... if (rec.Export == true) do ( append exporterRecords rec ) recordSet.MoveNext() ) progressEnd() ) -- Read all the Animation records out of the database, -- into the global array exporterRecords. -- fn read3DRTRecords recordSet = ( local record = 0 local records = recordSet.recordCount progressStart "Reading 3DRT Database..." recordSet.MoveFirst() while (recordSet.EOF() == false) do ( progressUpdate ((100 * record) / records) record += 1 local rec = getDB3DRTRecord recordSet -- Ignore all records that aren't to be exported... if (rec.Export == true) do ( append exporterRecords rec ) recordSet.MoveNext() ) progressEnd() ) -- Open the database, read the Animation records, and close the database. -- fn ReadDatabase = ( format "Opening database...\n" openDatabase() cmxExporterLoader.statusLabel1.text = "Reading Database..." cmxExporterLoader.statusLabel2.text = "Please wait while I remember..." format "Reading database...\n" exporterRecords = #() readAnimationRecords recordSet1 read3DRTRecords recordSet2 format "Closing database...\n" cmxExporterLoader.statusLabel1.text = "Closing Database..." cmxExporterLoader.statusLabel2.text = "I sure hope Access goes away!" closeDatabase() cmxExporterLoader.statusLabel1.text = "Closed Database!" cmxExporterLoader.statusLabel2.text = "Be gone!" format "Done!!!\n" ) -- Execute a DOS command in the given directory. -- fn doDosCommandInDir cmd dir = ( local lastDir = getCurrentDirectory() -- format "Changing to directory:\n%\n" dir setCurrentDirectory dir format "Executing DOS command in dir %: %\n" dir cmd local result = DOSCommand cmd format "Result: %\n" result -- format "Changing back to directory:\n%\n" lastDir setCurrentDirectory lastDir ) -- Select the given item from the animation list. -- fn selectAnimation itemName = ( local rec = undefined if (exporterRecords.count > 0) do ( local item = 1 for i = 1 to exporterRecords.count do ( rec = exporterRecords[i] if (rec.Name == itemName) do ( item = i exit ) ) selectedAnimationIndex = 1 local items = CMXExporter.animationList.items for i = 1 to items.count do ( if (items[i] == itemName) do ( selectedAnimationIndex = i exit ) ) curExporterRecord = exporterRecords[item] CMXExporter.animationList.selection = selectedAnimationIndex CMXExporter.cmxDirText.text = sourceSafeDir + gameDataPath + "\\" + curExporterRecord.CmxDirectory CMXExporter.cmxFileText.text = curExporterRecord.FileName + ".cmx" CMXExporter.maxFileText.text = curExporterRecord.MaxPath + curExporterRecord.FileName + ".max" CMXExporter.keywordsText.text = curExporterRecord.Keywords ) rec ) -- Select the given item index from the animation list. -- The first item index is 1. -- fn selectAnimationIndex i = ( if (i > exporterRecords.count) do (i = exporterRecords.count - 1) if (i < 1) do (i = 1) selectedAnimationIndex = i local animationName = cmxExporter.animationList.items[i] selectAnimation animationName ) -- Update the animation list from the cached database. -- fn updateAnimationList = ( local items = #() if (exporterRecords.count != 0) do ( local i for i = 1 to exporterRecords.count do ( append items exporterRecords[i].Name ) sort items ) CMXExporter.animationList.items = items if (items.count > 0) do ( if (selectedAnimationIndex > items.count) do ( selectedAnimationIndex = items.count ) selectAnimationIndex selectedAnimationIndex ) ) -- Load the Max file currently selected in the animation list. -- fn loadCurrentMaxFile = ( local sourceFile = sourceSafeDir + "\\" + CMXExporter.maxFileText.text local result = false resetMaxFile #noPrompt try ( loadMaxFile sourceFile if (maxFilename != "") do ( result = true ) ) catch ( result = false ) if ((getMasterUnitType() != 5) and (getMasterUnitScale() != 1.0)) do ( CMXExporter.messageText.text = "Bad Master Unit Scale in Max file \"" + sourceFile + "\"." format "%\n" CMXExporter.messageText.text result = false append exportMissingFiles sourceFile ) if (result) then ( CMXExporter.messageText.text = "Loaded Max file \"" + sourceFile + "\"." -- format "%\n" CMXExporter.messageText.text result = true append exportLoadedFiles sourceFile ) else ( CMXExporter.messageText.text = "Error loading Max file \"" + sourceFile + "\"." format "%\n" CMXExporter.messageText.text result = false append exportMissingFiles sourceFile ) result ) -- Export the currently loaded Max file, which must be currently selected in the animation list. -- fn exportCurrentMaxFile forReal = ( local fileNames = #() local exportDir = CMXExporter.cmxDirText.text local exportFileName = CMXExporter.cmxFileText.text local writeEnable = CMXExporter.writeEnableCheck.checked local doReports = CMXExporter.doReportsCheck.checked local doCompress = CMXExporter.doCompressCheck.checked local result = true local msg local cmxDir exportResult = undefined if (curExporterRecord != undefined) then ( cmxDir = curExporterRecord.CmxDirectory ) else ( cmxDir = "cmx" ) if ((getFileNameFile exportFileName) != (getFileNameFile maxFileName)) do ( if (queryBox \ "The Max file name doesn't match the CMX file name! Are you ***SURE***???" \ title: "No! Don't do it!") then ( messageBox \ "Don't say I didn't warn you!" \ title: "Foolish mortal." ) else ( return false ) ) -- try ( exportResult = exportCmxFile exportDir exportFileName writeEnable doReports doCompress forReal verbose local ssExportDir = (gameDataPath + "\\" + cmxDir + "\\") if (exportResult[1] == true) then ( if (verbose) do ( msg = "Exported to \"" + exportDir + "\\" + exportFileName + "\".\n" ) -- if verbose local i, j for i = 2 to 6 do ( local header = #("", "Files", "Skeletons", "Suits", "Skills", "Textures")[i] if (verbose) do ( msg += header + ":\n" ) -- if verbose local namelists = exportResult[i] for j = 1 to namelists.count do ( local names = namelists[j] if (verbose) do ( msg += " " ) -- if verbose for k = 1 to names.count do ( local name = names[k] if ((classof name) == String) do ( if (k != 1) do ( if ((header == "Files") or (header == "Suits") or (header == "Skills")) do ( append fileNames (ssExportDir + name) ) -- if if (verbose) do ( msg += "\n " ) -- if verbose ) -- if if (verbose) do ( msg += name ) -- if verbose ) -- if )-- for k if (verbose) do ( msg += "\n" ) -- if ) -- for j ) -- for i if (verbose == false) do ( msg = "No news is Good News!" ) -- if result = true ) else ( msg = exportResult[2] result = false ) -- if -- ) catch ( -- msg = "A bizarre error occurred exporting to \"" + exportDir + "\\" + exportFileName + "\".\n" -- result = false -- ) CMXExporter.messageText.text = msg format "%\n" msg local sourceFile = sourceSafeDir + "\\" + CMXExporter.maxFileText.text local exportFile = exportDir + "\\" + exportFileName exportResult = #( "success:", result, "sourceFile:", sourceFile, "exportFile:", exportFile, "message:", msg, "fileList:", fileList, "exportResult:", exportResult) append exportResults exportResult if (result == true) do ( result = fileNames ) result ) -- Return a list of exported file names, of the currently loaded Max file. -- fn currentExportedFileNames = ( local result = false local lastWriteEnable = CMXExporter.writeEnableCheck.checked CMXExporter.writeEnableCheck.checked = false local fileNames = exportCurrentMaxFile false CMXExporter.writeEnableCheck.checked = lastWriteEnable result = fileNames result ) fn getCmxDirFromMaxFileName fileName = ( local result = undefined for record in exporterRecords do ( if (((findString fileName record.fileName) != undefined) and ((findString fileName record.maxPath) != undefined)) do ( return record.CmxDirectory ) ) format "XXX: Can't figure out CMX directory from Max file name %\n" fileName result ) fn getFileNameRelPath fname = ( local path = getFileNamePath fname local ch while ((path.count > 0) and (((ch = path[1]) == "/") or (ch == "\\"))) do ( path = subString path 2 (path.count - 1) ) local i for i = 1 to path.count do ( ch = path[i] if (ch == "\\") do ( path[i] = "/" ) ) path ) fn makeSourceSafeBatchCommand cmd comment fileNames tempFileName batFileName = ( local f = createfile batFileName format "echo ------------------ > %\n" tempFileName to:f format "echo SourceSafe Results >> %\n" tempFileName to:f format "echo ------------------ >> %\n" tempFileName to:f format "set ssdir=%\n" sourceSafeServerDir to:f format "%\n" (subString sourceSafeDir 1 2) to:f format "\"%\\%\" Cd \\\n" sourceSafeProgDir sourceSafeCmd to:f local fileName for fileName in fileNames do ( local sscmd = "\"" + sourceSafeProgDir + "\\" + sourceSafeCmd + "\" " + cmd + " \"" + (getFileNameFile fileName) + (getFileNameType fileName) + "\" " local outputDir = sourceSafeDir + "\\" + (getFileNameRelPath fileName) format "%\n" (subString outputDir 1 2) to: f format "cd \"%\"\n" outputDir to:f format "\"%\\%\" Cd \"$/%\"\n" sourceSafeProgDir sourceSafeCmd (getFileNameRelPath fileName) to:f -- In SourceSafe, the options come *after* the file names. -- -I- stands for Ignore, which means don't ask for input. sscmd += sourceSafeIgnoreInputFlag sscmd += comment format "echo % >> %\n" sscmd tempFileName to:f format "% -O@%\n" sscmd tempFileName to:f format "echo ------------------ >> %\n" tempFileName to:f ) format "echo EOF>> %\n" tempFileName to:f close f ) fn doSourceSafeBatchCommand cmd comment fileNames = ( format "Starting % from SourceSafe.\n\n" cmd if (fileNames == false) then ( format "Can't figure out the files to do!\n\n" ) else ( local tempFileName = tempDir + "\\SSResults.txt" local batFileName = tempDir + "\\SSCommands.bat" makeSourceSafeBatchCommand cmd comment fileNames tempFileName batFileName doDosCommandInDir batFileName sourceSafeDir format "Finished % from SourceSafe.\n\n" cmd f = openFile tempFileName local done = false while (not done) do ( if (eof f) then ( done = true ) else ( local line = readLine f if (line == "EOF") then ( done = true ) else ( format "%\n" line ) ) ) format "\n\n" close f ) ) fn getCurrentMaxFileNames = ( return CMXExporter.exportList.items ) -- Check out the exported files from the currently loaded Max file from SourceSafe. -- fn getCurrentMaxFiles = ( doSourceSafeBatchCommand "Get" "" (getCurrentMaxFileNames()) ) -- Check out the exported files from the currently loaded Max file from SourceSafe. -- fn checkOutCurrentExportedFiles = ( doSourceSafeBatchCommand "Checkout" sourceSafeNoCommentFlag (currentExportedFileNames()) ) -- Undo the check out of the exported files from the currently loaded Max file from SourceSafe. -- fn undoCheckOutCurrentExportedFiles = ( doSourceSafeBatchCommand "Undocheckout" "" (currentExportedFileNames()) ) -- Add the exported files from the currently loaded Max file to SourceSafe. -- fn addCurrentExportedFiles = ( doSourceSafeBatchCommand "Add" sourceSafeNoCommentFlag (currentExportedFileNames()) ) -- Check in the exported files from the currently loaded max file to SourceSafe. -- fn checkInCurrentExportedFiles = ( doSourceSafeBatchCommand "Checkin" sourceSafeNoCommentFlag (currentExportedFileNames()) ) fn doSetSsRoot = ( local result result = getSavePath caption: "Select Your SourceSafe Root Directory" if (result != undefined) do ( if ((result.count == 3) and (result[2] == ":") and (result[3] == "\\")) do ( result = substring result 1 (result.count - 1) ) sourceSafeDir = result CMXExporter.ssRootText.text = sourceSafeDir selectAnimationIndex selectedAnimationIndex ) saveEnvironment() ) fn doSetSSProg = ( local result result = getSavePath caption: "Select Your SourceSafe Program Directory" if (result != undefined) do ( sourceSafeProgDir = result CMXExporter.tempDirText.text = tempDir selectAnimationIndex selectedAnimationIndex ) saveEnvironment() ) fn doSetSSServer = ( local result result = getSavePath caption: "Select Your SourceSafe Server Directory" if (result != undefined) do ( sourceSafeServerDir = result CMXExporter.tempDirText.text = tempDir selectAnimationIndex selectedAnimationIndex ) saveEnvironment() ) fn doSetTempDir = ( local result result = getSavePath caption: "Select Your Temp Directory" if (result != undefined) do ( tempDir = result CMXExporter.tempDirText.text = tempDir selectAnimationIndex selectedAnimationIndex ) saveEnvironment() ) fn doSetAccessCmd = ( local result result = getOpenFilename filename:"MSAccess.exe" types:"Microsoft Access Application|Msaccess.exe|" caption: "Select Your Access App (Microsoft Office\\Office\\Msaccess.exe)" if (result != undefined) do ( accessCmd = result CMXExporter.accessCmdText.text = accessCmd selectAnimationIndex selectedAnimationIndex ) saveEnvironment() ) fn doExportTo = ( local result = getSaveFilename caption: "Select CMX File to export" filename: CMXExporter.cmxFileText.text if (result != undefined) do ( local dir = getFileNamePath result local file = (getFileNameFile result) + ".cmx" CMXExporter.cmxDirText.text = dir CMXExporter.cmxFileText.text = file exportCurrentMaxFile true ) ) fn selectExportFileName sourceFile selAnim = ( local fileNames = cmxExporter.exportList.items if (fileNames.count > 0) do ( local foundRec = undefined for a in exportRecords do ( local rec = a[2] local recFileName = a[6] if (recFileName == sourceFile) do ( foundRec = rec exit ) ) local j for j = 1 to fileNames.count do ( if (fileNames[j] == sourceFile) do ( cmxExporter.exportList.selection = j exit ) ) if (selAnim and (foundRec != undefined)) do ( selectAnimation foundRec.name ) ) ) fn doLoadExportListFile = ( local sel = cmxExporter.exportList.selection local fileNames = cmxExporter.exportList.items local sourceFile = fileNames[sel] CMXExporter.maxFileText.text = sourceFile selectExportFileName sourceFile true loadCurrentMaxFile() ) fn getFilterField rec filterType = ( case filterType of ( 1: ( -- skill name rec.Name ) 2: ( -- file name rec.FileName ) 3: ( -- max path rec.MaxPath ) 4: ( -- keyword rec.Keywords ) 5: ( -- type rec.Type ) ) ) function transDist t0 t1 = ( local dx = t0[1] - t1[1] local dy = t0[2] - t1[2] local dz = t0[3] - t1[3] sqrt((dx * dx) + (dy * dy) + (dz * dz)) ) function transDistOffset t0 t1 = ( local dx = t0[1] - t1[1] local dy = t0[2] - t1[2] local dz = t0[3] - t1[3] #( sqrt((dx * dx) + (dy * dy) + (dz * dz)), #(dx, dy, dz)) ) function rotDist r0 r1 = ( local dx = r0[1] - r1[1] local dy = r0[2] - r1[2] local dz = r0[3] - r1[3] local dw = r0[4] - r1[4] sqrt((dx * dx) + (dy * dy) + (dz * dz) + (dw * dw)) ) function rotDistOffset r0 r1 = ( local dx = r0[1] - r1[1] local dy = r0[2] - r1[2] local dz = r0[3] - r1[3] local dw = r0[4] - r1[4] #( sqrt((dx * dx) + (dy * dy) + (dz * dz) + (dw * dw)), #(dx, dy, dz, dw)) ) fn getExportRecords = ( local i local recs = exporterRecords.count local filesDone = #() local filter = CMXExporter.filterText.text local filterType = CMXExporter.filterTypeList.selection exportRecords = #() exportResults = #() exportMissingFiles = #() exportLoadedFiles = #() unexpectedSkills = #() missingSkills = #() for i = 1 to recs do ( local rec = exporterRecords[i] local fileName = rec.MaxPath + "/" + rec.FileName + ".max" if (((findItem filesDone fileName) == 0) and ((filter == "") or ((findString (getFilterField rec filterType) filter) != undefined))) do ( append filesDone fileName append exportRecords #(i, rec, false, false, "skipped", fileName) ) ) cmxExporter.exportList.caption = (exportRecords.count as String) + " Files In The Batch" local fileNames = #() for a in exportRecords do ( local fileName = a[6] append fileNames fileName ) sort fileNames cmxExporter.exportList.items = fileNames cmxExporter.exportList.selection = 1 ) fn doListAll = ( getExportRecords() format "\n\n======= Batch List of % Files Beginning\n\n" exportRecords.count local i local fileNames = CMXExporter.exportList.items for i = 1 to fileNames.count do ( if ((i >= skipToRecord) and (i < (skipToRecord + processRecords))) do ( local sourceFile = fileNames[i] selectExportFileName sourceFile true format "%\n" sourceFile ) ) cmxExporter.exportList.selection = 1 format "\n\n======= Batch List of % Files Finished\n\n" exportRecords.count ) fn doLoadAll = ( getExportRecords() format "\n\n======= Batch Load of % Files Beginning\n\n" exportRecords.count local successfulFiles = #(), failedFiles = #() local i local fileNames = CMXExporter.exportList.items for i = 1 to fileNames.count do ( if ((i >= skipToRecord) and (i < (skipToRecord + processRecords))) do ( local sourceFile = fileNames[i] selectExportFileName sourceFile true format "Loading Max file \"%\", % of %.\n" sourceFile i fileNames.count if (loadCurrentMaxFile()) then ( format "Successfully loaded \"%\"!\n" sourceFile append successfulFiles sourceFile ) else ( format "Failed to load \"%\"!\n" sourceFile append failedFiles sourceFile ) resetMaxFile #noprompt ) ) format "\n\n======== Files that loaded successfully:\n\n" for i in successfulFiles do ( format "%\n" i ) format "\n\n======== Files that failed to load:\n\n" for i in failedFiles do ( format "%\n" i ) cmxExporter.exportList.selection = 1 format "\n\n======= Batch Load of % Files Finished\n\n" exportRecords.count ) -- The following was for a one-time batch name transformation. global renamedSkills = #() global unsavedMaxFiles = #() fn isSocial skillName = ( local rec for rec in exporterRecords do ( if ((findString rec.Name skillName) != undefined) do ( local maxPath = rec.MaxPath if (((findString maxPath "Character Global/Social") != undefined) or ((findString maxPath "Character Global/Conversation") != undefined) or ((findString maxPath "Character Global/Dancing") != undefined)) then ( return true ) else ( return false ) ) ) append missingSkills #(maxFilePath + maxFileName, skillName) format "XXX: Could not find skill named %\n" skillName return false ) global skillPrefixes = #("adult-", "kid-", "biped-ed-", "ross-", "duchess-", "mercedes-", "sam-") fn renameSkill oldSkill = ( local renamed = false local newSkill = oldSkill local prefix for prefix in skillPrefixes do ( if (not renamed) do ( if ((subString oldSkill 1 prefix.count) == prefix) do ( renamed = true newSkill = subString oldSkill (prefix.count + 1) (oldSkill.count - prefix.count) case prefix of ( "adult-": ( if (isSocial newSkill) then ( newSkill = "A2A-" + newSkill ) else ( newSkill = "A2O-" + newSkill ) ) "kid-": ( if (isSocial newSkill) then ( newSkill = "C2C-" + newSkill ) else ( newSkill = "C2O-" + newSkill ) ) "biped-ed-": ( if (isSocial newSkill) then ( newSkill = "A2A-" + newSkill ) else ( newSkill = "A2O-" + newSkill ) ) "ross-": ( newSkill = "A2O-" + newSkill ) "duchess-": ( newSkill = "A2O-" + newSkill ) "mercedes-": ( newSkill = "A2O-" + newSkill ) "sam-": ( newSkill = "A2O-" + newSkill ) ) ) ) ) if (not renamed) do ( -- format "XXX: Could not figure out how to rename skill %\n" oldSkill ) return newSkill ) global skillKeyNames = #("beginskill", "endskill", "skill") global allEvents fn getAllEvents = ( fileIn "c:\\temp\\allEvents.ms" local ev for ev in allEvents do ( ev[1] = (renameSkill ev[1]) as Name ) ) fn doSpecialEdit = ( local renamings = #() local foundSkills = #() for obj in objects do ( local tracks = numNoteTracks obj if (tracks > 0) do ( local track for track = 0 to (tracks - 1) do ( local timeKeys = getNoteTrackNumKeys obj track if (timeKeys > 0) do ( local timeKeyIndex for timeKeyIndex = 0 to (timeKeys - 1) do ( local keyTime = getNoteTrackKeyTime obj track timeKeyIndex local keyNote = getNoteTrackKeyNote obj track timeKeyIndex if ((keyNote != undefined) and (keyNote.count > 0)) do ( local key for key in skillKeyNames do ( local val = getNoteTrackKeyProp obj track timeKeyIndex key if (val != undefined) do ( -- format "obj % track % timeKeyIndex % key % val %\n" \ -- obj track timeKeyIndex key val local newVal = renameSkill val setNoteTrackKeyProp obj track timeKeyIndex key newVal local nowVal = getNoteTrackKeyProp obj track timeKeyIndex key -- format "Renamed skill % to %, now %\n" val newVal nowVal local renaming = #(maxFilePath + maxFileName, val, newVal) append renamings renaming if (key == "beginskill") do ( append renamedSkills renaming append foundSkills #( newVal, obj, track, timeKeyIndex, keyTime, timeKeyIndex, keyTime) ) if (key == "endskill") do ( if (foundSkills.count > 0) do ( local lastSkill = foundSkills[foundSkills.count] if (lastSkill[1] == newVal) then ( lastSkill[6] = timeKeyIndex lastSkill[7] = keyTime ) else ( format "XXX: Unmatched endskill at obj % track % timeKeyIndex % key % val %\n" \ obj track timeKeyIndex key newVal ) ) ) ) ) ) ) ) ) ) ) for skill in foundSkills do ( local skillName = skill[1] local skillObj = skill[2] local skillTrack = skill[3] local skillStartTimeKeyIndex = skill[4] local skillStartKeyTime = skill[5] local skillEndTimeKeyIndex = skill[6] local skillEndKeyTime = skill[7] local skillNameName = skillName as Name local skillEvents = undefined for skillData in allEvents do ( local nameName = skillData[1] if (nameName == skillNameName) do ( skillEvents = skillData ) ) if (skillEvents == undefined) then ( format "XXX: Found skill % in max file % that's not defined in the database!\n" \ skillName maxFileName ) else ( local rootMotion = undefined for motion in skillEvents[2] do ( if (motion[1] == "ROOT") do ( rootMotion = motion ) ) if (rootMotion != undefined) do ( local rootEvents = rootMotion[2] local event format "Found skill % with % events\n" skillName rootEvents.count for event in rootEvents do ( local eventTime = event[1] local eventProps = event[2] local eventIndex = undefined local eventTrack = undefined local searchTrack local tracks = numNoteTracks skillObj for searchTrack = 0 to (tracks - 1) do ( local keyCount = getNoteTrackNumKeys skillObj searchTrack local keyIndex for keyIndex = 0 to (keyCount - 1) do ( local absTime = getNoteTrackKeyTime skillObj searchTrack keyIndex local relTime = absTime - skillStartKeyTime relTime = ((relTime / 160.0) * 33.33333333) as integer if (relTime != eventTime) then ( -- format "Found an event NOT at that time % != %\n" eventTime relTime ) else ( format "Found an event at that time %\n" eventTime if (eventIndex != undefined) do ( format "XXX: Warning: Didn't expect to find more than one!\n" ) eventIndex = keyIndex eventTrack = searchTrack ) ) ) if (eventIndex == undefined) do ( local scaledEventTime = floor (0.5 + (eventTime / 33.33333333)) eventIndex = addNoteTrackKey skillObj skillTrack scaledEventTime "" eventTrack = skillTrack format "addNoteTrackKey obj % track % time % => index %\n" \ skillObj skillTrack scaledEventTime eventIndex ) local propCount = eventProps.count / 2 format "About to inject % events\n" propCount local i for i = 0 to (propCount - 1) do ( local propName = eventProps[i + i + 1]; local propVal = eventProps[i + i + 2] local oldVal = getNoteTrackKeyProp skillObj eventTrack eventIndex propName if (oldVal == propVal) then ( format "Found expected event #% %=%\n" i propName propVal ) else ( setNoteTrackKeyProp skillObj eventTrack eventIndex propName propVal format "Missing expected event #% %=% ...Injecting:\n" i propName propVal format "setNoteTrackKeyProp obj % track % eventIndex % eventTime % name % val %\n" \ skillObj skillTrack eventIndex eventTime propName propVal ) ) ) ) ) ) return renamings ) fn doSpecialEditAll = ( getExportRecords() format "\n\n======= Batch Edit of % Files Beginning\n\n" exportRecords.count local successfulFiles = #(), failedFiles = #() renamedSkills = #() missingSkills = #() unsavedMaxFiles = #() local fileNames = CMXExporter.exportList.items -- doSourceSafeBatchCommand "Get" "" fileNames -- doSourceSafeBatchCommand "Checkout" "" fileNames local i for i = 1 to fileNames.count do ( if ((i >= skipToRecord) and (i < (skipToRecord + processRecords))) do ( local sourceFile = fileNames[i] selectExportFileName sourceFile true format "Loading Max file \"%\", % of %.\n" sourceFile i fileNames.count if (loadCurrentMaxFile()) then ( format "Successfully loaded \"%\"!\n" sourceFile append successfulFiles sourceFile local renamings = doSpecialEdit() if (renamings.count > 0) do ( local renaming for renaming in renamings do ( format "Renamed skill in max file % from % to %\n" renaming[1] renaming[2] renaming[3] ) local fileName = maxFilePath + maxFileName format "Saving max file %\n" fileName try ( saveMaxFile fileName format "Successfully saved %!\n" fileName ) catch ( format "Could not save max file % !!!\n" fileName append unsavedMaxFiles fileName ) ) ) else ( format "Failed to load \"%\"!\n" sourceFile append failedFiles sourceFile ) resetMaxFile #noprompt ) ) format "\n\n======== Files that loaded successfully:\n\n" for i in successfulFiles do ( format "%\n" i ) format "\n\n======== Files that failed to load:\n\n" for i in failedFiles do ( format "%\n" i ) cmxExporter.exportList.selection = 1 format "\n\n======= Batch Special Edit of % Files Finished\n\n" exportRecords.count ) fn addPose skillName type trans rot dynamic = ( local tolerance = poseDynamicTolerance local closest = 1.0e+6 local closestIndex = 0 local i local poses = poseTable.count for i = 1 to poses do ( local pose = poseTable[i] local center = pose[1] local dist = transDist center trans if (dist < closest) do ( closest = dist closestIndex = i local poseDynamic = pose[2] tolerance = (if poseDynamic then poseDynamicTolerance else poseStaticTolerance) ) ) if (closest < tolerance) then ( local pose = poseTable[closestIndex] local poseDynamic = pose[2] local poseList = pose[3] local poseData = #(skillName, type, trans, rot) append poseList poseData -- Dynamic poses are averaged, static poses stay put! if (poseDynamic) do ( -- Average together all the matching points to find their new center. local j local x = 0.0 local y = 0.0 local z = 0.0 for j in poseList do ( x += j[3][1] y += j[3][2] z += j[3][3] ) x /= poseList.count y /= poseList.count z /= poseList.count pose[1] = #(x, y, z) ) ) else ( -- Make a new pose. local pose = #(trans, dynamic, #(#(skillName, type, trans, rot))) append poseTable pose ) ) fn oldInitPoses = ( poseTable = #() addPose "default" "pose" #(0.0, 3.0, 0.0) #(0, 0, 0, 1) false addPose "default-3x" "pose" #(-3.0, 3.0, 0.0) #(0, 0, 0, 1) false addPose "default+3x" "pose" #(3.0, 3.0, 0.0) #(0, 0, 0, 1) false addPose "default-3z" "pose" #(0.0, 3.0, -3.0) #(0, 0, 0, 1) false addPose "default+3z" "pose" #(0.0, 3.0, 3.0) #(0, 0, 0, 1) false addPose "sitting" "pose" #(0.01, 1.98, -0.07) #(0, 0, 0, 1) false addPose "sitting-3z" "pose" #(0.01, 1.98, -3.07) #(0, 0, 0, 1) false addPose "sitting+3z" "pose" #(0.01, 1.98, 3.0 - 0.07) #(0, 0, 0, 1) false ) fn printPoseTable = ( format "\n======== Pose Table:\n\n" local pose for pose in poseTable do ( local center = pose[1] local dynamic = pose[2] local poseList = pose[3] -- Don't show static poses with only one poseData. if (dynamic or (poseList.count > 1)) do ( -- Skip first poseData of static poses. local start = (if dynamic then 1 else 2) format "%: %\n" poseList[1][1] center local line local lines = #() for i = start to poseList.count do ( local poseData = poseList[i] local poseSkill = poseData[1] local poseType = poseData[2] local poseTrans = poseData[3] local dist = transDist center poseTrans local idealTrans = #(0.0, 3.0, 0.0) local idealDist = transDist poseTrans idealTrans format " dist % inches; from ideal % inches; pose % %\n" (dist * 12.0) (idealDist * 12.0) poseSkill poseType -- line = -- " dist " + -- ((dist * 12.0) as String) + -- " inches; from ideal " + -- ((idealDist * 12.0) as String) + -- " inches; pose " + -- poseSkill + -- " " + -- poseType -- append lines line ) -- sort lines -- for line in lines do ( -- format "%\n" line -- ) ) ) ) fn measureSkills = ( -- Format of skillDetails: -- #(1000.000000, -- duration -- 2.892072, -- distance -- true, -- isMoving -- false, -- isTurning -- #(-0.021508, 2.951144, 0.000000), -- start translation -- #(-0.023630, 2.951907, 0.002095), -- end translation -- #(-0.001354, -0.707387, -0.001355, -0.706824), -- start rotation -- #(-0.001300, -0.707387, -0.001301, -0.706824)) -- end rotation oldInitPoses() for a in exportResults do ( local results = a[12] local skillResults = results[5] if ((skillResults != undefined) and (skillResults.count > 0)) do ( local maxFile = a[6] print maxFile -- format "==== Max File: %\n\n" maxFile for i = 1 to skillResults.count do ( local skillInfo = skillResults[i] local skillName = skillInfo[1] local a0 = skillInfo[3] local str1 = a0[3] local skillDetails = execute str1 local trans0 = skillDetails[5] local trans1 = skillDetails[6] local rot0 = skillDetails[7] local rot1 = skillDetails[8] if false do ( local fileName = skillInfo[2] local duration = skillDetails[1] local distance = skillDetails[2] local isMoving = skillDetails[3] local isTurning = skillDetails[4] local idealTrans = #(0.0, 3.0, 0.0) local idealRot = #(-0.001354, -0.707387, -0.001355, -0.706824) local beginTransDist = transDistOffset trans0 idealTrans local beginRotDist = rotDistOffset rot0 idealRot local endTransDist = transDistOffset trans1 idealTrans local endRotDist = rotDistOffset rot1 idealRot format " beginskill %\n" skillName format " translation dist % offset % actual % ideal %\n" beginTransDist[1] beginTransDist[2] trans0 idealTrans format " rotation dist % actual % ideal %\n" beginRotDist[1] rot0 idealRot format " endskill %\n" skillName format " translation dist % offset % actual % ideal %\n" endTransDist[1] endTransDist[2] trans1 idealTrans format " rotation dist % actual % ideal %\n\n" endRotDist[1] rot1 idealRot ) addPose skillName "beginskill" trans0 rot0 true addPose skillName "endskill" trans1 rot1 true ) ) ) -- printPoseTable() ) fn findSkills = ( local foundSkills = #() for obj in objects do ( local tracks = numNoteTracks obj if (tracks > 0) do ( local track for track = 0 to (tracks - 1) do ( local timeKeys = getNoteTrackNumKeys obj track if (timeKeys > 0) do ( local timeKeyIndex for timeKeyIndex = 0 to (timeKeys - 1) do ( local keyTime = getNoteTrackKeyTime obj track timeKeyIndex local keyNote = getNoteTrackKeyNote obj track timeKeyIndex if ((keyNote != undefined) and (keyNote.count > 0)) do ( local key for key in skillKeyNames do ( local val = getNoteTrackKeyProp obj track timeKeyIndex key if (val != undefined) do ( local valname = val as Name if (key == "beginskill") do ( append foundSkills #( valname, obj.name, track, timeKeyIndex, keyTime, timeKeyIndex, keyTime) ) if (key == "endskill") do ( if (foundSkills.count > 0) do ( local lastSkill = foundSkills[foundSkills.count] if (lastSkill[1] == valname) then ( lastSkill[6] = timeKeyIndex lastSkill[7] = keyTime ) else ( format "Unmatched endskill at obj % track % timeKeyIndex % key % val %\n" \ obj track timeKeyIndex key val ) ) ) ) ) ) ) ) ) ) ) return foundSkills ) fn doValidate = ( -- Only validates skills so far. if (curExporterRecord.Type != "Skill") do ( return true ) local curMaxFile = curExporterRecord.FileName local foundSkills = findSkills() local foundProblem = false local expectedSkillNames = #() local expectedSkillRecs = #() for rec in exporterRecords do ( if (rec.FileName == curMaxFile) do ( append expectedSkillNames (rec.Name as Name) append expectedSkillRecs rec ) ) local goodNames = #() for skill in foundSkills do ( local skillName = skill[1] local skillObj = skill[2] local skillTrack = skill[3] local skillStartTimeKeyIndex = skill[4] local skillStartKeyTime = skill[5] local skillEndTimeKeyIndex = skill[6] local skillEndKeyTime = skill[7] local index = findItem expectedSkillNames skillName if (index == 0) then ( format "UNEXPECTED SKILL! Max file % skill name % obj % time %..%\n" \ curMaxFile skillName skillObj skillStartKeyTime skillEndKeyTime append unexpectedSkills #(curMaxFile, skillName, skill) foundProblem = true ) else ( append goodNames skillName deleteItem expectedSkillNames index deleteItem expectedSkillRecs index ) ) if (expectedSkillNames.count > 0) do ( for rec in expectedSkillRecs do ( local skillName = rec.name format "MISSING SKILL! Max file % skill name %\n" \ curMaxFile skillName append missingSkills #(curMaxFile, skillName, rec) foundProblem = true ) ) if ((foundProblem == false) and (goodNames.count > 0) and verbose) do ( format "Validated that Max file % has only the % expected skills:\n " \ curMaxFile goodNames.count for skillName in goodNames do ( format "% " skillName ) format "\n" ) ) fn doExportAll = ( getExportRecords() format "\n\n======= Batch Export of % Files Beginning\n\n" exportRecords.count local doBatchCheckOutIn = CMXExporter.doBatchCheckOutInCheck.checked clearAnimationHistogram 2001 -0.1 0.1 initPoses() local i local fileNames = CMXExporter.exportList.items for i = 1 to exportRecords.count do ( if ((i >= skipToRecord) and (i < (skipToRecord + processRecords))) do ( local sourceFile = fileNames[i] local a = undefined for aa in exportRecords do ( if (aa[6] == sourceFile) do ( a = aa exit ) ) selectExportFileName sourceFile true format "Loading Max file \"%\", % of %.\n" sourceFile i fileNames.count if (loadCurrentMaxFile()) then ( a[3] = true if (doBatchCheckOutIn) do ( checkOutCurrentExportedFiles() ) format "Exporting CMX file \"%\\%\".\n" CMXExporter.cmxDirText.text CMXExporter.cmxFileText.text exportCurrentMaxFile true if (exportResult[2] == true) then ( a[4] = true if (doBatchCheckOutIn) do ( checkInCurrentExportedFiles() ) doValidate() ) else ( if (doBatchCheckOutIn) do ( undoCheckOutCurrentExportedFiles() ) ) a[5] = exportResult[8] ) else ( a[5] = "Max file \"" + sourceFile + "\" not found." ) resetMaxFile #noprompt ) ) cmxExporter.exportList.selection = 1 format "\n======== Batch export completed!\n\n\n" -- disabled if (false) do ( local histogramFileName = "c:\\AnimationHistogram.txt" format "Writing histogram to \"%\"...\n" histogramFileName writeAnimationHistogram histogramFileName format "Finished writing histogram.\n" ) local a format "\n======== Files that exported successfully:\n\n" for a in exportRecords do ( if (a[4] == true) do ( format "%\n" a[6] ) ) format "\n======== Files that failed to export:\n\n" for a in exportRecords do ( if ((a[4] == false) and (a[5] != "skipped")) do ( format "%\n %\n" a[6] a[5] ) ) format "\n======== Unexpected skills:\n\n" for a in unexpectedSkills do ( format "%\n" a ) format "\n======== Missing skills:\n\n" for a in missingSkills do ( format "%\n" a ) -- format "\n======== Skill Measurements:\n\n" -- measureSkills() format "\n\n======= Batch Export of % Files Finished\n\n" exportRecords.count ) fn doAnalyzePoses = ( local fileName = sourceSafeDir + "/" + "PoseAnalysis.txt" analyzePoses poseStaticTolerance poseDynamicTolerance fileName ) fn makeCmxExporter = ( if (cmxExporterFloater != undefined) do ( removeRollout cmxExporter closeRolloutFloater cmxExporterFloater cmxExporterFloater = undefined ) cmxExporterFloater = newRolloutFloater "CMX Exporter" cmxExporterWidth cmxExporterHeight addRollout cmxExporter cmxExporterFloater ) fn loadCmxExporter = ( cmxExporterLoader.statusLabel1.text = "Opening Database..." cmxExporterLoader.statusLabel2.text = "Please enter Access password!" try ( readDatabase() cmxExporterLoader.statusLabel1.text = "Loaded Database!" cmxExporterLoader.statusLabel2.text = "Happy animating!" ) catch ( cmxExporterLoader.statusLabel1.text = "Loading Database Failed!" cmxExporterLoader.statusLabel2.text = "You're on your own..." format "Can't read the database from \"%\".\nYou're on your own, buddy!\n" \ databaseName ) makeCmxExporter() ) fn loadCmxExporterCache = ( cmxExporterLoader.statusLabel1.text = "Reading Cache..." cmxExporterLoader.statusLabel2.text = "This should happen fast!" try ( exporterRecords = #() fileIn databaseCacheName1 fileIn databaseCacheName2 format "Read % records from database cache.\n" exporterRecords.count cmxExporterLoader.statusLabel1.text = "Read Cache!" cmxExporterLoader.statusLabel2.text = "Happy animating!" ) catch ( cmxExporterLoader.statusLabel1.text = "Reading Cache Failed!" cmxExporterLoader.statusLabel2.text = "You're on your own..." format "Can't read the database from \"%\".\nYou're on your own, buddy!\n" \ databaseCacheName1 ) makeCmxExporter() ) fn doOpenExporter = ( updateAnimationList() selectAnimationIndex selectedAnimationIndex ) fn doCloseExporter = ( ) -- CMX Exporter Utility Panel -- rollout CMXExporter "CMX Exporter Turbo-Deluxe" ( -- User interface controls. label messageText "FASTEN SEAT BELTS" listbox animationList "Skills, Suits and Skeletons in Database" height:10 button loadButton "Load Selected File" across:2 align:#left button exportButton "Export Current File" across:2 align:#right button loadAndExportButton "Load & Export Selected File" across:2 align:#left button exportToButton "Export Current File To..." across:2 align:#right edittext maxFileText "Max File Name:" align:#left edittext cmxDirText "CMX Directory:" align:#left edittext cmxFileText "CMX File Name:" align:#left edittext keywordsText "Keywords:" align:#left checkbox writeEnableCheck "Write Enable" checked:true across:2 checkbox doCompressCheck "Compress Skills" checked:true across:2 checkbox doReportsCheck "Write Text Reports" checked:false across:2 checkbox doBatchCheckOutInCheck "Batch Check Out/In" checked:false across:2 label sourcesafeLabel "SourceSafe" align:#center button getButton "Get Max" across:5 button addButton "Add CMX" across:5 button checkOutButton "CheckOut" across:5 button undoCheckOutButton "UnCheckOut" across:5 button checkInButton "CheckIn" across:5 label exporterLabel "Batch Exporter" align:#center dropdownlist filterTypeList items:#( "Filter By Name", "Filter By File Name", "Filter By Max Path", "Filter By Keyword", "Filter By Type") align:#left edittext filterText "Filter:" align:#left button listAllButton "Batch List Files" across:3 button loadAllButton "Batch Load Files" across:3 button exportAllButton "Batch Export Files" across:3 listbox exportList "0 Files In The Batch" height:8 label configurationLabel "Configuration" align:#center edittext ssRootText "SS Root Dir:" text: sourceSafeDir align:#left edittext ssProgText "SS Program Dir:" text: sourceSafeProgDir align:#left edittext ssServerText "SS Server Dir:" text: sourceSafeServerDir align:#left edittext tempDirText "Temp Directory:" text: tempDir align:#left edittext accessCmdText "Access Application" text: accessCmd align:#left button setSsRoot "Set SS Root" across:5 button setSsProg "Set SS Prog" across:5 button setSsServer "Set SS Server" across:5 button setTempDir "Set Temp Dir" across:5 button setAccessCmd "Set Access App" across:5 -- Event handlers. on CMXExporter open do ( doOpenExporter() ) on CMXExporter close do ( doCloseExporter() ) on setSsRoot pressed do ( doSetSsRoot() ) on setSsProg pressed do ( doSetSsProg() ) on setSsServer pressed do ( doSetSsServer() ) on setTempDir pressed do ( doSetTempDir() ) on setAccessCmd pressed do ( doSetAccessCmd() ) on animationList selected item do ( selectAnimation animationList.items[item] ) on loadButton pressed do ( loadCurrentMaxFile() ) on getButton pressed do ( getCurrentMaxFiles() ) on addButton pressed do ( addCurrentExportedFiles() ) on checkOutButton pressed do ( checkOutCurrentExportedFiles() ) on undoCheckOutButton pressed do ( undoCheckOutCurrentExportedFiles() ) on checkInButton pressed do ( checkInCurrentExportedFiles() ) on loadAndExportButton pressed do ( if (loadCurrentMaxFile()) do ( exportCurrentMaxFile true ) ) on exportButton pressed do ( exportCurrentMaxFile true ) on exportToButton pressed do ( doExportTo() ) on listAllButton pressed do ( doListAll() ) on loadAllButton pressed do ( doLoadAll() ) on exportAllButton pressed do ( doExportAll() ) on exportList selected item do ( doLoadExportListFile() ) ) utility CMXExporterLoader "Load CMX Exporter" ( label databaseNameLabel "Database Name" edittext databaseNameText text: databaseName -- button loadButton "Load CMX Exporter Database" align:#center button loadCacheButton "Load CMX Exporter Cache" align:#center button openButton "Open CMX Exporter" align:#center label statusLabel1 "" label statusLabel2 "" on CMXExporterLoader open do ( -- loadCmxExporter() ) -- on loadButton pressed do ( -- loadCmxExporter() -- ) on loadCacheButton pressed do ( loadCmxExporterCache() ) on openButton pressed do ( makeCmxExporter() ) ) defRecordStruct "Animation" interestingKeys defRecordStruct "3DRT" interestingKeys -- keepBlowingChunks()