MAXScripting FumeFX
MAXScript is a powerful 3ds Max language that allows users to customize their interface, automate tasks, or increase functionality. FumeFX’s open architecture facilitates the use of this tool, which means you can tailor FumeFX to meet your own needs. Refer to the 3ds Max Reference Guide for general information on how to use this tool.
Globals
Here is a list of global variables exposed to MXS.
ffxSilent - No Message box will show up (like Stop/Continue).
Adding/Removing objects
Here is a list of all functions exposed by FumeFX Object Source.
<integer>NumObjects()
Returns the number of objects.
<node>GetObject <integer>index
Returns object.
index - its range goes from 0 to NumObjects()-1
<boolean>AddObject <node>node
It will add an object to the list. No duplicates or unsupported objects will be added.
<boolean>RemoveObjectByNode <node>node
Removes object based on the node.
<boolean>RemoveObjectByIndex <integer>index
Removed object based on the object's index.
Saving and Loading Presets
Here is a list of all functions exposed by FumeFX.
SavePresets <filename>presetFile
This saves all current FumeFX settings to a preset file “presetFile.fxp” in default preset folder. The output returns false on error.
If user provides a full path to the preset file (like c:\\mypresets\\explosion.fxp) then the preset file will be saved to the specified folder.
LoadPresets <filename>presetFile <rollout_names_array>rollouts
Use this to load all or some of the controls from the preset file “presetFile.fxp”. The output returns false upon error.
If user provides a full path to the preset file (like c:\\mypresets\\explosion.fxp) then the preset file will be loaded from the specified folder.
The Rollouts parameter is an array with some of the following values:
all: load all rollouts
grid: load all rollouts from Grid Tab
sim: load all rollouts from Simulation Tab
render: load all rollouts from Render Tab
illum: load all rollouts from Illumination Tab
grid_dim: load Grid Tab / General Parameters Rollout
grid_play: load Grid Tab / Playback Rollout
grid_rec: load Grid Tab / Recording Param
eters Rollout
sim_main: load Simulation Tab / Simulation Rollout
sim_smoke: load Simulation Tab / Smoke Rollout
sim_fuel: load Simulation Tab / Fuel Rollout
sim_temp: load Simulation Tab / Temperature Rollout
sim_fm: load Simulation Tab / Fluid Mapping Rollout
render_main: load Rendering Tab / Rendering Parameters
shader: load Rendering Tab / Shader Rollout
Examples:
-- have FumeFX01 save its settings to file mypreset.fxp in default presets folder
$FumeFX01.SavePresets “mypreset1”
-- have selected FumeFX object load Render Tab > Render parameters rollup and Simulation Tab > Fuel rollup from file mypreset1.fxd.
$.LoadPresets "mypreset1" #(“render_main”, “sim_fuel”)
-- have FumeFX01 load all rollouts in Grid Tab from file mypreset.fxd.
$FumeFX01.LoadPresets "mypreset" “grid”
Adding/Removing objects, lights, sources
<integer>NumSources()
Returns number of sources.
<node>GetSource <integer>index
Returns source.
index - its range goes from 0 to NumSources()-1
<boolean> AddSource <node>source
Use this to attach FumeFX Sources to FumeFX. It is equivalent to picking a Source object in the Objects panel of the Floater Window.
<boolean> RemoveSource <node>source
Use this to remove Force FumeFX Sources from FumeFX. It is equivalent to removing a Source object in the Objects panel of the Floater Window.
<boolean> AddForce <node>force
Use this to attach Force objects like Wind to FumeFX. It is equivalent to picking a Force object in Objects panel of the Floater Window.
<boolean> RemoveForce <node> force
Use this to remove Force objects like Wind from FumeFX. It is equivalent to removing a Force object in Objects panel of the Floater Window.
<integer> NumObjects()
Returns a number of objects that are currently picked.
<maxObject>GetObject <integer>index
Returns object based on index (zero based index).
<boolean>AddLight <node>node
Adds a light
<boolean>RemoveLight <node>node
Removes light
<integer> NumEffectors()
Returns a number of Effectors that are currently picked.
<maxObject>GetEffector <integer>index
Returns Effector based on index (zero based index).
<integer> NumSpaceWarps()
Returns a number of objects that are currently picked.
<maxObject>GetSpaceWarp <integer>index
Returns SpaceWarp based on index (zero based index).
Shader Access
<maxObject>GetShader()
Returns current shader.
<boolean>SetShaderByName <string>name
Sets current shader based on name.
<boolean>SetShaderByIndex <integer>index
Sets current shader based on index (zero based).
Preview Window
<void>MakePreview <integer>start <integer>end <integer>quality <integer>width <integer>height
This command will generate preview. If width and height are 0, size is left as it is.
quality - range is from 1 to 5
<void>ResizePreview <integer>width <integer>height
Simulation Control
<void>CancelSimulation()
This command will cancel the simulation
<void>StopSimulation()
This command will stop the simulation. This means it will finish the current frame and save stop/continue data according to the preferences settings.
<void>RunSimulation <integer>simMode
This does exactly the same as hitting the start button on the FumeFX Floater. Simulation will run from StartFrame to EndFrame.
simMode - 0 - Default Simulation
1 - Simulation from Initial State
2 - Wavelet Simulation
3 - Retimer
<void>ContinueSimulation <integer>simMode
This does exactly the same as hitting the continue button on the FumeFX Floater
simMode - 0 - Default Simulation
1 - Default Simulation
2 - Wavelet Simulation
<boolean>SetInitialStateFile <filename>filename
Use this command to set Initial State file to be used with Initial State Simulation Mode.
<filename>GetInitialStateFile()
Returns Initial State file name.
SaveOutput <filename>outputFile
This saves a .fxd type output file; note, you must enter the full file name. Output returns false on error.
SaveSim <filename>presetFile
This saves a .fdc type simulation file; note, you must enter full file name. Output returns false on error.
<boolean>LoadSim <filename>filename
This will load a .fdc file into the FumeFX.
<filename>GetPath <string>type
This returns the current base output file name without the frame number suffix.
cacheType -default - path for Deault Sim. caches
wavelet- path for Wavelet Sim. caches
retimer - path for Retimer caches
illummap - path for Illumination Map
preview- path for preview animation clip
presets - path for presets
defaultpreset – path for the default preset file (Preferences)
scrsim – FumeFX MXS file
<boolean>SetPath <filename>path <name array>params
params -match- if grid is of different proportions, FumeFX will be matched to it.
default - path for Deault Sim. caches
wavelet- path for Wavelet Sim. caches
retimer - path for Retimer caches
illummap - path for Illumination Map
preview- path for preview animation clip
defaultpreset – path for the default preset file (Preferences)
nocheck - path validity won’t be checked.
scrsim – FumeFX MXS file
This sets output file name. You must enter the path and base file name; the extension is not necessary.
Examples:
$FumeFX01.SetPath "F:\\temp\\FumeFX\\aa"
$FumeFX01.SetPath "F:\\temp\\FumeFX\\aa.fxd"
$FumeFX01.SetPath "F:\\temp\\FumeFX\\aa.fxd" #(“default”, “nocheck”)
$FumeFX01.SetPath "F:\\temp\\FumeFX\\aa_0000.fxd" #(“default”,”match”)
For #match, if you omit _0000 (or any existing frame number), FumeFX and cache won't be checked for grid dimensions and won't be matched if grid and cache have different sizes.
<void>DeleteCaches <string>name
This command will delete all the cache files
Name -default- path for Default Sim. caches
wavelet- path for Wavelet Sim. caches
retimer- path for Retimer Sim. caches
illummap - path for Illumination Map. caches
<boolean>ExportRenderingParams <string>name
This will save current frame rendering params to the file.
Misc. Functions
<int>GetBBox <point3>&dim0 <point3>&dim1
This command will return the cache number that is currently loaded and FumeFX bounding box in local coordinates. The return number of -99999 could indicate that there was no cache loaded in memory at the time this function was called.
Returned values in dim0 and dim1 are corners of the bounding box.
Example:
min=[0,0,0]
max=[0,0,0]
$.GetBBox &min &max
<int>GetFireInfo<float>&min <float>&max <float>&avg
<int>GetSmokeInfo<float>&min <float>&max <float>&avg
<int>GetTempInfo<float>&min <float>&max <float>&avg
<int>GetVelInfo<float>&min <float>&max <float>&avg
This command will return the cache number that is currently loaded. The return number of
-99999 could indicate that there was no cache loaded in memory at the time this function was called or that channel was not present inside the cache.
min – returns the minimum channel value inside the grid
min – returns the maximum channel value inside the grid
avg – returns the average channel value inside the grid (empty voxels are excluded).
After the simulation is finished FumeFX will load channels depending on the viewport or preview windows requirements. To force channel to be loaded you should enable its display inside the viewport.
FumeFX MAXScript Functions for Accessing the Simulation
By learning to use FumeFX functions created for MAXScript, you can do almost anything that you want with FumeFX – including things not provided for in the default user interface. It is possible for you to plug in script between all significant phases of the simulation or even disable some simulation steps altogether.
In this version of FumeFX, MAXScript cannot be called from the Listener, Macro Scripts, or in other way except from within FumeFX while the simulation is running or the output is being loaded. Preparation of the scripts takes place in the General panel of the Floater Window. You can access this by clicking the Edit Script button in the MAXScript rollout.
If you create a script that will modify all of the voxels in a simulation (which could be tens of millions), it will execute at reasonable speeds, measured in seconds.
To get the feel of it, here is an example script:
fn PostObjects = (
for i in 0 to (nx - 1) do
for j in 0 to (ny - 1) do
for k in 0 to (nz - 1) do(
val=k/(nz as float)
SetSmoke i j k val
)
)
PostObjects is a “callback” function that FumeFX will call in every frame of the simulation after it has applied all sources and objects. It will go though all voxels in the adaptive grid (nx, ny and nz are global variables provided by FumeFX) and initialize the Smoke channel with a vertical gradient going from 0 to 1 over the local Z-axis. Channel is a set using voxel coordinates.
MAXScript in FumeFX Simulation
How it works:
When you click Edit Script in the Simulation group, you will see a template in which to write your scripts. In it there are certain MAXScript functions that FumeFX will call during simulation. All of them are called once for each step of the simulation, unless otherwise noted.
Callback Functions
Note: Currently, the PostAdvection and PostPressure functions are for experimental use only, but are provided for those who are familiar with their use.
PreSim: Called once – at the beginning of simulation, before the first step has started, but after possible snapshot or stop/continue data has been loaded.
PostSim: Called once - after the last step of the simulation has ended and while the simulation results are still in memory.
PostObjects: Think of this as a scripted Source. It is called after the Adaptive grid has adjusted and FumeFX Sources, objects, and forces have all been applied. Here, you can additionally initialize the grid any way you like.
Vorticity: This is the only place where Vorticity can be customized. The AddVort function allows every voxel can have its own Vorticity strength. Be aware that the stability of simulation can become unreliable when using values greater than 1.
PostForces: Buoyancy, Gravity, Vorticity, Motion Drag have been applied. Here you can apply custom forces such as: radial gravity, motion drag dependent on object distance, etc…
PostAdvection: This is called after Velocities have moved through one step. If you have disabled FumeFX's Advection phase, you could theoretically write your own version in this function.
PostDiffuse: This is called after movement of Smoke and Fuel and their diffusion and dissipation. This is the proper time to set Fuel and Temperature values to control fires.
PostStep: This is called following a whole simulation step, after the Fuel has been burned and diffusion and dissipation applied. You can set any kind of influence between channels here. If you have disabled FumeFX’s Burn phase, you could use this to burn Fuel your way.
Skipping FumeFX Simulation Phases
If you want to skip certain FumeFX simulation phases completely, uncomment in the template, or write the following statements on the global level of the script:
skipForces = true
skipAdvectFields = true
skipAdvectVels = true
skipPressure = true
skipBurn = true
This is not recommended unless you have experience.
A few examples of when you can skip certain phases are:
You can skip Pressure and Advection phase if you simulate only the effects of diffusion.
You can skip the Burn phase if you want to burn Fuel some other way.
You can skip Forces if you want to apply buoyancy, gravity or motion drag in some specific way, such as depending on position or distance from objects.
So, what can you do within these functions? Basically, you can get and set values for the main channels: Smoke, Fuel, Temperature and Velocity. You can also stop or cancel simulation and write messages to the FumeFX status window.
Simulation Control Functions
ffxPrintToStatus <string>message
This prints message to FumeFX status window during simulation.
ffxStopSimulation()
This is equivalent to pressing the Stop button in the status window.
ffxCancelSimulation()
This is equivalent to pressing the Cancel button in the status window.
ffxHoldWindowOnce()
If this function is called during simulation, then after the simulation has stopped or cancelled, the simulation window will stay open, regardless of Hold Window checkbox selection.
Get Channel Functions
Smoke is used in this example; however, all other channels are analogous. The ways to get Smoke values are:
GetSmoke <integer>i <integer>j <integer>k
In this function, i, j, and k respectively represent the X, Y, and Z-axis coordinates in an adaptive grid. Voxel coordinates can range from 0 to nx-1 for i, 0 to ny–1 for j, and 0 to nz-1 for k. So, to get the smoke value in first voxel you would write
GetSmoke 0 0 0
Or, to get it from the top center voxel you would write:
GetSmoke (nx/2) (ny/2) (nz)
GetSmokeByIndex <integer>index
This is slightly faster than the form GetSmoke i j k. It uses only one parameter, voxel index, which is computed (i*ny + j)*nz + k.
To get the average value of smoke
average = 0.0
for ind in 0 to (voxels-1) do (average += GetSmokeByIndex ind)
average /= voxels
GetSmokeLocal <point3>lpt
Here lpt is a point in a FumeFX object or local space. Unless the pivot is moved, the center of that coordinate system is the bottom center point of the FumeFX object. FumeFX dimensions are exposed through FumeFX nodes: Width, Length, and Height.
GetSmokeLocal [10.0, 10.0, $FumeFX01.Heigth/2]
GetSmokeWorld <point3>wpt <matrix3>FumeFXt
Here wWpt is a position in world coordinates. You must also provide a FumeFX transformation matrix.
To get Smoke density at some object’s position
wpt = $sensor01.position
tm = $FumeFX01.transform
if ((GetSmokeWorld wpt tm) > threshold) then myfunction()
These functions will return undefined if you ask for non-existing coordinates or coordinates outside of the grid. They will also return undefined if the channel has not been allocated – for example GetFuel() if Simulate Fire has been turned off in the Simulation tab.
A complete list of Get functions:
float GetSmoke
float GetSmokeByIndex
float GetSmokeLocal
float GetSmokeWorld
float GetTemp
float GetTempByIndex
float GetTempLocal
float GetTempWorld
float GetOxy
float GetOxyByIndex
float GetOxyLocal
float GetOxyWorld
float GetFuel
float GetFuelByIndex
float GetFuelLocal
float GetFuelWorld
point3 GetVel
point3 GetVelByIndex
point3 GetVelLocal
point3 GetVelWorld
Usage of functions:
GetChannel i j k
GetChannelByIndex index
GetChannelLocal lpt
GetChannelWorld wpt TM
Index and i,j,k coordinates must be passed as integers.
Set Channel Functions
To set channel values, functions are provided that are analogous to the Get functions. They only have one additional parameter – value to set. Input ranges are not checked – so accuracy is important. The output can return true or false even if the value was, for some reason, not set.
These functions set value at one voxel only – the voxel that the point belongs to.
SetSmoke <integer>i <integer>j <integer>k <float>value
This uses adaptive grid voxel coordinates.
SetSmokeByIndex <integer>index <float>value
This uses voxel index.
SetSmokeLocal <point3>lpt <float>value
This uses FumeFX local coordinates.
SetSmokeWorld <point3>wpt <matrix3>FumeFXtm <float>value
This uses world coordinates.
A complete list of Set functions:
float SetSmoke
float SetSmokeByIndex
float SetSmokeLocal
float SetSmokeWorld
float SetTemp
float SetTempByIndex
float SetTempLocal
float SetTempWorld
float SetOxy
float SetOxyByIndex
float SetOxyLocal
float SetOxyWorld
float SetFuel
float SetFuelByIndex
float SetFuelLocal
float SetFuelWorld
point3 SetVel
point3 SetVelByIndex
point3 SetVelLocal
point3 SetVelWorld
Usage of functions:
SetChannel i j k value
SetChannelByIndex index value
SetChannelLocal lpt value
SetChannelWorld wpt TM value
Smoke, Fuel and Temperature are float channels, and Velocity is a point3 channel. Remember to set the point3 channels with the point3 values, for example:
SetVelByIndex index [x,y,z]
More Channel Functions
There are also functions for Vorticity and Turbulence. Used in simulation, they are cumulative with UI defined global Vorticity and Turbulence. If you wish to use only the scripted values, you must set Vorticity and Turbulence strength in the UI to 0.
Note: Vorticity strengths above 1 may, in rare cases, make the simulation unstable.
To add Turbulence, you can use:
AddTurb <integer>i <integer>j <integer>k <float>value
AddTurbByIndex <integer>index <float>value
AddVort functions can only be used during Vorticity callback.
AddVort <integer>i <integer>j <integer>k <float>value
AddVortByIndex <integer>index <float>value
You can fill the entire channel with a given value using the following functions:
FillSmoke <float>value
FillFuel <float>value
FillTemp <float>value
FillVel <point3>value
MAXScript after Loading Output
It is possible to post-process FumeFX output files at the time of loading. Here, for example, you can change the displayed result or the result used by the particles without affecting the simulation. Appropriate callback is called PostLoad.
Example: To set smoke to any given minimum value throughout the entire grid, achieving a fog-like effect, enter the following script.
fn PostLoad = (
sm = 1.0 -- desired minimum smoke value
for i in 0 to (voxels-1) do
(
s = GetSmokeByIndex i
if (s < sm) then ( SetSmokeByIndex i sm )
)
)
Switching Between Coordinates
If you want to make modifications to FumeFX voxels, you will most often run loops in voxel space or index range. However, you may need to do some calculations in local or world space for some modifications. For this and similar cases, converter functions exist between various coordinate spaces that FumeFX uses.
List of functions:
<integer> ffxVoxelPtToIndex <point3>vpt
<point3> ffxVoxelPtToLocal <point3>vpt
<point3> ffxVoxelPtToWorld <point3>vpt
<point3> ffxIndexToVoxel <integer>ind
<point3> ffxIndexToLocal <integer>ind
<point3> ffxIndexToWorld <integer>ind
<integer> ffxLocalPtToIndex <point3>lpt
<point3> ffxLocalPtToVoxel <point3>lpt
<point3> ffxLocalPtToWorld <point3>lpt
<integer> ffxWorldPtToIndex <point3>wpt
<point3> ffxWorldPtToVoxel <point3>wpt
<point3> ffxWorldPtToLocal <point3>wpt
VoxelPtTo… functions take point3 as argument, in adaptive grid coordinates, ranging [0.,0.,0.] – [nx, ny, nz].
IndexTo… functions take integer as argument, ranging from 0 to (voxels-1).
LocalPtTo… functions take point3 as argument, in FumeFX local coordinates, ranging from [-Width/2, -Length/2, 0.] - [Width/2, Length/2, Height] for values inside the FumeFX grid.
WorldPtTo… functions take point3 as argument, in world coordinates.
IndexTo… functions must get valid index or they return undefined, other functions will convert between spaces regardless of whether the point is within FumeFX grid or not.
…ToVoxel functions return point3 ranging from 0 to [0.,0.,0.] – [nx, ny, nz].
…ToIndex functions return integer ranging from 0 to (voxels-1).
…ToLocal functions return point3 ranging from 0 to [-Width/2, -Length/2, 0.] - [Width/2, Length/2, Height] for values inside FumeFX grid.
…ToWorld functions return point3 in world space.
Various Issues
FumeFX will execute MAXScript commands in a single thread, but the usual simulation phases still execute in multiple threads. So, if you do heavy scripting you can expect some slowdowns.
If you Get and Set just a few channel values at once, you will not have to be concerned with memory issues. However, should you want to keep a copy of a whole channel in memory as a MAXScript array, you will have to enlarge MAXScript’s heap size. To do this, go to Customize > Preferences > MaxScript tab > Memory group > Initial heap allocation. To copy a float channel with 1 million voxels to MAXScript’s array you will need 28MB of MAXScript Heap memory, and for a point3 channel you will need 36MB.
If you no longer need such a big array or if you want to overwrite it, do the following:
arrayname = #() -- make it an empty array
arrayname = undefined -- or undefine it completely
gc light:true -- wipe it from memory
The statement “gc” invokes the MAXScript garbage collector. Otherwise, if the array takes most of the MAXScript heap, just writing to the same array twice may take an incredibly long time. See the “Memory Allocation and Garbage Collection” and “Manual Garbage Collection” in the MAXScript User Reference for more detail.
Notes
You have to provide valid values to FumeFX when you set them.
For Fuel, ranges are –100 to 100. Set negative values to have fuel in non-burning state, or set positive values to have already burning fuel.
If you want to use the time slider’s global variable in FumeFX scripts, add one frame to it. FumeFX sets the time slider when it has finished calculating a frame. For example, when calculation of frame 5 is in progress, the slider time global variable will still be at frame 4.