Checking if a polygon is hidden


Here’s some Jscript that shows how to find clusters hidden by ToggleVisibility.

Note that clusters can also be hidden by adding a visibility property (Property > Polygon Cluster Visibility).
Finding those would be a separate, but more straightforward, task

// Get a polygon cluster
//var oCluster = Dictionary.GetObject( "cube.polymsh.cls.Polygon" );
var oCluster = Selection(0);

// Get array of polygon indices
var a = oCluster.Elements.Array.toArray();

// Get [basically undocumented] invisible polygon cluster
var oGeometry = Dictionary.GetObject( "cube" ).ActivePrimitive.Geometry;
var l_polyVisCluster = oGeometry.Clusters( siInvisiblePolygonsClusterName );


// Check if a polygon in our cluster is hidden
// It is possible for a poly to be in multiple clusters
// Otherwise we could assume that if a[0] is hidden, 
// then the whole cluster is hidden

for ( var i = 0; i < a.length; i++ )
{

                if ( l_polyVisCluster.Elements.FindIndex( a[i] ) == -1 )
                {
                                LogMessage( "poly[" + a[i] + "] : Visible" );
                }
                else
                {
                                LogMessage( "poly[" + a[i] + "] : Hidden" );
                }
}

Switching material libraries


Here’s how to switch from one material library to another. This script could be useful when you have a corrupted material library (eg missing render tree connections), but you have a backup copy of the material library. For example, you could export a good version of the material library from a backup copy of the scene, and then import the matlib into the current version of the scene.


// Material Library with bad render trees (missing connections)
var oMatLib = Dictionary.GetObject( "Sources.Materials.DefaultLib" );

// Material Library with good render trees
var oMatLib1 = Dictionary.GetObject( "Sources.Materials.DefaultLib1" );


oEnum = new Enumerator( oMatLib.Items ) ;
for (;!oEnum.atEnd();oEnum.moveNext() )
{
        var oMat = oEnum.item() ;
        if ( oMat.UsedBy.Count > 0 )
        {
                var oMat1 = oMatLib1.Items(oMat.Name);
                if ( oMat1 == null )
                {
                        LogMessage( "Cannot find " + oMat.Name + " in " + oMatLib1.Name );
                }
                else
                {
                        LogMessage( "Repacing " + oMat.FullName + " with " + oMat1.FullName );
                        
                        // Groups
                        var g = oMat.Owners.Filter( "#Group" );
                        SIAssignMaterial( g.GetAsText(), oMat1 );

                        // Objects
                        SIAssignMaterial( oMat.UsedBy.GetAsText(), oMat1 );
                }
        }

}

Looping through all materials and material libraries


Here’s a snippet that shows how to loop through all material libraries and materials and delete unconnected shaders:

var oMatLibs = ActiveProject.ActiveScene.MaterialLibraries;
oEnum = new Enumerator( oMatLibs ) ;
for (;!oEnum.atEnd();oEnum.moveNext() )
{
	var oMatLib = oEnum.item() ;
	oEnum1 = new Enumerator( oMatLib.Items ) ;
	for (;!oEnum1.atEnd();oEnum1.moveNext() )
	{
		var oMat = oEnum1.item() ;
		DeleteUnusedShaders( oMat );
	}

}

The same thing but in Python.

oMatLibs = Application.ActiveProject.ActiveScene.MaterialLibraries
for lib in oMatLibs:
	for mat in lib.Items:
		Application.DeleteUnusedShaders( mat )

Finding all Store in Channel shaders


// Get all shaders
var x = FindObjects(null, "{6495C5C1-FD18-474E-9703-AEA66631F7A7}" );
LogMessage( x.Count );
var oShaderCollection = new ActiveXObject( "XSI.Collection" );

// Build up a collection of all Store in Channel shaders
oEnum = new Enumerator( x ) ;
for (;!oEnum.atEnd();oEnum.moveNext() )
{
	var oShader = oEnum.item() ;
	try {
		//LogMessage( oShader.ProgID );
		if ( oShader.ProgID.indexOf( "_storeinchannel" ) > 0 )
		{
			oShaderCollection.Add( oShader );
		}

	}
	catch(e)
	{
		LogMessage(e.message);
	}
}


LogMessage( oShaderCollection.Count );

VBScript compilation errors with Python PPG logic


On a machine that has Softimage 2011 or 2011 SP1 installed, but not Python, you’ll get VBScript compilation errors when you run a script or plugin that uses Python for the PPGLayout.Logic of a dynamic (on-the-fly) property. For example, running this script

xsi = Application
oRoot = xsi.ActiveSceneRoot
oProp = oRoot.AddProperty("CustomProperty", False, "test")
oLayout = oProp.PPGLayout
oLayout.Language = "Python"
oLayout.Logic ="""
Application.LogMessage('Hello world')
"""
xsi.InspectObj( oProp )

will give this VBScript syntax error:

# ERROR : Syntax error - [line 2]
# ERROR : Property Page Script Logic Error (Microsoft VBScript compilation error)
# ERROR :    [1] (null)
# ERROR :   >[2] Application.LogMessage('Hello world')
# ERROR :    [3] (null)
# ERROR : 	Syntax error

This happens because Softimage 2011 doesn’t detect the Python installed with Softimage, so it falls back to the default VBScript when it tries to execute the Python PPG.Logic code. However, Python works as usual in all other respects (note that the above errors are logged as Python comments!).

You can workaround this by setting PPGLayout.Language to “pythonscript”.

Or you could leave the Language set to “Python” and add these registry entries to your system.
This way you don’t have to update existing code.

[HKEY_CLASSES_ROOT\Python]
@="Python ActiveX Scripting Engine"

[HKEY_CLASSES_ROOT\Python\CLSID]
@="{DF630910-1C1D-11d0-AE36-8C0F5E000000}"

[HKEY_CLASSES_ROOT\Python\CurVer]
@="Python.AXScript.2"
[HKEY_CLASSES_ROOT\Python\OLEScript]

To add the registry entries, save the above in a .reg file and then double-click it.

FindObjects() and shaders in Softimage 2011


Update 10 Dec 2010: See this post about finding shaders using the ProgID.

In Softimage 2011, all [factory] shaders have the same Class ID ({6495C5C1-FD18-474E-9703-
AEA66631F7A7}), so FindObjects returns a collection that contains shaders in the scene.

Note that the returned collection will include the soft_light shader, as well as several hidden Lambert shaders that are used under-the-covers by Softimage.

var x = FindObjects(null, "{6495C5C1-FD18-474E-9703-AEA66631F7A7}" );
LogMessage( x.Count );

oEnum = new Enumerator( x ) ;
for (;!oEnum.atEnd();oEnum.moveNext() )
{
	var oSelItem = oEnum.item() ;
	try {
		LogMessage( oSelItem.fullname );
		i++;
		
	}
	catch(e)
	{
		LogMessage(e.message);
		LogMessage( "\t" + ClassName(oSelItem) + ":" + oSelItem.name);
		LogMessage( "\t" + oSelItem.GetShaderContainer() );
	}
}

You could filter the returned collection for a specific type of shader. For example:

// Get all shaders
var x = FindObjects(null, "{6495C5C1-FD18-474E-9703-AEA66631F7A7}" );
LogMessage( x.Count );
var oShaderCollection = new ActiveXObject( "XSI.Collection" );

// Build up a collection of all Blinn shaders
oEnum = new Enumerator( x ) ;
for (;!oEnum.atEnd();oEnum.moveNext() )
{
	var oShader = oEnum.item() ;
	try {
		//LogMessage( oShader.ProgID );
		if ( oShader.ProgID == "Softimage.material-blinn.1.0" )
		{
			oShaderCollection.Add( oShader );
		}
		
	}
	catch(e)
	{
		LogMessage(e.message);
	}
}

Python – Adding parameter to SCOP


Here’s a Python version of the CustomOperator.AddParameter example in the SDK docs, which shows how to add parameters to a scripted operator (SCOP).

from win32com.client import constants
from win32com.client import constants

null1 = Application.GetPrim( "Null" )
f = XSIFactory

s = """def MySOP_Update( ctx, out ):
   xsi = Application
   xsi.logmessage( 'update' )"""

sop = f.CreateScriptedOp( "MySOP", s, "Python" )


sop.AddOutputPort( null1.posx )

param1 = sop.AddParameter( f.CreateParamDef2("text", constants.siString, "hello") )
param2 = sop.AddParameter( f.CreateParamDef2("bool", constants.siBool, True) )
param3 = sop.AddParameter( f.CreateParamDef2("int", constants.siInt4, 10, 0, 100) )
param4 = sop.AddParameter( f.CreateParamDef2("dbl", constants.siDouble, 0.5, 0.0, 1.0) )

sop.Connect()

Application.InspectObj( sop )

Python – Getting rules from a connection mapping template


This snippet shows how to read a connection (or value) mapping template into a Python dictionary.

t = Application.Dictionary.GetObject( "Model1.Mixer.MappingTemplate1" )

n = Application.GetNumMappingRules( t )

d = {}
for i in range(1,n+1):
        r = Application.GetMappingRule( t, i )
        d[r.Value("From")] = r.Value("To")
        
for k, v in d.iteritems():
     Application.LogMessage( k + " > " +  v )

Line 8 GetMappingRule uses output arguments, so we have to take the returned ISIVTCollection and extract the From and To output arguments. We use From as the dictionary key.

Not all numbers are exactly representable as floats


Representable numbers

The number of digits (or bits) of precision also limits the set of rational numbers that can be represented exactly. For example, the number 123456789 clearly cannot be exactly represented if only eight decimal digits of precision are available.

Inexactness

Floating-point arithmetic on digital computers is inherently inexact. The 24 bits (including the hidden bit) of mantissa in a 32-bit floating-point number represent approximately 7 significant decimal digits. Unlike the real number system, which is continuous, a floating-point system has gaps between each number. If a number is not exactly representable, then it must be approximated by one of the nearest representable values.

In Softimage, you can see the inexactness of floating point numbers with a floating point parameter on a PPG, or with the Scalar node in an ICE tree. In either case, try entering the value 123456789: you’ll end up with the value 123456792.

If you run this Python snippet in a script editor, you can try this out:

import win32com.client
from win32com.client import constants

xsi = Application

oCustomProperty = xsi.ActiveSceneRoot.AddProperty( "CustomProperty", False, "Test" )
oCustomProperty.AddParameter2("Int",constants.siInt4,0,None,None,0,None,constants.siClassifUnknown,constants.siPersistable + constants.siKeyable)
oCustomProperty.AddParameter2("Float",constants.siFloat,0,None,None,0,None,constants.siClassifUnknown,constants.siPersistable + constants.siKeyable)
oCustomProperty.AddParameter2("Double",constants.siDouble,0,None,None,0,None,constants.siClassifUnknown,constants.siPersistable + constants.siKeyable)

xsi.InspectObj( oCustomProperty )

xsi.SetValue("Test.Float", 123456789)
x = xsi.GetValue( "Test.Float" )

xsi.LogMessage( x )
# INFO : 123456792.0