Category:Scripting
If you are coming from xsibase, and the list of articles in this category are missing, try refreshing the page.
| Table of contents |
STICKY - Googling the XSI Wiki and the SDK documentation
Did you know you can google the XSI Wiki and the SDK documentation and the XSI list?
- Go to http://www.softimage.com/search/default.aspx
- In the search box, type something like get objects or get property.
- Click Search Softimage, and voila.
Sblair 05:30, 13 Apr 2007 (EDT)
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.avid.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.avid.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:
- DataRepository.GetIdentifier (http://softimage.wiki.avid.com/sdkdocs/DataRepository_GetIdentifier.htm)
- Finding Custom Properties
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.avid.com/sdkdocs/PPGLayout_AddButton.htm), you cannot change it into a bitmap button.
You can, however, add bitmap buttons (http://softimage.wiki.avid.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:
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.avid.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:
- Loopin' Lizards! A Comparison of Techniques for Looping through Properties
- Loopin' Lizards, Part Deux: More Secrets and Some Advice on Filtering
Sblair 08:26, 12 Sep 2007 (EDT)
Find An Operator's Parent3DObject
If you need to find the object an operator is applied to then you would use the Parent3DObject (http://softimage.wiki.avid.com/sdkdocs/ProjectItem_Parent3DObject.htm) property of an Operator (http://softimage.wiki.avid.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 an 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 cannot set the codec from scripting. The codec is a combination of codec ID and a slew of private codec-specific parameters, not really a human readable string.
The work-around for this is to set all the parameters in the UI, and save the preset. Load the preset back by scripting anytime you need it.
To save a preset with the codec, first set the codec you want through the Capture Viewport dialog box.
Then open an Explorer. Set the scope to Application, and then click on Application > Data > Capture Options to open the Capture Options property page. Save a preset from there. The preset will include the codec.
Sblair 04:45, 4 Sep 2007 (EDT)
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)
Subcategories
There is 1 subcategory to this category.C
Articles in category "Scripting"
There are 26 articles in this category.


