Installing PyQtForSoftimage in Softimage 2015

I use the Python 2.7.3 that comes with Softimage 2015 SP1, but I do also have Python 2.7 installed on my system.

  1. Download and install PyQt.
  2. Set the PYTHONPATH environment variable to point to the location of PyQt4. You could do this in setenv.bat, or in the System environment variables. In my case, I set it in setenv.bat to point to C:\Python27\Lib\site-packages, which is where I installed PyQt.
  3. Download and install the PyQtForSoftimage addon.
  4. Check that everything is working. Open the Plug-in Manager, find PyQtForSoftimage, and run some of the examples.

Getting the plugin path

You can use the OriginPath property to get the location of a plugin, but OriginPath is available in the scope of a plugin callback only.

__sifile__ and __sipath__, however, can be used in the global scope.

import win32com.client
from win32com.client import constants

import sipyutils

Application.LogMessage( "Global: __sipath__=%s" % __sipath__ )
Application.LogMessage( "Global: __sifile__=%s" % __sifile__ )

null = None
false = 0
true = 1

def XSILoadPlugin( in_reg ):
	in_reg.Author = "SOLIDANGLE"
	in_reg.Name = "TestPlugin"
	in_reg.Major = 1
	in_reg.Minor = 0

	#Register plugin items

	Application.LogMessage( "XSILoadPlugin: __sipath__=%s" % __sipath__ )
	Application.LogMessage( "XSILoadPlugin: __sifile__=%s" % __sifile__ )
	Application.LogMessage( "XSILoadPlugin: in_reg.OriginPath=%s" % in_reg.OriginPath )

	return true

The above will output something like the following:

# INFO : Global: __sipath__=C:\Users\SOLIDANGLE\Autodesk\Softimage_2015\Application\Plugins
# INFO : Global: __sifile__=C:\Users\SOLIDANGLE\Autodesk\Softimage_2015\Application\Plugins\
# INFO : XSILoadPlugin: __sipath__=C:\Users\SOLIDANGLE\Autodesk\Softimage_2015\Application\Plugins
# INFO : XSILoadPlugin: __sifile__=C:\Users\SOLIDANGLE\Autodesk\Softimage_2015\Application\Plugins\
# INFO : XSILoadPlugin: in_reg.OriginPath=C:\Users\SOLIDANGLE\Autodesk\Softimage_2015\Application\Plugins\

Loading order of workgroup plugins and addons

I recently had reason to investigate the order in which things are loaded from workgroups, and here’s what I found:

  • Workgroup plugins are loaded first.
    Softimage goes through all the workgroups, in the order they are listed in the Plug-in Manager, and loads all plugins that are not in an add-on.

  • Workgroup addons are loaded second.
    Again, Softimage goes through all the workgroups in list order, loading add-on plugins.

What was my motive for looking into this? A plugin that was trying to get the OriginPath of a plugin in the SItoA addon. That was never going to work, because the plugin was loaded before the SItoA addon.

I tested this on Windows 7, not Linux.

Importing multiple FBX files with drag-and-drop

As an exercise, I updated Tim Crowson’s Multi_ImporterPPG addon with a DragAndDrop event, so you can import multiple files with a single drag-and-drop. You can download the modified version here.

Here’s the DragAndDrop event handler. The doit() function is also used by the Import menu command; I just had to generalize it a bit to work in either case (menu or drag-and-drop).

def Multi_Importer_DragAndDrop_OnEvent( in_ctxt ):

	action = in_ctxt.GetAttribute( "DragAndDropAction" )
	source = in_ctxt.GetAttribute( "DragSource" )

	if action == constants.siSourceDragAction:
		if r"\obj$", source, re.I ):
			in_ctxt.SetAttribute( "DragSourceSupported", True )
		elif r"fbx$", source, re.I ): 
			in_ctxt.SetAttribute( "DragSourceSupported", True )
		elif r"emdl$", source, re.I ): 
			in_ctxt.SetAttribute( "DragSourceSupported", True )
		elif r"lwo$", source, re.I ): 
			in_ctxt.SetAttribute( "DragSourceSupported", True )
			in_ctxt.SetAttribute( "DragSourceSupported", False )
	if action == constants.siSourceDropAction:

		Application.SetValue('preferences.Interaction.autoinspect', False, '')

		if not Application.ActiveSceneRoot.Properties( 'Multi_Importer' ):
			vtcol = Application.AddProp('Multi_Importer','Scene_Root')
			p = Application.Dictionary.GetObject( vtcol.Value("Value") )

			# Set the flag that hides certain parts of the PPG layout
			p.Parameters("bMenuCommand").Value = False

			# Inspect the PPG in modal mode
			Application.InspectObj( vtcol.Value("Value"), "", "", 4 )

			p.Parameters("bMenuCommand").Value = True
		p = Application.ActiveSceneRoot.Properties('Multi_Importer')

		options = { 
			'OBJgrouping' : p.Parameters('importOBJgrouping').Value,
			'OBJhrc' : p.Parameters('importOBJhrc').Value,
			'importOBJnormals' : p.Parameters('importOBJNormals').Value,
			'includeOBJmat' : p.Parameters('includeOBJMaterial').Value,
			'includeOBJuv' : p.Parameters('includeOBJUV').Value,
			'includeOBJwrap' : p.Parameters('includeOBJUVWrap').Value,
			'fbxScale' : p.Parameters('fbxScale').Value,
			'importEMDLasRef' : p.Parameters('importEMDLasRef').Value,
			'lwoScaleFactor' : p.Parameters('lwoScaleFactor').Value
		doit( source, options )
	return True

New Tool: ICE Tree Trace from Bradley Gabe

ICE Tree Trace is a new tool from Bradley Gabe. Download it here.

It’s a plugin, so you want to save it in the Application\Plugins folder of either your Softimage User location, or of a workgroup.

Or drag this addon to a viewport.

After you install the plugin, you’ll have a new menu in the ICE Tree view:

Tool for tracing instances of strings inside ICE Trees. Can be used, for example, to track down the number of times a specific attribute is called. Via filters, may also be used to determine how an attribute is called, whether by Get Data, Set Data, or other ICE nodes that handle string parameters.

Here’s an example. I used Tree Trace to find all references to the Texture_Projection attribute in a CrowdFX scene:

Specify search options, enter a Match String, then press the trace button. If matches are found, the address of the ICE Nodes are listed in the Match List at the bottom of the GUI. If items are selected in the Match List, their corresponding nodes are selected in the scene

NOTE: At present, there is no access in the SDK to directly select nodes inside an ICE Tree node graph interface. ICE nodes are selected in the scene, and may be accessed via the explorer.

GUI Parameters:
Search Scope:
• Selected ICE Nodes – Search only within currently selected nodes in ICE Tree
• Local ICE Tree – Search all nodes in the currently open ICE Tree
• ICE Trees on Sel Scene Items – Any ICE Tree on selected scene items
• Global Scene – All ICE Trees in the scene

ICE Node Filter:
• All ICE Nodes – No filter
• Get Data only – Search for string matches only in Get Data nodes
• Set Data only – Search for string matches only in Set Data nodes
• Other Nodes – Search for string matches in nodes that are not Get Data or Set Data (Shape Instance, String nodes, etc)

Match String: String to be searched for inside ICE Tree

Cap Sensitive: Consider capitalization in search string (Overridden by RegExpr matching)

Use Regular Expression Matching: Allows user to specify match strings using regular expression syntax

# ERROR : 2356 – This plug-in is not installed

If you see errors like this at startup or in the script history:

# ERROR : 2356 - This plug-in is not installed: rcCustomFilterSettings
# ERROR : 2356 - This plug-in is not installed: jhBatchieOptions 
# ERROR : 2356 - This plug-in is not installed: CM_DefaultPrimitives_Preferences
# ERROR : 2356 - This plug-in is not installed: Arnold Render Options

it is because of custom preferences. Notice how all the errors include works like “settings”, “options”, or “preferences”.

These custom preferences use a custom property plugin to define their PPG (property page) layout. You’re getting an error because the custom property plugin isn’t loaded into Softimage.

It could be that the plugin is no longer installed on your system. In this case, you can delete the instance of the custom property from Preferences > Custom in the explorer, and delete the corresponding .preset file from the Data\Preferences folder (of your Softimage User folder, or from a workgroup folder, depending on how the plugin was installed).

The other possiblity is that some other plugin is calling RefreshCustomPreferences before the custom property was loaded. For example, this can happen if a plugin installed in your User location calls RefreshCustomPreferences, and the custom property used by a preference is installed in a workgroup. Softimage loads plugins in the User location first, before workgroup plugins, so this would give you the “plugin is not installed error”.

Disabling Customer Error Reporting (CER)

In general, we’d prefer you didn’t disable the CER reports. But if you’re in the middle of debugging a plugin that constantly crashes, for example, you might want to temporarily disable the CERs.

To disable CER reporting, edit the Softimage setenv.bat file and add this:


Maya, and presumbably 3ds Max, work the same way.


Finding the menu commands added by a plugin

So you installed a plugin, but now you cannot find out how to access the plugin in Softimage.

If the plugin added a menu command, and it’s a scripted plugin, we can find it by checking the plugin code (in the Plugin Manager > Tree, right-click the plugin and click Edit).

Find the definition of XSILoadPlugin.
In the XSILoadPlugin function, look for any calls to RegisterMenu.

def XSILoadPlugin( in_reg ): #{{{
	in_reg.Author = "kim"
	in_reg.Name = "EPSexportPlugin"
	in_reg.Email = ""
	in_reg.URL = ""
	in_reg.Major = 1
	in_reg.Minor = 0


	# register the export command
	# 	KA_EpsExport( Application.Selection, OutputFile, CullBackFaces, doInnerLines, doBorderLines, InnerLineThickness, BorderLineThickness, AutoDiscontinuity, Xres )

	return true

RegisterMenu uses what’s called a “menu anchor” to define a new menu command. In this example, the menu anchor is siMenuMainFileExportID.

Google the menu anchor or search the SDK docs, and you’ll find a page that describes the menu anchors:

Beware of ICE optimizations

It seemed like such a nice, simple way to filter polygons:

  • Use ICE to check the polygon area and then set a boolean attribute.
  • Write a custom subcomponent filter to filter based on that ICE attribute.

With ICE, it’s pretty easy to check if the PolygonArea is within a certain range:

But, beware of ICE optimizations!

Because that ICE attribute isn’t used anywhere else in the scene, ICE doesn’t evaluate that tree, so my boolean attribute is never defined, and my custom filter therefore fails. I have to do something like Show Values to force evaluation:

Note: In Show Values, I used Show Values for Tagged Components Only to cut down the visual clutter.

FWIW, here’s a Python example of a custom subcomponent filter:

# psCustomFilter Plug-in
# Initial code generated by Softimage SDK Wizard
# Executed Wed Nov 23 11:31:56 EST 2011 by blairs
# Tip: To add a command to this plug-in, right-click in the 
# script editor and choose Tools > Add Command.
import win32com.client
from win32com.client import constants

from siutils import si		# Application
from siutils import sidesk	# Desktop
from siutils import sidict	# Dictionary
from siutils import sifact	# XSIFactory
from siutils import simath	# XSIMath
from siutils import siproj	# ActiveProject2
from siutils import sisel	# Selection
from siutils import siuitk	# XSIUIToolkit
from siutils import siut	# XSIUtils
from siutils import log		# LogMessage
from siutils import disp	# win32com.client.Dispatch
from siutils import C		# win32com.client.constants

null = None
false = 0
true = 1

def XSILoadPlugin( in_reg ):
	in_reg.Author = "blairs"
	in_reg.Name = "psCustomFilter Plug-in"
	in_reg.Major = 1
	in_reg.Minor = 0

	#RegistrationInsertionPoint - do not remove this line

	return true

def XSIUnloadPlugin( in_reg ):
	strPluginName = in_reg.Name
	Application.LogMessage(str(strPluginName) + str(" has been unloaded."),constants.siVerbose)
	return true

# Match callback for the psCustomFilter custom filter.
def psCustomFilter_Match( in_ctxt ):
	Application.LogMessage("psCustomFilter_Match called",constants.siVerbose)

# 	Return value indicates if the input object matches the filter criterias.
	return true

# Subset callback for the psCustomFilter custom filter.
def psCustomFilter_Subset( in_ctxt ):
	log("psCustomFilter_Subset called",constants.siVerbose)

	out_coll = disp( "XSI.Collection" )
	in_coll = in_ctxt.GetAttribute( "Input" )
	for item in in_coll:
		log( item )
		polys = []
		for p in item.SubComponent.ComponentCollection:
			log( p.Index )

			attr = p.Parent.ICEAttributes("psCustomPolyFilter")
			if not attr.IsDefined:
				log( "Cannot apply filter. psCustomPolyFilter attribute is not defined" )
			if attr.IsDefined and attr.DataArray[ p.Index ] == -1:
				#log( "%d : %s" % ( p.Index, attr.DataArray[ p.Index ] ) )
				polys.append( p.Index )

		if len(polys) > 0:
			out_coll.Add( item.SubComponent.Parent3DObject.ActivePrimitive.Geometry.CreateSubComponent(C.siPolygonCluster, polys ) )

	in_ctxt.SetAttribute( "Output", out_coll )

# 	Return value indicates if a subset of the input objects matches the filter criterias.
	return true

Adding your ICE operators to menus

The User Tools menu in an ICE Tree view has an Add Operator to Menu command that adds your compounds to menus, so you can apply them to the selected object. “Add Operators to Menu” is implemented in VBScript in %XSI_HOME%\Addons\ICEUserTools\Application\Plugins\ICEUserTools.vbs.

Unfortunately, this command wasn’t updated after 2011 Advantage Pack, so it doesn’t know about the new ICE toolbar and menu structure.

So, here’s a Python version that adds ICE operators to either the Particles > Create or Deform > Create menus in the ICE toolbar. When you apply the operators, they will be applied to all selected objects.

The addon adds a “PSS Add Operators to Menu” command to the User Tools menu of the ICE Tree view, so it does add a bit of clutter (since I cannot programmatically remove the original Add Operators to Menu command).

To add operators to menus:

  1. Export your compound.
  2. In the ICE Tree view, click User Tools > PSS Add Operator to Menu.
  3. Type the name of the xsicompound file (without the .xsicompound extension).
  4. The next time you open the menu, it will be dynamically updated to include a command that applies your operator (with ApplyICEOp) to all selected objects.

See below the fold for the plugin code.
Continue reading