Scripting: Changing the default value of a shader color channel

This snippet changes the default value of a single color channel (R in this case) an existing shaderdef:

from siutils import si        # Application
from siutils import log        # LogMessage
from siutils import disp    # win32com.client.Dispatch
from siutils import C        # win32com.client.constants

def dispFix( badDispatch ):
    import win32com.client.dynamic
    # Re-Wraps a bad dispatch into a working one:
    return win32com.client.dynamic.Dispatch(badDispatch)

# Get ShaderDef for the Environment shader
sProgID = "Softimage.mia_material_phen.1.0"
oDef = si.GetShaderDef( sProgID )

# Get ShaderParamDef for the Tranformation parameter
oReflectivity = oDef.InputParamDefs.GetParamDefByName( "reflectivity" )

# Change the default value
oReflectivity.DefaultValue = 0.333

oRefl_Color = oDef.InputParamDefs.GetParamDefByName( "refl_color" )
print si.ClassName( oRefl_Color )

x = dispFix(oRefl_Color).SubParamDefs
oRed = x.GetParamDefByName( "red" )
print oRed.DefaultValue
oRed.DefaultValue = 0.325

If you are adding a new shaderdef, you can do it like this:

shaderParamDefOptions.SetDefaultValue( [0.5, 0.3, 0.2] )

hat tip: Vladimir and Vincent on the Softimage mailing list

Syntax highlighting in the text editor widget

Syntax highlighting works only if you set the siUILanguage to one of cpp, c#, HTML, JScript, PerlScript, Python, VBScript, or XML. Then the language keywords are highlight, plus any keywords you add with the siUIKeywords attribute.


from sipyutils import si			# win32com.client.Dispatch('XSI.Application')
from sipyutils import C			# win32com.client.constants


p = XSIFactory.Createobject( 'CustomProperty' )
oParam = p.AddParameter3("TextEditorWidget", C.siString, '')

oLayout = p.PPGLayout

oItem = oLayout.AddItem( "TextEditorWidget", "Source Code", C.siControlTextEditor)
oItem.SetAttribute( "Language", "JScript" )
oItem.SetAttribute(C.siUIFont, "Courier New")
oItem.SetAttribute(C.siUIKeywords, "foo bar fubar snafu")
oItem.SetAttribute(C.siUIAutoComplete, "fubar");
oItem.SetAttribute(C.siUICommentFont, "Courier New")
oItem.SetAttribute(C.siUICommentColor, 0x75715e)
oItem.SetAttribute(C.siUIPreprocessorColor, 0x808080)
oItem.SetAttribute(C.siUIToolbar, True)
oItem.SetAttribute(C.siUIFontSize, 10)
oItem.SetAttribute(C.siUIHeight, 500)
oItem.SetAttribute(C.siUIBackgroundColor, 0xf8f8f2)
oItem.SetAttribute(C.siUIForegroundColor, 0x272822)
oItem.SetAttribute(C.siUIHorizontalScroll, True)
oItem.SetAttribute(C.siUIVerticalScroll, True)
oItem.SetAttribute(C.siUILineNumbering, True)
oItem.SetAttribute(C.siUILineWrap, False)
oItem.SetAttribute("UseSpacesForTab", True)
oItem.SetAttribute("TabSize", 2) 

si.inspectobj( p )

Scripting: Finding all materials that contain a specific shader

Given a shader, it’s not too hard to find all materials that use (aka “own”) an instance of that shader. Here’s a Python snippet that does just that.

Note that I don’t check whether or not the shader is actually used. This snippet finds all instances, whether they are used or not (last week I posted another snippet for checking whether a shader instances was ultimately connected to the material).

from sipyutils import si			# win32com.client.Dispatch('XSI.Application')
from sipyutils import disp		# win32com.client.Dispatch
from sipyutils import C			# win32com.client.constants
si = si()

def get_materials_that_use_shader( s ):	
	mats = disp( "XSI.Collection" )	
	oShaderDef = si.GetShaderDef( s.ProgID )
	for i in oShaderDef.ShaderInstances:
			mats.Add( i.Owners(0) )
		except Exception:

	mats.Unique = True
	return mats

# Find all materials that use a specific shader
s = si.Selection(0)
if s.IsClassOf( C.siShaderID ):
	mats = get_materials_that_use_shader( s )
	for m in mats:
		print( "%s in %s" % (m.Name, m.Owners(0)) )
	si.LogMessage( "Cannot find shader instances. Please select a shader." )

# Material in Sources.Materials.DefaultLib
# Material1 in Sources.Materials.DefaultLib
# Material2 in Sources.Materials.DefaultLib
# Material3 in Sources.Materials.DefaultLib
# Material7 in Sources.Materials.DefaultLib
# Material6 in Sources.Materials.DefaultLib
# Material5 in Sources.Materials.DefaultLib
# Material4 in Sources.Materials.DefaultLib

Finding shader nodes with no connections

Here’s a JScript snippet that finds render tree nodes that are not connected to anything in the render tree.

LogMessage( isConnected( Selection(0) ) );

function isConnected( o )
	var oDR = XSIUtils.DataRepository ;

	var strOpInfo = oDR.GetConnectionStackInfo( o )
//	LogMessage( strOpInfo );
	var oTopNode = ParseXML( strOpInfo ) ;

	var oConnections = oTopNode.childNodes ;
	if ( oConnections.length == 0 )
		return false;
	return true;

function ParseXML( strXML )
	var oXMLParser = new ActiveXObject("Microsoft.XMLDOM") 
	oXMLParser.async = false	
	oXMLParser.loadXML( strXML ) ;

	if (oXMLParser.parseError.errorCode != 0) 
		logmessage( "Invalid XML " + oXMLParser.parseError.reason , siError ) ;	
		return null ;

	// the xsi_file node
	// If this is NULL we must have failed to load the XML
	var oTopNode = oXMLParser.documentElement ;

	return oTopNode ;

Most of this JScript came from the SDK Explorer code in $XSI_HOME\Addons\sdkui\Application\Plugins\SDKExplorer.js, because I noticed that disconnected shaders would have an empty connection stack, and I didn’t want to go through all the parameters individually looking for connections.

Here’s a Python version that does a little more: it follows the output connections to check whether or not the shader is ultimately connected the material.

# Python Code
import xml.etree.ElementTree as etree

def is_connected( o ):
	if not o.IsClassOf( 52 ):
		print "Input is not a shader"
		return False
	sXML = XSIUtils.DataRepository.GetConnectionStackInfo( o )
	root = etree.fromstring( sXML )
	for connection in root:
		if connection.find('type').text == 'out':
			x = Application.Dictionary.GetObject( connection.find('object').text )
			return True if x.IsClassOf( 64 ) else is_connected( x )
	return False

print is_connected( Application.Selection(0) )

Finding materials used by a model

Here’s one way to get the materials used by model. Note that this will also get any materials applied to clusters.


def get_mdl_materials( m ):
	from win32com.client import constants as c
	return m.FindObjects( c.siMaterialID )

Application.GetPresetModel("Man_Character", "Man_Character", "", "Character.Character_Designer")
for m in get_mdl_materials( si.Dictionary.GetObject( 'Man_Character' ) ):
	print m

And here’s an old-school way that uses a couple of string expressions:

mdl = si.Dictionary.GetObject( 'Man_Character' )

import win32com.client
mats = win32com.client.Dispatch( "XSI.Collection" )
mats.Items = '{0}.{1},{0}.{2}'.format(mdl.Name, "*.cls.*.material", "*.material")

for m in mats:
	print (m)

Checking what attributes are in a cache

On the Read tab of the Cache Manager there’s a Dump Header button:

Dump Header dumps the cache header and attribute list to XML and pops up NetView:

If you want to dump the XML and parse it yourself, you can do it with this scripting command:

Application.DumpCacheHeader("[project path]\\Simulation\\scene_root\\pointcloud\\pointcloud_AnimTake1_[1..100].icecache", False)

Fit Subcomponent UVs To Image

When you click Fit Subcomponent UVs to Image in the Texture Projection property editor, you’ll get these ERRORs in the script history:

' ERROR : Object doesn't support this property or method: 'in_uvprops.IsClassOf' - [line 1842 in C:\Program Files\Autodesk\Softimage 2014\Application\DSScripts\Texture.vbs]
' ERROR :  - [line 25]
' ERROR : Property Page Script Logic Error
' ERROR :    [23] 		end if
' ERROR :    [24] (null)
' ERROR :   >[25] 		FitSubcomponentUVsToImage Pset.InspectedObjects, filtered
' ERROR :    [26] 	End Sub

The fix for this requires that you edit $XSI_HOME\Application\DSScripts\Texture.vbs. You’ll probably need to edit the Permissions on Texture.vbs so that you have Full Control or Modify permissions.

Find line 1842 in Texture.vbs, and then replace the if..else..endif with this:

set coll = CreateObject("XSI.Collection")
if in_uvprops.Type = "XSICollection" then
	set coll = in_uvprops
elseif in_uvprops.IsClassOf( siUVPropertyID ) = True then
	coll.Add in_uvprops
end if

The fix I posted on the Softimage mailing list awhile ago was a one-liner that would enable you to use Fit Subcomponent UVs to Image on a single texture projection. This fix will work if you have multiple texture projections in the property editor.