// =========================================================================== // Copyright 2018 Autodesk, Inc. All rights reserved. // // Use of this software is subject to the terms of the Autodesk license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // =========================================================================== // // // Creation Date: Mar 15, 2002 // // Description: // This script performs the Save Fluid Contents As... // functionality. Current Fluid Contents are saved as // a Visor preset which includes the .mcfi initial // state cache file, and the .mel wrapper for dragging // and dropping. // // Input Arguments: // None. // // Return Vaalue: // None. // // another global string to override the default browser dir // that got added to fix 183390 - doing the safe ugly fix // for 185836, since it's already after CG - JP global string $gDefaultDefaultFileBrowserDir = ""; proc string evalAndDebug( string $printThis ) { // int $printIt = true; int $printIt = false; string $result = eval( $printThis ); if( $printIt ) { string $fmt = (uiRes("m_fluidSaveStateAs.kDebugInfo")); print( `format -s $printThis -s $result $fmt`); } return $result; } proc string getDiskCache( string $fluid ) // // Description: // Helper proc to figure out the name of the IC cache of a fluid. // { string $dskC = ""; if(`connectionInfo -id ($fluid + ".diskCacheIC")` > 0 ) { string $src = `connectionInfo -sfd ($fluid + ".diskCacheIC")`; string $buffer[]; tokenize($src, ".", $buffer); $dskC = $buffer[0]; } return $dskC; } proc string verifySuffix( string $origName, string $suffix ) // // Description: // Make sure the filename ends in $suffix // { string $filename = $origName; int $len = size( $filename ); int $startPos = $len - size( $suffix ) + 1; if(( $startPos < 1 ) || ( $startPos > $len ) || ( substring( $filename, $startPos, $len ) != $suffix )) { $filename = $filename + $suffix; } return $filename; } proc string saveWrapper( string $filename, string $cacheFile ) // // Description: // $filename is the full ile path the user picked in the // file browser. It's where the .mel wrapper for applying // the cache should go. // { if( !`exists getActiveFluidShapes`) { source getFluidShape.mel; } // We know that by getting here there's only one active fluid. // string $fluids[] = getActiveFluidShapes(); string $f = $fluids[0]; int $xRes = `getAttr( $f + ".resolutionW" )`; int $yRes = `getAttr( $f + ".resolutionH" )`; int $zRes = `getAttr( $f + ".resolutionD" )`; int $is2d = `getAttr( $f + ".is2d" )`; int $hasDen = `getAttr( $f + ".densityMethod" )` == 2; int $hasVel = `getAttr( $f + ".velocityMethod" )` == 2; int $hasTem = `getAttr( $f + ".temperatureMethod" )` == 2; int $hasRea = `getAttr( $f + ".fuelMethod" )` == 2; int $hasCol = `getAttr( $f + ".colorMethod" )` == 2; int $hasTex = `getAttr( $f + ".coordinateMethod" )` == 1; int $hasFall= `getAttr( $f + ".falloffMethod" )` == 1; string $scriptPath = verifySuffix( $filename, ".mel" ); int $fid = fopen( $scriptPath, "w" ); if( $fid != 0 ) { fprint( $fid, "// Apply this preset to the selected fluid\n" ); fprint( $fid, "// \n" ); fprint( $fid, "applyFluidDiskCache( " ); fprint( $fid, ( "\"" + $cacheFile + "\",\n" ) ); fprint( $fid, ( "\t" + 0 + ",\t\t// createFluidShape\n" ) ); fprint( $fid, ( "\t" + $xRes + ",\t\t// x resolution\n" ) ); fprint( $fid, ( "\t" + $yRes + ",\t\t// y resolution\n" ) ); fprint( $fid, ( "\t" + $zRes + ",\t\t// z resolution\n" ) ); fprint( $fid, ( "\t" + $is2d + ",\t\t// is fluid 2d?\n" ) ); fprint( $fid, ( "\t" + $hasDen + ",\t\t// has density\n" ) ); fprint( $fid, ( "\t" + $hasVel + ",\t\t// has velocity\n" ) ); fprint( $fid, ( "\t" + $hasTem + ",\t\t// has temperature\n" ) ); fprint( $fid, ( "\t" + $hasRea + ",\t\t// has fuel\n" ) ); fprint( $fid, ( "\t" + $hasCol + ",\t\t// has color\n" ) ); fprint( $fid, ( "\t" + $hasTex + ",\t\t// has texture coordinates\n" ) ); fprint( $fid, ( "\t" + $hasFall + " );\t// has falloff\n")); fclose( $fid ); } else { warning (uiRes("m_fluidSaveStateAs.kSaveStateAsWarn")); } return $scriptPath; } proc string saveState( string $root, string $filename ) // // Description: // Returns the basename of the initialState .mcfi cache // file. $filename is a full-path name to the user-browsed // location where the preset wrapper is to be saved. { // Only work on one fluid at a time. // if( !`exists getActiveFluidShapes` ) { source getFluidShape.mel; } string $fluids[] = getActiveFluidShapes(); if( size( $fluids ) == 0 ) { return ""; } string $fluid = $fluids[0]; // Make sure the filename ends in ".mcfi" // string $cacheFile = verifySuffix( $filename, ".mcfi" ); string $basename = basename( $cacheFile, "" ); // Set the target location, regardless of the input path // $fullPath = ( $root + $basename ); // Figure out the path to the data directory // and store it in $browserPath. // string $browserPath = `diskCache -q -tmp`; // We'll be creating a cache later and might need to undo // this creation if one never existed in the first place. // string $dskC = getDiskCache( $fluid ); int $cacheExisted = size( $dskC ) > 0; string $cacheName = ""; string $hiddenName = ""; string $hiddenPath = ""; string $tmpPath = ""; string $cmdStr = ""; if( $cacheExisted ) { // These could be relative or absolute... // $cacheName = `getAttr ( $dskC + ".cacheName" )`; $hiddenName = `getAttr ( $dskC + ".hiddenCacheName" )`; // Split hiddenName up into what we KNOW is relative // and abolute... // string $junkPath = dirname( $hiddenName ); if(( size( $junkPath ) == 0 ) || ( $junkPath == $browserPath )) { $hiddenName = basename( $hiddenName, "" ); $hiddenPath = ( $browserPath + "/" + $hiddenName ); } else { // $hiddenName is already a path, so just use // the same for $hiddenPath // $hiddenPath = $hiddenName; } } int $movedOK = false; // If the original name of the hidden cache is the same as // the browsed filename, there's nothing to do but // overwrite the initial state... // if( $hiddenPath == $fullPath ) { evalAndDebug( "SetInitialState" ); $movedOK = true; } // If browsing to the name of the real cache, the user // wants to replace the initial conditions. Set initial // state to create the new hidden file, and then // move it the real cache name... Then, // copy the file to the now-missing hidden file. // // (Note: $cacheName if non-null will be a full-path.) // else if( $cacheName == $fullPath ) { evalAndDebug( "SetInitialState" ); if( `file -q -exists $fullPath` ) { $cmdStr = ( "sysFile -delete \"" + $fullPath + "\"" ); evalAndDebug( $cmdStr ); } $cmdStr = ( "sysFile -copy " + "\"" + $fullPath + "\" " + "\"" + $hiddenPath + "\"" ); evalAndDebug( $cmdStr ); $cmdStr = ( "setAttr -type \"string\" " + $dskC + ".cacheName \"" + $fullPath + "\"" ); evalAndDebug( $cmdStr ); $movedOK = true; } if( !$movedOK ) { // If there was a cache, find its filename, generate a // unique name (by adding ".tmp" as many times as it takes) // and then move the original cache file to the ".tmp" file. // if( $cacheExisted ) { $tmpPath = $hiddenPath; do { $tmpPath = $tmpPath + ".tmp"; } while( `file -q -exists $tmpPath` ); $cmdStr = ( "sysFile -move " + "\"" + $tmpPath + "\" " + "\"" + $hiddenPath + "\"" ); evalAndDebug( $cmdStr ); } // Save the fluid's state and remember the name of the newly // created cache file. // evalAndDebug( "SetInitialState" ); $dskC = getDiskCache( $fluid ); // cache might just have been created string $justSavedPath = `getAttr ( $dskC + ".hiddenCacheName" )`; // This simplifies the case when $hiddenPath is absolute -- // regardless of relative or absolute, if there was already // a cache, its hidden name determines where the file gets // saved, so we can use the previously determined $hiddenPath // if( $cacheExisted ) { $justSavedPath = $hiddenPath; } // No IC cache means hiddenName is relative. Make absolute // from diskCache temp dir. // else { $justSavedPath = ( $browserPath + "/" + $justSavedPath ); } // The move command below fails if the file exists already // so we might have to delete it first. // if( `file -q -exists $fullPath` ) { $cmdStr = ( "sysFile -delete \"" + $fullPath + "\"" ); evalAndDebug( $cmdStr ); } // Move the just-saved cache file to the name specified // by the user in the file browser dialog. // $cmdStr = ( "sysFile -copy " + "\"" + $fullPath + "\" " + "\"" + $justSavedPath + "\"" ); evalAndDebug( $cmdStr ); // Here's where we delete the new cache if we're supposed to. // if( !$cacheExisted ) { $cmdStr = ( "delete " + $dskC ); evalAndDebug( $cmdStr ); } // Or else we just make sure things are set up like there were // before we messed with them: the .hiddenCacheName reset to // its original value, and the file associated with it moved // back to its original name (from the .tmp file). // else { $cmdStr = ( "setAttr -type \"string\" " + $dskC + ".hiddenCacheName \"" + $hiddenName + "\"" ); evalAndDebug( $cmdStr ); $cmdStr = ( "sysFile -move " + "\"" + $hiddenPath + "\" " + "\"" + $tmpPath + "\"" ); evalAndDebug( $cmdStr ); } } return $basename; } global proc int fluidSaveStateAsFilename( string $filename, string $filetype ) // // Description: // We'll use the SetInitialState bit of performFluids, but // that could be tricky since it would overwrite any // existing initial conditions cache... We'll need // to copy the existing cache to a temp location, // do the FluidSave to save the state, move that saved // state to the filename requested, then move the temp // location file back to the cache name. // // Three cases: // 1) User browses to save to a unique filename. // This is the most work: We do as described above. // // 2) User browses to save to the hidden cache filename. // We replace the initial conditions cache effectively // by doing a new Set As Initial State operation. // // 1) User browses to save to the real cache filename. // Save as Initial to get new hidden cache. Move it // to the cache name, then setAttr on the .cacheName // to automatically copy the .cacheName file to the // filename stored in .hiddenCacheName. // { // Update the location of the browser for next time // global string $gFluidInitialStatesDefaultDirRoot; global string $gFluidInitialStatesDefaultDir; $gFluidInitialStatesDefaultDir = dirname( $filename ); // Just in case we didn't get here through fluidSaveStateAs, // which sets up this value for the first time. // if( $gFluidInitialStatesDefaultDirRoot == "" ) { // If you change this, make sure to change the similar // initialization below in fluidSaveStateAs(). // $gFluidInitialStatesDefaultDirRoot = ( getenv( "MAYA_LOCATION" ) + "/presets/fluids/initialStates" ); } // Save the MEL wrapper for the preset, and then the .mcfi // disk cache that actually contains the state information. // // The wrapper .mel scripts are saved in the browsed location, // but there are two possibilities for locations of the .mcfi data // directory (.mayaData) which is created if it doesn't exist already. // // 1) The user has browsed to a place under the $MAYA_LOCATION // $gFluidInitialStatesDefaultRootDir hierarchy. // The .mcfi files are saved to // $MAYA_LOCATION/presets/fluids/.mayaData // 2) The user has browsed somewhere else entirely. The .mcfi // files are stored in the browsed directory, under ".mayaData" // string $rootOutputStr; string $rootDir; string $envVarSubstitution = substitute($gFluidInitialStatesDefaultDirRoot, $filename, "$MAYA_LOCATION" + "/presets/fluids/initialStates"); // The browsed location is NOT under MAYA_LOCATION // if( $envVarSubstitution == $filename ) { $rootDir = ( dirname( $filename ) + "/.mayaData/" ); $rootOutputStr = $rootDir; } // The browsed location IS under MAYA_LOCATION // else { $rootDir = getenv( "MAYA_LOCATION" ) + "/presets/fluids/.mayaData/"; $rootOutputStr = "$MAYA_LOCATION/presets/fluids/.mayaData/"; } // Create the target .mayaData directory if it doesn't exist // already. // if( !`file -q -exists $rootDir` ) { // Be careful with $rootDir; some platforms don't like // it when the argument to -makeDir ends in "/"... // string $dirToCreate = $rootDir; if( substring( $rootDir, size($rootDir), size($rootDir) ) == "/" ) { $dirToCreate = substring( $rootDir, 1, size( $rootDir ) - 1 ); } if( `sysFile -makeDir $dirToCreate` == 0 ) { string $warningStr = (uiRes("m_fluidSaveStateAs.kDirectoryCreationFailureWarn")); warning(`format -s $dirToCreate $warningStr`); } } string $mcfiFile = saveState( $rootDir, basename( $filename, ".mel" ) ); string $melPath = saveWrapper( $filename, ($rootOutputStr + $mcfiFile) ); string $mcfiPath = ( $rootDir + $mcfiFile ); int $result = true; string $fmt = (uiRes("m_fluidSaveStateAs.kCacheCreateFailure")); if( !`file -q -exists $mcfiPath` ) { warning( `format -s $mcfiPath $fmt` ); $result = false; } if( !`file -q -exists $melPath` ) { warning( `format -s $melPath $fmt` ); $result = false; } return $result; } global proc int fluidSaveStateAsFilenameBrowser( string $origDir, string $filename, string $filetype ) // // Description: // Same as fluidSaveStateAsFilename, but this one always // returns true if we should dismiss the fileBrowser that calls it. // { int $doIt = true; string $result; global string $gDefaultDefaultFileBrowserDir; // NT is nice enough to handle this internally via the // file browser dialog... // if(( !`about -nt` ) && ( `file -q -exists $filename` )) { string $cancel = (uiRes("m_fluidSaveStateAs.kCancel")); $result = `confirmDialog -message (uiRes("m_fluidSaveStateAs.kFileExists")) -button (uiRes("m_fluidSaveStateAs.kYes")) -button $cancel -defaultButton $cancel -dismissString $cancel -cancelButton $cancel`; if ($result == $cancel) { $doIt = false; } } if( $doIt ) { fluidSaveStateAsFilename( $filename, $filetype ); } $gDefaultFileBrowserDir = $gDefaultDefaultFileBrowserDir; workspace -dir $origDir; return $doIt; } global proc fluidSaveStateAs() // // Description: // Save the current state of the fluid as an initial // conditions cache, the name of which comes from a // file browser dialog. // { if( !`exists getActiveFluidShapes` ) { source getFluidShape.mel; } string $fluids[] = getActiveFluidShapes(); if( size( $fluids ) == 0 ) { error (uiRes("m_fluidSaveStateAs.kNoFluidSelectedError")); return; } else if( size( $fluids ) > 1 ) { error (uiRes("m_fluidSaveStateAs.kSaveStateAsError")); return; } global string $gFluidInitialStatesDefaultDir = ""; global string $gFluidInitialStatesDefaultDirRoot = ""; if( $gFluidInitialStatesDefaultDirRoot == "" ) { // If you change this, make sure to change the part // above that writes out $MAYA_LOCATION to the .mel // file as the relative location of the .mcfi data file. // $gFluidInitialStatesDefaultDirRoot = ( getenv( "MAYA_LOCATION" ) + "/presets/fluids/initialStates" ); } // This will reset the browser location each time Maya starts up. // if( $gFluidInitialStatesDefaultDir == "" ) { $gFluidInitialStatesDefaultDir = $gFluidInitialStatesDefaultDirRoot; } // Remember what the original working directory was so // we can restore it, otherwise we'll affect the default // directory for other types of save operations that use // the fileBrowser, like "export map," etc. // // Since the fileBrowser dialog is non-modal, we have // to pass in the orig directory so it can reset it when it's // done. // string $old = `workspace -q -dir`; workspace -dir $gFluidInitialStatesDefaultDir; global string $gDefaultFileBrowserDir; global string $gDefaultDefaultFileBrowserDir; $gDefaultDefaultFileBrowserDir = $gDefaultFileBrowserDir; $gDefaultFileBrowserDir = ""; string $callBack = ( "fluidSaveStateAsFilenameBrowser \"" + $old + "\" " ); fileBrowser $callBack (uiRes("m_fluidSaveStateAs.kSaveState")) "mel" 1; // fileBrowser.mel works on the assumption that you're // always browsing to a project-related directory. // // The current project is displayed in the title of the // browser window, after the action name in parentheses: // "Save As // (\\myMachine\myHomeDir\maya\projects\default)", for // instance. // // While this isn't ideal for "Save Fluid State As" // purposes, it's acceptable on Windows, because we use // the fileBrowserDialog, which displays the currently // browsed directory in a text field just below the // pulldown menu listing all the user's projects. // // On other platforms, however, fileBrowser.mel tries to // build its own window and, since the display of the // current directory is missing at the top of the // window, displaying the project in the window title // bar can be at odds with the display of the currently // selected filename/directory field at the bottom of // the window. // // Since we don't necessarily care about the active // project for Fluids Initial States, change the default // title of the projectViewerWindow so it doesn't try to // display the current project... // if( !`about -nt` && `window -exists projectViewerWindow`) { window -e -title (uiRes("m_fluidSaveStateAs.kSaveStateAs")) projectViewerWindow; } }