AWS Thinkbox Discussion Forums

3dsmax function Publishing from Thinkbox Software


Is there any function Publishing access from any of the plugin software? We have been developing a C++ plugin for our VFX tools.

So, to get access to our c++ functions we use 3dsmax SKD with function publishing. The way this works is our main C++ UI executes a simple line of maxscript “VFX_Fracture.OpenUI()” This will open an access our fracturing tool that is a different c++ plugin.

My Question is do you guys have a list of functions we can use to access your plugin that we use from our UI? Like open Xmesh saver? or any others?


Yes, the majority of Thinkbox Software’s 3ds Max plugins use Function Publishing. In fact, they use our own FP system that is easier to use (by us internally) than the one Autodesk provides, but that does not affect the end user. In addition, for most products, we made the decision to split the core functionality and UI code right in the middle and develop all UI in MAXScript, communicating with the core plugin via the published MAXScrcipt Functions and Interfaces.

XMesh Saver is one of those plugins whose UIs are completely written in MAXScript, and unprotected (not in an MSE). Opening the XMesh Saver really just involves the launch of its MacroScript, e.g.

Macros.Run "Thinkbox" "XMeshSaver"

You can take a look at the complete UI code, as well as some utility scripts by going to the folder
C:\Program Files\Thinkbox\XMeshSaver MX\Scripts\

In fact, we encourage studios to write their own implementations of the XMesh Saver UI that fit their pipeline - the UI we ship is a One Size Fits All and sometimes a bit too much :wink: Back in the days when we were still a production studio and XMesh was just an internal tool, I wrote at least 3 alternative UIs for the artists to expose only what they needed…

The complete core functionality of XMesh Saver is exposed via a single MAXScript FP Interface called XMeshSaverUtils:

showInterface XMeshSaverUtils Interface: XMeshSaverUtils Properties: .HasLicense : bool : Read .Version : string : Read .VersionNumber : string : Read .XMeshSaverHome : string : Read .SettingsDirectory : string : Read .TimeStepInitialOffset : float : Read|Write .TimeStepScale : float : Read|Write .DefaultMaterialID : integer : Read|Write .CompressionLevel : integer : Read|Write .ThreadCount : integer : Read|Write .PopupLogWindowOnError : bool : Read|Write .LogWindowVisible : bool : Read|Write .LoggingLevel : integer : Read|Write .SourceChannels : string by value array : Read|Write .ObjFlipYZ : bool : Read|Write Methods: <void>SetSequenceName <string>SequenceName <void>SaveMeshToSequence <node>Node <bool>IgnoreEmptyMeshes <bool>IgnoreTopologyChanges <bool>UseObjectSpace <bool>FindVelocity <void>SaveMeshesToSequence <node array>Nodes <bool>IgnoreEmptyMeshes <bool>IgnoreTopologyChanges <bool>FindVelocity <void>SavePolymesh <node>Node <string>Path <bool>VertsOnly WorldSpace:<bool> WorldSpace default value: false <void>SavePolymeshToSequence <node>Node <bool>IgnoreEmptyMeshes <bool>IgnoreTopologyChanges <bool>UseObjectSpace <bool>FindVelocity <void>SavePolymeshesToSequence <node array>Nodes <bool>IgnoreEmptyMeshes <bool>IgnoreTopologyChanges <bool>FindVelocity <void>SetSceneRenderBegin() <void>SetSceneRenderEnd() <void>ConfigureLicense() <void>AcquireLicense() <void>ReleaseLicense() <void>ClearAllMaterialIDMapping() <void>SetMaterialIDMapping <node>MeshNode <int array>FromMaterialIDList <int array>ToMaterialIDList <void>LogError <string>Msg <void>LogWarning <string>Msg <void>LogProgress <string>Msg <void>LogStats <string>Msg <void>LogDebug <string>Msg <void>FocusLogWindow() <string>ReplaceSequenceNumber <string>file <float>frame <void>SetUserData <string>key <string>value <fpvalue by value>GetUserDataArray() <void>DeleteUserData <string>key <void>ClearUserData() <void>LoadMetadata <string>filename <void>SaveMetadata <string>filename Actions: OK

Here is the relevant documentation page: … interface/

The XMesh Loader plugin on the other hand is written completely in C++, because there was no need for added MAXScript flexibility in its UI, and scripted plugins are generally slightly slower due to less granular notification management. Of course, it is still completely exposed to MAXScript as any other plugin, so you can construct and set up an XMesh Loader using MAXScript. Note that when saving XMesh data with the Saver, an MS file is written to the output folder that uses MAXScript to create an XMesh Loader if dropped into the 3ds Max viewport. It looks something like this:

local theXMeshLoader = XMeshLoader()
local thePath = getFilenamePath (getThisScriptFilename())
local goOn = true
if not doesFileExist (thePath+"\\"+"XMesh_MultiWS1_0000.xmesh" ) do (thePath = @"C:\temp\XMesh\Teapot\v0001\")
if not doesFileExist (thePath+"\\"+"XMesh_MultiWS1_0000.xmesh" ) do ((messagebox "Please ensure you are executing the script from a MAPPED PATH or local drive to automatically resolve the path.

 If you are executing from a Network location, make sure the hard-coded path in the script exists." title:"XMesh Source Sequence Path Not Found"); goOn = false) 
if goOn == true do (
local theXMeshLayer = LayerManager.getLayerFromName  "XMesh Loaders" 
if theXMeshLayer == undefined do theXMeshLayer = LayerManager.newLayerFromName "XMesh Loaders" 
theXMeshLayer.addnode theXMeshLoader 
theXMeshLoader.viewportSequenceID = 0 = uniquename "XML_XMesh_MultiWS1__" 
theXMeshLoader.enableViewportMesh = true 
theXMeshLoader.displayMode = 0 
theXMeshLoader.displayPercent = 5.0 
theXMeshLoader.limitToRange = true 
select theXMeshLoader 
theXMeshLoader.rangeFirstFrame = 0 
theXMeshLoader.rangeLastFrame = 0 
theXMeshLoader.viewportSequenceID = 0 
theXMeshLoader.proxySequence = "" 
theXMeshLoader.renderSequence = thePath + "XMesh_MultiWS1_0000.xmesh" 
local theMatLibPath = thePath + "XMesh_MultiWS1_.mat" 
if doesFileExist theMatLibPath do (
local theMatLib = loadTempMaterialLibrary theMatLibPath 
if theMatLib != undefined do theXMeshLoader.material = theMatLib[1] 

The same applies to Krakatoa MX - in fact, it was the first product to use this C++ / MAXScript split, and it has a lot more parts that are scripted, including the UI of the renderer itself, the PRT Loader UI, the Magma Editor, the Particle Data Viewer, the Krakatoa Schematic View, the Krakatoa Explorers, Shadows Manager and so on. Pretty much everything that is not the renderer itself or the core Magma system is in MAXScript, communicating with the core via functions and properties, including the FranticParticles interface.

You can learn a lot more about it here:

The Stoke MX plugin follows the same principles as Krakatoa MX. The Stoke Particle Simulator UI is fully scripted, and you can dissect the MS file to see how the simulator is run by MAXScript calls in the StokeGlobalInterface:

showInterface StokeGlobalInterface Interface: StokeGlobalInterface Properties: .HomeDirectory : TSTR by value : Read .Version : TSTR by value : Read .Licensed : bool : Read .ViewportType : enum : Read ViewportType enums: {#legacy|#nitrous} .PointSizeSupported : bool : Read .LoggingLevel : enum : Read|Write LoggingLevel enums: {#none|#error|#warning|#progress|#stats|#debug} .MaxDebuggerIterations : integer : Read|Write .MaxMarkerCount : integer : Read|Write Methods: <void>ShowLicenseDialog() <Interface>CreateAdvector <string>Type <Interface>CreateIDAllocator() <Interface>CreateReflowField <node>Node <Interface>CreateParticleVelocityField <node>Node <float>Spacing Min:<point3> Max:<point3> BoundsPadding:<integer> RemoveDivergence:<bool> Min default value: [0,0,0] Max default value: [0,0,0] BoundsPadding default value: 5 RemoveDivergence default value: true <Interface>CreateAdditiveVelocityField <Interface array>Fields <Interface>CreateKrakatoaGenerator <node>Node JitterRadius:<float> IgnoreIDs:<bool> JitterRadius default value: 0.0 IgnoreIDs default value: false <Interface>CreateGeometryGenerator <node>Node Mode:<string> VolumeSpacing:<float> SelectionType:<string> VertexJitterRadius:<float> Mode default value: "Surface" VolumeSpacing default value: 10.0 SelectionType default value: "FaceSelection" VertexJitterRadius default value: 0.0 <Interface>CreateFumeFXGenerator <node>Node <Interface>CreateParticleSet <string array>ExtraChannels <void>AdvectParticleSet <Interface>ParticleSet <Interface>Advector <Interface>VelocityField <float>TimeStepSeconds <void>WriteParticleSet <Interface>ParticleSet <string>FilePath <void>BeginRenderMode <node array>NodeList <void>EndRenderMode <node array>NodeList <enum>GetSourceType <node>Node GetSourceType enums: {#invalid|#particles|#geometry|#fumefx <enum>GetVelocityType <node>Node GetVelocityType enums: {#invalid|#particles|#fumefx|#force|#field <void>SetTempDirectory <string>Directory <bool>IsEmberField <node>Node <Interface>CreateMXSField <node>Node StackIndex:<integer> StackIndex default value: 0 <void>WriteFXDFile <string>FilePath <node>Node <point3>WSBoundsMin <point3>WSBoundsMax <float>Spacing StackIndex:<integer> StackIndex default value: 0 <void>WriteField3DFile <string>FilePath <node>Node <point3>WSBoundsMin <point3>WSBoundsMax <float>Spacing StackIndex:<integer> StackIndex default value: 0 <void>WriteOpenVDBFile <string>FilePath <node>Node <point3>WSBoundsMin <point3>WSBoundsMax <float>Spacing StackIndex:<integer> StackIndex default value: 0 Actions: OK

A lot of the other components of Stoke are also scripted plugins, scripted dialogs etc. They are all located in the \Scripts subfolder of the installation, have a look.

The Frost plugin is mostly C++, although it also has some scripted utilities, and is of course fully exposed to MAXScript like any other scene object.

theFrost = Frost() $Frost:Frost001 @ [0.000000,0.000000,0.000000] show theFrost .showIcon : boolean .iconSize : worldUnits .updateOnFrostChange : boolean .updateOnParticleChange : boolean .nodeList : node array .pfEventList : node array .fileList : string array .loadSingleFrame : boolean .frameOffset : integer .limitToRange : boolean .rangeStartFrame : integer .rangeEndFrame : integer .enablePlaybackGraph : boolean .playbackGraphTime : float .beforeRangeBehavior : integer .afterRangeBehavior : integer .fileLengthUnit : integer .fileCustomScale : float .meshingMethod : integer .enableRenderMesh : boolean .enableViewportMesh : boolean .radius : worldUnits .useRadiusChannel : boolean .randomizeRadius : boolean .radiusRandomVariation : float .radiusRandomSeed : integer .motionBlurMode : integer .renderUsingViewportSettings : boolean .renderMeshingResolution : float .renderVertRefinementIterations : integer .viewportMeshingResolution : float .viewportVertRefinementIterations : integer .metaballRadiusScale : float .metaballIsosurfaceLevel : float .zhuBridsonBlendRadiusScale : float .zhuBridsonEnableLowDensityTrimming : boolean .zhuBridsonLowDensityTrimmingThreshold : float .zhuBridsonLowDensityTrimmingStrength : float .geometryType : integer .geometryList : node array .geometrySelectionMode : integer .geometrySelectionSeed : integer .geometrySampleTimeOffsetMode : integer .geometrySampleTimeMaxRandomOffset : float .geometrySampleTimeSeed : integer .geometryOrientationMode : integer .geometryOrientationLookAtNode : node .geometryOrientationVectorChannel : string .geometryOrientationX : float .geometryOrientationY : float .geometryOrientationZ : float .geometryOrientationDivergence : float .geometryOrientationRestrictDivergenceAxis : boolean .geometryOrientationDivergenceAxisSpace : integer .geometryOrientationDivergenceAxisX : float .geometryOrientationDivergenceAxisY (geometryOrientationDivergenceAxisX) : float .geometryOrientationDivergenceAxisZ : float .writeVelocityMapChannel : boolean .velocityMapChannel : integer .viewportLoadMode : integer .viewportLoadPercent : float .anisotropicRadiusScale : float .anisotropicWindowScale : float .anisotropicIsosurfaceLevel : float .anisotropicMaxAnisotropy : float .anisotropicMinNeighborCount : integer .anisotropicPositionSmoothingWindowScale : float .anisotropicPositionSmoothingWeight : float .pfEventFilterMode : integer .materialMode : integer .undefinedMaterialID : integer .geometryMaterialIDNodeList : node array .geometryMaterialIDInList : index array .geometryMaterialIDOutList : index array .radiusScale (Radius_Scale) : float .radiusAnimationMode : integer .enableRadiusScale : boolean .geometrySampleTimeBaseMode : integer .nodeListFlags : int array .fileListFlags : int array .meshingResolutionMode : integer .renderVoxelLength : worldUnits .viewportVoxelLength : worldUnits .iconMode : integer .viewRenderParticles : integer .tpGroupFilterMode : integer .tpGroupList : maxObject array false

Hope this helps.
If you have any specific questions about any of our products, please let me know.

Thanks Bobo, this does help.

I’m using some of that stuff in our old UI that was maxscript and dotnet. I just was not sure if there was anything hidden. Talked to the Rayfire writer and he helped with a hidden function that solved some big problems.

I think the only utility that’s on my list from Thinkbox is a mass relinker for Xmesh. But the data can be fetched through maxscript.

Thanks for your help as always

Privacy | Site terms | Cookie preferences