<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Softimage Blog &#187; Python</title>
	<atom:link href="http://www.softimageblog.com/archives/category/scripting/python/feed" rel="self" type="application/rss+xml" />
	<link>http://www.softimageblog.com</link>
	<description>People and thoughts behind Softimage in production...</description>
	<lastBuildDate>Mon, 21 Jun 2010 20:40:48 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Dynamic Callbacks In Plugins</title>
		<link>http://www.softimageblog.com/archives/265#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=dynamic-callbacks-in-plugins</link>
		<comments>http://www.softimageblog.com/archives/265#comments</comments>
		<pubDate>Thu, 12 Jun 2008 01:12:46 +0000</pubDate>
		<dc:creator>Patrick Boucher</dc:creator>
				<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=265</guid>
		<description><![CDATA[I really like applications that provide rich SDKs for it&#8217;s users to play around with. I also like event based systems and callbacks. These are all things that make me absolutely love writing up plugins inside XSI. But there is one small problem, I find, with XSI&#8217;s callbacks.
Callbacks in XSI plugins are functions in the [...]]]></description>
			<content:encoded><![CDATA[<p>I really like applications that provide rich SDKs for it&#8217;s users to play around with. I also like event based systems and callbacks. These are all things that make me absolutely love writing up plugins inside XSI. But there is one small problem, I find, with XSI&#8217;s callbacks.</p>
<p>Callbacks in XSI plugins are functions in the global scope who&#8217;s name must be defined exactly the way XSI expects to find them.</p>
<p>I would have preferred callbacks to be supplied by the user in a way that allows them to have any name and in a way that allows them to be changed on the fly. In all fairness, Softimage probably has some very good reasons for the way they did things.</p>
<p>So this is where we, as users, exploit the power that has been given to us and take things into our own hands. Here is my implementation of dynamic, interchangeable, replaceable callbacks.</p>
<p><span id="more-265"></span><strong>Code Time!</strong></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com.<span style="color: black;">client</span>
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants <span style="color: #ff7700;font-weight:bold;">as</span> c
<span style="color: #ff7700;font-weight:bold;">from</span> vg.<span style="color: black;">xsi</span>.<span style="color: black;">dynamic</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #66cc66;">*</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> XSILoadPlugin<span style="color: black;">&#40;</span> in_reg <span style="color: black;">&#41;</span>:
	in_reg.<span style="color: black;">Author</span> = <span style="color: #483d8b;">&quot;Patrick Boucher&quot;</span>
	in_reg.<span style="color: black;">Name</span> = <span style="color: #483d8b;">&quot;dynCbCommandPlugin&quot;</span>
	in_reg.<span style="color: black;">Major</span> = <span style="color: #ff4500;">1</span>
	in_reg.<span style="color: black;">Minor</span> = <span style="color: #ff4500;">0</span>
	in_reg.<span style="color: black;">RegisterCommand</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;dynCbCommand&quot;</span>, <span style="color: #483d8b;">&quot;dynCbCommand&quot;</span><span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> XSIUnloadPlugin<span style="color: black;">&#40;</span> in_reg <span style="color: black;">&#41;</span>:
	strPluginName = in_reg.<span style="color: black;">Name</span>
	Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #008000;">str</span><span style="color: black;">&#40;</span>strPluginName<span style="color: black;">&#41;</span> + <span style="color: #008000;">str</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot; has been unloaded.&quot;</span><span style="color: black;">&#41;</span>,c.<span style="color: black;">siVerbose</span><span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> dynCbCommand_Init<span style="color: black;">&#40;</span> in_ctxt <span style="color: black;">&#41;</span>:
	oCmd = in_ctxt.<span style="color: black;">Source</span>
	oCmd.<span style="color: black;">Description</span> = <span style="color: #483d8b;">&quot;&quot;</span>
	oCmd.<span style="color: black;">ReturnValue</span> = <span style="color: #008000;">True</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
&nbsp;
@dynamic
<span style="color: #ff7700;font-weight:bold;">def</span> dynCbCommand_Execute<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
	Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'In original callback!'</span><span style="color: black;">&#41;</span>
	setCallback<span style="color: black;">&#40;</span>dynCbCommand_Execute, callbackTwo<span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> callbackTwo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
	Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'In %s!'</span> <span style="color: #66cc66;">%</span> callback<span style="color: black;">&#40;</span>dynCbCommand_Execute<span style="color: black;">&#41;</span>.<span style="color: black;">func_name</span><span style="color: black;">&#41;</span>
	setCallback<span style="color: black;">&#40;</span>dynCbCommand_Execute, callbackThree<span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>	
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> callbackThree<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
	Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'In callback three!'</span><span style="color: black;">&#41;</span>
	restoreCallback<span style="color: black;">&#40;</span><span style="color: #483d8b;">'dynCbCommand_Execute'</span><span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>	
&nbsp;
setCallback<span style="color: black;">&#40;</span>dynCbCommand_Execute, callbackTwo<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>The first time you run this command it will run the code in <code>callbackTwo</code> and switch itself to new code. The next time it will run <code>callbackThree</code> and the next time it will run the original callback code. It will then, with each consecutive invocation of the command, continue on in this loop. This plugin, as you have guessed, is just a demo of the dynamic callbacks.</p>
<p>The lines you have to concentrate on are the following:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> vg.<span style="color: black;">xsi</span>.<span style="color: black;">dynamic</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #66cc66;">*</span></pre></div></div>

<p>and</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@dynamic</pre></div></div>

<p>These are the lines that provide the core of the functionality which is imported from the <code>vg.xsi.dynamic</code> module. Here it is&#8230;</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
&nbsp;
__all__ = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'dynamic'</span>, <span style="color: #483d8b;">'setCallback'</span>, <span style="color: #483d8b;">'callback'</span>, <span style="color: #483d8b;">'restoreCallback'</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Wrapper<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, cb<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.__defaultCb = cb
        <span style="color: #008000;">self</span>.__cb = cb
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> call<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kwargs<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.__cb<span style="color: black;">&#40;</span><span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kwargs<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> setCallback<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, newCb<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.__cb = newCb
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> callback<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.__cb
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> restoreCallback<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.__cb = <span style="color: #008000;">self</span>.__defaultCb
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> dynamic<span style="color: black;">&#40;</span>func<span style="color: black;">&#41;</span>:
    cbWrap = Wrapper<span style="color: black;">&#40;</span>func<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> cbWrap.<span style="color: black;">call</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> setCallback<span style="color: black;">&#40;</span>bound, newCb<span style="color: black;">&#41;</span>:
    bound = _getCallable<span style="color: black;">&#40;</span>bound<span style="color: black;">&#41;</span>
    newCb = _getCallable<span style="color: black;">&#40;</span>newCb<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> bound.<span style="color: black;">im_self</span>.<span style="color: black;">setCallback</span><span style="color: black;">&#40;</span>newCb<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> callback<span style="color: black;">&#40;</span>bound<span style="color: black;">&#41;</span>:
    bound = _getCallable<span style="color: black;">&#40;</span>bound<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> bound.<span style="color: black;">im_self</span>.<span style="color: black;">callback</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> restoreCallback<span style="color: black;">&#40;</span>bound<span style="color: black;">&#41;</span>:
    bound = _getCallable<span style="color: black;">&#40;</span>bound<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> bound.<span style="color: black;">im_self</span>.<span style="color: black;">restoreCallback</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> _getCallable<span style="color: black;">&#40;</span>req<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">callable</span><span style="color: black;">&#40;</span>req<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> req
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">type</span><span style="color: black;">&#40;</span>req<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">str</span>:
        g = <span style="color: #dc143c;">sys</span>._getframe<span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>.<span style="color: black;">f_globals</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> g<span style="color: black;">&#91;</span>req<span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>I&#8217;ve removed any comments or docstrings for the sake of brevity.</p>
<p><strong>About Decorators</strong></p>
<p>The import line I said was of particular interest. Well it is but only in the sense that it will allow you to use the module in which the features are implemented&#8230; Moving on quickly.</p>
<p>The real fun is with the <code>@dynamic</code> decorator. What is a decorator and what happens when we use one (or this one in particular)? Before we look at what a decorator is and does, let&#8217;s look at what happens when we define a function.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> func<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></td></tr></table></div>

<p>When a function is defined, what is happening is that an identifier is created in your current scope and this identifier is given the name of your function. Now let&#8217;s add the decorator. A decorator is just a simple function, but a function who&#8217;s purpose is very specific and a function that must return a callable (another function most of the time).</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">@myDecorator
<span style="color: #ff7700;font-weight:bold;">def</span> func<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></td></tr></table></div>

<p>What happens here is that the identifier <code>func</code> is still created but what lies &#8216;in&#8217; this identifier is not the function itself but the result of passing said function to <code>myDecorator</code>. To help you visualize, the above code could just as easily been written like so:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> func<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
func = myDecorator<span style="color: black;">&#40;</span>func<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><strong>Let&#8217;s Get Specific</strong></p>
<p>The decorator <code>@dynamic</code> takes your callback and wraps it inside a <code>Wrapper</code> object and returns the <code>call</code> method of this new object. When XSI tries to call what it thinks is just a simple callback it is actually calling the <code>call</code> method of an object that in turn calls your original callback. Whew!</p>
<p>This means that you now have the liberty of querying and editing the <em>callback object</em> and telling it to call something different depending on context. This functionality is provided by the <code>setCallback</code>, <code>callback</code> and <code>restoreCallback</code> functions.</p>
<p>These three functions either take the callback function objects or the callback&#8217;s names (as a string) as arguments.</p>
<p>There are some more particularities to this code such as the use of <code>sys._getframe</code>, the <code>im_self</code> attribute or the use of the <code>call</code> method on the <code>Wrapper</code> object instead of implementing <code>__call__</code> but I think they might be outside the scope of this article. If there are requests for it I can expand in the comments or another post.</p>
<p>Meanwhile you can <a href='http://www.xsi-blog.com/userContent/upload/2008/06/dynamiccallbacks.zip'>download the example</a>. Drop the plugin into a plugin folder (user, factory or workgroup) and drop the <em>vg</em> folder somewhere in your Python path.</p>
<p>Cheers!</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=265&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/265/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>A Shortcut For Your Shortcuts</title>
		<link>http://www.softimageblog.com/archives/256#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=a-shortcut-for-your-shortcuts</link>
		<comments>http://www.softimageblog.com/archives/256#comments</comments>
		<pubDate>Thu, 03 Apr 2008 02:56:02 +0000</pubDate>
		<dc:creator>Patrick Boucher</dc:creator>
				<category><![CDATA[Bablings and Ramblings]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=256</guid>
		<description><![CDATA[When working with XSI in a facility I often use Python modules to better package my code and allow easy reuse of key parts in the different tools that are developed. These modules I usually keep in a location on the main drive of the workstations, for example: C:\&#60;facilityName&#62;\libs\python.
I&#8217;ll also put in place a system [...]]]></description>
			<content:encoded><![CDATA[<p>When working with XSI in a facility I often use Python modules to better package my code and allow easy reuse of key parts in the different tools that are developed. These modules I usually keep in a location on the main drive of the workstations, for example: C:\&lt;facilityName&gt;\libs\python.</p>
<p>I&#8217;ll also put in place a system to push central library changes to the local computers and insert the library location in the PYTHONPATH environment variable.</p>
<p>The <a href="http://softimage.wiki.avid.com">XSI Wiki</a> has a great page on the pros and cons of <a href="http://softimage.wiki.avid.com/index.php/Python_Custom_Modules_(XSISDK)">the module approach</a>. One of the cons is that the <code>Application</code> global variable is only accessible in your script files and plugin files and not in your modules.</p>
<p><span id="more-256"></span>In the past I would put the following code at the top of my various modules:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants <span style="color: #ff7700;font-weight:bold;">as</span> c
xsi = win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'XSI.Application'</span><span style="color: black;">&#41;</span>.<span style="color: black;">Application</span>
log = xsi.<span style="color: black;">Logmessage</span></pre></td></tr></table></div>

<p>Following this would be a bunch of other shortcuts and dispatch commands for XSIUtils, XSIUIToolkit, XSIMath, etc&#8230;</p>
<p>Being tired of inserting these lines at the header of every module and a subset of this code (for shortcuts) at every script or plugin, I decided to centralize them and move them to their own module.</p>
<p>The library structure looks like so:</p>
<pre>../libpath/
    vg/
        xsi/
            __init__.py
            xsiModule1.py
            xsiModule2.py
            [...]</pre>
<p>In the <code>__init__.py</code> file I put the following code:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants <span style="color: #ff7700;font-weight:bold;">as</span> c
xsi = win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'XSI.Application'</span><span style="color: black;">&#41;</span>.<span style="color: black;">Application</span>
log = xsi.<span style="color: black;">Logmessage</span>
<span style="color: #808080; font-style: italic;"># [... more shortcuts and dispatch commands ...]</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> getShortcuts<span style="color: black;">&#40;</span>g<span style="color: black;">&#41;</span>:
    g<span style="color: black;">&#91;</span><span style="color: #483d8b;">'win32com'</span><span style="color: black;">&#93;</span> = win32com
    g<span style="color: black;">&#91;</span><span style="color: #483d8b;">'c'</span><span style="color: black;">&#93;</span> = c
    g<span style="color: black;">&#91;</span><span style="color: #483d8b;">'xsi'</span><span style="color: black;">&#93;</span> = xsi
    g<span style="color: black;">&#91;</span><span style="color: #483d8b;">'log'</span><span style="color: black;">&#93;</span> = log
    <span style="color: #808080; font-style: italic;"># [... more shortcuts and dispatch commands scope transfers...]</span></pre></td></tr></table></div>

<p>This allows me to benefit from the shortcuts in the rest of the <code>__init__.py</code> file where some general XSI functions are kept but also start my other modules, scripts and plugins with the following two lines:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> vg.<span style="color: black;">xsi</span> <span style="color: #ff7700;font-weight:bold;">as</span> vx
vx.<span style="color: black;">getShortcuts</span><span style="color: black;">&#40;</span><span style="color: #008000;">globals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>By providing the <code>getShortcuts</code> function with a dict representing the global scope of my module, script or plugin, the function can edit the members of the scope and give me all the necessary symbols.</p>
<p>Another stupid trick rears it&#8217;s head! ;)<br />
Happy scripting!</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=256&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/256/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Steven Caron, OBJ Files, Sexy Bits and Waste</title>
		<link>http://www.softimageblog.com/archives/249#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=steven-caron-obj-files-sexy-bits-and-waste</link>
		<comments>http://www.softimageblog.com/archives/249#comments</comments>
		<pubDate>Sat, 01 Mar 2008 20:25:45 +0000</pubDate>
		<dc:creator>Patrick Boucher</dc:creator>
				<category><![CDATA[Bablings and Ramblings]]></category>
		<category><![CDATA[Modeling]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/249</guid>
		<description><![CDATA[What do all these things have in common?
The weird wirings in my brain. That&#8217;s what.
Back to the beginning
At the end of last October Steven Caron sent me a plugin he wrote that allows an XSI user to drag and drop .obj files into the interface and have them import automatically according to settings in a [...]]]></description>
			<content:encoded><![CDATA[<p>What do all these things have in common?</p>
<p>The weird wirings in my brain. That&#8217;s what.</p>
<p><strong>Back to the beginning</strong></p>
<p>At the end of last October Steven Caron sent me a <a href="http://www.xsi-blog.com/userContent/scaron/ObjDnD.xsiaddon">plugin</a> he wrote that allows an XSI user to drag and drop .obj files into the interface and have them import automatically according to settings in a custom preference. I was a bit swamped at the time putting up the infrastructure for a new VFX department at my new workplace. And then I totally forgot about it&#8230; Sorry Steven.</p>
<p>This past week Steven politely reminded me of my omission so I ran back to my email archives and installed it. Neat piece of work.</p>
<p>I didn&#8217;t want to just post up the tool as I don&#8217;t really see this blog being about tool distribution but about how said tools work and the neat tricks they use. With that philosophy in mind I cracked open Steven&#8217;s tool to dig out some of its sexier bits and maybe demystify them for the audience.</p>
<p>To my surprise there weren&#8217;t any sexy bits. Please don&#8217;t get me wrong, the tool works superbly, how much more intuitive can you get than drag and drop. The tool is also extremely well written, concise and straightforward. There just aren&#8217;t any weird tricks or convoluted syntax or things that would generally have you scratching your head.</p>
<p>Then I noticed this in his version trapping code (yes folks, the drag and drop event is new in XSI 6.5):</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>27
28
29
30
31
32
33
34
35
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">int</span><span style="color: black;">&#40;</span> tXSIVersion<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> <span style="color: black;">&#41;</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">6</span>:
    xsiPrint<span style="color: black;">&#40;</span> xsi.<span style="color: black;">Version</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">&quot; doesn't support the 'siOnDragAndDrop' event!&quot;</span> <span style="color: black;">&#41;</span>
    xsi.<span style="color: black;">UnloadPlugin</span><span style="color: black;">&#40;</span> in_reg.<span style="color: black;">name</span>, <span style="color: #008000;">True</span> <span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">int</span><span style="color: black;">&#40;</span> tXSIVersion<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: black;">&#41;</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">5</span>:
        xsiPrint<span style="color: black;">&#40;</span> xsi.<span style="color: black;">Version</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">&quot; doesn't support the 'siOnDragAndDrop' event!&quot;</span> <span style="color: black;">&#41;</span>
        xsi.<span style="color: black;">UnloadPlugin</span><span style="color: black;">&#40;</span> in_reg.<span style="color: black;">name</span>, <span style="color: #008000;">True</span> <span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span></pre></td></tr></table></div>

<p><span id="more-249"></span><b>About the waste part</b></p>
<p>Bare with me&#8230;</p>
<p>Last week I was reading a really neat <a href="http://www.wired.com/techbiz/it/magazine/16-03/ff_free">article</a> in <a href="http://www.wired.com">Wired</a> by Chris Anderson entitled <i>&#8220;Free! Why $0.00 Is the Future of Business&#8221;</i>. I invite you to read it, especially the section beginning on page two: <i>Waste and Waste Again</i>.</p>
<p>&#8230;So if in the seventies you had to be really tight with your algorithms and optimizations because cpu cycles were so scarce, today I guess we can really waste them. Just the other day I setup an 8 core MacPro with BootCamp, XP x64 and XSI. Whatever I did to the machine, I swear, I could hear it yawn because it was bored.</p>
<p>Which brings me back to Steven&#8217;s code. Could it have been written like so:</p>
</pre>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>27
28
29
30
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">int</span><span style="color: black;">&#40;</span> tXSIVersion<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> <span style="color: black;">&#41;</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">6</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #008000;">int</span><span style="color: black;">&#40;</span> tXSIVersion<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: black;">&#41;</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">5</span>:
    xsiPrint<span style="color: black;">&#40;</span> xsi.<span style="color: black;">Version</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">&quot; doesn't support the 'siOnDragAndDrop' event!&quot;</span> <span style="color: black;">&#41;</span>
    xsi.<span style="color: black;">UnloadPlugin</span><span style="color: black;">&#40;</span> in_reg.<span style="color: black;">name</span>, <span style="color: #008000;">True</span> <span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span></pre></td></tr></table></div>

<p>Is it more or less readable by a programmer?<br />
It probably executes in a few cycles less but is it worth it?<br />
Python is interpreted so if the parser goes through this version quicker, is it worty of any mention?<br />
Is it more maintainable?</p>
<p>This example is really simple but in a bigger project, or with more complex cases, should we really be worrying if today transistors and cpu cycles are so cheap as to not even matter anymore</p>
<p>When I'm stuck on a piece of code or when I hit that time of the afternoon where most of my energy is diverted to digesting, I'll often reread my code and tighten it, remove redundancy, put in functions instead of copying a few lines in two locations, consolidate <i>if</i> statements, etc... One of my colleagues used to tell me that <i>"Premature optimization is the root of all evil."</i></p>
<p>Honestly, if I look back at a good proportion of the code I have written, it either executes in a blink of an eye on today's computers or it sits there waiting 90% of the time for user interaction. Should we, as technical XSI users, XSI scripters or TDs, even worry about optimization from a performance standpoint or a maintainability standpoint? What can be considered a good optimization and what should be considered a bad one?</p>
<p>And now that you're really wondering what I'm rambling about, I'll shut up and let you <a href="http://www.xsi-blog.com/userContent/scaron/ObjDnD.xsiaddon">download Steven's addon</a>.</p>
<p>Have a good weekend.</pre>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=249&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/249/feed</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Scripting shaders</title>
		<link>http://www.softimageblog.com/archives/245#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=scripting-shaders</link>
		<comments>http://www.softimageblog.com/archives/245#comments</comments>
		<pubDate>Fri, 10 Aug 2007 20:43:56 +0000</pubDate>
		<dc:creator>Bernard Lebel</dc:creator>
				<category><![CDATA[Programming / Scripting]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Rendering]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/245</guid>
		<description><![CDATA[ABSTRACT
This article is sort of a tutorial-cookbook for scripting beginners. It discusses quick and simple techniques to script minor tasks related to shaders. We will try to focus more on Object Model techniques than Command Model ones. Please feel free to contribute to it if you know better!
SHADERS
Shader name spaces
Shaders live in name spaces. In [...]]]></description>
			<content:encoded><![CDATA[<p><strong>ABSTRACT</strong></p>
<p>This article is sort of a tutorial-cookbook for scripting beginners. It discusses quick and simple techniques to script minor tasks related to shaders. We will try to focus more on Object Model techniques than Command Model ones. Please feel free to contribute to it if you know better!</p>
<p><span id="more-245"></span><strong>SHADERS</strong></p>
<p><strong>Shader name spaces</strong><br />
Shaders live in name spaces. In a single name space, no two shaders can have the same name. A bit like objects under models. There are several name spaces available. The material, the override, the camera, the light and the pass are all name spaces. Some name spaces are easier to deal with than others, like materials and lights. We could probably group name spaces in two larger categories: the material type (materials, lights and overrides), and the stack type (passes and cameras). The material type is always easy to handle (well, overrides have some particularities, but we shall address them later). Stack types on the other hand a somewhat problematic, as the way they expose shaders differently depending on the context.</p>
<p>One more name space deserves few lines. This is the TransientObjectContainer. This space is an invisible location where unconnected pieces of data live, like unconnected shaders. Notice that when you create a new shader in the Render Tree and open its property page right away, it&#8217;s name is prefixed with &#8220;TransientObjectContainer&#8221;. As soon as you connect this shader, it is now prefixed with the material name (you may need to re-open the ppg to see the change). When a shader is disconnected from all inputs, it returns to the TransientObjectContainer space. If you close the Render Tree, the shader is destroyed.</p>
<p><a href='http://www.xsi-blog.com/userContent/upload/2007/08/sshaders_transientoc.jpg' title='The TransientObjectContainer Namespace'><img src='http://www.xsi-blog.com/userContent/upload/2007/08/sshaders_transientoc.thumbnail.jpg' alt='The TransientObjectContainer Namespace' /></a></p>
<p>One advantage of name spaces is that the shader name is garanteed to be unique within that name space. So if you know the name space of a shader, you can retreive it anyway you like and be sure it&#8217;s the right shader. When I talk about the name of a shader, I mean name as in the &#8220;Name&#8221; property.</p>
<p><strong>Shader fullnames</strong><br />
Dealing with the shader fullname (as in the &#8220;FullName&#8221; property) is somewhat problematic. Only a handful of shaders have a &#8220;unique&#8221; fullname, because the fullname of a shader depends entirely from where it is read. In other word, most shaders have multiple fullnames, which are potentially all valid. A shader fullname is like a path in the render tree to that shader. It includes the name of all shaders traversed to reach this shader.</p>
<p>Let&#8217;s do a quick exercice.<br />
Create an object. Create a Phong material for this object. Open the render tree for that material. Disconnect the Phong shader from the Shadow input of the material node.<br />
Create a cell shader. Plug it in the diffuse input of the Phong shader, and in the Photon input of the material node.</p>
<p>Then run this code, which prints the name of shader inputs in a render tree:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> printShaderSources<span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop parameters of the shader</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oParameter <span style="color: #ff7700;font-weight:bold;">in</span> oShader.<span style="color: black;">parameters</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if the parameter has a source</span>
		oSource = oParameter.<span style="color: black;">source</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oSource <span style="color: #66cc66;">!</span>= <span style="color: #008000;">None</span>:
&nbsp;
			<span style="color: #808080; font-style: italic;"># Check if classname of the source is a shader</span>
			sClassName = xsi.<span style="color: black;">classname</span><span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> sClassName == <span style="color: #483d8b;">'Shader'</span> <span style="color: #ff7700;font-weight:bold;">or</span> sClassName == <span style="color: #483d8b;">'Texture'</span>:
&nbsp;
				xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oSource.<span style="color: black;">fullname</span> <span style="color: black;">&#41;</span>
&nbsp;
				<span style="color: #808080; font-style: italic;"># Parameter has a shader source,</span>
				<span style="color: #808080; font-style: italic;"># call function again with that source</span>
				printShaderSources<span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Call the function using the selected object's material</span>
printShaderSources<span style="color: black;">&#40;</span> xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">material</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>This should print:</p>
<pre># INFO : sphere.Material.Phong
# INFO : sphere.Material.Phong.Cell
# INFO : sphere.Material.Cell</pre>
<p>At line 16, we print the fullname of the shader currently visited. You can see in the output that we got two different values: sphere.Material.Phong.Cell and sphere.Material.Cell. They are both valid and could be used to retrieve the shader using a string. The first is the result of accessing the shader through the Phong shader, while the second one is because we read it from the material node.</p>
<p>The shaders that have a unique fullname are the material, the override, the light, and basically the root shaders in pass and camera shader stacks.</p>
<p><strong>Collecting shaders the hard way: recursive traversal of the render tree</strong><br />
Aka &#8220;walking&#8221; the render tree, traversing the render tree is a very common task. There are several ways to do it, it depends on what you want to do.</p>
<p>In &#8220;Shader fullnames&#8221;, we&#8217;ve seen one way of doing it. This way is not clever at all. Shaders will be evaluated as many times as the number of inputs they plug in. In complex render trees, this means lots of cpu cycles can be lost. Worse, if you&#8217;re doing things like shader replacement, you may end up replacing the same shader more than once.</p>
<p>The trick is to remember which shader was visited. For the following examples, we&#8217;ll use a similar test scene: create a Phong material, plug a Cell shader in in its diffuse input and in the shadow input of the material. But don&#8217;t disconnect anything.</p>
<p>If you traverse the render tree without checking if a shader has been visited, you&#8217;ll get the same shader printed sevaral times:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> printShaderSources<span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop parameters of the shader</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oParameter <span style="color: #ff7700;font-weight:bold;">in</span> oShader.<span style="color: black;">parameters</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if the parameter has a source</span>
		oSource = oParameter.<span style="color: black;">source</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oSource <span style="color: #66cc66;">!</span>= <span style="color: #008000;">None</span>:
&nbsp;
			<span style="color: #808080; font-style: italic;"># Check if classname of the source is a shader</span>
			sClassName = xsi.<span style="color: black;">classname</span><span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> sClassName == <span style="color: #483d8b;">'Shader'</span> <span style="color: #ff7700;font-weight:bold;">or</span> sClassName == <span style="color: #483d8b;">'Texture'</span>:
&nbsp;
				xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oSource.<span style="color: black;">name</span> <span style="color: black;">&#41;</span>
&nbsp;
				<span style="color: #808080; font-style: italic;"># Parameter has a shader source,</span>
				<span style="color: #808080; font-style: italic;"># call function again with that source</span>
				printShaderSources<span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Call the function using the selected object's material</span>
printShaderSources<span style="color: black;">&#40;</span> xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">material</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Output:</p>
<pre># INFO : Phong
# INFO : Cell
# INFO : Phong
# INFO : Cell
# INFO : Cell</pre>
<p>The simplest way to prevent this is to have a list of all shader names. Each time you visit a shader, check if its name is in the list, if not, evaluate its parameters.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> printShaderSources<span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop parameters of the shader</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oParameter <span style="color: #ff7700;font-weight:bold;">in</span> oShader.<span style="color: black;">parameters</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if the parameter has a source</span>
		oSource = oParameter.<span style="color: black;">source</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oSource <span style="color: #66cc66;">!</span>= <span style="color: #008000;">None</span>:
&nbsp;
			<span style="color: #808080; font-style: italic;"># Check if classname of the source is a shader</span>
			sClassName = xsi.<span style="color: black;">classname</span><span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> sClassName == <span style="color: #483d8b;">'Shader'</span> <span style="color: #ff7700;font-weight:bold;">or</span> sClassName == <span style="color: #483d8b;">'Texture'</span>:
&nbsp;
				<span style="color: #808080; font-style: italic;"># Check if shader was already visited</span>
				<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> oSource.<span style="color: black;">name</span> <span style="color: #ff7700;font-weight:bold;">in</span> aShaderNames:
&nbsp;
					xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oSource.<span style="color: black;">name</span> <span style="color: black;">&#41;</span>
&nbsp;
					aShaderNames.<span style="color: black;">append</span><span style="color: black;">&#40;</span> oSource.<span style="color: black;">name</span> <span style="color: black;">&#41;</span>
&nbsp;
					<span style="color: #808080; font-style: italic;"># Parameter has a shader source,</span>
					<span style="color: #808080; font-style: italic;"># call function again with that source</span>
					printShaderSources<span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
aShaderNames = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Call the function using the selected object's material</span>
printShaderSources<span style="color: black;">&#40;</span> xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">material</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Output:</p>
<pre># INFO : Phong
# INFO : Cell</pre>
<p>This approach has one big problem. There is one big exception to the name space rule: in a material, one shader can have the same name as the material. So if you collected the material name before all else and another shader has the same name, it will be skipped. You can deal with this in a plenty of ways, but they all come down to what to do if a name already exists. My suggestion is to simply rename the shader if it&#8217;s not of the material type. Append a 1 to its name. So all names are garanteed to be unique.</p>
<p>A very different approach to render tree traversal is to store all shaders in a collection that has the Unique flag enabled. You don&#8217;t even have to check if the name exists.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
xsi = Application
xsifactory = win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Factory'</span> <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> getShaderSources<span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Add shader right away to the collection</span>
	oShaders.<span style="color: black;">add</span><span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop parameters of the shader</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oParameter <span style="color: #ff7700;font-weight:bold;">in</span> oShader.<span style="color: black;">parameters</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if the parameter has a source</span>
		oSource = oParameter.<span style="color: black;">source</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oSource <span style="color: #66cc66;">!</span>= <span style="color: #008000;">None</span>:
&nbsp;
			<span style="color: #808080; font-style: italic;"># Check if classname of the source is a shader</span>
			sClassName = xsi.<span style="color: black;">classname</span><span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> sClassName == <span style="color: #483d8b;">'Shader'</span> <span style="color: #ff7700;font-weight:bold;">or</span> sClassName == <span style="color: #483d8b;">'Texture'</span>:
&nbsp;
				<span style="color: #808080; font-style: italic;"># Parameter has a shader source,</span>
				<span style="color: #808080; font-style: italic;"># call function again with that source</span>
				getShaderSources<span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Create collection to store shaders of a material</span>
oShaders = xsifactory.<span style="color: black;">createobject</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Collection'</span> <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Enable the Unique flag of the collection,</span>
<span style="color: #808080; font-style: italic;"># which means no shader can be stored twice</span>
oShaders.<span style="color: black;">unique</span> = <span style="color: #008000;">True</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Call the function using the selected object's material</span>
getShaderSources<span style="color: black;">&#40;</span> xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">material</span> <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Finally, print all shader names</span>
<span style="color: #ff7700;font-weight:bold;">for</span> oShader <span style="color: #ff7700;font-weight:bold;">in</span> oShaders:
	xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oShader.<span style="color: black;">name</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Output:</p>
<pre># INFO : Material
# INFO : Phong
# INFO : Cell</pre>
<p><strong>Collecting shaders the easy way: FindShaders()</strong><br />
Materials, lights and cameras implemente the FindShaders() method. It allows you to collect all shaders as a flat collection and then get them by name or index, or you can loop over the collection items.</p>
<p>The example in the FindShaders (Material) entry of the documentation is pretty clear on this matter. This method is an alternative to recursively traverse the render tree. I have not tested performance, though.</p>
<p><strong>Storing a material&#8217;s render tree into a data structure</strong><br />
This is an expansion of the previous topic. If you ever have to do complex shader replacement, render tree export, or render tree reconstruction, storing the render tree in your own data structure will be essential. In this structure you will store the shaders, as well as the connections that exists between them. The following techniques I propose are based on my experience in this area, it is by no mean an absolute way of doing things.</p>
<p>First, each shader must be uniquely identified. That can be done using their name, although you must make sure that there is no name collision with the material. Personally I prefer to use an integer. The reason is that if you ever have to rename a shader, you may end up having to update the structure to reflect the name change. With an integer you don&#8217;t bother about this at all. For the remainder of this topic, I&#8217;ll use an integer as the master key for shaders. I&#8217;ll refer to this master key as the shader ID.</p>
<p>Using that ID, you&#8217;ll be able to store masses of information. You want to record the shader name, the shader object, the parameters that have a shader input, and the ID of the input shaders. The map will look like this:</p>
<pre>dShadersByID
{
	id (integer) :
	{
		'name' : shader name (string),
		'object' : shader object (instance),
		'parameters' (dictionary) :
		{
			'parameter name' : shader id,
			'parameter name' : shader id,
			...
		}
	},
	id (integer) :
	{
		'name'  : shader name (string),
		'object' : shader object (instance),
		'parameters' (dictionary) :
		{
			'parameter name' : shader id,
			'parameter name' : shader id,
			...
		}
	},
	...
}</pre>
<p>Let&#8217;s talk about how to populate this structure. Aside from the actual main structure, I&#8217;d have another map, where the the shader name is the key, and its ID is the value. It will be a lot easier to retreive the ID of an existing shader when one is encountered.</p>
<p>When a new shader is visited, each parameter is tested for the presence of a shader source. If one is found, it must be checked if the source has been evaluated already, if so, retreive its ID, and write for that parameter. Otherwise, visit that new shader.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
c = win32com.<span style="color: black;">client</span>.<span style="color: black;">constants</span>
xsi = Application
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> getShaderSources<span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>:
&nbsp;
	<span style="color: #483d8b;">&quot;&quot;&quot;
	We assume that when this function is called,
	the passed shader has never been visited.
&nbsp;
	The function doesn't know if the passed
	shader is just a shader or the material.
	&quot;&quot;&quot;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Get new ID for this shader</span>
	<span style="color: #ff7700;font-weight:bold;">global</span> iID
	iID += <span style="color: #ff4500;">1</span>
	iShaderID = iID
&nbsp;
	<span style="color: #808080; font-style: italic;"># Map the ID to the shader name</span>
	dShadersByName<span style="color: black;">&#91;</span>oShader.<span style="color: black;">name</span><span style="color: black;">&#93;</span> = iID
&nbsp;
	<span style="color: #808080; font-style: italic;"># Store this shader in the main shader structure</span>
	dShadersByID<span style="color: black;">&#91;</span>iID<span style="color: black;">&#93;</span> = <span style="color: black;">&#123;</span>
		<span style="color: #483d8b;">'name'</span>: oShader.<span style="color: black;">name</span>,
		<span style="color: #483d8b;">'object'</span>: oShader,
		<span style="color: #483d8b;">'parameters'</span>: <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
	<span style="color: black;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop parameters of the shader</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oParameter <span style="color: #ff7700;font-weight:bold;">in</span> oShader.<span style="color: black;">parameters</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if the parameter has a source</span>
		oSource = oParameter.<span style="color: black;">source</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oSource <span style="color: #66cc66;">!</span>= <span style="color: #008000;">None</span>:
&nbsp;
			<span style="color: #808080; font-style: italic;"># Check if classname of the source is a shader</span>
			sClassName = xsi.<span style="color: black;">classname</span><span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> sClassName == <span style="color: #483d8b;">'Shader'</span> <span style="color: #ff7700;font-weight:bold;">or</span> sClassName == <span style="color: #483d8b;">'Texture'</span>:
&nbsp;
				<span style="color: #808080; font-style: italic;"># Check if the source is the material and has the same name as the material</span>
				<span style="color: #ff7700;font-weight:bold;">if</span> oSource.<span style="color: #008000;">type</span> <span style="color: #66cc66;">!</span>= c.<span style="color: black;">siMaterialType</span> <span style="color: #ff7700;font-weight:bold;">and</span> oSource.<span style="color: black;">name</span> == oMAT.<span style="color: black;">name</span>:
&nbsp;
					<span style="color: #808080; font-style: italic;"># Rename source</span>
					oSource.<span style="color: black;">name</span> = <span style="color: #483d8b;">'%s1'</span> <span style="color: #66cc66;">%</span> oSource.<span style="color: black;">name</span>
&nbsp;
				<span style="color: #808080; font-style: italic;"># Check if that source shader has already been visited</span>
				<span style="color: #ff7700;font-weight:bold;">if</span> oSource.<span style="color: black;">name</span> <span style="color: #ff7700;font-weight:bold;">in</span> dShadersByName:
&nbsp;
					<span style="color: #808080; font-style: italic;"># Get the source ID</span>
					iSourceID = dShadersByName<span style="color: black;">&#91;</span>oSource.<span style="color: black;">name</span><span style="color: black;">&#93;</span>
&nbsp;
					<span style="color: #808080; font-style: italic;"># Store the source ID for this parameter</span>
					dShadersByID<span style="color: black;">&#91;</span>iShaderID<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'parameters'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>oParameter.<span style="color: black;">scriptname</span><span style="color: black;">&#93;</span> = iSourceID
&nbsp;
				<span style="color: #ff7700;font-weight:bold;">else</span>:
					<span style="color: #808080; font-style: italic;"># Source shader has never been visited</span>
					<span style="color: #808080; font-style: italic;"># Visit it, and map its ID to the current parameter</span>
					dShadersByID<span style="color: black;">&#91;</span>iShaderID<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'parameters'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>oParameter.<span style="color: black;">scriptname</span><span style="color: black;">&#93;</span> = getShaderSources<span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Return the shader ID to the caller</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> iShaderID
&nbsp;
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># Main data structure</span>
dShadersByID = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># ID mapped to the shader name</span>
dShadersByName = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Starting ID</span>
iID = -<span style="color: #ff4500;">1</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Constants to indicate which shader is the material</span>
oMAT = xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">material</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Call the function using the selected object's material</span>
getShaderSources<span style="color: black;">&#40;</span> oMAT <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>To verify the dShadersByID structure, you can simply convert it to string and print it:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>dShadersByID<span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Using our example of the Cell shader, the output should be like this (I formatted it so it&#8217;s easier to read):</p>
<pre>{
	0:
	{
		'object': "COMObject "unknown""(3),
		'name': u'Material',
		'parameters':
		{
			u'shadow': 1,
			u'Photon': 2,
			u'surface': 1
		}
	},

	1:
	{
		'object': "COMObject "unknown""(3),
		'name': u'Phong',
		'parameters':
		{
			u'diffuse': 2
		}
	},

	2:
	{
		'object': "COMObject "unknown""(3),
		'name': u'Cell',
		'parameters': {}
	}
}</pre>
<p>So we got our map of the render tree. We can then use to navigate the render tree and do any operation we want without risking loosing relationships. For instance, if you wanted to replace the Phong shader, you know you have to look for ID 1. Once replaced, you can find in other shaders to which inputs is the phong connected, and perform the connections. You also know, looking at the phong entry, that ID 2 is connected into its diffuse parameter.</p>
<p>The next topics should help you tackle these operations.</p>
<p><strong>Creating a shader</strong><br />
You can create a &#8220;floating&#8221; shader, that is, a shader object not connected to anything, by using the CreateObjectFromPreset() command.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oShader = xsi.<span style="color: black;">createobjectfrompreset</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">&quot;shaders/material/blinn.preset&quot;</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>To find out what string you should use as the first parameter, just create the shader in the Render Tree. This will give you the path of the preset file, and this is all you need to create the shader.<br />
The command also accepts a second argument, for its name. Usually I don&#8217;t specify a name unless I&#8217;m creating trees that enforce the project standards.</p>
<p>The command returns the shader object. This object lives in the TransientObjectContainer, it&#8217;s not connected to anything. The cool thing is that contrary to unconnected shaders in the Render Tree, which are destroyed when the Render Tree is closed, TransientObjectContainer shaders created by script remain there as long as you don&#8217;t create a new scene or explicitely remove them. Remember however that because the shader is not connected, it may hard to retrieve if you destroy the variable that holds the command return value.</p>
<p><strong>Connecting a shader</strong><br />
Once you have a shader, you may connect it into a texturable parameter. You can use the SIConnectShaderToCnxPoint() command, but personnally I find the Connect() method of the parameter object to be faster and cleaner. All you need is to have the input parameter as an object.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oParameter.<span style="color: black;">connect</span><span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>What I usually do when building shader trees in script is to create all the shaders in one phase (using the CreateObjectFromPreset() command), and then perform all the connections in another phase.</p>
<p>If you are connecting the shader in a rather large amount of parameters, then perhaps using the SIConnectShaderToCnxPoint() command might be faster, as it allows you to pass a list of input parameters:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">sPhong = <span style="color: #483d8b;">'Sources.Materials.DefaultLib.Scene_Material.Phong'</span>
&nbsp;
xsi.<span style="color: black;">siconnectshadertocnxpoint</span><span style="color: black;">&#40;</span>
   oShader.<span style="color: black;">fullname</span>,
    <span style="color: #483d8b;">','</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span> <span style="color: black;">&#91;</span>
        <span style="color: #483d8b;">'%s.diffuse'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>sPhong,<span style="color: black;">&#41;</span>,
        <span style="color: #483d8b;">'%s.ambient'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>sPhong,<span style="color: black;">&#41;</span>,
        <span style="color: #483d8b;">'%s.specular'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>sPhong,<span style="color: black;">&#41;</span>
    <span style="color: black;">&#93;</span> <span style="color: black;">&#41;</span>,
    <span style="color: #008000;">False</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><strong>Disconnecting a shader</strong><br />
As with connecting shaders, there are two ways to disconnect shaders. The first one is the RemoveShaderFromCnxPoint() command, which works like SIConnectShaderToCnxPoint() command.</p>
<p>The other way is to use the Disconnect() method on the parameter you want to remove the connected shader from.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oParameter.<span style="color: black;">disconnect</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Now you have to be very careful: if you disconnect the shader and the shader is no longer connected to any parameter, it is destroyed!</p>
<p><strong>Getting the &#8220;sub-parameters&#8221; after a disconnection</strong><br />
If you have a compound parameter, like a color or a vector, and you disconnect a shader from it, you won&#8217;t be able to access its sub-parameters (like red or x) right away, for some reason. If you try to read or write to the red parameter, for example, you&#8217;ll raise an exception.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oDiffuse.<span style="color: black;">disconnect</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oDiffuse.<span style="color: black;">parameters</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'red'</span> <span style="color: black;">&#41;</span>.<span style="color: black;">value</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Output:</p>
<pre># ERROR : Traceback (most recent call last):
#   File "", line 14, in ?
#     xsi.logmessage( oDiffuse.parameters( 'red' ).value )
# AttributeError: 'NoneType' object has no attribute 'value'
#  - [line 14]</pre>
<p>The solution is to &#8220;rebind&#8221; the variable to the disconnected parameter.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oDiffuse.<span style="color: black;">disconnect</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Rebind parameter and variable</span>
oDiffuse = oDiffuse.<span style="color: black;">parent</span>.<span style="color: black;">parameters</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'diffuse'</span> <span style="color: black;">&#41;</span>
&nbsp;
xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oDiffuse.<span style="color: black;">parameters</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'red'</span> <span style="color: black;">&#41;</span>.<span style="color: black;">value</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Output:</p>
<pre># INFO : 0.699999988079071</pre>
<p>Thanks to François Painchaud for this one.</p>
<p><strong>Getting all shaders of a same kind</strong><br />
A common task is to change the value of a shader parameter globally. For example, you may want to enable elliptical filtering in all image shaders. Materials may have varying render trees, so using overrides would be prove very tedious.</p>
<p>To get all shader instances of a same kind, you can run the command FindObjects(). This is very fast and reliable.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oBlinns = xsi.<span style="color: black;">findobjects</span><span style="color: black;">&#40;</span> <span style="color: #008000;">None</span>, <span style="color: #483d8b;">'{8FAC63AC-E392-11D1-804C-00A0C906835D}'</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>This will return a XSICollection containing all shader instances as objects. The first argument is always null or None. The second argument is a shader ClassID</p>
<p><strong>Getting the ClassID of a shader</strong><br />
To know the ClassID of a shader, you can create it in the Render Tree, open its property page, right-click on the space next to the shader name, and choose Edit.</p>
<p><a href='http://www.xsi-blog.com/userContent/upload/2007/08/sshaders_classid.jpg' title='Shader classid'><img src='http://www.xsi-blog.com/userContent/upload/2007/08/sshaders_classid.thumbnail.jpg' alt='Shader classid' /></a></p>
<p>A text page will open either in the script editor or in a floating text editor. The ClassID is generally located at the third line, it starts with Reference.</p>
<pre>Reference = "{8FAC63AC-E392-11D1-804C-00A0C906835D}";</pre>
<p>If you already have the shader object in your script and want to find out its ClassID, you can use the GetIdentifier() method of the DataRepository object.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">sClassID = oRepository.<span style="color: black;">getidentifier</span><span style="color: black;">&#40;</span> oShader, c.<span style="color: black;">siObjectCLSID</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>To create a DataRepository instance in Python:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
xsiutils = win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Utils'</span> <span style="color: black;">&#41;</span>
oRepository = xsiutils.<span style="color: black;">datarepository</span></pre></td></tr></table></div>

<p><strong>Finding a specific kind of shader in a render tree</strong><br />
We&#8217;ve seen pretty much all means of getting and identifying shaders. But you might be looking for shaders of a specific type inside a shader name space. There are several ways to do that:</p>
<p>The first approach is to run FindObjects(). Since it works globally, you&#8217;ll have to loop each shader and check if its Root property matches the one we&#8217;re looking for. Remember that the Root property suffers from an important limitation that we hinted at in <strong>Shader fullnames</strong> and shall discuss more in <strong>PASSES</strong>.</p>
<p>The second approach is to use FindShaders() or recursive traversal of the render tree to collect shaders. Remember however that those last techniques also suffer from certain limitations. FindShaders() works only on materials, cameras and lights, and recursive traversal of the render tree (as it has been presented in this article so far) will work only on materials, lights and overrides. For traversal of other trees you&#8217;ll have to make important modifications to your code. More on that in <strong>PASSES</strong>.</p>
<p>No matter how you collect shaders, you must test their class id against the one you&#8217;re looking for.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
&nbsp;
c = win32com.<span style="color: black;">client</span>.<span style="color: black;">constants</span>
xsi = Application
xsiutils = win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Utils'</span> <span style="color: black;">&#41;</span>
oRepository = xsiutils.<span style="color: black;">datarepository</span>
&nbsp;
sPhongCLSID = <span style="color: #483d8b;">'{67120BBE-C98C-11D1-9723-00A0243E3672}'</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Get shaders of the selected material</span>
oShaders = xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">findshaders</span><span style="color: black;">&#40;</span> c.<span style="color: black;">siShaderFilter</span> <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Iterate shaders</span>
<span style="color: #ff7700;font-weight:bold;">for</span> oShader <span style="color: #ff7700;font-weight:bold;">in</span> oShaders:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Get current shader classid</span>
	sShaderCLSID = oRepository.<span style="color: black;">getidentifier</span><span style="color: black;">&#40;</span> oShader, c.<span style="color: black;">siObjectCLSID</span> <span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Compare classids</span>
	<span style="color: #ff7700;font-weight:bold;">if</span> sShaderCLSID == sPhongCLSID:
&nbsp;
		xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oShader.<span style="color: black;">fullname</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><strong>Dealing with texture projections, tangents, and similar shader parameters</strong><br />
Shader parameters that point to scene objects like texture projections, tangent properties, color at vertices, weight maps and the likes, deserve special attention. They are not dealt with using the typical means of evaluating and setting parameters, and may get you stuck easily. Let&#8217;s look at how to use each option.</p>
<p>The HasInstanceValue property allows you to know if you&#8217;re dealing with that kind of parameter. So if you use very generic code that handle any kind of parameter, that should be the first thing to check.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">if</span> oParameter.<span style="color: black;">hasinstancevalue</span>:
	<span style="color: #808080; font-style: italic;"># do something</span></pre></td></tr></table></div>

<p>The next thing to do is to find out to which scene object is this parameter pointing to. This is where it gets nasty. The SDK doesn&#8217;t provide any form of relationship between such parameters and the objects they point to. You have to tests every friggin object you suspect might be used!</p>
<p>What you can do, if you work with a material, is to use its UsedBy property get the objects using that material. Then, you have to find the appropriate cluster properties on those objects. Finally, you can test if those cluster properties are used by the parameter.</p>
<p>Let&#8217;s imagine we want to know the texture projections used by a shader parameter named oParameter.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Get objects using this material</span>
oUsers = oMat.<span style="color: black;">usedby</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Loop objects using this material</span>
<span style="color: #ff7700;font-weight:bold;">for</span> oUser <span style="color: #ff7700;font-weight:bold;">in</span> oUsers:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop its clusters</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oCluster <span style="color: #ff7700;font-weight:bold;">in</span> oUser.<span style="color: black;">activeprimitive</span>.<span style="color: black;">geometry</span>.<span style="color: black;">clusters</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if the cluster is a sample cluster</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oCluster.<span style="color: #008000;">type</span> == <span style="color: #483d8b;">'sample'</span>:
&nbsp;
			<span style="color: #808080; font-style: italic;"># Loop the cluster properties of the current cluster</span>
			<span style="color: #ff7700;font-weight:bold;">for</span> oClusterProp <span style="color: #ff7700;font-weight:bold;">in</span> oCluster.<span style="color: black;">localproperties</span>:
&nbsp;
				<span style="color: #808080; font-style: italic;"># Check if the cluster property is a texture projection</span>
				<span style="color: #ff7700;font-weight:bold;">if</span> oClusterProp.<span style="color: #008000;">type</span> == <span style="color: #483d8b;">'uvspace'</span>:
&nbsp;
					<span style="color: #808080; font-style: italic;"># Test if the parameter points to this object</span>
					sInstanceValue = oParameter.<span style="color: black;">getinstancevalue</span><span style="color: black;">&#40;</span> oProjection <span style="color: black;">&#41;</span>
&nbsp;
					<span style="color: #808080; font-style: italic;"># If returns empty string, it means it does NOT point to that object</span>
					<span style="color: #ff7700;font-weight:bold;">if</span> sInstanceValue <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">''</span>:
						<span style="color: #808080; font-style: italic;"># do something</span></pre></td></tr></table></div>

<p>Be careful. The GetInstanceValue() method has a significant limitation: it allows you to test against properties of X3DObjects only. If you pass objects like groups or partitions to the GetInstanceValue() method, it will fail. You may pass the members of those objects for the method to succeed.</p>
<p>Setting the parameter to point to cluster properties is done exactly the same way. You have to know in advance what cluster property to use, and then run the SetInstanceValue() method.</p>
<p>The last SDK doc entries worthy of mentioning are IsSupportedInstanceValue (which allows you find out what kind of cluster property does the parameter supports).</p>
<p><strong>MATERIAL LIBRARIES</strong></p>
<p>We can&#8217;t talk of materials without talking about material libraries. The two go together. Let&#8217;s look at few ways to deal with those.</p>
<p><strong>Getting all material libraries</strong><br />
The object model way:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oLibs = xsi.<span style="color: black;">activeproject</span>.<span style="color: black;">activescene</span>.<span style="color: black;">materiallibraries</span></pre></td></tr></table></div>

<p>The command model way:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oLibs = xsi.<span style="color: black;">findobjects</span><span style="color: black;">&#40;</span> <span style="color: #008000;">None</span>, <span style="color: #483d8b;">&quot;{61782E14-9177-412e-BF9B-2B44B9A668DD}&quot;</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Now be careful with the command model way. It may return an extra library that apparently lives in the TransientObjectContainer. If you run this:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
oLibs = xsi.<span style="color: black;">findobjects</span><span style="color: black;">&#40;</span> <span style="color: #008000;">None</span>, <span style="color: #483d8b;">&quot;{61782E14-9177-412e-BF9B-2B44B9A668DD}&quot;</span> <span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">for</span> oLib <span style="color: #ff7700;font-weight:bold;">in</span> oLibs:
	xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oLib.<span style="color: black;">fullname</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>It may print:</p>
<pre># INFO : Sources.Materials.DefaultLib
# INFO : library_source</pre>
<p>I suspect that the second one is an instance of the first, to be used as a pointer to the current library. But what do I know ;-)</p>
<p><strong>Getting a material&#8217;s library</strong><br />
If you need to get the library object from the material, simply use the Library property of the material object.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oLib = oMaterial.<span style="color: black;">library</span></pre></td></tr></table></div>

<p><strong>Looping materials from libraries</strong><br />
It&#8217;s very straightforward:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Loop over all matlibs</span>
<span style="color: #ff7700;font-weight:bold;">for</span> oLib <span style="color: #ff7700;font-weight:bold;">in</span> xsi.<span style="color: black;">activeproject</span>.<span style="color: black;">activescene</span>.<span style="color: black;">materiallibraries</span>:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop materials in the current library</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oMat <span style="color: #ff7700;font-weight:bold;">in</span> oLib.<span style="color: black;">items</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># do something</span></pre></td></tr></table></div>

<p><strong>MATERIALS</strong></p>
<p>The majority of the work done with shaders will probably be done through materials.</p>
<p><strong>Creating materials</strong><br />
As usual, there are several means for creating materials. If you want to create a material on an object, you can use the ApplyShader() command. This, unfortunately, doesn&#8217;t give you control the library the material is created in, it&#8217;s always the current library. Alternatively, you could use the AddMaterial() method. Since it&#8217;s the Object Model approach, it&#8217;s probably going to be faster, especially if the creation is done in a loop.</p>
<p>Personally, I prefer to create the material in a library, and then assign it to the target object. The SICreateMaterial() command allows you not only to specify the library, but also the objects that shall use this material. But it depends a lot on performance, I usually favor the fastest execution.</p>
<p><strong>Assigning materials</strong><br />
If you don&#8217;t use the SICreateMaterial() command, you could use SIAssignMaterial() (which is a low-level version of AssignMaterial). You could also use the CopyPaste() command (which is printed when you drag and drop a material onto an object, but personally I prefer more explicit approaches.</p>
<p><strong>Unassigning materials</strong><br />
There is no other way than to use the UnAssignMaterial() and SIUnAssignMaterial() commands.</p>
<p><strong>Getting the material of an object</strong><br />
It may seem very straightforward to do that, but there are a few things to talk about. The first and foremost way to get an object&#8217;s material is to use the Material property.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oMat = oObject.<span style="color: black;">material</span></pre></td></tr></table></div>

<p>This will work on X3DObjects, polygon clusters, groups, layers and partitions. Basically anything that supports material assignment. However, there is a chance the object it returns is not the one you&#8217;re looking for. The Material property returns the &#8220;current&#8221; material, or that is, the inherited material. If an object is in a group that has a material, the Material property will return the group material, not the object one.</p>
<p>If you&#8217;re looking to get the actual material of the object and ignore inheritance, you&#8217;ll have to go through the local properties of the object.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
c = win32com.<span style="color: black;">client</span>.<span style="color: black;">constants</span>
xsi = Application
&nbsp;
<span style="color: #ff7700;font-weight:bold;">for</span> oProp <span style="color: #ff7700;font-weight:bold;">in</span> xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">localproperties</span>:
	<span style="color: #ff7700;font-weight:bold;">if</span> oProp.<span style="color: #008000;">type</span> == c.<span style="color: black;">siMaterialType</span>:
		xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oProp.<span style="color: black;">fullname</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>The LocalProperties approach will also work on any object that supports material assignment.</p>
<p><strong>Getting the objects using a material</strong><br />
A material has the UsedBy property, which returns all objects using that material.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oMat.<span style="color: black;">UsedBy</span>.<span style="color: black;">getastext</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>The UsedBy property, however, has always suffered from severe limitations. In the past, it would return only X3DObjects. Now it also returns polygon clusters. However, it won&#8217;t return groups, layers and partitions using that material. Instead, it will print the X3DObjects and clusters that have inherited the material from the group/layer/partition.</p>
<p>If you want to find the groups/layers/partitions using that material, you&#8217;ll have to look the owners of the material. The first owner is always the material library. The other owners are the objects using this material. In the case of a material assigned to a group:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">oMat = xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">for</span> oOwner <span style="color: #ff7700;font-weight:bold;">in</span> oMat.<span style="color: black;">owners</span>:
	xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'%s - %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>oOwner.<span style="color: black;">fullname</span>, oOwner.<span style="color: #008000;">type</span><span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Output:</p>
<pre># INFO : Sources.Materials.MatLib - library_source
# INFO : Group - #Group</pre>
<p><strong>Deleting materials</strong><br />
To my knowledge, there is no other way to delete a material than to use the DeleteObj() command.</p>
<p><strong>Shotcuts to access material components</strong><br />
The object model offers a few shortcuts to retrieve data of a material. You should read the Material and Shader entries of the documentation to find out about those.</p>
<p><strong>PASSES</strong></p>
<p>Passes are special. They support shaders through their stacks. However, passes cannot be dealt like with like materials. Two situations come to mind, both being somewhat related.</p>
<p>In the first situation, if you take the script presented in &#8220;Storing the render tree into a data structure&#8221;, it will fail if you supply the pass object as the starting value for the function. Passes do not allow accessing shaders through its Parameters, unlike materials. So you have to do clever things to get stack shaders from the stack.</p>
<p>The second situation is the opposite: if you use the Root property of a shader to get to the, well, shader tree root, you&#8217;ll get the pass object. This is wrong, for the very same reason as the preceding examples, that is, passes do not expose shaders through their Parameters.</p>
<p>To make things worse, if you open the render tree with a pass selected, it looks like the pass is a materials and shaders plug directly into them. This is a lie. And to seal the deal, the render tree view shows only the first shader of each stack, even though the stacks support multiple shaders at the top level. Another lie. Fortunately, the Object Model helps solving those problems.</p>
<p><strong>Accessing shader stacks from passes</strong><br />
There is no shortcut to access a pass stack. You have lookup the pass&#8217;s NestedObjects, using the stack name. Once you get the stack object, you can check its NestedObjects to see if there are shaders. This will return a collection of the &#8220;top&#8221; shaders of the stack.</p>
<p>Create a pass with an environment shader, selected that pass, and run this:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
&nbsp;
aStackNames = <span style="color: black;">&#91;</span>
	<span style="color: #483d8b;">'Environment Shader Stack'</span>,
	<span style="color: #483d8b;">'Output Shader Stack'</span>,
	<span style="color: #483d8b;">'Volume Shader Stack'</span>
<span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Get selected pass</span>
oPass = xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Iterate pass stacks</span>
<span style="color: #ff7700;font-weight:bold;">for</span> sStackName <span style="color: #ff7700;font-weight:bold;">in</span> aStackNames:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Get the stack object</span>
	oStack = oPass.<span style="color: black;">nestedobjects</span><span style="color: black;">&#40;</span> sStackName <span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Check if the stack has shaders</span>
	<span style="color: #ff7700;font-weight:bold;">if</span> oStack.<span style="color: black;">nestedobjects</span>.<span style="color: black;">count</span> <span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">0</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Iterate root shaders of this stack</span>
		<span style="color: #ff7700;font-weight:bold;">for</span> oStackShader <span style="color: #ff7700;font-weight:bold;">in</span> oStack.<span style="color: black;">nestedobjects</span>:
&nbsp;
			xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oStackShader.<span style="color: black;">fullname</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Unfortunately, you cannot use reliable string searching. Select the Environment shader you created. It should print this line in the History Log:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">Application.<span style="color: black;">SelectObj</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Passes.Default_Pass.Environment&quot;</span>, <span style="color: #483d8b;">&quot;&quot;</span>, <span style="color: #483d8b;">&quot;&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Now, rename the shader to something already in use in that pass, like &#8220;Background_Objects_Partition&#8221;. Select the shader again. It should print this:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">Application.<span style="color: black;">SelectObj</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Passes.Default_Pass.EnvironmentShaderStack.Background_Objects_Partition&quot;</span>, <span style="color: #483d8b;">&quot;&quot;</span>, <span style="color: #483d8b;">&quot;&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>So now you got the stack name, probably to avoid name collisions in the pass name space. Because this behavior is not constant, how can you reliably use strings to find pass shaders? If Softimage reads this, I politely invite you to correct this by putting the stack name as at all times. That will save us, poor coders, lots of trouble.</p>
<p><strong>Getting the &#8220;root&#8221; shader from a shader</strong><br />
As previously mentioned, the Root property of a shader returns the pass object, which can be incorrect depending on how close to reality you want the information to be. To me this is very incorrect. In this case, the &#8220;root&#8221; shader is a shader visible in the stack. Because you can put multiple shaders in a stack, it means that there can be an infinite amount of root shaders for the the pass.</p>
<p>Walking down the render tree (down being &#8220;away from the root shader&#8221;) is easy, but walking up is not so. I don&#8217;t know any fast, clever, or elegant way of doing this. The only constant is that no two shaders can be named the same way in the pass name space(1).</p>
<p>For the following chunk of code, do this:</p>
<ul>
<li>Put an Environment shader on the default pass.</li>
<li>Open the render tree, and connect a Cell shader into the tex input of the Environment shader.</li>
<li>Then, create a _2D_background_color shader (output shader)</li>
<li>Create another Cell shader, and plug it in the bg input of the _2D_background_color shader(2). Normally, the second Cell shader should have been renamed Cell1.</li>
</ul>
<p>To find the root shader for a given shader, I&#8217;ve written a sort of state machine that remembers in which stack and root shader it is walking down. Run this code:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com
&nbsp;
c = win32com.<span style="color: black;">client</span>.<span style="color: black;">constants</span>
xsi = Application
xsifactory = win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Factory'</span> <span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> PassShaderState:
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span> <span style="color: #008000;">self</span>, sPassName, sShaderName <span style="color: black;">&#41;</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># CONSTANTS</span>
&nbsp;
		<span style="color: #808080; font-style: italic;"># Name of the shader we're looking for</span>
		<span style="color: #008000;">self</span>.<span style="color: black;">passname</span> = sPassName
		<span style="color: #008000;">self</span>.<span style="color: black;">shadername</span> = sShaderName
&nbsp;
		<span style="color: #808080; font-style: italic;"># Stack names</span>
		<span style="color: #008000;">self</span>.<span style="color: black;">stacknames</span> = <span style="color: black;">&#91;</span>
			<span style="color: #483d8b;">'Environment'</span>,
			<span style="color: #483d8b;">'Output'</span>,
			<span style="color: #483d8b;">'Volume'</span>
		<span style="color: black;">&#93;</span>
&nbsp;
&nbsp;
		<span style="color: #808080; font-style: italic;"># VARIABLES</span>
&nbsp;
		<span style="color: #808080; font-style: italic;"># Shader identification</span>
		<span style="color: #808080; font-style: italic;"># All point to scene objects</span>
		<span style="color: #008000;">self</span>.<span style="color: black;">currentstack</span> = <span style="color: #008000;">None</span>
		<span style="color: #008000;">self</span>.<span style="color: black;">currentroot</span> = <span style="color: #008000;">None</span>
		<span style="color: #008000;">self</span>.<span style="color: black;">currentshader</span> = <span style="color: #008000;">None</span>
&nbsp;
&nbsp;
&nbsp;
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> getroot<span style="color: black;">&#40;</span> <span style="color: #008000;">self</span> <span style="color: black;">&#41;</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Get the pass object</span>
		oColl = xsifactory.<span style="color: black;">createobject</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Collection'</span> <span style="color: black;">&#41;</span>
		oColl.<span style="color: black;">items</span> = <span style="color: #483d8b;">'Passes.%s'</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">passname</span>
&nbsp;
		<span style="color: #808080; font-style: italic;"># Did we find the pass</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oColl.<span style="color: black;">count</span></pre></td></tr></table></div>

<p>  Storing the pass shaders into a data structure<br />
We&#8217;ve seen already how to store a material&#8217;s render tree into a data structure. The presented approach cannot be used without modifications for pass shaders, again because of the foggy relationship that exists between the pass object, its stacks and its root shaders. Here I will not present code to achieve this task (as my solution is extremely elaborate), but rather explain what approach I use to do it. My goal was to have the ability to re-create the pass shaders simply by reading a flat XML list of shaders.</p>
<p>With a material it&#8217;s simple: when you get to the material, you can create anything you want the same way, and plug anything you want. With shader stacks it&#8217;s different.</p>
<p>The data structure I presented above works, but will need extra informations. Each shader will have to be flagged as being a stack root shader or not. The reason is that creating stack root shaders requires can only be done through the SIApplyShaderToCnxPoint() command, while creating the shaders underneath use the CreateObjectFromPreset() command, presented earlier.</p>
<p>Root shaders should state to which stack they belong (including the pass name, of course). When creating shaders with SIApplyShaderToCnxPoint(), you must specify the shader stack. Having this information already available as a string will make it easy. Btw, in the data structure, store the Type property of the stack, not its Name.</p>
<p><strong>CAMERAS</strong></p>
<p>The camera is similar to the pass, with a significant difference: it has only one stack. Once the camera shader stack is accessed, code that handles pass shader stacks should work with the camera shader stack. A word of advice: while I&#8217;m not aware of any such plans from Softimage, it would be a good idea to design your code to assume that there might be more than one stack in a camera. Let&#8217;s imagine Softimage, in some near future, adds a new stack to the camera&#8230;&#8230;.</p>
<p><strong>LIGHTS</strong></p>
<p>Lights can be handled pretty much like a material. For instance, you can traverse the light&#8217;s render tree using the same code we used with the material one, but pass it the light&#8217;s primitive:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> printShaderSources<span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>:
&nbsp;
	<span style="color: #808080; font-style: italic;"># Loop parameters of the shader</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> oParameter <span style="color: #ff7700;font-weight:bold;">in</span> oShader.<span style="color: black;">parameters</span>:
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if the parameter has a source</span>
		oSource = oParameter.<span style="color: black;">source</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> oSource <span style="color: #66cc66;">!</span>= <span style="color: #008000;">None</span>:
&nbsp;
			<span style="color: #808080; font-style: italic;"># Check if classname of the source is a shader</span>
			sClassName = xsi.<span style="color: black;">classname</span><span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> sClassName == <span style="color: #483d8b;">'Shader'</span> <span style="color: #ff7700;font-weight:bold;">or</span> sClassName == <span style="color: #483d8b;">'Texture'</span>:
&nbsp;
				xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> oSource.<span style="color: black;">name</span> <span style="color: black;">&#41;</span>
&nbsp;
				<span style="color: #808080; font-style: italic;"># Parameter has a shader source,</span>
				<span style="color: #808080; font-style: italic;"># call function again with that source</span>
				printShaderSources<span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Call the function using the selected light's primitive</span>
printShaderSources<span style="color: black;">&#40;</span> xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">activeprimitive</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Output:</p>
<pre># INFO : soft_light
# INFO : soft_light</pre>
<p>Additionnally, the light object (not the primitive) have a Shaders property. This will access the shaders connected into the primitive. If you want to access the soft_light shader (or any shader connected into the light&#8217;s primitive), use:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi.<span style="color: black;">logmessage</span><span style="color: black;">&#40;</span> xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">shaders</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">fullname</span> <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><strong>OVERRIDES</strong></p>
<p>Overrides are another beast. Their implementation looks and behaves similarly to other shader implementations in XSI, although there are several differences that need to be taken care of. Here we&#8217;ll talk about creating overrides by script.</p>
<p><strong>Shader overrides</strong><br />
You&#8217;ve probably noticed that shader overrides use strings separated by dashes. I suspect this is because the dot character is reserved to namespace notation, having it in a parameter script name would break things. Also you deal with relative names, where the material name is kind of masked by &#8220;material&#8221;. For instance, if you override the Surface input of a material, the created parameter looks like &#8220;material-surface&#8221;.</p>
<p>If you want to re-create a shader parameter in an override, you&#8217;ll have to format that string a little.<br />
First, replace the dashes by dots, obviously.<br />
Second, you&#8217;ll have to find a material among the group/layer/partition members that has the parameter you want to override, and use the material name in replacement of &#8220;material&#8221;. So &#8220;material-surface&#8221; become something like &#8220;cylinder.Material.surface&#8221;.</p>
<p><strong>Absence of members</strong><br />
The #1 problem, in my opinion, is that you can&#8217;t create an override group/layer/partition doesn&#8217;t expose the parameter you want to override. This makes re-creating overrides by script very difficult. I know 3 solutions to this:</p>
<ul>
<li>Save a preset of the override. Probably the most reliable way to do it, however it requires that you create the override at least once and store a preset. Another big advantage is that this approach works even if the group/layer/partition has no members to override.</li>
<li>Create temporary objects. This is easy if you have to overrides parameters such as visibility or geometry approximation, but for shaders it can become extremely elaborate. Definitely consider other solutions.</li>
<li>Wait for the group/layer/partition to be populated before attempting to create the override parameters. Better than #2, but still not 100% reliable.</li>
</ul>
<p><strong>Refresh issues</strong><br />
Another problem is that when you manage to create a parameter in an override, that parameter is not always immediately available through the object model. For reasons I don&#8217;t have a clue about, there are situations where if you try to reach the created parameter through the Parameters property of the override, it would fail:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
&nbsp;
<span style="color: #808080; font-style: italic;"># Get the selected override</span>
oOverride = xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Define parameter to override</span>
sParameterName = <span style="color: #483d8b;">'viewvis'</span>
sParameterFullName = <span style="color: #483d8b;">'cube.visibility.%s'</span> <span style="color: #66cc66;">%</span> sParameterName
&nbsp;
<span style="color: #808080; font-style: italic;"># Create the override entry</span>
xsi.<span style="color: black;">SIAddEntryToOverride</span><span style="color: black;">&#40;</span> oOverride, sParameterFullName <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Get the created override</span>
oOverride.<span style="color: black;">parameters</span><span style="color: black;">&#40;</span> sParameterName <span style="color: black;">&#41;</span>.<span style="color: black;">value</span> = <span style="color: #008000;">True</span></pre></td></tr></table></div>

<p>The solution is that once the parameter is created, use a string to retrieve it:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">xsi = Application
&nbsp;
<span style="color: #808080; font-style: italic;"># Get the selected override</span>
oOverride = xsi.<span style="color: black;">selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Define parameter to override</span>
sParameterName = <span style="color: #483d8b;">'viewvis'</span>
sParameterFullName = <span style="color: #483d8b;">'cube.visibility.%s'</span> <span style="color: #66cc66;">%</span> sParameterName
&nbsp;
<span style="color: #808080; font-style: italic;"># Create the override entry</span>
xsi.<span style="color: black;">SIAddEntryToOverride</span><span style="color: black;">&#40;</span> oOverride, sParameterFullName <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Get the created override</span>
oColl = xsifactory.<span style="color: black;">createobject</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Collection'</span> <span style="color: black;">&#41;</span>
oColl.<span style="color: black;">items</span> = <span style="color: #483d8b;">'%s.%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span> oOverride.<span style="color: black;">fullname</span>, sParameterName <span style="color: black;">&#41;</span>
oParameter = oColl<span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
oParameter.<span style="color: black;">value</span> = <span style="color: #008000;">True</span></pre></td></tr></table></div>

<p><strong>FURTHER SCRIPTING</strong></p>
<p>There are many more topics we could cover, including texture layers, OGLTextures, shader input type, the ProgID, etc. These are all things I&#8217;m not deeply familiar with so I&#8217;ll leave it to you to learn about those.</p>
<p><strong>NOTES</strong></p>
<p>(1) XSI 6.01 had this nasty bug. If you select the pass, open the render tree and start connecting shaders all over the place, they would all have a unique name. So far so good. But if you selected one of the stack shaders, open the render tree and connected shaders from there, you could name a shader the same as in another stack. In the end, it means that several shaders could have the same name in the pass name space. This was corrected in XSI 6.02.<br />
(2) If your _2D_background_shader doesn&#8217;t expose a bg input, you can modify the spdl. Simply enable the texturable flag.<br />
(3) The real value is &#8220;COMObject &#8220;unknown&#8221;", where &#8221; are replaced by &#8220;smaller than&#8221; and &#8220;greater than&#8221; tags.</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=245&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/245/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Facial Animation, The Static KineState, and Dorritos?</title>
		<link>http://www.softimageblog.com/archives/209#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=facial-animation-the-static-kinestate-and-dorritos</link>
		<comments>http://www.softimageblog.com/archives/209#comments</comments>
		<pubDate>Thu, 24 May 2007 03:12:35 +0000</pubDate>
		<dc:creator>Steven Caron</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Rigging]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/209</guid>
		<description><![CDATA[This is my first post at XSIBlog, so I want to thank Patrick and the other authors for keeping this going. I have always wanted to share but time is always against me not to mention having something worthy of sharing.
PLEASE NOTE: The following python scripts require this procedure to work properly.
Facial Animation
A thread started [...]]]></description>
			<content:encoded><![CDATA[<p>This is my first post at XSIBlog, so I want to thank Patrick and the other authors for keeping this going. I have always wanted to share but time is always against me not to mention having something worthy of sharing.</p>
<p><strong>PLEASE NOTE</strong>: The following python scripts require <a href="/?p=24">this procedure</a> to work properly.</p>
<p><strong>Facial Animation</strong></p>
<p>A thread started at <a href="http://www.xsibase.com/">XSI Base</a> simply named, <a href="http://www.xsibase.com/forum/index.php?board=11;action=display;threadid=29757">facial animation (questions)</a>, many facial rigging and animation concepts were shared. The two concepts discussed mostly were your traditional shape animation controlled by in scene controllers (a la Osipa) and also using Control Splines or curves with many complex rig elements that give you the desired motion. At the end of the day shape animation gets you pretty far with many character types ( realistic and stylized ) and setup time is pretty fast. The setup time is also important here, Softimage considers this a feature for FaceRobot that they market and rightfully so. Time is money, producers know this as should you! So if you will hear me out I am going to explain a way for you to continue to use shape animation and still get the low level control over subtle face movements.</p>
<p><span id="more-209"></span><strong>The Static KineState</strong></p>
<p>What is the Static KineState? I am sure anyone spending any amount of time rigging knows something about it. So lets define it for those that don&#8217;t.</p>
<p>The <a href="http://softimage.wiki.avid.com/xsidocs/skel.htm">Static KineState</a> indicates that an envelope is applied to the skeleton element. The StaticKineState is the initial position of the skeleton when the envelope was applied, and is used for envelope calculations.</p>
<p>If this property is storing these initial values then I must be able to change them. This is the gem that makes this rig work.</p>
<p><strong>Dorritos</strong></p>
<p>Now we are going to make the rig. This is not intended to be a tutorial so I apologize if I miss steps, all the project files are included at the bottom. So lets go over the rig!</p>
<p>I am starting with Primitive>Character>Face Man and I first made a quick &#8220;smile&#8221; shape, followed by a typical neck, head, and jaw for the character. I used nulls instead of joints for simplicity sake.</p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/05/rignsmile.gif' alt='rignsmile.gif' /></p>
<p>Now I am going to make a clone of this enveloped head and immediately remove the duplicated shape cluster, envelope cluster, and the &#8220;ShapeWeights&#8221; property. The clone uses the &#8220;CopyOp&#8221;, with this we get all the deformations from the first head which include the envelope deform and shape deform. Now we need to setup the local deformers for the cloned mesh.</p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/05/exploreclone.jpg' alt='exploreclone.jpg' /></p>
<p>These local deformers use an &#8220;Object To Cluster&#8221; constraint. I like to use edge clusters but you could use any SubComponent you choose. So I picked an edge in the corner of the mouth and ran this handy script that creates the cluster, the object, constrains them, and makes the local deformer. You need to select a component on the original head, and run the script.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#imports</span>
<span style="color: #ff7700;font-weight:bold;">import</span> win32com.<span style="color: black;">client</span>
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants
&nbsp;
<span style="color: #808080; font-style: italic;">#globals</span>
xsi	= win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">&quot;XSI.Application&quot;</span> <span style="color: black;">&#41;</span>.<span style="color: black;">Application</span>
xsiPrint = xsi.<span style="color: black;">LogMessage</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> setupDorrito<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
	<span style="color: #ff7700;font-weight:bold;">if</span> xsi.<span style="color: black;">Selection</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">1</span>:
		xsiPrint<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Select an component!&quot;</span>,constants.<span style="color: black;">siError</span><span style="color: black;">&#41;</span>
		<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
	root = xsi.<span style="color: black;">ActiveSceneRoot</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">#create cluster constrained null, setup its look</span>
	clsCnsNull = root.<span style="color: black;">AddNull</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;clsCnsNull&quot;</span><span style="color: black;">&#41;</span>
	clsCnsNull.<span style="color: black;">primary_icon</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0</span>
	clsCnsNull.<span style="color: black;">size</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0.1</span>
	clsCnsNull.<span style="color: black;">shadow_icon</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">7</span>
	clsCnsNull.<span style="color: black;">shadow_colour_custom</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">1</span>
	clsCnsNull.<span style="color: black;">B</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">1</span>
	clsCnsNull.<span style="color: black;">G</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0.5</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">#make cluster from selection</span>
	subComponent = xsi.<span style="color: black;">Selection</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">subComponent</span>
	cnsCls = subComponent.<span style="color: black;">CreateCluster</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;cnsCls&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">#constrain object to cluster</span>
	cns = clsCnsNull.<span style="color: black;">Kinematics</span>.<span style="color: black;">AddConstraint</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;ObjectToCluster&quot;</span>,cnsCls<span style="color: black;">&#41;</span>
	cns.<span style="color: black;">tangent</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">1</span>
	cns.<span style="color: black;">dirx</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0</span>
	cns.<span style="color: black;">diry</span>.<span style="color: black;">value</span> = -<span style="color: #ff4500;">1</span>
	cns.<span style="color: black;">upvct_active</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">1</span>
	cns.<span style="color: black;">upx</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">1</span>
	cns.<span style="color: black;">upy</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">#make dorrito</span>
	dorrito = root.<span style="color: black;">AddNull</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;dorrito&quot;</span><span style="color: black;">&#41;</span>
	dorrito.<span style="color: black;">primary_icon</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0</span>
	dorrito.<span style="color: black;">size</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0.2</span>
	dorrito.<span style="color: black;">shadow_icon</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">8</span>
	dorrito.<span style="color: black;">shadow_colour_custom</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">1</span>
	dorrito.<span style="color: black;">G</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">1</span>
	dorrito.<span style="color: black;">shadow_offsetX</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0.1</span>
	dorrito.<span style="color: black;">shadow_scaleX</span>.<span style="color: black;">value</span> = <span style="color: #ff4500;">0</span>
&nbsp;
	clsCnsNull.<span style="color: black;">AddChild</span><span style="color: black;">&#40;</span>dorrito<span style="color: black;">&#41;</span>
	dorrito.<span style="color: black;">kinematics</span>.<span style="color: black;">local</span>.<span style="color: black;">transform</span> = XSIMath.<span style="color: black;">CreateTransform</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
setupDorrito<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>The result&#8230;</p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/05/setupdorrito.jpg' alt='setupdorrito.jpg' /></p>
<p>At work we call this controller a &#8220;Dorrito&#8221; because when the first &#8220;on the face&#8221; rig came out, one of our animators, Jason Taylor, started calling it that and it stuck. So my controller is a flat pyramid that looks like a Dorrito, so I suggest you do the same :) Now I will add an envelope to the cloned mesh and choose this Dorrito as a deformer. I now need to paint the cloned mesh&#8217;s envelope. The way I do this is add another deformer to the deformer list, set all the points to 100 % of this new deformer, and then choose the Dorrito and paint it&#8217;s influence. Once I am happy with the deformation I just remove the extra deformer from the envelope and I get zero weights except for where I painted the Dorrito&#8217;s influence. This deformer moves with the cluster constrained null, but it causes this nasty double transform when I rotate the head!</p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/05/doubletransform.jpg' alt='doubletransform.jpg' /></p>
<p>Why is this happening? Because you have the first envelope on the original head which is deforming the points based on the transformation of the deformers (neck, head, and jaw). Then you have a &#8220;CopyOp&#8221; and another envelope above that is getting transformed by the same movement so XSI just does it twice! What you want to do is subtract that first envelope transformation from the second envelope transformation. The Static KineState gives you access to that intial position. If you take your deformer&#8217;s Static KineState property and set it&#8217;s parameters equal to the global transform parameters of your cluster constrained null you will effectively be constantly reseting the deformer&#8217;s initial position. Here is a script to setup the expressions between the cluster constrained null and the local deformer. Just select cluster constrained null first, then the Dorrito.</p>
</pre>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#imports</span>
<span style="color: #ff7700;font-weight:bold;">import</span> win32com.<span style="color: black;">client</span>
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants
&nbsp;
<span style="color: #808080; font-style: italic;">#globals</span>
xsi	= win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span> <span style="color: #483d8b;">&quot;XSI.Application&quot;</span> <span style="color: black;">&#41;</span>.<span style="color: black;">Application</span>
xsiPrint = xsi.<span style="color: black;">LogMessage</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> setupStaticState<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
	defList = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
	clsList = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>,<span style="color: #008000;">len</span><span style="color: black;">&#40;</span>xsi.<span style="color: black;">Selection</span><span style="color: black;">&#41;</span>,<span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>:
		clsList.<span style="color: black;">append</span><span style="color: black;">&#40;</span>xsi.<span style="color: black;">Selection</span><span style="color: black;">&#40;</span>i<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
		defList.<span style="color: black;">append</span><span style="color: black;">&#40;</span>xsi.<span style="color: black;">Selection</span><span style="color: black;">&#40;</span>i+<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">for</span> a,b <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">zip</span><span style="color: black;">&#40;</span>defList,clsList<span style="color: black;">&#41;</span>:
		defKineState = a.<span style="color: black;">Properties</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Static KineState&quot;</span><span style="color: black;">&#41;</span>
		clsKine = b.<span style="color: black;">Properties</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Kinematics&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
		defKineState.<span style="color: black;">orix</span>.<span style="color: black;">AddExpression</span><span style="color: black;">&#40;</span>clsKine.<span style="color: black;">FullName</span> + <span style="color: #483d8b;">&quot;.global.rotx&quot;</span><span style="color: black;">&#41;</span>
		defKineState.<span style="color: black;">oriy</span>.<span style="color: black;">AddExpression</span><span style="color: black;">&#40;</span>clsKine.<span style="color: black;">FullName</span> + <span style="color: #483d8b;">&quot;.global.roty&quot;</span><span style="color: black;">&#41;</span>
		defKineState.<span style="color: black;">oriz</span>.<span style="color: black;">AddExpression</span><span style="color: black;">&#40;</span>clsKine.<span style="color: black;">FullName</span> + <span style="color: #483d8b;">&quot;.global.rotz&quot;</span><span style="color: black;">&#41;</span>
		defKineState.<span style="color: black;">posx</span>.<span style="color: black;">AddExpression</span><span style="color: black;">&#40;</span>clsKine.<span style="color: black;">FullName</span> + <span style="color: #483d8b;">&quot;.global.posx&quot;</span><span style="color: black;">&#41;</span>
		defKineState.<span style="color: black;">posy</span>.<span style="color: black;">AddExpression</span><span style="color: black;">&#40;</span>clsKine.<span style="color: black;">FullName</span> + <span style="color: #483d8b;">&quot;.global.posy&quot;</span><span style="color: black;">&#41;</span>
		defKineState.<span style="color: black;">posz</span>.<span style="color: black;">AddExpression</span><span style="color: black;">&#40;</span>clsKine.<span style="color: black;">FullName</span> + <span style="color: #483d8b;">&quot;.global.posz&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
setupStaticState<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Now you can rotate the head or jaw and the deformer just rides the mesh, you can even use shape animation and the deformer just rides on top of that with no double transform.</p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/05/nodoubletransform.jpg' alt='nodoubletransform.jpg' /><br />
<a href='http://www.xsi-blog.com/userContent/upload/2007/05/dorritocap.mov' title='dorritocap.mov'>Viewport Capture</a></p>
<p><strong>Closing</strong></p>
<p>There are many ways to rig a face and none should be discarded from any rigger's toolbox. I hope other riggers will get some use out of this technique and will share their work with us. Before I go, I want to thank my rigging supervisor Remi McGill for suggesting the usage of the Static KineState and <a href="http://www.blur.com/">Blur</a> for allowing me to share with the community. Below are the project files which includes the scripts. Thanks for reading!</p>
<p><a href='http://www.xsi-blog.com/userContent/upload/2007/05/dorritoproject.zip' title='dorritoproject.zip'>Project Files</a></p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=209&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/209/feed</wfw:commentRss>
		<slash:comments>21</slash:comments>
<enclosure url="http://www.xsi-blog.com/userContent/upload/2007/05/dorritocap.mov" length="254200" type="video/quicktime" />
		</item>
		<item>
		<title>Python Modules like XSI Plugins</title>
		<link>http://www.softimageblog.com/archives/179#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=python-modules-like-xsi-plugins</link>
		<comments>http://www.softimageblog.com/archives/179#comments</comments>
		<pubDate>Fri, 09 Mar 2007 01:52:33 +0000</pubDate>
		<dc:creator>Daniele Niero</dc:creator>
				<category><![CDATA[Programming / Scripting]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/179</guid>
		<description><![CDATA[Starting from my last post I&#8217;d like to show you how I use Python modules in XSI instead of normal self installing plugins.
You know that XSI automatically recognizes plugins when a proper file is putted under a folder like this: [workgroup or user]\Application\Plugins. We don&#8217;t need to specify anything else. Very clever and very useful [...]]]></description>
			<content:encoded><![CDATA[<p>Starting from my last <a href="/archives/172">post</a> I&#8217;d like to show you how I use Python modules in XSI instead of normal self installing plugins.</p>
<p>You know that XSI automatically recognizes plugins when a proper file is putted under a folder like this: <em>[workgroup or user]\Application\Plugins</em>. We don&#8217;t need to specify anything else. Very clever and very useful especially for sharing plugins with all your colleagues.</p>
<p>You also know that Python can find modules and other Python scripts when they are in the same path as the file which is importing them or in a folder specified in the pythonpath. This means that if we set the pythonpath from within XSI and add to it some specific folders from our workgroups, we&#8217;ll be able to import Python files or modules in XSI (if it is connected with the workgroups) in every computer in our company, just like the XSI plugins do.</p>
<p><span id="more-179"></span><strong>The First Step: Create An OnStartup Event</strong></p>
<p>The first thing we need is create an event, a siOnStartup event, and set properly the pythonpath when XSI start.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Purpose:</span>
<span style="color: #808080; font-style: italic;"># Scanning all the Workgroups installed and put in the Python Path all the ...\Application\Plugins\PythonModules folders we will find.</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> win32com.<span style="color: black;">client</span>
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants
&nbsp;
null = <span style="color: #008000;">None</span>
false = <span style="color: #ff4500;">0</span>
true = <span style="color: #ff4500;">1</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> XSILoadPlugin<span style="color: black;">&#40;</span> in_reg <span style="color: black;">&#41;</span>:
in_reg.<span style="color: black;">Author</span> = <span style="color: #483d8b;">&quot;Daniele Niero&quot;</span>
in_reg.<span style="color: black;">Name</span> = <span style="color: #483d8b;">&quot;Set Python Path Plug-in&quot;</span>
in_reg.<span style="color: black;">Email</span> = <span style="color: #483d8b;">&quot;&quot;</span>
in_reg.<span style="color: black;">URL</span> = <span style="color: #483d8b;">&quot;&quot;</span>
in_reg.<span style="color: black;">Major</span> = <span style="color: #ff4500;">1</span>
in_reg.<span style="color: black;">Minor</span> = <span style="color: #ff4500;">0</span>
&nbsp;
in_reg.<span style="color: black;">RegisterEvent</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;SetPythonPath&quot;</span>, constants.<span style="color: black;">siOnStartup</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;">#RegistrationInsertionPoint - do not remove this line</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">return</span> true
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> XSIUnloadPlugin<span style="color: black;">&#40;</span> in_reg <span style="color: black;">&#41;</span>:
	<span style="color: #ff7700;font-weight:bold;">return</span> true
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> SetPythonPath_OnEvent<span style="color: black;">&#40;</span> ctxt <span style="color: black;">&#41;</span>:
	<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
	wkgrLocations = Application.<span style="color: black;">WorkGroups</span><span style="color: #66cc66;">;</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> wkgr <span style="color: #ff7700;font-weight:bold;">in</span> wkgrLocations:
		workgroupLibFolder = XSIUtils.<span style="color: black;">BuildPath</span><span style="color: black;">&#40;</span> wkgr, <span style="color: #483d8b;">&quot;Application<span style="color: #000099; font-weight: bold;">\\</span>Plugins<span style="color: #000099; font-weight: bold;">\\</span>PythonModules&quot;</span> <span style="color: black;">&#41;</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> workgroupLibFolder <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">path</span> :
			<span style="color: #dc143c;">sys</span>.<span style="color: black;">path</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span> workgroupLibFolder <span style="color: black;">&#41;</span>
	userLocation = Application.<span style="color: black;">InstallationPath</span><span style="color: black;">&#40;</span> constants.<span style="color: black;">siUserPath</span> <span style="color: black;">&#41;</span>
	userLibFolder = XSIUtils.<span style="color: black;">BuildPath</span><span style="color: black;">&#40;</span>userLocation, <span style="color: #483d8b;">&quot;Application<span style="color: #000099; font-weight: bold;">\\</span>Plugins<span style="color: #000099; font-weight: bold;">\\</span>PythonModules&quot;</span> <span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">if</span> userLibFolder <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">path</span> :
		<span style="color: #dc143c;">sys</span>.<span style="color: black;">path</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span> userLibFolder <span style="color: black;">&#41;</span>
	Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">path</span>, constants.<span style="color: black;">siVerbose</span> <span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Return value is ignored as this event can not be aborted.</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> true</pre></td></tr></table></div>

<p>So, what did we do exactly? We have just scanned all the workgroups installed (and the user folder as well), built a path like this <em>WorkGroup\Application\Plugins\PythonModules</em> and appended it to the pythonpath list.</p>
<p>The good thing is that all the paths added to the sys.path will be removed when XSI is closed. Helping us to keep the pythonpath clean.</p>
<p>At this point, if we have a PythonModules folder in the Plugins folder in one of the workgroups connected with XSI, we are able to just import every file or modules saved in this PythonModules folder. Since in your company everyone have the workgroups correctly connected, they are able to import a python file or module written by someone else, without specifying or doing anything, just like standard XSI plugins!</p>
<p><strong>The first test and utility python file</strong></p>
<p>The first test we can do is create a file that exposes, once imported, some common and useful stuff we need when we use Python in XSI.<br />
This file could be something like this:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> win32com <span style="color: #ff7700;font-weight:bold;">as</span> win
<span style="color: #ff7700;font-weight:bold;">import</span> win32com.<span style="color: black;">server</span> <span style="color: #ff7700;font-weight:bold;">as</span> winServer
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span>.<span style="color: black;">dynamic</span> <span style="color: #ff7700;font-weight:bold;">import</span> Dispatch
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> Dispatch <span style="color: #ff7700;font-weight:bold;">as</span> ComObject
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">server</span>.<span style="color: black;">util</span> <span style="color: #ff7700;font-weight:bold;">import</span> wrap
&nbsp;
App = Dispatch<span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Application'</span> <span style="color: black;">&#41;</span>.<span style="color: black;">Application</span>
XSIFactory = Dispatch<span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Factory'</span> <span style="color: black;">&#41;</span>
XSIMath = Dispatch<span style="color: black;">&#40;</span> <span style="color: #483d8b;">'XSI.Math'</span> <span style="color: black;">&#41;</span>
&nbsp;
null = <span style="color: #008000;">None</span>
false = <span style="color: #ff4500;">0</span>
true = <span style="color: #ff4500;">1</span></pre></td></tr></table></div>

<p>Save it as MyGlobals.py and put it in a PythonModules folder. Open XSI and just test if everything work writing just this few lines in the script editor:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> MyGlobals <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #66cc66;">*</span>
&nbsp;
App.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;note that App works, it is a global variable now!&quot;</span><span style="color: black;">&#41;</span>
anXSICollection = ComObject<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;XSI.Collection&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>If you don&#8217;t have errors form this simple testing code it means we are ready to move on the next step and create a complete class that can help us to handle a GenericProperty.</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=179&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/179/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>wxPython in XSI</title>
		<link>http://www.softimageblog.com/archives/138#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=wxpython-in-xsi</link>
		<comments>http://www.softimageblog.com/archives/138#comments</comments>
		<pubDate>Thu, 11 Jan 2007 02:47:50 +0000</pubDate>
		<dc:creator>Aloys Baillet</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[User Interface]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/138</guid>
		<description><![CDATA[You’ll find here some nice discoveries I made trying to use wxPython inside XSI. For that I will assume that you, dear Reader, have some knowledge of Python and UIs…

Animal Logic is also kindly giving you an example XSI Plugin that should be a good starting point if you want to implement a wxPython interface in XSI.

I apologize in advance for the technical and boring stuff ahead… it’s the unfortunate price to pay to get nice and shiny UIs!]]></description>
			<content:encoded><![CDATA[<p>Hi, and Happy New Year!</p>
<p>It&#8217;s my first post in here, so I take the opportunity to thank Patrick for this great XSI site!</p>
<p>You&#8217;ll find here some nice discoveries I made trying to use wxPython inside XSI. For that I will assume that you, dear Reader, have some knowledge of Python and UIs&#8230;</p>
<p>Animal Logic is also kindly giving you an example XSI Plugin that should be a good starting point if you want to implement a wxPython interface in XSI.</p>
<p>I apologize in advance for the technical and boring stuff ahead&#8230; it&#8217;s the unfortunate price to pay to get nice and shiny UIs!</p>
<p>You can download the plugin <a href="/userContent/upload/2007/01/wxpythonxsiexplorer.zip">here</a>.</p>
<p><span id="more-138"></span><strong>But why would one do that, Custom Properties should be enough?</strong></p>
<p>Mainly, the absence of a Tree widget, the clunkiness of the Grid widget, the need for finer control on callbacks for complex UIs, the difficulty of having session-persistent UI (ones that don&#8217;t get trashed on a New Scene).</p>
<p>The main goal of being able to write wxPython UIs inside XSI is for pipeline tools. I would not try to do better than Softimage for animation controls for example. But when it comes to streamline your pipeline, having consistent UI across your applications will save a lot of development time and user frustration.</p>
<p><strong>Prerequisites</strong></p>
<ul>
<li>XSI version 5.11 or 6</li>
<li>WinXP 32 (win2k supported)</li>
<li><a href="http://www.python.org/download/">Python</a> (2.5 recommended, 2.4)</li>
<li><a href="https://sourceforge.net/projects/pywin32/">pywin32 210</a></li>
<li><a href="http://wxpython.org/download.php#binaries">wxPython</a> (2.8.0.1 recommended, older supported)</li>
</ul>
<p><strong>A look at the plugin!</strong></p>
<p>In the Python XSI plugin called wxPythonXSIExplorer.py I implemented a really crude XSI Selection Explorer. This is an highly useless tool, but it shows how this is possible to nicely interact with XSI using a set of basic wxPython widgets. Please consider the explorer itself as a proof-of-concept tool!</p>
<p><a title="wxPython in XSI" href="http://www.xsi-blog.com/userContent/upload/2007/01/wxpythonxsiexplorer.gif"><img width="600" alt="wxPython in XSI" src="http://www.xsi-blog.com/userContent/upload/2007/01/wxpythonxsiexplorer.gif" /></a></p>
<p>The interesting part of this plugin is the class called XSISubFrame. This is the class that implements all the gritty details described below, and I highly recommend you start your new wxPython XSI frame by looking at how I inherited from this class to create the XSIExplorerFrame. And how I use the XSISubFrame.create static factory method to create a new instance of the explorer.</p>
<p>I also implemented a SelectionChange event to detect the selection change in all the running instances of the wxPython XSI Explorer. Unfortunately there is no general hook for data change in XSI so if you move an object, the parameter value won&#8217;t update in our explorer.</p>
<p><strong>And now for the gritty details&#8230;</strong></p>
<p>The base trick to get a wxPython frame nicely displayed inside XSI, meaning on top of the XSI main window, but behind other XSI views when they have the focus, is to create the main frame as a child of the XSI top-level window. And for that we need to get its MS Window Handle.</p>
<p>The pywin32 package is here to help us: we can use it to list all the running windows and filter them by process ID, then pick the one which names begin with SOFTIMAGE. It sounds like a hack, but it&#8217;s just a small hack, believe me, MS Windows programming can get far more hacky than that!</p>
<p>The real magic happens when you use the AssociateHandle method on a dummy wxFrame so that wx believes that the XSI top level window is an actual wx window&#8230;</p>
<p>Then we just create our own sub frame using the wx.FRAME_FLOAT_ON_PARENT flag, which does what we want.</p>
<p>Then don&#8217;t forget to call DissociateHandle on your dummy frame, if not, XSI will not get some critical events anymore, like EVT_CLOSE, meaning you can&#8217;t close XSI anymore&#8230;</p>
<p>You will also need to do some cleanup when closing the wx window. The main problem is that wxWindow::Destroy does not destroy the top level window, it just waits for the<br />
wxApp to destroy it on it&#8217;s next event loop. But as we don&#8217;t have a wx loop running, we need to destroy the top window by hand using the win32gui module again.</p>
<p><strong>Some warnings!</strong></p>
<p>wxPython is great, but it can crash easily if you do something wrong. In XSI 6 where there is no standard output from Python displayed in the script logging window (as there is in 5), it&#8217;s better to handle all exceptions, and display them yourself, or you might miss some important message from wxPython that causes a crash in the app (see line 370 of the plugin).</p>
<p>This is Windows-Only stuff for now!</p>
<p>Fortunately for Windows users, the wxWindows framework uses standard MS Windows event loop, and wxPython integrates amazingly well inside XSI. I haven&#8217;t tried under Linux, but the big problem over there is that wxWindows is using GTK as its UI framework, and I don&#8217;t think MainWin and GTK will go very well together unless Softimage builds a version of wxMSW using their version of Mainwin and give it to us&#8230;</p>
<p>BTW, wxPython works well in Maya as well, using the still experimental <a href="http://cgkit.sourceforge.net/download.html">python for Maya</a> plugin from <a href="http://cgkit.sourceforge.net/">cgkit</a>, meaning that mixed pipeline could use the exact same UI in XSI and Maya!</p>
<p>Some native widgets have been &#8220;hacked&#8221; by Luc-Eric and his team to look better, the base TextCtrl is one of them. If you use it, its background will never refresh at all. I didn&#8217;t find an easy way of making it refresh. But you can get a nice text control by using the TE_RICH2 style, which has not been hacked. I provide a class called XSITextCtrl in my plugin that sets the style automatically as well as the background color.</p>
<p>Modality is a problem&#8230; The problem is that wx and XSI do not know well each other. If a wx dialog is shown modal, you can still work in XSI, and if a XSI dialog is shown modal (lik a new scene prompt), you can still do stuff in wx. And that could be really dangerous!</p>
<p>I worked around it by providing a custom showModalDialog method that disactivate the XSI top level window while the wx modal dialog is shown. And I implemented a EVT_ACTIVATE event to detect if there is a modal dialog running in XSI and I disable the content of the wx activated window until the top level window is activated.</p>
<p>This is not perfect, but it gives the right functionality!</p>
<p><strong>Conclusion</strong></p>
<p>This is surprisingly possible to seamlessly run wxPython UIs inside XSI!</p>
<p>I hope you will find some use to this and push Softimage to support this unknown feature, especially under Linux.</p>
<p>Have fun!</p>
<p>Aloys Baillet<br />
R&amp;D, Animal Logic</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=138&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/138/feed</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Controlling XSI remotely using sockets</title>
		<link>http://www.softimageblog.com/archives/132#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=controlling-xsi-remotely-using-sockets</link>
		<comments>http://www.softimageblog.com/archives/132#comments</comments>
		<pubDate>Sun, 31 Dec 2006 14:21:09 +0000</pubDate>
		<dc:creator>Kim Aldis</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=132</guid>
		<description><![CDATA[A short Python socket server script for remotely communicating with XSI using Telnet]]></description>
			<content:encoded><![CDATA[<p>XSI 6.0 comes with a nice little example of how to connect with XSI remotely. It uses C# and being a little bit dumb and a lottle bit the worse for Christmas wear, I asked on the mailing list if it wouldn&#8217;t have been easier to do using Python, it not having dawned on me that it was, well, a C# example. Luc-Eric immediately chucked the gauntlet back again; &#8220;show us&#8221;, he said. So I will.</p>
<p>It is, of course, more useful than just an example as anyone who&#8217;s used Maya&#8217;s CommandPort can tell you. The C# example is cleverly wrapped in a timer event, allowing it to run in the background and quietly check for a connection every few seconds. More for reasons of clarity than anything else I&#8217;m just going to wrap this up in a function and let someone else decide how best to use it but I will get a little bit into timeouts and how to handle the hang you can get if there&#8217;s nothing at the other end.</p>
<p>Python is a really good example of just how sensible it was to use good existing scripting languages  rather than writing  bad new ones. You not only get the language for free, you also get most of the Open Source community too and with Python that means quite a lot of modules, allowing you to do more stuff than you could shake a stick at in a lifetime of stick shaking.</p>
<p><span id="more-132"></span>I&#8217;d done stuff like this before in Perl and C but not Python and I was interested in how long it was going to take to do this. As it turned out, around 20 minutes, which is pretty good but more testament to the other thing you get for free with Python &#8211; shedloads of free examples &#8211; than any skills I may have.</p>
<p>Python is pretty good at wrapping complicated stuff up and making it easy to use and this is no exception. To read something from a port the sequence of events is:</p>
<ul>
<li>Create a socket</li>
<li>Bind to the socket</li>
<li>Listen on the socket</li>
<li>Accept the connection</li>
<li>Read whatever&#8217;s being sent from the other end</li>
<li>Optionally send something back</li>
<li>Close the connection</li>
</ul>
<p>This is all fine and dandy, except that if there&#8217;s nothing at the other end then the Accept hangs until there is. If nothing ever tries to connect we get a nasty hang that&#8217;s not easy to get out of. So we could do with something that  can peek at the socket and see if there&#8217;s something there. Such a thing is a thing called &#8220;select&#8221;. In generic terms Select() is used to check any kind of file that&#8217;s open for reading to see if there&#8217;s something to read and, at least of grownup operating systems, a socket is treated in the same way as a file. We call select() on the socket just after we start listening. If select returns a list that isn&#8217;t empty then someone&#8217;s knocking at the other end and it&#8217;s safe to go ahead. If it&#8217;s not we ignore the accept call and just return nothing and bingo! no hang.</p>
<p>And finally, I&#8217;ve taken a slightly different slant in executing code to the C# example. It accepts commands to either log a string or run  a script file. It seemed more natural to me to run a string than a file, partly because it seems such a pain creating script files all over the place but mostly because I figured if I could run a string then I could do both the other two things with the code in that string.</p>
<p>Here&#8217;s the code. You can test it by running the script in XSI and using telnet to connect. Either <em>&#8220;telnet localhost 50007&#8243; </em>from the local machine or <em>&#8220;telnet machine 50007&#8243;</em> from some other machine. At the telnet prompt type something like <em>jscript|logmessage(&#8220;Testing&#8221;);</em> will run logmessage(&#8220;Testing&#8221;) at the XSI end. Enjoy.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">socket</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">select</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">re</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> ReadSocket<span style="color: black;">&#40;</span>PORT, TimeOut=<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
    s = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_INET</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
    s.<span style="color: black;">bind</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span>, PORT<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    s.<span style="color: black;">listen</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># is someone knocking at the other end?</span>
    <span style="color: #808080; font-style: italic;">#</span>
    a = <span style="color: #dc143c;">select</span>.<span style="color: #dc143c;">select</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span>s<span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>, TimeOut<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># someone's there</span>
    <span style="color: #808080; font-style: italic;">#</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span>a <span style="color: #66cc66;">!</span>= <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># accept the connection</span>
        <span style="color: #808080; font-style: italic;">#</span>
        conn, addr = s.<span style="color: black;">accept</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># get data from the socket</span>
        <span style="color: #808080; font-style: italic;">#</span>
        data = conn.<span style="color: black;">recv</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1024</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># parse the result</span>
        <span style="color: #808080; font-style: italic;">#</span>
        tokens = <span style="color: #dc143c;">re</span>.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\|</span>'</span>, data<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>tokens<span style="color: black;">&#41;</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>:
            Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Useage: Language | script code&quot;</span><span style="color: black;">&#41;</span>
            sRet = <span style="color: #483d8b;">&quot;Error&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            sRet = <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>Application.<span style="color: black;">ExecuteScriptCode</span><span style="color: black;">&#40;</span>tokens<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, tokens<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># be polite and send something back</span>
        <span style="color: #808080; font-style: italic;">#</span>
        conn.<span style="color: black;">send</span><span style="color: black;">&#40;</span>sRet<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># close the connection and return the result</span>
        <span style="color: #808080; font-style: italic;"># of the last command</span>
        <span style="color: #808080; font-style: italic;">#</span>
        conn.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> sRet
&nbsp;
    <span style="color: #808080; font-style: italic;"># there was nobody there so return false</span>
    <span style="color: #808080; font-style: italic;">#</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
<span style="color: #808080; font-style: italic;">###############################################################</span>
<span style="color: #808080; font-style: italic;">#</span>
<span style="color: #808080; font-style: italic;"># Testing on port 50007</span>
<span style="color: #808080; font-style: italic;"># with a 10 second timeout.</span>
<span style="color: #808080; font-style: italic;"># You can test this using &quot;telnet hostname 50007&quot; from any other machine</span>
<span style="color: #808080; font-style: italic;"># or &quot;telnet localhost 50007&quot; from this machine</span>
<span style="color: #808080; font-style: italic;"># typing something like 'jscript | logmessage(&quot;testing&quot;)' will run 'logmessage(&quot;testing&quot;)' in the remote XSI.</span>
<span style="color: #808080; font-style: italic;"># 10 seconds should be enough time for you to run across the room and kick off a telnet. If you don't</span>
<span style="color: #808080; font-style: italic;"># make it then it'll just time out.</span>
<span style="color: #808080; font-style: italic;"># The connection is closed once the first string has been parsed but you can, if you wish, concatenate</span>
<span style="color: #808080; font-style: italic;">#  commands, as in 'jscript|logmessage(&quot;testing&quot;)|python Application.LogMessage(&quot;Python Testing&quot;)'</span>
<span style="color: #808080; font-style: italic;">#</span>
s = ReadSocket<span style="color: black;">&#40;</span><span style="color: #ff4500;">50007</span>, <span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span><span style="color: #ff7700;font-weight:bold;">not</span> s<span style="color: black;">&#41;</span>:
    Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Timeout&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
    Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Connected: %s&quot;</span> <span style="color: #66cc66;">%</span> s<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=132&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/132/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Stupid Python Trick: print statement revisited</title>
		<link>http://www.softimageblog.com/archives/129#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=stupid-python-trick-print-statement-revisited</link>
		<comments>http://www.softimageblog.com/archives/129#comments</comments>
		<pubDate>Wed, 20 Dec 2006 17:24:51 +0000</pubDate>
		<dc:creator>Patrick Boucher</dc:creator>
				<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=129</guid>
		<description><![CDATA[This must be the most stupid piece of code I&#8217;ve ever written in Softimage XSI but unfortunately, sometimes I can&#8217;t help myself. And, truth be told, some of these wacky possibilities are some of the reasons I prefer Python over other scripting languages for a lot of my work.
A while back I talked about the [...]]]></description>
			<content:encoded><![CDATA[<p>This must be the most stupid piece of code I&#8217;ve ever written in Softimage XSI but unfortunately, sometimes I can&#8217;t help myself. And, truth be told, some of these wacky possibilities are some of the reasons I prefer Python over other scripting languages for a lot of my work.</p>
<p>A while back I talked about the <a href="http://www.xsi-blog.com/?p=13">print statement inside XSI</a>. Well, at some version, I don&#8217;t remember which, it stopped working. I miss it. I miss it mainly because some of the code I write is meant to be used outside XSI as much as inside it and using a the more ubiquitous <em>print</em> would be nice instead of <em>Application.LogMessage</em>. To get that functionality back I cooked up a little recipe that makes the print statement in XSI useful again.</p>
<p><span id="more-129"></span><strong>Some Background</strong></p>
<p>What happens when you use the print statement? Python has a builtin module called <em>sys</em>. This module has three very usefull objects called <em>stdin</em>, <em>stdout</em> and <em>stderr</em>. They are respectively used to get input, send output and send errors. When you do</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Hello World!'</span></pre></td></tr></table></div>

<p>in Python it can be roughly interpreted as</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #dc143c;">sys</span>.<span style="color: black;">stdout</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Hello World!'</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stdout</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><strong>The Code</strong></p>
<p>What my little reciepe does is replace <em>sys.stdout</em> with a wrapper to <em>Application.LogMessage</em> thus redirecting all print statements to the XSI log window. You can copy the code below to your script editor and run it.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">from</span> win32com.<span style="color: black;">client</span> <span style="color: #ff7700;font-weight:bold;">import</span> constants <span style="color: #ff7700;font-weight:bold;">as</span> c
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> xsiLogger<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, verboseCode=c.<span style="color: black;">siInfo</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.__verboseCode = verboseCode
        <span style="color: #008000;">self</span>.__buffer = <span style="color: #483d8b;">''</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> write<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, output<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> letter <span style="color: #ff7700;font-weight:bold;">in</span> output:
            <span style="color: #ff7700;font-weight:bold;">if</span> letter == <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span>:
                <span style="color: #008000;">self</span>.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">else</span>:
                <span style="color: #008000;">self</span>.__buffer += letter
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> flush<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        Application.<span style="color: black;">LogMessage</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.__buffer, <span style="color: #008000;">self</span>.__verboseCode<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.__buffer = <span style="color: #483d8b;">''</span>
&nbsp;
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stdout</span> = xsiLogger<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span> = xsiLogger<span style="color: black;">&#40;</span>c.<span style="color: black;">siError</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'test<span style="color: #000099; font-weight: bold;">\n</span>test2'</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'test5'</span>,
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'test6'</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'test8'</span>, <span style="color: #483d8b;">'test9'</span>
<span style="color: #ff7700;font-weight:bold;">print</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #66cc66;">&gt;&gt;</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>, <span style="color: #483d8b;">'test3'</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'test<span style="color: #000099; font-weight: bold;">\n</span>test2'</span></pre></td></tr></table></div>

<p><strong>A Few Explanations</strong></p>
<p>The wrapper is implemented as a class that has a write method. Python&#8217;s print statements thus wind up calling the write method of my xsiLogger instance that then calls <em>Application.LogMessage</em>. The first argument to the initialization of the the class is the type of message that will be logged in XSI (siInfo, siError, etc&#8230;). Because of this we can also replace <em>sys.stderr</em> by an instance of the xsiLogger but this time give it c.siError as it&#8217;s verboseCode and have it spew errors instead of info messages.</p>
<p>Neat huh?</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=129&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/129/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>XSI Arrays and Python</title>
		<link>http://www.softimageblog.com/archives/120#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=xsi-arrays-and-python</link>
		<comments>http://www.softimageblog.com/archives/120#comments</comments>
		<pubDate>Thu, 16 Nov 2006 07:48:58 +0000</pubDate>
		<dc:creator>Patrick Boucher</dc:creator>
				<category><![CDATA[Programming / Scripting]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=120</guid>
		<description><![CDATA[I was helping out a buddy the other day, helping him get a script running in Softimage XSI. I had the error fixed in a few minutes and then I started goofing off with his code and cutting it down. Then I hit a part of his script where XSI returns an array of data. [...]]]></description>
			<content:encoded><![CDATA[<p>I was helping out a buddy the other day, helping him get a script running in Softimage XSI. I had the error fixed in a few minutes and then I started goofing off with his code and cutting it down. Then I hit a part of his script where XSI returns an array of data. Hmmm&#8230;.</p>
<p>Now, often when you ask multi dimensional information from XSI it will return it to you in what might seem an odd manner. Let&#8217;s take some polygonal geometry for example. XSI will give you vertex data organized as three lists representing x, y and z positions with each element in the list representing a successive vertex. There is a <a href="http://softimage.wiki.avid.com/index.php/Python_Object_Model_Basics_%28XSISDK%29#Demonstration_of_how_returned_arrays_are_representing_as_Tuples_in_Python">demonstration</a> of this situation at the <a href="http://softimage.wiki.avid.com/index.php">Softimage Wiki</a>. This happens well&#8230; regularly.</p>
<p>When you&#8217;re coding in C++ it is a rather nice and efficient way to do things. You know how many data points you have so you can use simple pointer arithmetic to walk your  multiple data points. But we&#8217;re not in C++, we&#8217;re in Python and the equivalent walk would look something like this:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">cube = Application.<span style="color: black;">ActiveSceneRoot</span>.<span style="color: black;">AddGeometry</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;cube&quot;</span>, <span style="color: #483d8b;">&quot;MeshSurface&quot;</span><span style="color: black;">&#41;</span>
pa = cube.<span style="color: black;">ActivePrimitive</span>.<span style="color: black;">Geometry</span>.<span style="color: black;">Points</span>.<span style="color: black;">PositionArray</span>
<span style="color: #ff7700;font-weight:bold;">for</span> p <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
    null = cube.<span style="color: black;">AddPrimitive</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Null&quot;</span><span style="color: black;">&#41;</span>
    t = null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span>
    t.<span style="color: black;">SetTranslationFromValues</span><span style="color: black;">&#40;</span>pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>p<span style="color: black;">&#93;</span>, pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>p<span style="color: black;">&#93;</span>, pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>p<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
    null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span> = t</pre></td></tr></table></div>

<p>In this example we create a cube and position eight nulls at the cube&#8217;s corners. It is a truly stupid example but it gets the point across. We first get the array of positions of the vertices, then loop over each vertex and move the appropriate null. In this first example we have to deal with XSI&#8217;s multidimensional array. Let&#8217;s get rid of that.</p>
<p><span id="more-120"></span><strong>3D Programming in One Dimension</strong></p>
<p>One way to get rid of the multidimensional array syntax is to flip it around. Instead of having data[x][v], data[y][v] and data[z][v], where x, y and z are positional components and v is a vertex we&#8217;ll convert the data to data[v][x], data[v][y] and data[v][z]. In this example flipping the data might seem trivial but in more complex scenarios I find it just fits my brain better. Maybe I just have a bizarre brain.</p>
<p>One way that is suggested in the Python Cookbook and that has made it&#8217;s way a few times on the XSI mailing list is a list comprehension.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">cube = Application.<span style="color: black;">ActiveSceneRoot</span>.<span style="color: black;">AddGeometry</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;cube&quot;</span>, <span style="color: #483d8b;">&quot;MeshSurface&quot;</span><span style="color: black;">&#41;</span>
pa = cube.<span style="color: black;">ActivePrimitive</span>.<span style="color: black;">Geometry</span>.<span style="color: black;">Points</span>.<span style="color: black;">PositionArray</span>
<span style="color: #ff7700;font-weight:bold;">for</span> p <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">&#91;</span><span style="color: black;">&#91;</span>r<span style="color: black;">&#91;</span>column<span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> r <span style="color: #ff7700;font-weight:bold;">in</span> pa<span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> column <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>:
    null = cube.<span style="color: black;">AddPrimitive</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Null&quot;</span><span style="color: black;">&#41;</span>
    t = null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span>
    t.<span style="color: black;">SetTranslationFromValues</span><span style="color: black;">&#40;</span>p<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>, p<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, p<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
    null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span> = t</pre></td></tr></table></div>

<p>Alright! The multidimensional array syntax in SetTranslationFromValues is gone but we are left with a list comprehension that is utterly incomprehensible. An alternative is to zip things up.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">cube = Application.<span style="color: black;">ActiveSceneRoot</span>.<span style="color: black;">AddGeometry</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;cube&quot;</span>, <span style="color: #483d8b;">&quot;MeshSurface&quot;</span><span style="color: black;">&#41;</span>
pa = cube.<span style="color: black;">ActivePrimitive</span>.<span style="color: black;">Geometry</span>.<span style="color: black;">Points</span>.<span style="color: black;">PositionArray</span>
<span style="color: #ff7700;font-weight:bold;">for</span> p <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">zip</span><span style="color: black;">&#40;</span>pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>, pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, pa<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>:
	null = cube.<span style="color: black;">AddPrimitive</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Null&quot;</span><span style="color: black;">&#41;</span>
	t = null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span>
	t.<span style="color: black;">SetTranslationFromValues</span><span style="color: black;">&#40;</span>p<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>, p<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, p<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
	null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span> = t</pre></td></tr></table></div>

<p>I&#8217;m feeling much better now&#8230; The zip function takes an arbitrary number of arguments, all of which must be lists. It will then create a resulting list containing all the first elements in the multiple lists, then all the second elements, then all the third elements and so on and so forth. Check out the <a href="http://docs.python.org/lib/built-in-funcs.html">python docs</a> for an in depth explanation.</p>
<p><strong>The Star of the Show</strong></p>
<p>Check this last one out! I was really having fun here!</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">cube = Application.<span style="color: black;">ActiveSceneRoot</span>.<span style="color: black;">AddGeometry</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;cube&quot;</span>, <span style="color: #483d8b;">&quot;MeshSurface&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">for</span> p <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">zip</span><span style="color: black;">&#40;</span><span style="color: #66cc66;">*</span>cube.<span style="color: black;">ActivePrimitive</span>.<span style="color: black;">Geometry</span>.<span style="color: black;">Points</span>.<span style="color: black;">PositionArray</span><span style="color: black;">&#41;</span>:
	null = cube.<span style="color: black;">AddPrimitive</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Null&quot;</span><span style="color: black;">&#41;</span>
	t = null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span>
	t.<span style="color: black;">SetTranslationFromValues</span><span style="color: black;">&#40;</span><span style="color: #66cc66;">*</span>p<span style="color: black;">&#41;</span>
	null.<span style="color: black;">Kinematics</span>.<span style="color: black;">Global</span>.<span style="color: black;">Transform</span> = t</pre></td></tr></table></div>

<p>There is a special syntax in Python that you can use when <a href="http://docs.python.org/ref/calls.html">calling a method</a>. If you have a list and it contains the right number of arguments for a method call you wish to do, you can put *myList in the parenthesis of the method call instead of myList[0], myList[1], &#8230;, myList[n].</p>
<p>Again, the example I am using is a very trivial one but in many cases flipping the returned XSI data has been more than useful.</p>
<p>In case you were wondering, the performance hit is minimal. Running 500 iterations of the last flipped version of the code on this page took 63.157 seconds vs. 63.125 seconds for the first version of the code in it&#8217;s unflipped glory. And in case you were wondering it was 63.342 seconds for the list comprehension version. These are all wallclock times so minimal is hardly the word, I think at this point the difference is rather insignificant.</p>
<p>Its just all about typing a bit less and being a bit clearer.</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=120&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/120/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
