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
;
}