Saturday Snippet: Getting data from a DataArray2D ICE attribute


I wanted to do this JScript, but I had to do it in Python first, to establish that it was actually possible (with JScript, you’ve got to mess around with VBarrays and such).

# Using one of the CrowdFX sample scenes:
Application.SelectObj("Pedestrian_Mesh.Actor_Copies", None, None);
o = Application.Selection(0)

a = o.ActivePrimitive.Geometry.GetICEAttributeFromName("Materials")
print len(a.DataArray2D)
print len(a.DataArray2D[0] )
print a.DataArray2D[0][0]
for s in a.DataArray2D[0][0]:
    print s

# 1
# 1
# (u'', u'Sources.Materials.PedestrianLib.Shoes', u'Sources.Materials.PedestrianLib.Hair', u'Sources.Materials.PedestrianLib.Legs', u'Sources.Materials.PedestrianLib.Skin', u'Sources.Materials.PedestrianLib.Shirt')
# Sources.Materials.PedestrianLib.Shoes
# Sources.Materials.PedestrianLib.Hair
# Sources.Materials.PedestrianLib.Legs
# Sources.Materials.PedestrianLib.Skin
# Sources.Materials.PedestrianLib.Shirt

After I had it working in Python, I was able to figure it out in JScript:

// Using one of the CrowdFX sample scenes:
SelectObj("Pedestrian_Mesh.Actor_Copies", null, null);
o = Selection(0);

a = o.ActivePrimitive.Geometry.GetICEAttributeFromName("Materials");

x = new VBArray( a.DataArray2D ).toArray();
y = new VBArray( x[0] ).toArray();
for ( var i = 0; i < y.length; i++ )
{
    LogMessage( y[i] );
}

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 – Writing the DataArray of an ICE attribute


Here’s a little Python snippet that shows how to write to the DataArray of an ICE attribute.

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

pc = si.GetPrim("PointCloud", "", "", "")
a = pc.ActivePrimitive.AddICEAttribute("MyScalarArray", C.siICENodeDataFloat, C.siICENodeStructureArray, C.siICENodeContextSingleton  )
a.DataArray = [[ 0.03, 3.33, 2.22, 3.333 ]]

a = pc.ActivePrimitive.AddICEAttribute("MyLong", C.siICENodeDataLong, C.siICENodeStructureSingle, C.siICENodeContextSingleton  )
a.DataArray = [3.1416*1000]

a = pc.ActivePrimitive.AddICEAttribute("MyScalar", C.siICENodeDataFloat, C.siICENodeStructureSingle, C.siICENodeContextSingleton  )
a.DataArray = [3.1416]

#
# Add some Attribute Display properties
# to show the attribute values
#

p = pc.AddProperty( "AttributeDisplay", False, "" )
p.Parameters( "attrname" ).Value = "MyScalar"

p = pc.AddProperty( "AttributeDisplay", False, "" )
p.Parameters( "attrname" ).Value = "MyLong"
p.Parameters( "offsety" ).Value = 16

p = pc.AddProperty( "AttributeDisplay", False, "" )
p.Parameters( "attrname" ).Value = "MyScalarArray"
p.Parameters( "offsety" ).Value = 32

I tried to do the same thing in JScript, but I couldn’t get it to work for arrays. Very frustrating.

pc = GetPrim("PointCloud", "", "", "");

a = pc.ActivePrimitive.AddICEAttribute("MyScalarArray", siICENodeDataFloat, siICENodeStructureArray, siICENodeContextSingleton  )

a.DataArray = [[ 0.03, 3.33, 2.22, 3.333 ]]
// WARNING : 3390 - This ICEAttribute doesn't refer to a 2D array: <Attribute: MyScalarArray>
//

a.DataArray = [ 0.03, 3.33, 2.22, 3.333 ]
// WARNING : 3392 - Invalid offset specified while extracting data from this attribute: <Attribute: MyScalarArray>
// <Offset: 108384008>
// 


// But this does works
a = pc.ActivePrimitive.AddICEAttribute("MyLong", siICENodeDataLong, siICENodeStructureSingle, siICENodeContextSingleton  )
a.DataArray = [3000]

PS You can find some usage of DataArray in the CrowdFX plugin (in the Softimage install dir).

Custom wire colors in the palette revisited



Here’s some updates to the wire frame color script I posted last week. See that post for info on how to add a button to the color palette.

First, I wanted to be able to pick multiple objects, one after the other. So I simplified the script by replacing most of the code with a single call to ColorizeObject, which does let you pick a sequence of objects.

CreateColorizeTool();
function CreateColorizeTool()
{
		var color_tool = XSIFactory.CreateObject( "CustomProperty" )
        if (color_tool)
        {
			var r = color_tool.AddParameter( "R", siDouble );
			var g = color_tool.AddParameter( "G", siDouble );
			var b = color_tool.AddParameter( "B", siDouble );
			var a = color_tool.AddParameter( "A", siDouble );
			
			var layout = color_tool.PPGLayout ;

			layout.AddGroup( "Color" );
			item = layout.AddColor( "R", "",true );
			item.SetAttribute( "NoLabel", true );
			layout.EndGroup();

			layout.AddRow();
			layout.AddButton( "ColorizeObject", "Colorize object" );
			layout.EndRow();

			layout.Language = "JScript" ;
			layout.Logic = ColorizeTool_ColorizeObject_OnClicked.toString();

			layout.SetAttribute( "LogicPrefix", "ColorizeTool_" ) ;
        }
        InspectObj( color_tool, "Colorize Tool", "", siLock ); 
}
function ColorizeTool_ColorizeObject_OnClicked()
{
		LogMessage( "v0.5" );
		ColorizeObject( PSet.R.Value,PSet.G.Value,PSet.B.Value );
}			

But this code doesn’t let you change the color in-between picks. So I modified the original example script and put a while loop around the call to PickObject (line 62), and I changed the script to get the colors directly from the color widget (line 78). That way, you can set the wire color, pick an object to apply the wire color, set another wire color, pick another object, and so on…

CreateColorizeTool();
function CreateColorizeTool()
{
	var color_tool = XSIFactory.CreateObject( "CustomProperty" )
    if (color_tool)
    {
		var color_tool = ActiveSceneRoot.AddCustomProperty( "ColorizeTool" );
		var wirecolor = color_tool.AddParameter( "wirecolor", siInt4 );
		wirecolor.ReadOnly = true;
		var r = color_tool.AddParameter( "R", siDouble );
		var g = color_tool.AddParameter( "G", siDouble );
		var b = color_tool.AddParameter( "B", siDouble );
		var a = color_tool.AddParameter( "A", siDouble );
		var layout = color_tool.PPGLayout ;
		layout.AddRow();
		var item = layout.AddItem( "wirecolor", "wirecolor" );
		item.SetAttribute( "NoSlider", true );
		layout.AddButton( "ColorizeObject", "Colorize object" );
		layout.EndRow();
		layout.AddGroup( "Color" );
		item = layout.AddColor( "R", "",true );
		item.SetAttribute( "NoLabel", true );
		layout.EndGroup();
		layout.Language = "JScript" ;
		layout.Logic = 
				ColorizeTool_R_OnChanged.toString() + 
				ColorizeTool_G_OnChanged.toString() + 
				ColorizeTool_B_OnChanged.toString() + 
				RGBToWireframeColor.toString() + 
				ColorizeTool_ColorizeObject_OnClicked.toString();
		layout.SetAttribute( "LogicPrefix", "ColorizeTool_" ) ;
	}
	InspectObj( color_tool, "Colorize Tool", "", siLock ); 
}

function ColorizeTool_R_OnChanged()
{
        PSet.wirecolor.ReadOnly = false;
        PSet.wirecolor.Value = RGBToWireframeColor(PSet.R.Value,PSet.G.Value,PSet.B.Value);
        PSet.wirecolor.ReadOnly = true;
}
function ColorizeTool_G_OnChanged()
{
        PSet.wirecolor.ReadOnly = false;
        PSet.wirecolor.Value = RGBToWireframeColor(PSet.R.Value,PSet.G.Value,PSet.B.Value);
        PSet.wirecolor.ReadOnly = true;
}
function ColorizeTool_B_OnChanged()
{
        PSet.wirecolor.ReadOnly = false;
        PSet.wirecolor.Value = RGBToWireframeColor(PSet.R.Value,PSet.G.Value,PSet.B.Value);
        PSet.wirecolor.ReadOnly = true;
		PSet.Refresh();
}
function ColorizeTool_ColorizeObject_OnClicked()
{
		LogMessage( "v0.3" );
        var color = PSet.wirecolor.Value;
        var o = null;
        var siRMB = 0;
        var button = -1, modifier;
        while (button != siRMB )
        {
				LogMessage( button != siRMB );
                Application.StatusBar ="Pick object to colorize";
                var rtn = PickObject( "Select object", "");
				button = rtn.Value("ButtonPressed");
				modifier = rtn.Value("ModifierPressed");
				o = rtn.Value("PickedElement");
				
				if ( o != null )
				{
					var display = o.Properties("Display");
					if (display.isa(siSharedPSet))
					{
							display = MakeLocal( display, siNodePropagation )(0);
					}
					display.wirecol.Value = RGBToWireframeColor(PSet.R.Value,PSet.G.Value,PSet.B.Value);
				}
        }
        if ( button == siRMB )
                return;

        return color;
}
// Convert wireframe color index to double-precision RGB color
function WireframeColorToRGB(lWireframeColor)
{
        var aColor = new Array(3);
        aColor[0] = ((lWireframeColor >>> 1) & 0x7)/7;
        aColor[1] = ((lWireframeColor >>> 4) & 0x7)/7;
        aColor[2] = ((lWireframeColor >>> 7) & 0x7)/7;
        return aColor;
}
// Convert double-precision RGB color to wireframe color index
function RGBToWireframeColor(dR,dG,dB)
{
        // Convert RGB to wirecolor
        var wirecolR, wirecolG, wirecolB;
        wirecolR = (Math.round(dR * 7)) << 1
        wirecolG = (Math.round(dG * 7)) << 4
        wirecolB = (Math.round(dB * 7)) << 7
        return wirecolR | wirecolG | wirecolB;
}

#include statement in JScript


JScript doesn’t include anything like the #include statement, or like modules in Python.

But what you can do is read the contents of a file into a string, and then eval() the string. For example:

var fso = ActiveXObject("Scripting.FileSystemObject");
var f = fso.OpenTextFile( "//server/scripts/toinclude.js", ForReading );
var s = f.ReadAll();
eval(s);
s.Close();

Note that if there’s any errors in the included code, you won’t get the right line numbers in the error messages.

In JScript, another approach would be to create a custom object that has all the helper functions as methods. For an example of that kind of thing, check out the WizardHelperObj object in

%XSI_HOME%\Addons\sdkui\Application\Plugins\SDKWizards.js

Then you just provide one custom command that returns an instance of the custom object to whatever code needs to use the helper functions.