Scripting Tips and Tricks (XSISDK)


Location for general tips and tricks. If any particular topic becomes very extensive it can be moved into its own page. You can also check the latest scripting post (http://xsisupport.wordpress.com/category/scripting/) on the eX SI Support Blog (http://xsisupport.wordpress.com/).


Table of contents

Scripting Primer

All new XSI SDK users should start with this document XSI Scripting Primer

Script Debugging

See Script Debugging (XSISDK)

Dictionary


VBScript and JScript coders may find the scripting Dictionary (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/jsobjdictionary.asp) object interesting. This is a built-in object (not part of XSI) and builds a table mapping between "keys" and "values". The keys are often strings.

To create a dictionary use this syntax (JScript):

var oDictionary = new ActiveXObject("Scripting.Dictionary");

For a demonstration of its use check out the implementation of the SetGlobal (http://softimage.wiki.softimage.com/sdkdocs/SetGlobal.htm) and GetGlobal (http://softimage.wiki.softimage.com/sdkdocs/GetGlobal.htm) commands (XSI 5.0). Hint: Look at C:\Softimage\XSI_5.0\Application\Commands\GlobalVarPlugin.js.

Python has a structure like this available as a native type but could potentially use this object as well.

Mixing Scripting Languages

Normally it might be best to work primarily in a single scripting language, for example JScript. However in some cases certain actions are only available (or easier to achieve) in other languages, including the C++ API.

The solution is to write certain functionality as custom commands in the other language. It is straightfoward to call a custom command and arguments and the return value can be used to pass values between the different environments.

An example is the InputBox function which is part of VBScript and an easy way to get simple input from a user. It has been exposed as a command for XSI v5.0 called XSIInputBox (see SDKVBHelpers.vbs), so now it is available from any scripting language and even C++.

By this same approach it is easier to re-use script code written by external sources - even if you are not extensively familiar with the particular scripting language it is written in you may find it easier to invoke it "as is" rather than to try to rewrite it in a language you are more comfortable with.

For simple scripts that do not need to pass and return values you may prefer to use Application.ExecuteScript. This lets you execute any script in any supported language. However this requires that you can determine the exact full path to the scripts location.

How to Find

If you cannot find something directly through the Object Model (OM), there are generally two alternatives:

  • Use a string expression (http://softimage.wiki.softimage.com/sdkdocs/StringExpression.htm) to populate an XSICollection (http://softimage.wiki.softimage.com/sdkdocs/XSICollection.htm).
  • Use the Object Model and NestedObjects (http://softimage.wiki.softimage.com/sdkdocs/NestedObjects.htm) property.

Some things, such as the PlayControl parameters, are accessible through the OM but it is not obvious how to do it.

Partitions

Using an XSICollection:

// Get a collection of partitions from a pass
var partColl = new ActiveXObject('XSI.Collection');
partColl.SetAsText( passname + '.Partitions.*' );

Using NestedObjects property:

// The NestedObjects for a Pass includes a Parameter object named "Partitions".
// The NestedObjects for this Parameter object are the partitions (Group objects).

var oPartitions = oPass.NestedObjects.Item("Partitions").NestedObjects;

Render Options

Using an XSICollection:

var oPass = ActiveProject.ActiveScene.ActivePass;

// Get the RenderOptions Property object
var oColl = XSIFactory.CreateObject( "XSI.Collection" );

oColl.SetAsText( oPass.fullname + ".RenderOptions" );
var oRenderOptions = oColl(0);	// oRenderOptions is a Property (http://softimage.wiki.softimage.com/sdkdocs/Property.htm)

Using the NestedObjects property:

var oPass = ActiveProject.ActiveScene.ActivePass;

// Get the RenderOptions Property (http://softimage.wiki.softimage.com/sdkdocs/Property.htm)
var oRenderOptions = oPass.NestedObjects.Item("Render Options");

PlayControl

The PlayControl property is located under the ActiveProject (http://softimage.wiki.softimage.com/sdkdocs/ActiveProject.htm).

// Get the PlayControl property
// "Play Control" is the property Name
var oPlayControl = ActiveProject.Properties.Item("Play Control");

// Set a PlayControl parameter
// "Current" is the ScriptName
oPlayControl.Parameters.Item("Current").Value = 32;

See Playback Controls for a detailed discussion.

FxTrees

FxTrees are a type of property, so you can get FxTree (http://softimage.wiki.softimage.com/sdkdocs/FxTree.htm) objects from the Properties (http://softimage.wiki.softimage.com/sdkdocs/SceneItem_Properties.htm) collection of the scene root

// Get the FxTrees under the scene root
var oFxTrees = ActiveSceneRoot.Properties.Filter("FxTree");

FxTrees can also be found under models:

// Get the FxTrees located under models
// Note that ActiveSceneRoot.Models does not include the scene root.

var oEnum = new Enumerator( ActiveSceneRoot.Models ) ;
for (;!oEnum.atEnd();oEnum.moveNext() )
{
	var oModel = oEnum.item() ;
	var oFxTrees = oModel.Properties.Filter("FxTree");
	
	// Enumerate the FxTrees under the current model
	var oFxTreesEnum = new Enumerator( oFxTrees ) ;
	for (;!oFxTreesEnum.atEnd();oFxTreesEnum.moveNext() )
	{
		oFxTree = oFxTreesEnum.item();
	}
}

You can use to an XSICollection (http://softimage.wiki.softimage.com/sdkdocs/XSICollection.htm) to hold all the FxTree objects found under the scene root or any other model.

var oFxTrees = new ActiveXObject('XSI.Collection');

// Add the FxTree nodes found under the scene root
// AddItems does not raise an error if you pass an empty string
oFxTrees.AddItems( ActiveSceneRoot.Properties.Filter("FxTree") );

// Get the FxTrees located under models
oEnum = new Enumerator( ActiveSceneRoot.Models ) ;
for (;!oEnum.atEnd();oEnum.moveNext() )
{
	var oModel = oEnum.item() ;
	oFxTrees.AddItems( oModel.Properties.Filter("FxTree") );
}

FxTree nodes may also be located under 3D objects (for example, under Model.null). The above techniques would not find these FxTrees. To make sure you got all FxTrees no matter where they are in the scene, you'd have to do something like this:

var oFxTrees = new ActiveXObject('XSI.Collection');

// "FxTree" gets all the FxTree nodes under the scene root
// "*.FxTree" gets the first FxTree under the scene root and 
// all the other FxTree nodes under models and 3D objects
oFxTrees.Items = "FxTree, *.FxTree";

// Make sure the first FxTree node under the scene root
// is not listed twice
oFxTrees.Unique = true;

You could also use SetAsText, but note that it raises an error if the string does not evaluate to one or more valid objects:

try {
	oFxTrees.SetAsText( "FxTree, *.FxTree" );
	oFxTrees.Unique = true;
} catch (e) {
	// do nothing, or log a message
	LogMessage( "No FxTrees found", siWarning );
}


Just About Anything

Use FindObjects (http://softimage.wiki.softimage.com/sdkdocs/FindObjects.htm).

For example, see Custom_Properties_(XSISDK)#Find_Custom_Properties.


Name Collisions

One of the most perplexity bugs to figure out in scripting happens when you have a conflict in your variable naming.

For example

// JScript
var in = 4 ;

Gives this perplexing error message:

//ERROR : Expected identifier - [line 1]

It turns out that "in" is not a valid variable name, so you just need to rename it.

Similarily if you have variables that have the same name as XSI commands (or methods of the Application) object you will get similar strange error messages.

As an example someone at Softimage recently added a command called "Frame", but this was found to cause scripts containing the variable name "frame" to start mysteriously failing. The command was renamed to something that is much less likely to cause conflict.

Each language has its own particular list of reserved words and built-in objects or constants.

So if you become really stumped by a mysterious script error try making sure your variables, functions and other elements have clear names that are not likely to conflict with anything in the system. Sometimes naming conventions, like standard prefixes in your code can help avoid this problem entirely.

Creating a PPG with collapsible sections

When you inspect many objects in XSI, you get a PPG that displays a combination of property sets in collapsible sections. For example, if you inspect a polygon mesh, you get this PPG:

Collapsible sections in PPG
Enlarge
Collapsible sections in PPG

To get the collapsible sections in your own custom property page (PPG), create a hierarchy of custom properties. Then when you inspect the top custom property, the PPG will have a collapsible section for each custom property. For example:

SelectObj("Scene_Root", null, null);
AddProp("Custom_parameter_list", "", "", "CustomPSet", null);
SelectObj("CustomPSet", null, null);
AddProp("Custom_parameter_list", "", "", "CustomPSet", null);
InspectObj( "CustomPSet" ); 

Sblair 02:29, 7 Nov 2008 (EST)

Accessing the native Windows shell

The WshShell (http://msdn.microsoft.com/en-us/library/aew9yb99(VS.85).aspx) object allows you to do things like run a program locally, manipulate the contents of the registry, create a shortcut, or access a system folder. The WshShell object provides the Environment collection. This collection allows you to handle environmental variables (such as WINDIR, PATH, or PROMPT).


var oWshShell = new ActiveXObject ("WScript.Shell");
LogMessage( oWshShell.CurrentDirectory );

Sblair 10:20, 10 Jun 2008 (EDT)


Checking the status of the Immed button in the MCP

Here's how to check the state of the Immed button (http://softimage.wiki.softimage.com/xsidocs/basic_mod_ImmediateMode.htm):

GetUserPref("OperationMode") 

If the Immed button has not been clicked, GetUserPref("OperationMode") returns null (in JScript) instead of false. After Immed is clicked at least once, then you will get either true or false.

So if you want to get the state in all situations, do something like this:

var bImmed = ( GetUserPref( "OperationMode" ) == true ) ? true : false;
LogMessage( bImmed );


Sblair 03:30, 10 Jun 2008 (EDT)

Checking if a scene is dirty

function SIIsSceneDirty()
	Dim isDirty

	'1. check the project
	isDirty = getvalue ("Project.dirtycount")
	If CLng(isDirty) > 0 Then
		SIIsSceneDirty = True
		exit function
	End if

	'2. Check root model; all sub-models will notify up
	Dim oModel : set oModel = activesceneroot
	isDirty = getvalue (oModel & ".dirty_count")
	If CLng(isDirty) > 0 Then
		SIIsSceneDirty = True
		exit function
	end if

	'3. ok all non-dirty
	SIIsSceneDirty = False

end function


if SIIsSceneDirty then
	logmessage "The scene is dirty"
else
	logmessage "The scene is NOT dirty"
end if

Sblair 04:21, 19 Apr 2008 (EDT)


Enumerating Polygon Islands

function GetIslands(obj) {
 var islands = new Array();
 var filter = Application.Filters("Polygon_Island");
 var polys = obj.ActivePrimitive.Geometry.Polygons;
 var polyidx = new Array(polys.count);
 var curpoly = new ActiveXObject("XSI.Collection");
 
 for (var i=0; i<polys.count; i++) {
  if (polyidx[i]) continue;
  curpoly.Add(polys(i));
  var curisland = filter.Subset(curpoly);
  curisland = Dictionary.GetObject(curisland).SubComponent.ComponentCollection;
  curpoly.RemoveAll();
  for (var j=0; j<curisland.count; j++) {
   polyidx[curisland(j).index] = 1;
  }
  islands.push(curisland);
 }
 
 DeselectAll();
 return islands;
}
 
var obj = selection(0);
var islands = GetIslands(obj);
logmessage(islands.length);
SelectObj(islands[0]);


Sblair 05:29, 17 Jan 2008 (EST)

Deleting custom properties added to the scene root with Branch propagation

There is no way to delete properties added to the scene root with Branch propagation. XSI has never allowed this (to prevent users from deleted default properties like Display and Geometry Approximation).

Exporting a hierarchy as a model, and then importing the model into a new scene, does remove the branch-applied property.

This also seems to remove the branch-applied property:

  • Select everything under the Scene Root
  • Create a model
  • Export model
  • Import model into a new scene

Sblair 01:15, 7 Jan 2008 (EST)


Scripting Troubleshooting 101

Suppose you try to run a script like this:

var sel = application.selection;
for  (i=0;i<sel.count;i++) {
    logmessage(sel(i).fullname) ;
    var OldShader = sel(i).Shaders;

    // rest of script omitted

}

but you get this error message:

// INFO : sphere
// ERROR : Object doesn't support this property or method - [line 4]


For someone familiar with XSI and scripting, the problem is pretty obvious. But I'm not going to tell you the answer right away, instead I'll walk you through how to figure it out yourself.


First, add logmessage( classname( sel(i) ) ) to the script:

var sel = application.selection;
for  (i=0;i<sel.count;i++) {
    logmessage(sel(i).fullname) ;
    logmessage( classname( sel(i) ) );
    var OldShader = sel(i).Shaders;

    // rest of script omitted

}

Then run the script again. This time you'll see this in the history log:

// INFO : sphere
// INFO : X3DObject
// ERROR : Object doesn't support this property or method - [line 4]

sel(i) is a selected object, and classname() tells you what type of XSI object you have selected. In this case, it is an X3DObject. X3DObject is an Object Model class that represents a 3D object in XSI.

In the scripting history log, double-click X3DObject and then press F1. In the Help dialog, double-click X3DObject again to view the help page for X3DObject. Now take a look at the list of Properties...do you see Shaders listed? No, you don't, and that's why you are getting the error. The script is not designed to work with objects.

Now look up Shaders in the SDK index. See the subentries for Light, Camera, and Material? Those object types support the Shaders property, so those are the type of objects you should select (using the explorer).


Ref: [1] (http://www.xsibase.com/forum/index.php?board=12;action=display;threadid=33983;start=10#msg219244)


Sblair 03:24, 7 Dec 2007 (EST)


Finding Annotation Properties

You cannot use FindObjects (http://softimage.wiki.softimage.com/sdkdocs/FindObjects.htm) to directly find all Annotations. You have to use FindObjects to find all custom properties, and then filter that list. This is because all custom properties have the same CSLID, which is "{76332571-D242-11d0-B69C-00AA003B3EA6}".

Unfortunately, Annotation properties have Type = customparamset, so you use the Type to filter the list of all custom properties. Here's a JSCript example that uses the ObjectCLSID to find Annotation properties.


var oAnnotations = FindAnnotationProperties();

// Function to only find the "Annotation" Custom Property 

function FindAnnotationProperties() 
{
        var sAnnotationCLSID = "{940BBA5A-AE7D-11D3-806E-00A0C9ED67BD}";

        var oAllCustomProperties = FindObjects( 
                                        null, 
                                        "{76332571-D242-11d0-B69C-00AA003B3EA6}" ) ;
        
        var oFilteredList = new ActiveXObject( "XSI.Collection" ) ;
        
        for ( var i = 0 ; i < oAllCustomProperties.Count ; i++ )
        {

                var l = XSIUtils.DataRepository.GetIdentifier (oAllCustomProperties(i), siObjectCLSID);
                if ( l == sAnnotationCLSID )
                {
                        oFilteredList.Add( oAllCustomProperties(i) ) ;
                }
        }                
        
        return oFilteredList ;
}


References:


Sblair 06:04, 7 Nov 2007 (EST)

String Expression Trivia

Recently, a customer wrote me about a seeming inconsistency in the scripting SDK:

// ".Framebuffers[0]" works if you have just one framebuffer:
logmessage ("Passes.Default_Pass.Framebuffers[0].format");

// But if you have more than one framebuffer, you have to use
// "Framebuffer[0]". 
logmessage ("Passes.Default_Pass.Framebuffer[1].format");

The apparent inconsistency is whether to use Framebuffers plural or Framebuffer singular.

Here's my answer, which took a bit of digging around (especially since I was curious to see whether this was documented anywhere, which it isn't).

Passes.Pass_A.Framebuffers is actually a Parameter. It is the "Framebuffers" node you see in the Explorer under a pass. I used this code to determine that:

logmessage(classname(Dictionary.GetObject("Passes.Pass_A.Framebuffers")));

The string expressions Passes.Pass_A.Framebuffers and Passes.Pass_A.Framebuffers[0] evaluate to the same object.

[0] basically tells XSI to resolve the path expression and give back the first object in the resulting 'collection' of objects. In this case Passes.Pass_A.Framebuffers resolves to a single object, so Framebuffers and Framebuffers[0] give you the same thing.

When you have only one framebuffer, XSI is able to resolve the expression Passes.Pass_A.Framebuffers.Format (the expression parser is able to figure out you mean something like Passes.Pass_A.framebuffers.framebuffer[0].Format.

Passes.Pass_A.framebuffer[0] is the first framebuffer under the Framebuffers node in the explorer. Passes.Pass_A.framebuffer[0] is the same thing as Passes.Pass_A.framebuffer. To prove it, run this two lines of script:

logmessage(classname(Dictionary.GetObject("Passes.Pass_A.Framebuffer")));
logmessage(classname(Dictionary.GetObject("Passes.Pass_A.Framebuffer[0]")));


You can do the same thing for passes too: Passes.#pass[1] gives you the second pass under the Passes List.


Sblair 10:39, 5 Nov 2007 (EST)


Bitmap Buttons on Property Pages

Every now and again someone will ask me if it is possible to create PPG buttons with icons or bitmaps instead of text. The answer is no. When you add a button with PPGLayout.AddButton (http://softimage.wiki.softimage.com/sdkdocs/PPGLayout_AddButton.htm), you cannot change it into a bitmap button.

You can, however, add bitmap buttons (http://softimage.wiki.softimage.com/xsidocs/toolbars_shelves_CustomToolbars.htm) to a toolbar. And you can use a relational view to combine a property and a toolbar, like so:

Description

This relational view uses a property panel view to display a property (a Custom Color property in this case) and a toolbar to display a couple of buttons.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsi_file type="RelationalView" xsi_version="6.5.2007.0829" syntax_version="1.1">
	<relationalview clsid="{187E65B3-6C67-4D38-A526-116D392CEE35}" name="MyView" height="140" width="379">
		<definition maxinstances="10000" acceptfocus="true" private="false" defaultsize="100,100,500,500" cmdmap="{00000000-0000-0000-0000-000000000000}" supportedtypes="6" category=""> </definition>
		<relations>
		</relations>
		<frameset orientation="vertical" name="Frameset1" buttonsize="80,40" splitter="none" sizechild="100,30">
			<frame name="pane1" type="Property Panel" primary="false" toolbar="own"> </frame>
			<frame name="pane2" type="NewToolbar" primary="false" toolbar="own"> </frame>
		</frameset>
	<script language="JScript"><![CDATA[

function RV_Init( in_rv )
{
	var oPropertyPanel = in_rv.Views.Item(0);
//	logmessage (oPropertyPanel.fullname);
	var oProperty = XSIFactory.CreateObject( "CustomColor" );
	oPropertyPanel.SetAttributeValue( "targetcontent", oProperty.fullname ) }

	]]></script>

	</relationalview>
</xsi_file>


Sblair 09:08, 19 Oct 2007 (EDT)

GetRaycastIntersections

The reference page for Geometry.GetRaycastIntersections (http://softimage.wiki.softimage.com/sdkdocs/Geometry_GetRaycastIntersections.htm) has an example, but the example is wrong (the example does not take into consideration the fact that by default, the positions and rays must be defined in the local space of the object).

Here's a correct example:


JScript example of Geometry.GetRaycastIntersection

// Pick a position
var pos = Application.PickPosition( "Pick a position", "Pick it" );

// Get the camera
viewports = Application.Desktop.ActiveLayout.Views( "vm" );
var vport = viewports.GetAttributeValue( "viewportundermouse" );

var cam = viewports.GetAttributeValue( "activecamera:" + vport );
var oCam = Application.Dictionary.GetObject( cam );

// We will check if the ray intersects this object
var o = Dictionary.GetObject( "cone" );

var objectSpace = o.Kinematics.Global.Transform;

// Get the position of the camera interest in the local space of the object
var oCamInterestPos = XSIMath.MapWorldPositionToObjectSpace(objectSpace, o.Kinematics.Global.Transform.Translation) 


// Convert the picked position to the local space of the object
var oPos = XSIMath.CreateVector3( pos("PosX"), pos("PosY"), pos("PosZ") );
var oPickPos = XSIMath.MapWorldPositionToObjectSpace(objectSpace, oPos) 


// Determine the ray
var a1 = new Array( oPickPos.X, oPickPos.Y, oPickPos.Z );
var a2 = new Array( oCamInterestPos.X, oCamInterestPos.Y, oCamInterestPos.Z );

var a = [ a2[0] - a1[0], a2[1] - a1[1], a2[2] - a1[2] ]


// Do the raycast intersection
var geom = o.ActivePrimitive.Geometry;
var ClosestPointLocator = geom.GetRaycastIntersections( a1, a );
var ClosestPosition = geom.EvaluatePositions(ClosestPointLocator).toArray();

// Log the closest position (in local space coordinates)
LogMessage( ClosestPosition[0] );
LogMessage( ClosestPosition[1] );
LogMessage( ClosestPosition[2] );

// Convert from local space to global

oPos = XSIMath.CreateVector3( ClosestPosition[0], ClosestPosition[1], ClosestPosition[2] );
var globalClosestPosition = XSIMath.MapObjectPositionToWorldSpace( objectSpace, oPos )


Sblair 06:10, 26 Sep 2007 (EDT)

Python: What does vars() do?

The XSI SDK docs have this example in a couple of different places:

# Set up aN SIVector3 math object
v3 = XSIMath.CreateVector3()
v3.set( 10.0, 20.0, 30.0)
v3.ScaleInPlace(2)

# Define the variables we will use
x=0
y=0
z=0

# Retrieve the three values from the SIVector3.Get method
x, y, z = v3.Get(x,y,z)

# Write the results to the Script Editor
Application.LogMessage( '%(x).2f %(y).2f %(z).2f' % vars() )

# Output of the above script:
#INFO : "20.00 40.00 60.00"

Note the LogMessage call. What exactly is going on there?

The first thing I did was google vars(), but that didn't turn up much about vars(), although I did find this interesting cookbook article: ASPN : Python Cookbook : Dictionary of Function Parameters (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/201195).

So eventually I decided to RTFM, and there it was in the Python docs:

vars( [object])
Without arguments, return a dictionary corresponding to the current local symbol table. With a module, class or class instance object as argument (or anything else that has a __dict__ attribute), returns a dictionary corresponding to the object's symbol table. The returned dictionary should not be modified: the effects on the corresponding symbol table are undefined.2.4

So vars() is returning a dictionary with x, y, and z.

For example, this:

x = 10.0
y = 20.0
z = 30.0
Application.LogMessage( '%(x).2f %(y).2f %(z).2f' % vars() )

is basically the same thing as this:

my_dict = { 'x':10.0, 'y' : 20.0, 'z' : 30.0 }
Application.LogMessage( '%(x).2f %(y).2f %(z).2f' % my_dict )

PS - Note that the examples in the doc show the wrong output.


Sblair 13:53, 19 Sep 2007 (EDT)

DragAndDrop Event Example for Importing Files

UPDATE: See Events for a Python example that imports FBX by drag-and-drop.

Here's how to set up an event up so it launches the import command when you drop a .bob file. The regexp part might not be optimal, but this should give you the general idea.

The drag and drop event is a two part event. The siSourceDropAction will be called on drop, for a source that you previously accepted during the siSourceDragAction part.


function siOnDragAndDropEvent_OnEvent( in_ctxt ) {
	var DnDAction = in_ctxt.GetAttribute("DragAndDropAction");
	var DnDSource = in_ctxt.GetAttribute("DragSource"); 

	if (DnDAction == siSourceDragAction)
	{
		// Is it a .bob file?
		//
        RegExp = /\.bob$/i;
        if (DnDSource.search(RegExp) != -1)
        {
			in_ctxt.SetAttribute("DragSourceSupported",true);
        }
        else
        {
			in_ctxt.SetAttribute("DragSourceSupported",false);
		}
	}
	else if (DnDAction == siSourceDropAction)
	{
		/**********************************************************/
		/******* Launch the import command here *******************/
		/**********************************************************/
		LogMessage("This file was dropped: " + DnDSource);	
	}

	return true;
}

Hat tip: David Lassonde, Softimage SWAT Team


Sblair 03:46, 14 Sep 2007 (EDT)

Collections versus JScript arrays/Python lists

Some thoughts from the XSI list on the use of XSI collections versus the use of native data types (JScript arrays, Python lists).


Bradley Gabe (http://softimage.com/community/discussion/default.aspx)

The simplest way of storing a list of complex Objects is to use the list or tuple object in Python. What you get is just an Array of address pointers to whatever your objects are. The problem is, you have absolutely no information about any of those Objects other than their pointers. If you want to pull a specific object out of the list, or test if a certain object is in the list, you will always need to iterate and pull out each item.

Let's assume that minimally, you want to see if the list contains an item with a specific name. For this, you can set up a Python dictionary object where each key is the fullname of the object. This will cost you running one loop through your list to build the dictionary, but then you have that data indexed if you need to do a lot of compares.

But why spend the effort constructing a more advanced list object when Collections do all that for you? They provide multiple ways of testing and accessing the contained data, iterating functionality, and also methods for converting text addresses into an object pointers. Behind the scenes, when a Collection is instantiated, it is likely building its own indexing tables the same way we would have to in order to expand the powers of our list of pointers. We just have to hope and assume that since they are compiled, and since they have access to the scene graph in ways we don't, instantiating a Collection Object is the fastest and most efficient way to conveniently manage multiple objects in XSI scripting.


Bernard Lebel (http://softimage.com/community/discussion/Archives/xsi.archive.0709/msg00260.htm)

There are cases where using lists or tuples is necessary, for like when storing numbers, strings and the likes. XSICollections really are for storing more complex objects.

But I guess you're for storing complex objects.

On top of my head:

- In many scripts I create some elaborate data structures, where list and dictionary are nested. In a list you can store different data types, as mentioned above. So I could store an object along with numbers or strings if I really want it (although this is not the approach I prefer for these things).

- Lists can be fairly straightforward to generate via list comprehensions. There is no such thing with XSICollection (unless perhaps someone found a way to populate a XSICollection directly through list comprehension?) However I don't very often build lists of objects through list comprehension, so it's not such a big advantage.

- Lists can be easily nested. XSICollections, last time I tried, cannot (please correct me if I'm wrong).

- Lists have many useful methods and range notations that XSICollections do not support. I don't think you can do things like oColl( 0:3:2 ). Still, this is not something I'd do a lot with list and objects, I use list ranges mostly for string parsing.


In summary, for XSI objects container I generally use an XSICollection. It was designed with that goal in mind and in most cases I have no reason for using a list. It'd be interesting to know how other people use list for object containers!


Andy Nicholas (http://www.softimage.com/community/discussion/Archives/xsi.archive.0708/msg01108.htm)

There are lots of situations where collections are extremely useful or even mandatory:

  • When digging down into the SDK and dealing with geometry (points,

polygons, samples, etc.), everything is passed around as collections. They are usually specialised forms of the XSICollection and will only hold objects of a particular type, e.g. points. You have to use these if you are to communicate with XSI effectively.

  • For easily manipulating groups (in the non-xsi sense) of objects. One of

the really nice features of collections is that you can turn on the "Unique" flag. This'll automatically cull any duplicate references to a particular object. This, for me, is that killer feature that I couldn't do without.

  • Fast retrieval of objects from a scene. As I think has been mentioned,

using the SetAsText() function is an incredibly convenient way of obtaining references to objects in a scene. I think it was Brad who demonstrated on CGSoup that it's also an extremely fast way to do it too.

Standard JScript arrays are great for storing text, numbers and other non-XSI types, but for any sort of XSI object I'd always prefer to use a collection.


One of the main advantages of XSICollections is that they provide a fast and easy way to get collections of objects: 3D objects, parameters, or whatever.

Just look what you can do with two lines:

var oColl = XSIFactory.createobject( "XSI.Collection" );
oColl.items = "*.Object_Pnt_Ctrl_*"

If you have not done so already, check out the two articles by Bradley Gabe:

Sblair 08:26, 12 Sep 2007 (EDT)

Find An Operator's Parent3DObject

note: Operator.Parent3DObject has been fixed for 7.0, you no longer need this workaround.

If you need to find the object an operator is applied to then you would use the Parent3DObject (http://softimage.wiki.softimage.com/sdkdocs/ProjectItem_Parent3DObject.htm) property of an Operator (http://softimage.wiki.softimage.com/sdkdocs/Operator.htm) Sadly it doesn't seem to work ( tested in 6.02 ) here is your workaround in Jscript:

// Get object name
sFullName = oOperator.fullname;
aFullName = sFullName.split( "." );
aObjectFullName = aFullName.slice(0, -2);
sObjectFullName = aObjectFullName.join( "." );

// Get object
oColl = XSIFactory.createobject( "XSI.Collection" );
oColl.items = sObjectFullName;
oObject = oColl(0);

Hat Tip Bernard Lebel (http://www.softimage.com/community/discussion/Archives/xsi.archive.0709/msg00126.htm)

Upon further discussion this method was shared...

//Suppose object<589> is an operator, say a TwistOp
op = Application.Dictionary.GetObject( "object<589>" )

Application.LogMessage( op.Parent3DObject )
Application.LogMessage( Application.Dictionary.GetObject( op.FullName ).Parent3DObject )

Scaron 06:28, 6 Sept 2007 (PDT)

Scripting Viewport Captures

This is truly a FAQ.

Here's some JScript that shows how to script viewport captures.

// Get the current frame
var fc = ActiveProject.Properties.Item("Play Control").Parameters.Item("Current").Value;

// Set the capture options
var oViewportCapture = Dictionary.GetObject("ViewportCapture") ;

// Capture a 1-frame sequence 
oViewportCapture.NestedObjects.Item("Start Frame").Value = fc;
oViewportCapture.NestedObjects.Item("End Frame").Value = fc;         

// Specify the output file name
// The output file format is determined by the extension
oViewportCapture.NestedObjects.Item("File Name").Value = "C:\\test.pic";

// Do not display dialog
CaptureViewport( 2, false );

You can see the capture option property page in the Scene Explorer. Change the scope to Application, and click on Application > Data > Capture Options to open the Capture Options property page. These are the settings that are shown in the Capture Viewport dialog box, where they are shown in a different format.

You cannot set the codec from scripting. The codec is a combination of codec ID and a slew of private codec-specific parameters, and must be selected interactivly.

The work-around for this is to select the codec in the Softimage user interface and do a viewport capture. Then save the preset, either from the Capture Options property page (in the explorer, set the scope to Application and go to Application > Data > Capture Options) or with the SavePreset command. You can then load back the preset back by scripting anytime you need it, using the command

LoadPreset <path to preset>, "ViewportCapture"

You could generate a few presets for your favorite codecs, and use them with any scene. The preset will save all parameters of the viewport capture settings, so loading the preset would be the first thing you would do before setting the other parameters.

Sblair 04:45, 4 Sep 2007 (EDT) --lucer 07:48, 21 Jan 2009 (EST)

How do I create one particle per point on a grid?

Select your grid and run this (Jscript):

var oRoot = Application.ActiveProject.ActiveScene.Root;
var oObj = selection(0);
var vbArray = oObj.ActivePrimitive.Geometry.Points.PositionArray;
var aPos = vbArray.toArray();
var oPType = CreateParticleType(siSphereType, oPType)(0);
var oCloud = oRoot.AddParticleCloud(oPType, "myCloud");
var oCloudPrim = oCloud.ActivePrimitive;
oCloudPrim.AddParticles(aPos.length/3, oPType);
var oParticles = oCloudPrim.Particles;
oParticles.PositionArray = vbArray;

After that select the new cloud and Create > Particles > From Initial State.

Hat Tip: Vincent Fortin (http://www.softimage.com/community/discussion/Archives/xsi.archive.0708/msg01377.htm)

Sblair 02:28, 31 Aug 2007 (EDT)


Python Tip: Joining Strings

In python, it's faster to join multiple string variables with

''.join([A,B,C]) 

than with

A + B + C.


Hat Tip Greg Smith (http://www.softimage.com/community/discussion/Archives/xsi.archive.0708/msg01354.htm)

Sblair 01:03, 31 Aug 2007 (EDT)

Checking for Pass Overrides

Q How do I check if a user specified a different Pass Output Resolution so I can use it instead of the scene option in my script?


A

# Python
if oPass.ImageFormatOverride.Value:
    #User has overriden the resolution
    Application.KillUser()

Hat Tip Francois Lord (http://www.softimage.com/community/discussion/Archives/xsi.archive.0708/msg01274.htm)


Sblair 01:14, 31 Aug 2007 (EDT)


Finding Commands you don't know about

XSI ships with over 2300 commands. Not all of them are logged in the script editor, and not all of them are documented. Here's a crude way to search for commands with certain strings in their names.

var c = Application.Commands;
LogMessage( c.Count );

oEnum = new Enumerator( c  ) ;

// Search for commands whose scripting name
// contains a specific string of characters.
// The search string is specified between the forward slashes.
// 
var re = /SetGlobal/ig;

for (;!oEnum.atEnd();oEnum.moveNext() )
{
	var oSelItem = oEnum.item() ;
	if ( oSelItem.ScriptingName.match( re ) )
	{
			LogMessage( oSelItem.scriptingname );
			//EditCommand( oSelItem.scriptingname );

	}
} 

Sblair 09:59, 24 Aug 2007 (EDT)

This page was last modified 17:40, 9 Dec 2010.
This page has been accessed 53184 times.

© Copyright 2009 Autodesk Inc. All Rights Reserved. Privacy Policy | Legal Notices and Trademarks | Report Piracy