*Fixed/Updated* Maya 2018 Vector Field Exporter +Normalize Option

I was having a hell of a time trying to make the FGA exporter work and couldn’t find any posted fix/solution to this yet, so I decided to fix the bug in the code. It turned out that it was a really simple error at:


//Grab the Grid resolution
***float*** $res] = `eval("getAttr " + $myfluidShape + ".res")`;

and it just needed to be changed to int because the resolution is only ever going to be in integers anyways:


***int*** $res] = `eval("getAttr " + $myfluidShape + ".res")`;

Also, while looking around for solutions/fixes, I found some code to make this work for win/mac. I can’t remember where I found it, so I don’t know who to thank. Whoever it was, thanks!:


if( `about -mac`)
        {
        $outpath = `workspace -q -rd`;
        }
    else if( `about -win`)
        {
        $outpath = "C:\\" ;
        }

Lastly, I added in an option to normalize everything based on the longest vector in the array. I’m not sure how Unreal handles FGAs, but my gut instinct tells me that it’s probably better to use normalization. Use the normalize vectors button to enable/disable it. Under the hood, it makes an array of all the vector lengths, finds the longest one and then divides the XYZ outputs by that length. I’ve never coded anything in MEL, so don’t roast me too hard if things could have been done cleaner. It works and I don’t really care how efficient it is lol…

It won’t let me upload a *.mel file, so If people don’t trust a zip file, then here’s the source that you can throw into notepad and save it as a *.mel file. Place the file inside your “C:\Users<USERNAMEHERE>\Documents\maya\2018\prefs\shelves” folder:


global proc shelf_UE4velocityGridExporter () {
    global string $gBuffStr;
    global string $gBuffStr0;
    global string $gBuffStr1;


    shelfButton
        -enableCommandRepeat 1
        -enable 1
        -width 34
        -height 34
        -manage 1
        -visible 1
        -preventOverride 0
        -annotation "Export a 3D fluid container as a vector field for use in UE4"
        -enableBackground 0
        -backgroundColor 0 0 0
        -highlightColor 0.321569 0.521569 0.65098
        -align "center"
        -label "Fixed_Unreal_FGA_Exporter_2018"
        -labelOffset 0
        -rotation 0
        -flipX 0
        -flipY 0
        -useAlpha 1
        -font "plainLabelFont"
        -imageOverlayLabel "FGA"
        -overlayLabelColor 0.8 0.8 0.8
        -overlayLabelBackColor 0 0 0 0.5
        -image "fluidEditRes.png"
        -image1 "fluidEditRes.png"
        -style "iconOnly"
        -marginWidth 1
        -marginHeight 1
        -command "// Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
// Export velocity grid data from a Maya fluid container 
// Description: Writes out velocity grid data in a custom formatted ascii file

global proc gridExport(){

    //Check to see if the UI window already exists. If it does, it is deleted
	if(`window -exists gridExport`){
		
		deleteUI -window gridExport;
	}
	//Create new UI Window
	window -t \"gridExport\" -rtf 1 -widthHeight 300 300 gridExport;
	columnLayout rootLayout;
	frameLayout -labelVisible false -marginWidth 5 -marginHeight 5 ;
	columnLayout verticalSubframe;
			
	setParent ..;
	text -label \"UE4 Vector Field Exporter\";
	radioButtonGrp -numberOfRadioButtons 2 -label \"Export Mode\"
	    -labelArray2 \"Single Frame\" \"Sequence\"
	    -select 1
	    -onCommand1 \"intFieldGrp -edit -enable 0 endFrame;\"
	    -onCommand2 \"intFieldGrp -edit -enable 1 endFrame;\"
	myRadBtnGrp;
	checkBoxGrp - l \"Cached Fluid?\" -v1 0 isCached;
	checkBoxGrp - l \"Normalize Vectors?\" -v1 0 isNormalized;
	intFieldGrp -l \"Start Frame\" startFrame;
	intFieldGrp -l \"End Frame\"  -enable 0 -v1 3 endFrame;
	//intFieldGrp -l \"Increment\" -enable 0 -v1 1 increment;

	string $outpath = \"\";	
	if( `about -mac`)
		{
		$outpath = `workspace -q -rd`;
		}
	else if( `about -win`)
		{
		$outpath = \"C:\\\\\" ;
		}

	textFieldGrp -l \"Path\"  -text $outpath folderPath;
	textFieldGrp -l \"Filename prefix\" -text \"vel\" filename;	
	columnLayout  -columnAttach \"both\" 0 -adjustableColumn true exportButton;
	    button -l \"export\" -c \"iterateExport\";
	
	showWindow gridExport;
}

global proc iterateExport(){
    
    int $startFrame = `intFieldGrp -q -v1 startFrame`;
	int $endFrame = `intFieldGrp -q -v1 endFrame`;
	//int $increment = `intFieldGrp -q -v1 increment`;
	string $dataName] =  {\"velocity\"};
    int $n = $startFrame;
    
    string $sel] = `ls -sl`;
    
    if (size($sel)>1 || size($sel)<1){
		
		print(\"ERROR: Please select a single fluid container \
\");
		
	} else {

	    string $fluidShape] = `listRelatives -s $sel[0]`;
	    string $objectCheck = `objectType $fluidShape[0]`;
	   
	    if ($objectCheck == \"fluidShape\") {	       
            
            int $doit = `checkBoxGrp -q -v1 isCached`;
            print $doit;
             
             if ($doit == 0 ) {
                          
                 int $sceneCurTime = `currentTime -q`;
                 int $sceneMinTime = `playbackOptions -q -minTime`;
                 if ($sceneCurTime > $startFrame) {
                     
                     currentTime $sceneMinTime;
                     runupToStart($sceneMinTime, $startFrame);             
                 
                 } else {
                 
                     if ($sceneCurTime < $startFrame) {
                         runupToStart($sceneCurTime, $startFrame);
                     }
                 }
            }
            
            string $filePath = \"\";

            if (`radioButtonGrp -q -select myRadBtnGrp` == 1) {
        	    
        	    currentTime $n;
		        string $folder = `textFieldGrp -q -text folderPath`;
		        string $filename = `textFieldGrp -q -text filename`;
		        
		        	
				if( `about -mac`)
					{
					$filePath = $folder + $filename + \".\" + $n + \".fga\";
					}
				else if( `about -win`)
					{
					$filePath = $folder +\"\\\\\"+ $filename + \".\" + $n + \".fga\";
					}

		        print (\"Wrote: \" + $filePath + \"\
\");		        
     		    dataExport($dataName[0],$filePath, $fluidShape[0]);
		   
            } else {

		          for($n = $startFrame; $n < ($endFrame+1); $n++){
			     
			         currentTime $n;
			         string $folder = `textFieldGrp -q -text folderPath`;
			         string $filename = `textFieldGrp -q -text filename`;
			         if( `about -mac`)
						{
						$filePath = $folder + $filename + \".\" + $n + \".fga\";
						}
					else if( `about -win`)
						{
						$filePath = $folder +\"\\\\\"+ $filename + \".\" + $n + \".fga\";
						}
			         print (\"Wrote: \" + $filePath + \"\
\");
			         dataExport($dataName[0],$filePath,$fluidShape[0]);	
			     
			       }
		     }
      
         } else {
             
             print(\"ERROR: Please select a fluid container \
\");
        
         }
    }
}

global proc dataExport(string $dataName, string $filePath, string $myfluidShape){
		
	int $voxCount = 0;
		
	//Grab the Grid resolution
	int $res] = `eval(\"getAttr \" + $myfluidShape + \".res\")`;
	
	//switch back to parent transform
	string $fluidShapeParent] = `listRelatives -p $myfluidShape`;

	//Grab the voxel container bounding box
	float $bb] = `xform -q -ws -bb $fluidShapeParent[0]`;

    //create and open the output file in write mode
	int $fileId=`fopen $filePath \"w\"`;
	
	//Write voxel res
	fprint $fileId (\"\"+$res[0]+\",\");
	fprint $fileId (\"\"+$res[1]+\",\");
	fprint $fileId (\"\"+$res[2]+\",\");
	//Write bounding Box info
	fprint $fileId (\"\"+$bb[0]+\",\");
	fprint $fileId (\"\"+$bb[1]+\",\");
	fprint $fileId (\"\"+$bb[2]+\",\");
	fprint $fileId (\"\"+$bb[3]+\",\");
	fprint $fileId (\"\"+$bb[4]+\",\");
	fprint $fileId (\"\"+$bb[5]+\",\");

    int $x = 0;
	int $y = 0;
	int $z = 0;
    vector $myV = 0;
	
	int $whereareweinlist = 0;
	float $length];
	float $largestvector = 0;
	int $arewenormalizing = `checkBoxGrp -q -v1 isNormalized`;
	
	if ($arewenormalizing == 1 ){

		//calculate lengths
		for($z = 0; $z < $res[2]; $z++){
			for($y = 0; $y < $res[1]; $y++){
				for($x = 0; $x < $res[0]; $x++){
					float $v] = `getFluidAttr -at $dataName -xi $x -yi $y -zi $z`;		
					$myV = << $v[0], $v[1], $v[2] >>;
					$length$whereareweinlist] = sqrt(($v[0]*$v[0])+($v[1]*$v[1])+($v[2]*$v[2]));
					$whereareweinlist = $whereareweinlist + 1;
				}
			}
		}
		
		//find largest length in array
		for ($current in $length){
			if ($current > $largestvector)
				$largestvector = $current;
		}
		
		//write normalized vectors into file
		for($z = 0; $z < $res[2]; $z++){
			for($y = 0; $y < $res[1]; $y++){
				for($x = 0; $x < $res[0]; $x++){
					float $v] = `getFluidAttr -at $dataName -xi $x -yi $y -zi $z`;		
					$myV = << $v[0], $v[1], $v[2] >>;
					fprint $fileId (($v[0]/$largestvector)+\",\"+($v[1]/$largestvector)+\",\"+($v[2]/$largestvector)+\",\");
				}
			}
		}
		fclose $fileId;
	}

	else{	
		for($z = 0; $z < $res[2]; $z++){
			for($y = 0; $y < $res[1]; $y++){
				for($x = 0; $x < $res[0]; $x++){
					float $v] = `getFluidAttr -at $dataName -xi $x -yi $y -zi $z`;		
					$myV = << $v[0], $v[1], $v[2] >>;
					fprint $fileId ($v[0]+\",\"+$v[1]+\",\"+$v[2]+\",\");
				}
			}
		}
		fclose $fileId;
	}
}

global proc runupToStart(int $baseframe, int $exportFirstFrame) {
    int $i;
    for ($i = $baseframe; $i < $exportFirstFrame; $i++) {
	    //print($i+\"...\
\");
	    currentTime $i;
    }
}

gridExport();"
        -sourceType "mel"
        -commandRepeatable 1
        -flat 1
    ;

}


5 Likes

Was just running into this, thanks for sharing the solution!

Hi. Just thought of giving an update for anyone who’d need this info. The above script does not work in Maya 2023.

The Exporter mel script from the 4.27 package perfectly works for me in Maya 2023.

Pasting its code below so you don’t have to download the full 4.27 package. paste this in notepad and save as ‘shelf_UE4velocityGridExporter.mel’ in ‘C:\Users<yourusername>\Documents\maya\2023\prefs\shelves’.

here it is down below: >>>>

global proc shelf_UE4velocityGridExporter () {
    global string $gBuffStr;
    global string $gBuffStr0;
    global string $gBuffStr1;


    shelfButton
        -enableCommandRepeat 1
        -enable 1
        -width 35
        -height 35
        -manage 1
        -visible 1
        -preventOverride 0
        -annotation "Export a 3D fluid container as a vector field for use in UE4" 
        -enableBackground 0
        -align "center" 
        -label "StaticGridExport" 
        -labelOffset 0
        -font "plainLabelFont" 
        -overlayLabelColor 0.8 0.8 0.8 
        -overlayLabelBackColor 0 0 0 0.2 
        -image "fga.png" 
        -image1 "fga.png" 
        -style "iconOnly" 
        -marginWidth 1
        -marginHeight 1
        -command "// Copyright Epic Games, Inc. All Rights Reserved.\n// Export velocity grid data from a Maya fluid container \n// Description: Writes out velocity grid data in a custom formatted ascii file\n\nglobal proc gridExport(){\n\n    //Check to see if the UI window already exists. If it does, it is deleted\n\tif(`window -exists gridExport`){\n\t\t\n\t\tdeleteUI -window gridExport;\n\t}\n\t//Create new UI Window\n\twindow -t \"gridExport\" -rtf 1 -widthHeight 300 300 gridExport;\n\tcolumnLayout rootLayout;\n\tframeLayout -labelVisible false -marginWidth 5 -marginHeight 5 ;\n\tcolumnLayout verticalSubframe;\n\t\t\t\n\tsetParent ..;\n\ttext -label \"UE4 Vector Field Exporter\";\n\tradioButtonGrp -numberOfRadioButtons 2 -label \"Export Mode\"\n\t    -labelArray2 \"Single Frame\" \"Sequence\"\n\t    -select 1\n\t    -onCommand1 \"intFieldGrp -edit -enable 0 endFrame;\"\n\t    -onCommand2 \"intFieldGrp -edit -enable 1 endFrame;\"\n\tmyRadBtnGrp;\n\tcheckBoxGrp - l \"Cached Fluid?\" -v1 0 isCached;\n\tintFieldGrp -l \"Start Frame\" startFrame;\n\tintFieldGrp -l \"End Frame\"  -enable 0 -v1 3 endFrame;\n\t//intFieldGrp -l \"Increment\" -enable 0 -v1 1 increment;\n\ttextFieldGrp -l \"Path\"  -text \"C:\\\\\" folderPath;\n\ttextFieldGrp -l \"Filename prefix\" -text \"vel\" filename;\t\n\tcolumnLayout  -columnAttach \"both\" 0 -adjustableColumn true exportButton;\n\t    button -l \"export\" -c \"iterateExport\";\n\t\n\tshowWindow gridExport;\n}\n\nglobal proc iterateExport(){\n    \n    int $startFrame = `intFieldGrp -q -v1 startFrame`;\n\tint $endFrame = `intFieldGrp -q -v1 endFrame`;\n\t//int $increment = `intFieldGrp -q -v1 increment`;\n\tstring $dataName[] =  {\"velocity\"};\n    int $n = $startFrame;\n    \n    string $sel[] = `ls -sl`;\n    \n    if (size($sel)>1 || size($sel)<1){\n\t\t\n\t\tprint(\"ERROR: Please select a single fluid container \\n\");\n\t\t\n\t} else {\n\n\t    string $fluidShape[] = `listRelatives -s $sel[0]`;\n\t    string $objectCheck = `objectType $fluidShape[0]`;\n\t   \n\t    if ($objectCheck == \"fluidShape\") {\t       \n            \n            int $doit = `checkBoxGrp -q -v1 isCached`;\n            print $doit;\n             \n             if ($doit == 0 ) {\n                          \n                 int $sceneCurTime = `currentTime -q`;\n                 int $sceneMinTime = `playbackOptions -q -minTime`;\n                 if ($sceneCurTime > $startFrame) {\n                     \n                     currentTime $sceneMinTime;\n                     runupToStart($sceneMinTime, $startFrame);             \n                 \n                 } else {\n                 \n                     if ($sceneCurTime < $startFrame) {\n                         runupToStart($sceneCurTime, $startFrame);\n                     }\n                 }\n            }\n            \n            if (`radioButtonGrp -q -select myRadBtnGrp` == 1) {\n        \t    \n        \t    currentTime $n;\n\t\t        string $folder = `textFieldGrp -q -text folderPath`;\n\t\t        string $filename = `textFieldGrp -q -text filename`;\n\t\t        string $filePath = $folder +\"\\\\\"+ $filename + \".\" + $n + \".fga\";\n\t\t        print (\"Wrote: \" + $filePath + \"\\n\");\t\t        \n     \t\t    dataExport($dataName[0],$filePath, $fluidShape[0]);\n\t\t   \n            } else {\n\n\t\t          for($n = $startFrame; $n < ($endFrame+1); $n++){\n\t\t\t     \n\t\t\t         currentTime $n;\n\t\t\t         string $folder = `textFieldGrp -q -text folderPath`;\n\t\t\t         string $filename = `textFieldGrp -q -text filename`;\n\t\t\t         string $filePath = $folder +\"\\\\\"+ $filename + \".\" + $n + \".fga\";\n\t\t\t         print (\"Wrote: \" + $filePath + \"\\n\");\n\t\t\t         dataExport($dataName[0],$filePath,$fluidShape[0]);\t\n\t\t\t     \n\t\t\t       }\n\t\t     }\n      \n         } else {\n             \n             print(\"ERROR: Please select a fluid container \\n\");\n        \n         }\n    }\n}\n\nglobal proc dataExport(string $dataName, string $filePath, string $myfluidShape){\n\t\t\n\tint $voxCount = 0;\n\t\t\n\t//Grab the Grid resolution\n\tfloat $res[] = `eval(\"getAttr \" + $myfluidShape + \".res\")`;\n\t\n\t//switch back to parent transform\n\tstring $fluidShapeParent[] = `listRelatives -p $myfluidShape`;\n\n\t//Grab the voxel container bounding box\n\tfloat $bb[] = `xform -q -ws -bb $fluidShapeParent[0]`;\n\n    //create and open the output file in write mode\n\tint $fileId=`fopen $filePath \"w\"`;\n\t\n\t//Write voxel res\n\tfprint $fileId (\"\"+$res[0]+\",\");\n\tfprint $fileId (\"\"+$res[1]+\",\");\n\tfprint $fileId (\"\"+$res[2]+\",\");\n\t//Write bounding Box info\n\tfprint $fileId (\"\"+$bb[0]+\",\");\n\tfprint $fileId (\"\"+$bb[1]+\",\");\n\tfprint $fileId (\"\"+$bb[2]+\",\");\n\tfprint $fileId (\"\"+$bb[3]+\",\");\n\tfprint $fileId (\"\"+$bb[4]+\",\");\n\tfprint $fileId (\"\"+$bb[5]+\",\");\n\n    int $x = 0;\n\tint $y = 0;\n\tint $z = 0;\n    vector $myV = 0;\n\t\n\tfor($z = 0; $z < $res[2]; $z++){\n\t   \n\t    for($y = 0; $y < $res[1]; $y++){\n\t        \n\t        for($x = 0; $x < $res[0]; $x++){\n\n\t\t        float $v[] = `getFluidAttr -at $dataName -xi $x -yi $y -zi $z`;\t\t\n\t\t\n\t\t\t    $myV = << $v[0], $v[1], $v[2] >>;\n \n              \tfprint $fileId ($v[0]+\",\"+$v[1]+\",\"+$v[2]+\",\");\n    \n\t        }\n\t    }\n\t}\n\t\n\tfclose $fileId;\n}\n\nglobal proc runupToStart(int $baseframe, int $exportFirstFrame) {\n    int $i;\n    for ($i = $baseframe; $i < $exportFirstFrame; $i++) {\n\t    //print($i+\"...\\n\");\n\t    currentTime $i;\n    }\n}\n\ngridExport();\n\n\n\n\n\n\n" 
        -sourceType "mel" 
        -commandRepeatable 1
    ;

}