Saturday Snippet: How many operators in Softimage?


Dictionary.Info gives you a list of all members in a particular family.

ops = Application.Dictionary.Info( '',"Operators").split(',')
print len(ops)
for op in ops:
	print op

For example, there are 592 operators in 2013 SP1 (and 585 in 2012 SP1).

# 592
#  RaiseNurbsCrvDegree
#  ExtrusionAlongAxis
#  4DVectorTo3DVectorNode
#  CreatePolygon
#  GetSetSumNode
#  deformbyspineop2
#  weighmapdeformbyspineop2
#  GetDistanceBetweenNode
#  MarkHardEdgeVertex
#  FCurveNode
#  BooleanSrfTopo
#  OldGenerateSampleSetNode
#  HipIcon
#  WeightPainter
#  DirectionToRotationNode
#  PropWeightMapBySpineOp
#  BaseRetargetOp
#  SkeletonUpVector
#  SkeletonPrefAxis
#  ReferenceToStringNode
#  BooleanSrfOp
#  RemoveNurbsCrvKnot
#  DeleteParticleOp
#  MergeDisjointVerticesNode
#  CenterManip
#  FillHole
#  SubtractNode
#  BlasterOp
#  NegateNode
#  CrvOffset
#  FourSided
#  DissolveComponent
#  Cap
#  3DVectorTo4DVectorNode
#  GetClosestPointsNode
#  FilterEdge
#  SubdivideEdge
#  MergePolygonNode
#  AnimationMarker
#  ExponentNode
#  Fixed Length Operator
#  AddEdge
#  SrfSwap
#  SrfDump
#  OffsetPolygons
#  ExtractPolygonsOp
#  SurfaceUVControl
#  Copy Shape Op
#  2DVectorNode
#  GetArraySizeNode
#  SortArrayNode
#  UpVectorDefiner
#  HairCutOp
#  DetachPolygonsOp
#  RotationToScalarNode
#  Birail2Contours
#  ToeGuide
#  GetSetMedianNode
#  4x4MatrixNode
#  Primitive Grid
#  Primitive Circle
#  Primitive Arc
#  Primitive Spiral
#  IntegerNode
#  NurbsCrvDeletePoint
#  HairScaleOp
#  HSVAToColorNode
#  ICETree
#  HLSAToColorNode
#  Primitive Cylinder
#  AreParallelNode
#  ManCharEyeOp
#  AddNode
#  VectorTo4x4MatrixNode
#  CrvDeform
#  ScalarNode
#  HairResampleOp
#  CompoundNode
#  SimulationStepNode
#  GetSetSizeNode
#  Primitive Square
#  Primitive Octahedron Operator
#  3x3MatrixNode
#  SRTToMatrixNode
#  MeshSubdivideWithCenter
#  PrimitivePreSimulationMarker
#  SrfOffset
#  SpeechBlendOp
#  TextToCurveListOp
#  SimpleIKRetargetOp
#  TextureOp
#  Quadrangulate
#  FrameStepNode
#  4x4MatrixToVectorNode
#  ChestBone
#  CopyGeo
#  PopFromArrayNode
#  SelectCaseNode
#  ColorNode
#  InvertPolygonNode
#  VertexColorChangeDatatype
#  MotionDerivator
#  FRBulgeNode
#  ExtractSubCrvOp
#  BipedRigSteppingAngle
#  ShapeNode
#  UndefinedLocationNode
#  GetClosestLocationNode
#  SplitPolygon
#  SetPositionNode
#  FingerBladeOp
#  CurrentTimeNode
#  SubdividePolygon
#  Surface curve adaptor
#  QStretch
#  3DVectorTo4x4MatrixNode
#  Primitive Disc
#  MatrixToSRTNode
#  RelaxUVW
#  UVToLocationNode
#  CrvPass
#  CrvExtract
#  SrfCrvShift
#  ReinterpretLocationToNewGeometryNode
#  CrvReparam
#  TangentOp_cpp
#  SubtractColorNode
#  SrfFit
#  PrimitiveMeshNode
#  MaximumNode
#  HairPopOp
#  ColorToGrayscaleNode
#  BooleanNode
#  MoveComponent
#  SCMFixer
#  SubdivideLocallyNode
#  MoveComponentProportional
#  BlobEncodeTestNode
#  PropVolume
#  GetDeterminantNode
#  CrvExtractSeg
#  GenerateSampleSetNode
#  MultiplyByScalarNode
#  CollapseEdgeNode
#  ScalarTo3DVectorNode
#  SplitEdge
#  HairMergeOp
#  SquaredLengthNode
#  TrigonometryNode
#  SparksOp
#  GetDataNode
#  SpringOp
#  Primitive Dodecahedron Operator
#  LengthNode
#  Loft
#  WeldPoints
#  QuaternionToAxisAndAngleNode
#  SortArrayWithKeyNode
#  TestBackcompNode
#  RemoveFromArrayNode
#  Loft
#  Extrusion
#  FourSided
#  PolyMeshPNet
#  CrvOffset
#  SrfOffset
#  SrfFillet
#  SrfIntersect
#  SrfMerge
#  FillHole
#  CrvFit
#  CrvExtract
#  Cap
#  CrvProject
#  CurveCreation
#  CrvNet
#  Birail
#  CrvFillet
#  CrvStitch
#  CrvMerge
#  CrvExtractSeg
#  ExtrusionTwoProfiles
#  SCMTopology
#  Revolution
#  BlendCrv
#  BlendSrf
#  CopyOp
#  GetArrayProductNode
#  FlexibleEnvelope
#  4x4MatrixTo3DVectorNode
#  SrfSubdivision
#  TangentOp2_cpp
#  ModuloNode
#  BooleanToIntegerNode
#  ClusterSimpleShapeCombinerOp
#  CollisionAvoidanceNode
#  HairGenOp
#  HairDynamicsOp
#  3x3MatrixToVectorNode
#  CrvSnap
#  TrimByProjection
#  SrfStitch
#  FRTugNode
#  VectorTo3x3MatrixNode
#  CurrentFrameNode
#  SCMFixer2
#  Lattice
#  RevolutionAlongAxis
#  FilterNode
#  Bulge
#  SetData
#  DelaySetDataNode
#  ImageCropOp
#  MultiplyVectorByMatrixNode
#  CrvPassByPoint
#  IsSingularNode
#  IntegerToBooleanNode
#  SetUserDataMapValueOp
#  SplitEdgeNode
#  BlobDecodeTestNode
#  CopyOp
#  LockEnvelopeWeights
#  CrvExtendTo
#  SrfClean
#  CrvFillet
#  CrvStitchToSrf
#  CrvStitch
#  CrvMerge
#  ExtendCrvToPoint
#  Surface To Surface Curve Adaptor
#  DicePolygons
#  AddVertexNode
#  CurveToCurvePointAdaptor
#  Modify Envelope Weight Prop Op
#  Skeleton Controller
#  BevelComponent
#  DeformBySpine
#  SimulateBulletRigidBodiesNode
#  RotationNode
#  First Bone Roll Division
#  Rotation Order
#  ConstrainCurveIsopoint
#  Revolution
#  SrfInverse
#  HairLockOp
#  CurveToCurveBoundaryAdaptor
#  LimitEnvelopeDeformersOp
#  HairSplitOp
#  GetArrayAverageNode
#  SetOneDataNode
#  InsertSrfKnot
#  RemoveSrfKnot
#  CrvProject
#  BlendNode
#  LogarithmNode
#  SrfFillet
#  EdgeDelete
#  ExtrudeComponent
#  AddEdgeNode
#  SimulateRigidBodiesNode
#  Spine Curve Operator
#  GetDataAtPreviousFrameNode
#  TriangulatePolygonNode
#  IntegerToScalarNode
#  Gator
#  SCMTopology
#  GetClosestPointToLineSegmentNode
#  First Bone Roll Division
#  MergeTopoArrayNode
#  ExtrudePolygonIslandNode
#  IsOnGeometryNode
#  ScalarToRotationNode
#  ExclusiveOrNode
#  DeleteComponent
#  ResizeArrayNode
#  DeletePointNode
#  GetAngleBetweenNode
#  SteppingAngle30Op
#  SymmetryMap
#  FlexibleEnvelopeAutoAssign
#  PropWeightMapBySpineOp
#  Snip
#  EditPolygon
#  GroupGeometryNode
#  CreateTopoNode
#  NurbsToMesh
#  Shear
#  GradientNode
#  HyperFlowOp
#  CameraTxt
#  PolyUVContourStretchingOnTopOp
#  BlendCrv
#  CustomOperator
#  ColorToHLSANode
#  QuaternionToRotationNode
#  FRLiftNode
#  SplineGenOp
#  PolygonReduction
#  ImageSourceOp
#  AddPolygonNode
#  IfNode
#  SimulationMarker
#  ScalarTo2DVectorNode
#  GetMinimumInSetNode
#  InsertInArrayNode
#  LinearInterpolateNode
#  ArcTan2Node
#  StringFilePathNode
#  SnapFace
#  HairPuffOp
#  PrimToNonLocalMatCls
#  GetElementIndicesNode
#  SetClusterPropSizeOp
#  RepeatNode
#  DisconnectComponentNode
#  ParTypeClsGenOp
#  FindInArrayNode
#  Fold
#  OldGetGeometrySampleNode
#  PushOnArrayNode
#  Birail
#  TextureOp
#  SurfacePQ
#  Cluster
#  MotionIntegrator
#  PassThroughNode
#  Multiply Rotation Operator
#  Spring
#  Collapse
#  QuaternionNode
#  BridgePolygon
#  HairUnlockOp
#  SymmetrizePolygon
#  FlipUVW
#  SetUVPropPinInfoOp
#  ImageFXOp
#  GetSetOrNode
#  TurbulenceNode
#  RGBAToColorNode
#  ClonePointNode
#  GetSetAndNode
#  RotationToEulerNode
#  SimMotionDerivOp
#  CompareNode
#  PointIndexToLocation
#  MergeTopoNode
#  MapCompOp2
#  DisconnectComponent
#  BlendTransformNode
#  Copy Shape Op
#  GetGeometrySampleNode
#  MatchUVW
#  NXDynamicsOp
#  FlowFieldRenderOp
#  Push
#  SoftBodyOp
#  RaycastNode
#  FlexibleEnvelopeAutoAssign
#  OldNurbsCrvDeletePoint
#  SlicePolygons
#  VertexColorPainter
#  PolyMeshPNet
#  BuildArrayFromConstantNode
#  AbsoluteValueNode
#  Cls Shape Combiner Op
#  ModelingMarker
#  CrvInverse
#  SrfShift
#  CrvShift
#  OffsetComponents
#  ExtractEdgeLoopOp
#  TurnEdgeOp
#  Fore Arm Roll Division
#  LogValuesNode
#  AddPointNode
#  IDToLocation
#  ExtrudeComponentAxis
#  SetEdgeCreaseValueOp
#  GetSetAverageNode
#  Primitive Tetrahedron Operator
#  SrfOpenClose
#  MixerOp
#  CloneTopoNode
#  Bend
#  Taper
#  ReserveArrayNode
#  Surface To Boundary Curve Adaptor
#  NormalizeNode
#  Fracture
#  Primitive Text
#  InvertNode
#  RotationToQuaternionNode
#  InitializeNode
#  SlicePolygonNode
#  PrimitivePostSimulationMarker
#  DeletePolygonNode
#  ClothOp
#  ShapeJitter
#  DotProductNode
#  SrfPass
#  MapAdaptOp
#  Spin
#  SplineDynamicsOp
#  Primitive Icosahedron Operator
#  Primitive Sphere
#  Extrusion
#  DivideByScalarNode
#  FilterPolygon
#  OLD Cache On File --REPLACEME
#  TestNode
#  InvertPolygon
#  Primitive Cone
#  Primitive Cube
#  QuaternionToScalarNode
#  CacheDataNode
#  PrimaryShapeMarker
#  MeshMerge
#  ICETreeComment
#  3DVectorNode
#  PaintWeight
#  WeightMap
#  SrfMerge
#  AddPointToMiddleOfCurve
#  GetArrayMaximumNode
#  Connector_Head
#  DieNode
#  DogLeg
#  CombineLogicNode
#  4DVectorNode
#  CrvClean
#  CrvOpenClose
#  PostSimulationMarker
#  Smooth
#  WeldEdges
#  CrossProductNode
#  Spine PointAt Operator
#  SrfIntersect
#  ShrinkWrap
#  QuadrupedGuideOp
#  WMCnxOp
#  DeformHairAdaptor
#  Revolution
#  Extrusion
#  ProjectVectorNode
#  SrfDeform
#  TransposeNode
#  Connector_L
#  InsertNurbsCrvKnot
#  BlowUpOp
#  ClusterCenter
#  NotNode
#  CurveKnotAlignTangent
#  CurveListToMesh
#  AddColorNode
#  ScalarTo4DVectorNode
#  CrvFit
#  MapCompOp
#  SrfCrvInverse
#  Softimage
#  FacialSoftTissueOp
#  GetArraySubIndicesNode
#  EvolveOp
#  HairClumpOp
#  HairAttenuateOp
#  BlendSrf
#  ScriptedOpHost
#  AddParticleOp
#  QuaternionToEulerNode
#  Heal
#  MinimumNode
#  MeshSubdivide
#  SrfReparam
#  FRSkullUVsToPtLocsNode
#  CustomNode
#  Proportional
#  Connector_T
#  PointInVolumeNode
#  ArePerpendicularNode
#  RotateVectorNode
#  BBoxCns
#  ColorToRGBANode
#  PolyUVContourStretchingOp
#  SpinEdge
#  CompoundNode
#  Connector_Box
#  LocalTextureOp
#  GetClosestPointToLineNode
#  PropertyXferOp
#  3DVectorToScalarNode
#  StringFilePathSequenceNode
#  ScalarToQuaternionNode
#  HairCombOp
#  PrimitiveSimulationMarker
#  BasicCollideNode
#  ExecuteNode
#  CurveCreation
#  SecondaryShapeMarker
#  CrvNet
#  SetInArrayNode
#  MultiplyColorByScalarNode
#  PropWeightMapBySpineOp
#  ClsKeyWeightMapOp
#  EulerToQuaternionNode
#  QuatSpineOp
#  WhileNode
#  Weight Maps Mixer Op
#  GetActionSourceAtFrameNode
#  Noise
#  Wave
#  BallisticSim
#  VertexColor
#  Cage Deform Auto Assign
#  CycleUVW
#  Proportional
#  GetArraySumNode
#  InstanceShapeNode
#  DynamicsOp
#  EdgeAdd
#  FlowFieldOp
#  BuildArrayNode
#  PolyUVPackingOp
#  ExtrudeComponentNormal
#  SelectInArrayNode
#  RescaleNode
#  CacheOnFileNode
#  RotationToAxisAndAngleNode
#  DeleteVertexNode
#  ColorToHSVANode
#  AddToClsOp
#  Twist
#  IslandHealUVWOp
#  SimulateParticlesNode
#  MultiplyNode
#  AxisAndAngleToQuaternionNode
#  HealUVW
#  2DVectorToScalarNode
#  FirstValidNode
#  SwitchContextNode
#  TangentOp2_cpp
#  StringNode
#  GetArrayMinimumNode
#  ClusterToObj
#  RoundNode
#  GetMaximumInSetNode
#  Primitive Torus
#  HairRotateOp
#  FilterPoints
#  FRSkinNode
#  RandomValueNode
#  CrvDump
#  DeleteTrim
#  PolygonAdd
#  ClampNode
#  Randomize
#  CageDeform
#  MeshLocalSubdivision
#  
#  MeshSrfDeletePoint
#  TopoTransformNode
#  CopyUVW
#  SquareRootNode
#  HairShatterOp
#  SetNurbsCrvKnotMultiplicity
#  HairRecombOp
#  AxisAndAngleToRotationNode
#  EulerToRotationNode
#  BuildArrayFromSetNode
#  ColorToBrightnessNode
#  BlendColorNode
#  4DVectorToScalarNode
#  QuaternionInterpolateNode
#  syCollidePla
#  syZipper
#  POXSI
#  syWind
#  syProperties
#  syCollide
#  syMimic
#  syCache
#  syVolume
#  syTurbulence
#  sySpring
#  syPin
#  sySelfCollide
#  syPropertiesFlesh
#  syGravity
#  syDamp
#  syCloth
#  syPropertiesSkin
#  syNail
#  syAir
#  syCollideSph

Finding degenerate polygons by area


Degenerate polygons are usually zero-area polygons.

Here’s a script that uses the ICE attribute PolygonArea to find polygons with area less than a specified epsilon:

si = Application
epsilon = 0.00001

# Get PolygonArea DataArray (which is a tuple)
attr = si.Selection(0).ActivePrimitive.GetICEAttributeFromName( "PolygonArea" )
areaData = attr.DataArray

#
# Find the indices of the bad polys
#
bad = [ x for x,y in enumerate( areaData ) if y < epsilon]

# Select the degenerates with a string like 'cube.poly[112,114,155]'
si.SelectGeometryComponents( 'cube.poly[%s]' % ','.join(str(i) for i in bad) )


### OR ###

#
# Get the actual Polygon objects
#
polys = si.Selection(0).ActivePrimitive.Geometry.Polygons
bad = []
for i in range( len(areaData) ):
	if areaData[i] < epsilon:
		bad.append( polys(i) )

si.SelectObj( polys )

Saturday snippet: Python classes, __getattr_, lambda, and-or trick, and getattr


Here’s a snippet of some python posted on the Softimage mailing list this week.

from win32com.client import constants
class Softimage:
    __getattr__ = lambda x, a: getattr(Application, a, False) or getattr(constants, a)
softimage = Softimage()

Let’s break this down…

First, this snippet defines a class named Softimage (lines 1 and 2) and then instantiates an instance of that class (line 4).

Once you have an instance of a class, you can access the attributes of the class. Now this Softimage class doesn’t define any attributes, but it does provide a __getattr__ method. For example, if you ask for softimage.GetValue, then that __getattr__ method gives you back the function object Application.GetValue. If you ask for softimage.ActiveSceneRoot, you get back the value of the Application.ActiveSceneRoot property. And if you ask for softimage.siX3DObjectID, you get back constants.siX3DObjectID.

So, how does that work?

__getattr__ is a method that is called when an attribute isn’t found on a class instance.
If you define __getattr__ without using lambda or the and-or trick, it could look something like this:

from win32com.client import constants
class Softimage:
#    __getattr__ = lambda x, a: getattr(Application, a, False) or getattr(constants, a)
    def __getattr__( self, name ):
        a = getattr(Application, name, False) 
        if not a:
            a = getattr(constants, name)
        return a

getattr is a built-in Python function that gets an attribute on a specified object. Here, we’re getting an attribute of the Softimage Application object. For example:

# Get the scene root
a = getattr(Application, "ActiveSceneRoot", False)
print a
print a.Parameters.Count

# Get the selection with the GetValue command
a = getattr(Application, "GetValue", False)
print a
print a( 'SelectionList' )

Getting selected text from the script history


Here’s how to get the selection from the script history. This is how the Repeat command works.

def GetScriptHistoryView():
	oLayout = Application.Desktop.ActiveLayout

	# See if the script history is a view of it's own
	oViews = oLayout.Views.Filter( "Script History" )
	if oViews.Count == 0:
		oViews = oLayout.Views.Filter( "Script Editor" )

	return oViews(0)

oLog = GetScriptHistoryView()
text = oLog.GetAttributeValue( "historyline" ) if oLog else "<Nothing>"
print text

Saturday Snippet: Selecting a range of polygons by polygon index


Given a selected polygon, add the next 5 polygons to the selection. For example, if polygon 22 is selected, then add polygons 23,24,25,26, and 27 to the selection.

You can do this without using any loops, by using a string expression for the polygon components.

Here’s some JScript that shows two ways to do it. Method 1 uses a string expression. Method 2 is a loop, which can either use the Polygons collection or a string expression.

var o = Selection(0).SubComponent.Parent3DObject
var p = Selection(0).SubComponent.ComponentCollection(0)
var LAST = o.ActivePrimitive.Geometry.Polygons.Count;

// Method 1
// SelectGeometryComponents("grid.poly[22-27]", null, null);

var sPoly = ".poly["+p.Index+"-"+Math.min( p.Index+5,LAST-1 )+"]"
//SelectGeometryComponents( o.FullName + sPoly );
//Selection.SetAsText( o.FullName + sPoly  );

// Method 2
//for ( var i = p.Index+1 ; i < Math.min( p.Index+5,LAST ) ; i++ )
//{
//	ToggleSelection( o.ActivePrimitive.Geometry.Polygons(i) );
// -or-
//	ToggleSelection( o.FullName + ".poly["+i+"]" )
//}

Here’s some Python that does something similar but different:

si = Application
o = si.Selection(0).SubComponent.Parent3DObject
p = si.Selection(0).SubComponent.ComponentCollection(0)
LAST = o.ActivePrimitive.Geometry.Polygons.Count

s = ".poly[%s-%s]" % ( p.Index, min( p.Index+5,LAST-1 ) )
si.SelectGeometryComponents( s )

Saturday Snippet: russian roulette


I commented out the things that would actually “shoot a bullet”.

CrashXSI() is an actual Softimage command, but it doesn’t do anything in a release build.

import random

# bullets
def crashxsi():
	Application.LogMessage( "Bang!" )
	Application.CrashXSI()
	
def blank():
	Application.LogMessage( "Try again" )
	
def losework():
	Application.LogMessage( 'NewScene( "", False )' )
	#Application.NewScene( "", False )
	
def quitxsi():
	Application.LogMessage( "Application.Quit()" )
	#Application.Quit()


#load chambers
chambers = {1: blank,
			2: crashxsi,
			3: quitxsi,
			4: blank,
			5: blank,
			6: losework
			}
	
#spin and shoot
chambers[ random.randint(1,6) ]()

Writing a python script for processing scenes with xsibatch


Here’s the basic skeleton of a Python script that calls xsibatch -processing -script on all scene files in a given folder.

Python command line:
The python script takes two arguments: the root folder for the location of the scene files, and the name of the script file to run with xsibatch -script.

python process_scenes.pys --dir ""C:\Program Files\Autodesk\Softimage 2013 SP1\Data\XSI_SAMPLES\Scenes" --script "test.pys"

process_scenes.pys:
The python script takes care of finding all the scene files, and then running xsibatch -processing -script on each .scn file.

import os
import fnmatch
import subprocess
import sys
import getopt

XSI_BINDIR=r"C:\\Program Files\\Autodesk\\Softimage 2013 SP1\\Application\\bin"

opts, extraparams = getopt.getopt(sys.argv[1:], "d:s:", ["dir=","script="]) 

#
# Get root directory to scan for scene files
#
SCENES_DIR="C:\\Program Files\\Autodesk\\Softimage 2013 SP1\\Data\\XSI_SAMPLES\\Scenes\\OLD"
SCRIPT="test1.pys"

for o,p in opts:
  if o in ['-d','--dir']:
     SCENES_DIR = p
  elif o in ['-s','--script']:
     SCRIPT = p

#
# Generator function for finding files
#
def find_files(directory, pattern):
     for root, dirs, files in os.walk(directory):
         for basename in files:
             if fnmatch.fnmatch(basename, pattern):
                 filename = os.path.join(root, basename)
                 yield filename

#
# Open each scene file and run the specified script
#
for scn in find_files(SCENES_DIR, '*.scn'):
	sXsiBatch = "%s\\xsibatch" % XSI_BINDIR
	subprocess.call( [ sXsiBatch, '-processing', '-script', SCRIPT, '-args', '-sSceneName', scn ] )

Softimage script test.pys:
A simple test script to run with xsibatch. Note that because the function is named “main”, I don’t have to specify that on the xsibatch command line. I just have to specify the arguments.

def main( sSceneName ):
	LogMessage( sSceneName )	

Saturday Snippet – Building a cross-platform path


…and exporting the selected objects to an ASS file.

from win32com.client import constants as C

filename = XSIUtils.BuildPath( 
	Application.InstallationPath( C.siProjectPath ),
	'Arnold_Scenes',
	'[Scene].[Frame].ass' )
Application.SITOA_ExportScene( 1,1,1,True,True,filename)
# Application.SITOA_ExportScene(1, 1, 1, True, True, "C:\\Users\\Steve\\My Project\\Arnold_Scenes\\[Scene].[Frame].ass")

Saturday snippet – Launching a command-line utility and viewing its output


Here’s a snippet that shows how to launch a command-line program in a command prompt window, and keep the command prompt window open so you can see the output. Note that does not block Softimage.

#sKick = "C:/Users/SOLIDANGLE/Documents/Workgroups/sitoa-2.5.0-2013/Addons/SItoA/Application/bin/nt-x86-64/kick.exe"
sKick = XSIUtils.BuildPath( Application.InstallationPath( 2 ), "Addons", "SItoA", "Application", "bin", XSIUtils.Environment("XSI_CPU_OPT"), "kick.exe" )
XSIUtils.LaunchProcess( "cmd /C start cmd /K %s -licensecheck" % sKick )

Basically, what this does is launch a cmd.exe process, and in that process, run the command “start cmd /K kick -licensecheck”. The second “cmd” is required to open a command prompt, where you’ll see the output of “kick -licensecheck”.

In JScript, it would be something similar:

sKick = XSIUtils.BuildPath( Application.InstallationPath( 2 ), "Addons", "SItoA", "Application", "bin", XSIUtils.Environment("XSI_CPU_OPT"), "kick.exe" )
XSIUtils.LaunchProcess( "cmd /C start cmd /K " + sKick + " -licensecheck" )

In Python, you could also do this:

from subprocess import call
call(["cmd", "/K", sKick, "-licensecheck"])

The above would open a non-blocking command prompt window (eg you could go back to Softimage), but this next snippet would prevent Softmage from responding until you closed the command prompt:

import os
os.system("cmd /k %s -licensecheck" % sKick)

Scripting – Shuffling a collection to randomly select by percentage


randompercentageVia a tech-artists.org tweet, I came across some MAXScript for randomly selecting a specified percentage of mesh elements (for example, give me a random selection that includes 35% of all vertices).

I converted it to Python in Softimage. Note that I don’t really shuffle a collection: you can’t set items in a collection, so there’s no way to swap items. Instead, I put the collection in a list and shuffle the list.

Shuffling actually makes this harder than it has to be. Check out Alan’s nice script for a better way to do this.

si = Application
log = si.LogMessage
sisel = si.Selection
#http://creativescratchpad.blogspot.ca/2011/06/select-random-elements-by-percentage.html

import random

#
# Return a list that includes a randomly selected
# percentage of the items in a collection
#
def get_random_percentage( collection, percentage ):
	v = [x for x in collection]

	# random shuffle
	for i in range( len(v) ):
		j = random.randint( i, len(v)-1 )
		v[i], v[j] = v[j], v[i]

	# select by percentage
	step = 100/percentage
	w = []
	for i in xrange( 0, len(v), step ):
		w.append( v[i] )

#	print len(w)
#	print (percentage/100.0) * len(v)
	
	return w


Application.SelectObj("torus", "", True)
# Select a random 50% of the vertices
x = get_random_percentage( sisel(0).ActivePrimitive.Geometry.Vertices, 50 )
si.SelectObj(x)

print sisel(0).SubComponent.ComponentCollection.Count

# Suppose you had 2000 cubes.
# Select a random 25% of those 2000...
Application.SelectObj("cube*", "", True)
x = get_random_percentage( sisel, 25 )
si.SelectObj(x)

I learned a couple of things about MAXScript:

  • MAXScript arrays are 1-based
  • In the MaxScript docs, there aren’t any function/method reference pages. You have to go to a “value” page (eg Number Values) and there you’ll find all the methods. That’s fine once you know, but it was confusing at first when I didn’t see anything in the TOC for Functions or Methods.