<?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; Rendering</title>
	<atom:link href="http://www.softimageblog.com/archives/category/rendering/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>Vintage Softimage &#8211; A tribute to Phoenix Tools and Arete</title>
		<link>http://www.softimageblog.com/archives/504#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=vintage-softimage-a-tribute-to-phoenix-tools-and-arete</link>
		<comments>http://www.softimageblog.com/archives/504#comments</comments>
		<pubDate>Fri, 04 Jun 2010 16:55:40 +0000</pubDate>
		<dc:creator>Stefano Jannuzzo</dc:creator>
				<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Rendering]]></category>
		<category><![CDATA[Texturing]]></category>
		<category><![CDATA[World of VFX]]></category>

		<guid isPermaLink="false">http://www.softimageblog.com/?p=504</guid>
		<description><![CDATA[The middle-aged of you will probably remember those names from the 90s.
Phoenix Tools was a plugin company for Softimage 3D, and I was one of the founders. We did good and bad, and we eventually closed down in 2002.
Arete had an excellent reputation for their ocean and atmospheric library, called Digital Nature Tools. I think [...]]]></description>
			<content:encoded><![CDATA[<p>The middle-aged of you will probably remember those names from the 90s.</p>
<p>Phoenix Tools was a plugin company for Softimage 3D, and I was one of the founders. We did good and bad, and we eventually closed down in 2002.</p>
<p>Arete had an excellent reputation for their ocean and atmospheric library, called Digital Nature Tools. I think their main business was in military simulation, however they had a part in every cg-generated ocean in the movies of the 90s.</p>
<p>In 2001 we joined forces to port their software under XSI (I think it was 2.0). Unfortunately, after the first version came out, both companies shut down.</p>
<p>8 years later, I am working on a feature animation with a few shots in water, and I realized I still had on some cd the arete psunami libraries. So, I decided to give it a try. I removed the license check from the shaders and I compiled them with good old Visual Studio 6, linking against the oldest mental ray library I could find (3.3), and in the end it worked. I was kind of touched when I finally saw the displaced grid rendering in Softimage 2010.</p>
<p>I think I will do no harm to anybody releasing these shaders. Both companies are dead since long, and this is my little tribute to them and the talented people who worked there.</p>
<p>The <a href='http://www.softimageblog.com/userContent/upload/2010/06/PT_Arete_Dnt.xsiaddon.zip'>addon is only available for win32</a>, and no, no chances for other platforms. That Arete library is the only one I have. If you do, use it at your own risk (and fun, i would say. There are probably better ways to do oceans nowadays).</p>
<p>And, you know what, I don&#8217;t even know how the full package works. I don&#8217;t have anymore the documentation, nor the scripts we provided. If my old companions will find them, I will post them later.</p>
<p>The basic usage however is easy. You have to connect a DNT_Ocean_Sh and DNT_Time to a DNT_Ocean_Evolver. What come out is the ocean instance, that can then go into the other nodes. Also, you want to apply DNT_Air as environment to have nice reflection and the atmosphere.</p>

<a href='http://www.softimageblog.com/archives/504/arete-1' title='Arete #1'><img width="150" height="103" src="http://www.softimageblog.com/userContent/upload/2010/06/arete.1.jpg" class="attachment-thumbnail" alt="Arete - displacement and optics" title="Arete #1" /></a>
<a href='http://www.softimageblog.com/archives/504/arete-2' title='Arete #2'><img width="150" height="100" src="http://www.softimageblog.com/userContent/upload/2010/06/arete.2.jpg" class="attachment-thumbnail" alt="Arete - Optics only" title="Arete #2" /></a>
<a href='http://www.softimageblog.com/archives/504/arete-3' title='Arete #3'><img width="150" height="99" src="http://www.softimageblog.com/userContent/upload/2010/06/arete.3.jpg" class="attachment-thumbnail" alt="Arete - Bump only rendered with a phong material" title="Arete #3" /></a>

<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=504&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/504/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Constant passes without constant materials</title>
		<link>http://www.softimageblog.com/archives/467#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=constant-passes-without-constant-materials</link>
		<comments>http://www.softimageblog.com/archives/467#comments</comments>
		<pubDate>Fri, 19 Feb 2010 05:17:20 +0000</pubDate>
		<dc:creator>Stefano Jannuzzo</dc:creator>
				<category><![CDATA[Rendering]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=467</guid>
		<description><![CDATA[Recently we had to face this interesting problem: extracting a constant pass out of an arbitrarely complex rendertree. In short, we received scenes set up for rendering, with a given number of passes and channels already set up. However, we needed an extra constant pass which was not planned in advance.
The rendertrees have all kinds [...]]]></description>
			<content:encoded><![CDATA[<p>Recently we had to face this interesting problem: extracting a constant pass out of an arbitrarely complex rendertree. In short, we received scenes set up for rendering, with a given number of passes and channels already set up. However, we needed an extra constant pass which was not planned in advance.</p>
<p>The rendertrees have all kinds of materials, with bump, transparency and reflection in place.</p>
<p>The most obvious approach to solve the problem is brute force: writing a script that would traverse all the materials, and substitute each material with a constant one, using as color the diffuse color of the original material, and inheriting all the subtrees for the transparency and reflection mixing. This would have required a couple of days of scripting, and, as any brute force approach, is not really elegant.</p>
<p><span id="more-467"></span>Another idea we tried was tuning the normal. We replaced all the lights with a single directional white light, pointing downward. Then, we connected a constant vector to the bump port of each material, pointing upward (0,1,0). The light has no specular contribution and the ambience is set to black. This way, only the diffuse component of the materials is returned, and since the normal always points upward, the incidence with the light is always 1, so all the materials behave like constant.</p>
<p>Unfortunately this method has a major disadvantage: since the direction of the reflected and refracted rays leaving a (reflecting/refracting) material depends on the normal, the result is completely wrong for secondary rays, because we are moving the normal to an arbitrary (up) direction. So, using the bump port was not a solution.</p>
<p>However, this failure pointed us towards a better solution. </p>
<p>Again, we just had to fix the diffuse component. In all the material shaders (except a few exceptions like hair, which we treat separately), the way the diffuse component is computed is as follows:</p>
<ol>
<li>Get the light color</li>
<li>Multiply it by the incidence</li>
<li>Multiply the result by the diffuse color</li>
</ol>
<p>So, if you want to get back the plain diffuse color, you just have to tell the light to return 1 (white) divided by the incidence. This way, the material shader returns diffuse * incidence * (1 / incidence) == diffuse.</p>
<p>This the starting case (a phong sphere &#8211; top), and our first result (bottom)</p>
<p>
<a href="http://www.xsi-blog.com/userContent/upload/2010/02/l_500_333_3B015D1E-4E38-4451-BBA2-75CAE8E4FD9B.jpeg"><img src="http://www.xsi-blog.com/userContent/upload/2010/02/l_500_333_3B015D1E-4E38-4451-BBA2-75CAE8E4FD9B.jpeg" alt="" class="alignnone size-full" /></a><br />
<a href="http://www.xsi-blog.com/userContent/upload/2010/02/l_500_333_7004049C-F9D0-4B35-88DA-C9EFCF4CA49B.jpeg"><img src="http://www.xsi-blog.com/userContent/upload/2010/02/l_500_333_7004049C-F9D0-4B35-88DA-C9EFCF4CA49B.jpeg" alt="" class="alignnone size-full" /></a>
</p>
<p>And this is the rendertree for the light</p>
<p><a href="http://www.xsi-blog.com/userContent/upload/2010/02/l_890_753_AA4C0181-477E-42A4-8F9B-5CEDC53377A1.jpeg"><img src="http://www.xsi-blog.com/userContent/upload/2010/02/l_890_753_AA4C0181-477E-42A4-8F9B-5CEDC53377A1.jpeg" alt="" class="alignnone size-full" /></a></p>
<p>Also in light shaders, the normal can be used (it&#8217;s the lit point normal). The dot product of the normal with the light direction L0 is the incidence, which we then use to divide 1, and return the result as the intensity of the light.</p>
<p>That was ok for a half dome, but what about the rest? A possibility is simply to double the light, specularly to the xz plane, cloning the rendertree (and L0 becoming 0,-1,0 for the other light). That would cover 99% of the cases, being any oriented surface sample being lit only by one of the two lights at most. </p>
<p>The &#8220;at most&#8221; is the remaining problem. If you have a cube, its vertical faces won&#8217;t be lit by any!</p>
<p>So, we have to expand a bit this setup. We start setting up a minimal light rig, ensuring that al least one of the lights will always hit any oriented surface. The minimum number of lights for such a rig is 4, equally distributed in space.</p>
<p>So, we take a tetrahedron of radius 1, centered at the origin, and cluster constrain a directional white light to each vertex, all pointing toward the origin.</p>
<p><a href="http://www.xsi-blog.com/userContent/upload/2010/02/l_523_415_482A408E-88B3-46DD-89EC-C17C7664D474.jpeg"><img src="http://www.xsi-blog.com/userContent/upload/2010/02/l_523_415_482A408E-88B3-46DD-89EC-C17C7664D474.jpeg" alt="" class="alignnone size-full" /></a></p>
<p>Each light has the following rendertree (with a small change for each of them)</p>
<p><a href="http://www.xsi-blog.com/userContent/upload/2010/02/l_1050_630_1071D6AB-AFF9-4021-8AE5-C2E6B5C2DEC7.jpeg"><img src="http://www.xsi-blog.com/userContent/upload/2010/02/l_1050_630_1071D6AB-AFF9-4021-8AE5-C2E6B5C2DEC7.jpeg" alt="" class="alignnone size-full" /></a></p>
<p>This is the tree for light L0: the left side vectors are the four lights positions, set by expression. Since The tetrahedron has radius one, they also represent the direction toward each lights. All their incidence with the normal is computed and sent to a positivity check, and this check drives a switcher between zero and one. So, the sum of these 4 switchers is the number of lights (1 to 3) visible from the rendered point.<br />
This number is multiplied by the light incidence and the result divides 1, as in the single light case.</p>
<p>Why do we need the number of visible light? Since we have 4 lights, the total amount of light that gets received by the point is<br />
L = L0*I0 + L1*I1 + L2*I2 + L3*I3, for each positive Iight. In fact, the material shaders won&#8217;t even query a light if its incidence is negative.<br />
So, as before, we still return 1 divided by the light incidence, but also divided by the number of visible lights, so that the overall sum of the visible lights is always 1.</p>
<p>The other 3 lights have the same rendertree, except the dividing incidence comes from I1, I2, I3 respectively. For instance, this is the tree for light L1.</p>
<p><a href="http://www.xsi-blog.com/userContent/upload/2010/02/l_675_287_B319D53B-A452-40DC-BDC3-5A381C52C2E4.jpeg"><img src="http://www.xsi-blog.com/userContent/upload/2010/02/l_675_287_B319D53B-A452-40DC-BDC3-5A381C52C2E4.jpeg" alt="" class="alignnone size-full" /></a></p>
<p>And finally, we have our constant result. You can download the rig <a href="http://www.xsi-blog.com/userContent/upload/2010/02/TetraLight.emdl.zip">here</a>.</p>
<p><a href="http://www.xsi-blog.com/userContent/upload/2010/02/l_500_333_4E492BE9-0BA1-4BAC-82C8-C7EA69A231EF.jpeg"><img src="http://www.xsi-blog.com/userContent/upload/2010/02/l_500_333_4E492BE9-0BA1-4BAC-82C8-C7EA69A231EF.jpeg" alt="" class="alignnone size-full" /></a></p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=467&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/467/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Fast Radiosity using Diffuse convolved environment map</title>
		<link>http://www.softimageblog.com/archives/248#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=fast-radiosity-using-diffuse-convoluted-environment-map</link>
		<comments>http://www.softimageblog.com/archives/248#comments</comments>
		<pubDate>Sat, 21 Mar 2009 19:10:33 +0000</pubDate>
		<dc:creator>Harry Bardak</dc:creator>
				<category><![CDATA[Rendering]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=248</guid>
		<description><![CDATA[What you need to reproduce theses examples : 
HDRShop
    HDRShop plugin Diffuse_SH.
&#160;
I used to work for a 2 years at Framestore-CFC in London. That mean I use Maya and PRMan as my main application. During this year I learn a lot of thing but also hear a lot of false assumtion about [...]]]></description>
			<content:encoded><![CDATA[<p>What you need to reproduce theses examples : </p>
<p align="left"><font face="Arial, Helvetica, sans-serif"><a href="www.hdrshop.com">HDRShop</a><br />
    <a href="http://www.banterle.com/francesco/download.html">HDRShop plugin Diffuse_SH.</a></font></p>
<p align="left">&nbsp;</p>
<p>I used to work for a 2 years at Framestore-CFC in London. That mean I use Maya and PRMan as my main application. During this year I learn a lot of thing but also hear a lot of false assumtion about rendering in general. Typically the common comment was that PRman was more suitable to render complex character and that MR was very slow for this kind of job. In fact people arguing about this doesn&#8217;t not really know what are the status of modern rendering such as MR, V-ray or any others assuming everything was frozen since 5 years or just repeating what the veteran keep repeating. </p>
<p>But I am not engaging a new flame wars between renderer. Actually I don&#8217;t care which is the better today in 2008 you can produce beautiful picture with any renderer available in the market ( and yes that include maya&#8217;s software render well known to be a piece of crap ;) ) The most important is the guys behind the scene. But I need to put everything in their respective context to elaborate and may be justify what and why I will describe later.</p>
<p>Actually mental ray is very fast, if you do things correctly. If you don&#8217;t do things correctly ( such as massive spatial and/or temporal oversampling ) it as well as any other renderer will be very slow. But I always get this &quot;PRMan displaces faster&quot; stuff. Of course it does&#8230;. until you actually trace a ray.<br />
You have to know that PRMan lives in a mindset where raytracing is so slow that you avoid it at all cost. The thing is that in PRMan, if you try to shoot a ray, it too has to do all those things that a raytracer do by nature. So the minute you actually shoot a single ray, PRMan has to do what mental ray always does&#8230;. and the comparasion suddenly isn&#8217;t so much in favour of PRMan any more.</p>
<p>So people using PRman developed a completely different approach to same problem : Lighting and Rendering a scene while trying to avoid at all cost raytracing. That what is interesting. What will happen if I use these approach with Mentalray within XSI ? Maybe I can have a hybrid solution keep the best of the two world to render my character(s) ? </p>
<p align="left">&nbsp;</p>
<h3 align="left"><strong><font face="Arial, Helvetica, sans-serif">Diffuse Convolution on Environment map </font></strong></h3>
<p>What&#8217;s that ? To keep it simple you can assume that a diffuse convolved map is envmap where you apply a smart blur that will give you the result of sampling this envmap with an infinite number of rays with the Final Gathering on a perfect lambertian surface. For more information you can refer to Debevec&#8217;s research ( the father of HDR images ). </p>
<p><img alt="" src="http://gl.ict.usc.edu/HDRShop/tutorial/images/image001.jpg" class="alignnone" width="128" height="64" />       <img alt="" src="http://gl.ict.usc.edu/HDRShop/tutorial/images/image002.jpg" class="alignnone" width="128" height="64" /></p>
<p>Having your envmap pre-blurred is a great advantage because you don&#8217;t need to sample it several times to get the correct illumination. Only one sample suffice to get the illumination on your surface. Actually you must cast only one ray. Otherwise you will average twice the map and the illumination will not be correct any more.</p>
<p>The ray direction that you will cast must also be in the same direction as your surface normal. The Xsi&#8217;s Ambient Occlusion setup correctly will do perfectly the job. You need one sample , the spread will be very small to not deviate from the normal ( 0.01 ) and the mode should be set to environement sampling. Obviously you can code a shader that do the correct environement lookup. I am using the XSI&#8217;s AO shader because it&#8217;s available out of the box.</p>
<p align="left">&nbsp;</p>
<h3 align="left"><strong><font face="Arial, Helvetica, sans-serif">Render Balls simple test.</font></strong></h3>
<p align="left"><img /></p>
<div class="wp-caption alignnone" style="width: 910px"><img alt="Env sampling brute vs FG vs Diffuse Conv" src="http://www.harrybardak.co.uk/img/diffConv/DC_renderballs.jpg" width="600" height="200" /><p class="wp-caption-text">Env sampling brute vs FG vs Diffuse Conv</p></div>
<p align="left">&nbsp;</p>
<p>The way I set up the scene is very simple. I tried to get the contribution of the envmap once convolved and only this. So there is no illumination model just different approach.<br />
The HDR used for this test the one called beach.hdr that you can find at <a href="www.debevec.org">debevec&#8217;s website</a>. The map that you download is an angular map so you have to convert it to spherical coordinate system. The easiest way is to HDRshop and convert to angular map to longitute/latitude map. Once the map ready, I put it in my scene as an environement map.<br />
The first sphere is the result of an AO shader set to environement sampling mode with a spread of 0.8. I had to crank up the sampling to 1024 to get a smooth solution. AO env sampling is brute force approach. There is no importance sampling so that mean you have cast a lot of ray to get a smooth result specially with high dynamic range image. And obviously that be very slow. It&#8217;s the slowest render.<br />
With the second sphere I used to FG to sample env. Same settings as before except that I use a shader that return irradiance and activate FG. FG is a lot faster that the previous method because it doesn&#8217;t sample every point with 1024 rays. Instead FG will sample a certain amount of point and interpolate the result between the point calculated. But for this comparaison I pushed the number of rays to 4096 to make sure I got enough accuracy to have fair comparaison with the quality of the next approach. Even by pushing the number of rays to 4096, it was still faster to render with FG.</p>
<p>With last sphere I made a diffuse convolution in HDRshop ( using the SH_diffuse plug in because it &#8217;s a lot faster than HDRshop&#8217;s function), applied the resulting image as an environement map and use the XSI&#8217;s AO shader with the sampling set to 1 and the spread to 0.01.<br />
The render time is ultra fast and match FG solution. It &#8217;s actually realtime because the convolution has been already calculated once in HDRshop. </p>
<h3 align="left">&nbsp;</h3>
<h3 align="left"><strong><font face="Arial, Helvetica, sans-serif">Render Balls too simple let&#8217;s try with something that got balls.</font></strong></h3>
<p>We got a exact match with the FG for a fraction of time. We can conclude that the diffuse convolution is good enough to simulate FG. But in our test we were using simple sphere. This an ideal case but we needed to check first if the lookup was correct before to move on something more serious. </p>
<p>I like buddha. I like him because it&#8217;s a one million polygon model that can be compared to any displaced model push out from Zbrush or Mudbox.<br /> I have simply applied what I did with the spheres.<br />
The first render is with an diffuse convolved envmap. It took approximately 50 sec to render with a large part of preprocessing the scene. If I had a realtime shader could get the same result ( minus anti aliasing ) in realtime. Obviously there is no occlusion calculated but that due to fact I asked only an environment lookup around the normal of the surface.<br />
The second image is what you get if you were using FG and third one is a difference done in shake<br />
to highlights the difference between the two renders.<br />
<br />
<img alt="" src="http://www.harrybardak.co.uk/img/diffConv/DC_buddha.jpg" class="alignnone" width="720" height="300" /><br />
<br />
As you can see only the occlusion and the FG colored bounce are missing. If you apply a simple occlusion on the top you will endup with an image that is fairly close to the FG solution but with only a fraction of the time involved by the process.<br />
<br />
<img alt="" src="http://www.harrybardak.co.uk/img/diffConv/DC_buddha_with_AO.jpg" class="alignnone" width="240" height="342" /></p>
<p align="left"><img /></p>
<p align="left">&nbsp;</p>
<h3 align="left"><strong><font face="Arial, Helvetica, sans-serif">OK but in production is it worth to use it ?</font></strong></h3>
<p>Well I will say yes and no. It depand the number of shot you have to do and the time you got to complete them. This technique involve a bit of setup at the shading level while the using FG is straight foward. So if you are working on movie that need a 2K ( or more ) render then yes. Memory foot print is very low and it&#8217;s damn fast to render it specially with very heavy object like displaced object or Hair object..<br />
At the moment you need to set this at the shader for every object. Ideally the best will be to set this as a global ambience. Unfortunately you can&#8217;t plug anything in the global ambience parameter. So the best if to use a light that cast only ambient light. And for that you need to code it so ask your favourites shader writer to do it. </p>
<p align="left">&nbsp;</p>
<p align="left"><strong><font face="Arial, Helvetica, sans-serif">Publications :</font></strong></p>
<p align="left"><font face="Arial, Helvetica, sans-serif"><a href="http://www1.cs.columbia.edu/%7Eravir/papers/envmap/">An Efficient Representation for Irradiance Environment Maps</a></font></p>
<p align="left">&nbsp;</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=248&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/248/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Previewing Shadowmaps</title>
		<link>http://www.softimageblog.com/archives/269#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=previewing-shadowmaps</link>
		<comments>http://www.softimageblog.com/archives/269#comments</comments>
		<pubDate>Sun, 06 Jul 2008 14:30:21 +0000</pubDate>
		<dc:creator>Stefano Jannuzzo</dc:creator>
				<category><![CDATA[Rendering]]></category>
		<category><![CDATA[.zt]]></category>
		<category><![CDATA[imf_disp]]></category>
		<category><![CDATA[mentalray]]></category>
		<category><![CDATA[mi2]]></category>
		<category><![CDATA[ray3]]></category>
		<category><![CDATA[shadowmaps]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/?p=269</guid>
		<description><![CDATA[It is sometimes desirable to be able to check the shadow maps look.
Since XSI does not allow for shadow maps paths, this is how you can do (a ray3 standalone license is required).
After setting up a shadow mapped light, export a single frame archive (mi2) ascii file.
Edit the file with a text editor, and look [...]]]></description>
			<content:encoded><![CDATA[<p>It is sometimes desirable to be able to check the shadow maps look.</p>
<p>Since XSI does not allow for shadow maps paths, this is how you can do (a ray3 standalone license is required).</p>
<p>After setting up a shadow mapped light, export a single frame archive (mi2) ascii file.</p>
<p>Edit the file with a text editor, and look for the light options section. There you find the shadowmap settings.</p>
<p>To have the map written, just add a line with the &#8220;shadowmap file&#8221; statement, followed by the path for the file (.zt format mandatory).</p>
<pre>light "Spot_Root/Spot/Light" = "Spot_Root/Spot/Light/soft_light"
origin 0 0 0
direction 0 0 -1
spread 0.866025
shadowmap on
shadowmap resolution 500
shadowmap softness 0.002
shadowmap samples 20
#the following line added
shadowmap file "C:\temp\sm.zt"
energy 7500 7500.01 7500
...
end light
</pre>
<p>Save the file and render it with ray3.</p>
<p>To just render the shadow map and not the image, you can use the handy &#8220;-shadowmap only&#8221; option. In my case, being the archive file named C:\temp\out.mi2, I run</p>
<pre>ray3 C:\temp\out.mi2 -shadowmap only -verbose on</pre>
<p>Once finished, you should find the map on disk (C:\temp\sm.zt in my case), and view it by</p>
<pre>imf_disp C:\temp\sm.zt</pre>
<p>This is a simple sphere over a grid</p>
<p><a href="http://www.xsi-blog.com/userContent/upload/2008/07/sm.jpg"><img class="alignnone size-medium wp-image-270" title="Shadow Maped Sphere" src="http://www.xsi-blog.com/userContent/upload/2008/07/sm-276x300.jpg" alt="Shadow Maped Sphere" width="276" height="300" /></a></p>
<p>The grid does not show up, not being a shadow caster itself.</p>
<p>So you can now check the shadow map extent, which is particularly interesting for infinite lights, being determined by the extent of the parts of the scene that cast shadows (for spot light sources, only the cone spread parameter matters).</p>
<p>Also, by left clicking on the image, you can read the z value for each pixel, and finally (maybe) understand the obscure bias option ;).</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=269&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/269/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Render Tree-Gonometry</title>
		<link>http://www.softimageblog.com/archives/252#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=render-tree-gonometry</link>
		<comments>http://www.softimageblog.com/archives/252#comments</comments>
		<pubDate>Thu, 13 Mar 2008 12:44:04 +0000</pubDate>
		<dc:creator>Stefano Jannuzzo</dc:creator>
				<category><![CDATA[Rendering]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/252</guid>
		<description><![CDATA[After so many years, Xsi still does not ship any basic trigonometric functions like sine and cosine. Although they are very easy to code for a shader writer, it is, however, possible to dig them out of the existing nodes.
This is an example of how sometimes you can write a phenomenon just to downgrade a [...]]]></description>
			<content:encoded><![CDATA[<p>After so many years, Xsi still does not ship any basic trigonometric functions like sine and cosine. Although they are very easy to code for a shader writer, it is, however, possible to dig them out of the existing nodes.</p>
<p>This is an example of how sometimes you can <a href="http://www.xsi-blog.com/archives/105">write a phenomenon</a> just to downgrade a more powerful node to get what you need.</p>
<p>The starting point is noticing that the node that more resembles the sine and cosine look is the Wave one. By connecting it straight to the surface and setting the parameters as shown, you have something really looking like a cosine function.</p>
<p><span id="more-252"></span><a href='http://www.xsi-blog.com/userContent/upload/2008/03/trigo1.jpg' title='Wave shader'><img src='http://www.xsi-blog.com/userContent/upload/2008/03/trigo1.jpg' alt='Wave shader' /></a></p>
<p>To enlarge the wave period to match the cosine&#8217;s, we simply need to devide the input by 2*PI.</p>
<p><a href='http://www.xsi-blog.com/userContent/upload/2008/03/trigo2.jpg' title='Cosine Shader'><img src='http://www.xsi-blog.com/userContent/upload/2008/03/trigo2.jpg' alt='Cosine Shader' /></a></p>
<p>Now we can push everything into a single phenomenon, having just one input parameter (the cosine argument).<br />
This is the connection section:</p>
<pre>Node "sib_scalar_math_basic" = guid "{3843DA00-A1F4-11D3-AE51-00A0C96E63E1}";
Node "sib_scalars_to_vector" = guid "{6405C7C0-623C-11D3-8C0A-00A0243E3672}";
Node "mib_texture_wave" = guid "{5092B017-B473-11D1-90E1-0000F804EB21}";
Node "sib_color_to_scalar" = guid "{865EBB7A-BCFA-11D1-90E9-0000F804EB21}";

Connection "sib_scalar_math_basic::input1" = interface "x";
Connection "sib_scalar_math_basic::input2" = value 6.28318;
Connection "sib_scalar_math_basic::op" = value 3;

Connection "sib_scalars_to_vector::inputX" = "sib_scalar_math_basic";
Connection "sib_scalars_to_vector::inputY" = value 0.0;
Connection "sib_scalars_to_vector::inputZ" = value 0.0;
Connection "sib_scalars_to_vector::modeX" = value 1;
Connection "sib_scalars_to_vector::modeY" = value 2;
Connection "sib_scalars_to_vector::modeZ" = value 3;

Connection "mib_texture_wave::coord" = "sib_scalars_to_vector";
Connection "mib_texture_wave::amplitude_x" = value 1.0;
Connection "mib_texture_wave::amplitude_y" = value 0.0;
Connection "mib_texture_wave::amplitude_z" = value 0.0;
Connection "mib_texture_wave::offset" = value 0.0;

Connection "sib_color_to_scalar::input" = "mib_texture_wave";

Connection root = "sib_color_to_scalar";</pre>
<p><a href='http://www.xsi-blog.com/userContent/upload/2008/03/trigo3.jpg' title='Cosine shader used as displacement'><img src='http://www.xsi-blog.com/userContent/upload/2008/03/trigo3.jpg' alt='Cosine shader used as displacement' /></a></p>
<p>Having the cosine, it is now easy to get the sine also, since</p>
<p>sin(x) = -cos(x+PI/2)</p>
<p>Just make up another phenomenon and &#8220;code&#8221; the identity:</p>
<pre>Node "cos" = guid "{19671972-1008-0000-8410-6A45FDB990DD}";
Node "sib_scalar_math_basic" = guid "{3843DA00-A1F4-11D3-AE51-00A0C96E63E1}";
Node "sib_scalar_math_unary" = guid "{A0471300-AC2E-11D3-AE56-00A0C96E63E1}";

//add PI/2 to the input parameter
Connection "sib_scalar_math_basic::input1" = interface "x";
Connection "sib_scalar_math_basic::input2" = value 1.57079;
Connection "sib_scalar_math_basic::op" = value 0;

//send result to cos
Connection "cos::x" = "sib_scalar_math_basic";

//negate result
Connection "sib_scalar_math_unary::input" = "cos";
Connection "sib_scalar_math_unary::op" = value 1;

//done
Connection root = "sib_scalar_math_unary";</pre>
<p>You can download the two spdl&#8217;s from <a href="http://sjannuz.com/sj_trigo/">here</a>.</p>
<p>Of course you can also go further, since tan = sin/cos, cot = cos/sin, etc. Getting the inverse functions, instead, is another matter.</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=252&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/252/feed</wfw:commentRss>
		<slash:comments>4</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>About exporting shaders</title>
		<link>http://www.softimageblog.com/archives/243#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=about-exporting-shaders</link>
		<comments>http://www.softimageblog.com/archives/243#comments</comments>
		<pubDate>Mon, 09 Jul 2007 01:33:00 +0000</pubDate>
		<dc:creator>Bernard Lebel</dc:creator>
				<category><![CDATA[Programming / Scripting]]></category>
		<category><![CDATA[Rendering]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/243</guid>
		<description><![CDATA[ABSTRACT
This article discusses some of the problems and solutions involved in exporting shaders from XSI, using custom tools and custom file formats.
THE INITIAL GOALS
At Big Bang I wanted the ability to write materials to XML files (and read them back, of course). This would serve two major purposes. First, it would offer a granular set [...]]]></description>
			<content:encoded><![CDATA[<p><strong>ABSTRACT</strong></p>
<p>This article discusses some of the problems and solutions involved in exporting shaders from XSI, using custom tools and custom file formats.</p>
<p><strong>THE INITIAL GOALS</strong></p>
<p>At Big Bang I wanted the ability to write materials to XML files (and read them back, of course). This would serve two major purposes. First, it would offer a granular set of data, which could be re-used in different contexts. I could have used preset file instead of a XML file, but the second major purpose of the project prevented that: I wanted to be able to not only read the file, but also to make modifications in it if there was ever such a need.</p>
<p>To be honest I was quite disappointed that after I spent so much time and effort into this project, it turned out I needed to make such edits to material files only for about 20 of them (as of this writing, there are about 3,800 such material files in the pipeline). I could have also used the XML file at other things, like building templates for standard materials, using it for the approval of shading tasks, etc. But in the end that did not happen. However, the work I did for exporting materials could be used for other things.</p>
<p><span id="more-243"></span><strong>THE CHALLENGES OF MATERIAL EXPORT</strong></p>
<p>It didn&#8217;t take too long until I felt that exporting materials can be a rocky business. At first glance it seems very straightforward: select an object, run a script to export its material. In the material file, I wanted not only the entire material described, but I also wanted to list the image clips and the image sources used by this material. Plus, I wanted to know which scene objects are using this material, including geometry, clusters, groups and partitions. So that when I import the material, I could re-create it, as well as its image clips and sources, and apply it to objects using it if they are in the scene.</p>
<p>Next, I wanted the code to handle inputs that consisted of more than one object. For instance, given an input of objects (either geometry, partitions, clusters, or even materials), the code had to figure out what to export and do it right. I didn&#8217;t want the script to attempt to export the material of a chain, for example. Given that input management allowed many objects to be visited, and that some of these objects could use the same material, I had to keep track of which material had already been exported.</p>
<p>On top of the material, the image sources, the image clips and the objects using this material, I had to write the actual shaders to the file. Afterall the goal was to be able to re-create the material from an XML file, so I needed the list of shaders. One may think that to write shaders to a file, all that is needed is to recursively walk down the material&#8217;s Render Tree and, well, write the shaders as they are visited.</p>
<p>Not quite. Some shaders are connected into several other inputs. Unaware recursive traversal of the Render Tree meant that entire branches could end up being written several times. That was a waste of space and CPU cycles. Plus, if many branches are being repeated, how would the importing code know that it&#8217;s actually the same branch?</p>
<p>So I had to find a way to organise shaders with a unique &#8220;key&#8221;. At first I considered using their FullName. I was in for a big surprise.</p>
<p>In the Explorer, switch to Scene Root scope. If you iterate the Properties/LocalProperties of an object, find a material, and print its FullName, it will return the name of the material with the object&#8217;s FullName prefixed. Same if you select the material under the object. However, switch the Explorer&#8217;s scope to Materials. If you select the same material and print its FullName, then you get the name of the material prefixed with the library&#8217;s FullName, which is garanteed to be unique.</p>
<p>I call this last one the material&#8217;s &#8220;AbsoluteName&#8221;. There might be better terms for this, however I thought of it as browsing the file system, where there can be no two absolute file name for the same file (well, it&#8217;s possible, but let&#8217;s not get carried away).</p>
<p>It&#8217;s easy to work out the AbsoluteName of a material, even when you get the material from the objects owning it instead of the library. But to my knowledge, it&#8217;s impossible to use the same approach for shaders under this material. The FullName of a shader changes depending on the parameter it is being read from. For example, if you have an Image shader connected into an input of a Blinn shader and the input of an another shader, logging the Image&#8217;s FullName when traversing the Render Tree will return ..Blinn.Image and &#8230;Image.</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;">xsi = Application
&nbsp;
<span style="color: #808080; font-style: italic;"># Get selected object</span>
oSel = 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;"># Get selected object's material</span>
oMat = oSel.<span style="color: black;">material</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> walk<span style="color: black;">&#40;</span> oShader <span style="color: black;">&#41;</span>:
	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>
&nbsp;
	<span style="color: #808080; font-style: italic;"># Iterate the shader parameters</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;"># Get the parameter source</span>
		oSource = oParameter.<span style="color: black;">source</span>
&nbsp;
		<span style="color: #808080; font-style: italic;"># Check if we got a 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;"># Get the classname of the source</span>
			sClassName = xsi.<span style="color: black;">classname</span><span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
			<span style="color: #808080; font-style: italic;"># Check if the classname is a shader</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>:
				walk<span style="color: black;">&#40;</span> oSource <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Loop over material parameters</span>
walk<span style="color: black;">&#40;</span> oMat <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Prints:</p>
<pre># INFO : sphere.Material
# INFO : sphere.Material.Blinn
# INFO : sphere.Material.Blinn.Image
# INFO : sphere.Material.Blinn.Image
# INFO : sphere.Material.Blinn
# INFO : sphere.Material.Blinn.Image
# INFO : sphere.Material.Blinn.Image
# INFO : sphere.Material.Cell
# INFO : sphere.Material.Cell.Image</pre>
<p>So this means you can&#8217;t use the shader FullName as a unique key. If you can&#8217;t rely on the shader&#8217;s FullName, then how can you make sure that you have not visited this shader already? You could possibly use the shader&#8217;s Name. Afterall, in a material, all shaders have a unique name.</p>
<p>But instead of using the names as the key, I preferred to give them a unique ID number (unsigned integer). Image clips would also receive such an ID. When writing the shader parameters to the file, if they had a shader or an image clip connected, it would use the previously defined ID of the shader/image clip. This meant that before writing anything to the XML file, all shaders and image clips would need to be visited. Ultimately, a map of all shaders, based on their ID and their name, could be established.</p>
<p>This resulted in somewhat complex dictionaries and lists, which were hard to maintain and debug. Nonetheless, I managed to make the material export work reliably. It was slow (since it&#8217;s uncompiled code), but it was totally reliable.</p>
<p>I should add that handling parameters such as texture projection pointers posed a challenge worthy of talking about. Those parameters are not like other parameters, they require a different handling. Of course all that is needed is available in the SDK, still, I thought I&#8217;d mention it to those attempting at writing shaders to file.</p>
<p><strong>BEYOND MATERIALS</strong></p>
<p>All went smooth for many many months, until we started looking into lighting and rendering. Then it occured to me that I needed the ability to export passes, so they could be re-created later on. I thought that since I had code that could write shaders to file, then surely I could reuse this code for other things than materials: overrides and pass shader stacks. Well, that&#8217;s what I thought. But my code was built entirely around the material export, so I could hardly use it for overrides and pass stacks without making drastic modifications.</p>
<p><strong>Pass shader stacks</strong><br />
There are two major problems with pass shader stacks.</p>
<p>The first problem is that from all practical purposes, passes have the appearance of materials. If you select a pass and open the Render Tree, you&#8217;ll see the pass object being displayed as if it was a material. In reality, that is not quite right.</p>
<p>For instance, you can iterate the parameters of material, and effectively traverse the Render Tree from there. That is not happening with passes. First, passes do not have a Parameters property, and second, they do not expose shaders in any way. So the Render Tree display is highly misleading, and I had to write code to handle passes differently than materials. In short, I did not find any quick and easy way to get the stack shaders. This is illustrated by this stripped down method that collects the stack &#8220;root&#8221; shaders (see next paragraph):</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
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> addpass<span style="color: black;">&#40;</span> <span style="color: #008000;">self</span>, oPass <span style="color: black;">&#41;</span>:
&nbsp;
	aStacks = <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;"># Iterate pass stacks</span>
	<span style="color: #ff7700;font-weight:bold;">for</span> sStack <span style="color: #ff7700;font-weight:bold;">in</span> aStacks:
&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> sStack <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;
				<span style="color: #808080; font-style: italic;"># do something with shader</span></pre></td></tr></table></div>

<p>The second problem is that in a pass, there can be multiple shaders living at the top-level of a stack (which I shall call &#8220;root&#8221; shaders). For instance, if you apply two volume shaders to a pass and then open the Render Tree of that pass, you&#8217;ll only see the latest volume shader. That&#8217;s another misleading aspect of the pass&#8217;s Render Tree. So it can be assumed a pass shader stack can have multiple &#8220;root&#8221; shaders, and each of them is entirely valid and must be exported if you want to fully export the pass shader stacks. The upshot is that the pass object act as a name space, just as a material: among all the shader stacks of a pass, no two shaders can share the same name. This is great, but that doesn&#8217;t solve our problem of multiple root shaders for a same stack.</p>
<p>Also, when importing a pass shader stack, using the typical ways of creating shaders and plugging them afterward worked for all but the &#8220;root&#8221; shaders. To import pass shader stacks, each shader had to have a &#8220;stackroot&#8221; flag in the XML file. When meeting such a flag, the script would not simply create the shader and plug it later, it would have to run the command that creates root shaders in stacks. This command is the only way to not destroy other existing root shaders!</p>
<p><strong>Overrides</strong><br />
Overrides pose an entirely different set of problems.</p>
<p>They are not materials, and they are not pass shader stacks, yet they can host complex networks of shaders. There also can be more than one override per partition! An override could not be handled as a material nor a pass. Materials, in my framework, were meant to be exported as single files, overrides needed to be exported in a pass file, along the shader stacks, partitions and render options. An override doesn&#8217;t have things like the material&#8217;s UsedBy property, it is a partition property.</p>
<p>Also, overrides on partitions are visible only in the current pass. I thought that this was case only in the GUI, but found out that this extends to the scripting interface. If you iterate the properties of a partition not of the current pass, you&#8217;ll never catch that partition&#8217;s overrides. The only solution was to use a low-level command such as FindObjects() to collect all overrides, which works, and then organise them by pass and by partition, to later write them to file along the partition that&#8217;s being written.</p>
<p>Using FindObjects() to collect overrides returned a lot more overrides that what I expected. All these overrides were named VisibilityOverrideXX (&#8220;XX&#8221; being a number). I basically found out that group objects (groups, layers, partitions) implement the parameter inheritance via an override. This override is created when a parameter of the group object is not set to &#8220;no effect on members&#8221;, but remain invisible at all times, except to commands like FindObjects(). This can be tricky, because you don&#8217;t want to export this override. However you never know when a user will create an override to disable a parameter like &#8220;viewvis&#8221;, and name this override &#8220;VisibilityOverride&#8221;. The only solution when such an override name is encountered is to check that the partition parameters all set to &#8220;no effect on members&#8221;.</p>
<p>But the biggest problem with overrides came with the import. This article is not about shader import (which may be covered in another article), but few things are worth mentioning.</p>
<p>You can&#8217;t add a parameter to an override if the parameter cannot be found in the partition members. The only way to add a parameter to an override is to use SIAddEntryToOverride(), which requires a parameter FullName string. My solution was not very elegant, and is not garanteed. Basically the code tries to find the parameter among the partition members. If find its, then good, the entry can be added. Otherwise, you&#8217;re out of luck. Materials are also tricky because the override parameter name doesn&#8217;t exactly reflect the real shader parameter FullName that has to be overridden. With elaborate string manipulation this can all be worked out, but this is slow and like I said, not very reliable.</p>
<p>Another annoying thing is that an entry added to an override cannot be accessed right away via the object model. At least that was the case in XSI 5.11 (I tried in 6.01 and it seems to work). For instance, the following code would raise an error:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</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;
sParameterName = <span style="color: #483d8b;">'viewvis'</span>
sParameterFullName = <span style="color: #483d8b;">'cube.visibility.%s'</span> <span style="color: #66cc66;">%</span> sParameterName
&nbsp;
xsi.<span style="color: black;">SIAddEntryToOverride</span><span style="color: black;">&#40;</span> oOverride, sParameterFullName <span style="color: black;">&#41;</span>
&nbsp;
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>So to get back the added parameters I had to use strings.</p>
<p><strong>Cameras and lights</strong><br />
I have not talked about cameras and lights so far, simply because as the time of this writing, the need to export their shaders never arose.</p>
<p>In our pipeline, cameras and lights are exported as full-fledge models. Their importance is rather low compared to other models (like characters, for instance), so that exporting their network of shaders was not felt as a necessity. Also, cameras and lights are very light-weight compared to other models, so managing them always as models posed no particular problem.</p>
<p>My belief is that lights can be handled not too differently than materials. The catch word is &#8220;too&#8221;. The light primitive is a bit like a material, it has inputs for connecting shaders. But a light is not a material, and I&#8217;m affraid that handling lights would require yet another dedicated wrapper to hide the real nature of the light.</p>
<p>In the case of camera shaders, my guess is that they behave in a similar fashion as pass shader stacks do. The camera shader stack lives right under the camera primitive, and appears to be accessible only through the NestedObjects property. Just as a pass shader stack, the camera shader stacks accepts multiple shaders at its top level, but when you open the Render Tree, it shows only the first shader of the stack.</p>
<p><strong>THE SHADER NAME SPACE</strong></p>
<p>Obviously, trying to adapt the existing material export code to also export pass shader stacks and overrides turned out to be very tedious. So I had to make drastic changes to the framework, and implement a new interface: the shader name space.</p>
<p>I consider the material, the override and the pass shader stack to be shader name spaces. That is, a space where a collection of shader names live. A material, a pass and an override as all shader name spaces. Lights and cameras are also shader name spaces. As of this writing, however, my framework does not include anything to export and import lights and cameras.</p>
<p>The object used as the shader name space is not necessarily the &#8220;root&#8221; shader, that is, the shader of the tree where all others ultimately plug in. Materials and overrides are both name space and root, but in the case of pass shader stacks, each &#8220;root&#8221; shader of a stack is a shader name space root. So a shader name space can have multiple roots. In the shader name space, the exact nature of the name space is not really important, as it is hidden by the shader name space interface.</p>
<p>Treating data that way made things far more easier to work with. Low-level functions would write to file without knowing the context of use, while wrappers would handle all the specificities of the name space.</p>
<p><strong>PROPOSITION OF IMPROVEMENT FOR MATERIALS</strong></p>
<p>All of this hassle with shader export and shader name space made me think about how it could have been a lot easier. I had to write code to handle all types of shader name space, which resulted in significant amounts of code and increased opportunities for bugs to make it through. The shader name space is in fact an attempt at implementing something that, I believe, should be in XSI: new kinds of materials.</p>
<p>Lights, passes and cameras should implement a material. I don&#8217;t see any reason why not.</p>
<p>- Imagine if light, pass, and camera materials could be managed from the Explorer, just like other materials. Light materials could use an orange color, while pass materials could be green and camera materials could use a gray color. The color is not important, but this mean they could a lot easier to manage, especially in large amounts. They could be organised in libraries, exported, and referenced.</p>
<p>- The mess of pass and camera shader stacks would not be visible anymore.</p>
<p>- Sharing materials among lights, passes and cameras would be a breeze. Increased reusability.</p>
<p>- Programming-wise, they would make the process very homogenous. No matter if you select a light, geometry, or a pass, you can always use its Material property to access the material. Simple, straightforward and consistent.</p>
<p>I don&#8217;t really see where overrides would fit in this design. Perhaps they could be left as they are, but at least it&#8217;s only one special case to handle, not every case.</p>
<p>I&#8217;m strongly against the idea of having a &#8220;super&#8221; material. For instance this material could be applied to anything, however it would expose only parameters suitable for the context it&#8217;s being used in. When applied to different kinds of objects, I hardly see how it would be easy to work with. It would make scripting very inconsistent and require the same kind of tedious handling I have been discussing in this article.</p>
<p>Get a material and a passes file here: <a href='http://www.xsi-blog.com/userContent/upload/2007/07/xml.zip' title='Material and passes XML files'>Material and passes XML files</a></p>
<p><strong>ABOUT DATA INPUT MANAGEMENT IN MATERIAL EXPORT</strong></p>
<p>If you think that this section title sounds redundant, you are right. I have written an article on XSI-Blog about this very subject. I talk again about it because it really is during the development of this project that the importance of data input management struck me. I think that the data input management is one of the most fundamental aspects of any larger programming project.</p>
<p>I wanted the material export to be easy for the user. I wanted the interactive input to be simple and predictable. Users tend to favor the simplest ways of doing things. Given a model, they would expect to export everything related to this model. They don&#8217;t really care about the details, but they know that if all components of character lives under a unique model, then they have to be able to do things by simply handing the model to the script (via selection or picking, for example). However, exporting everything under a model can generate lots of data and take considerable time, so I wanted to give the users the ability to &#8220;focus&#8221; the export: given a selected object, it would export only the object&#8217;s material(s). Or the user could even select the material and export it. Painless, fast and predictable.</p>
<p>But giving the users various ways of exporting things meant that I had to write considerable code to manage the data input. The code that wrote to XML files was dumb, but lives below thick wrappers that would manage all the data input from the user. When the materials to export would reach the actual exporting functions, it would have to be guaranteed to be adequate.</p>
<p>Here is a little chart to show the framework design to cope with this task.</p>
<p>- call from the user<br />
    &#8211; user data input management (objects, materials&#8230;)<br />
        &#8211; final data input management (by type of data)<br />
            &#8211; export of shader name spaces<br />
                &#8211; fetch data from structures<br />
                    &#8211; write to file</p>
<p>The difference between &#8220;user data input management&#8221; and &#8220;final data input management&#8221; is that they used a different interface. Both interface would handle data, but with different purposes in mind. Ultimately, they would end up populating data structures that, too, served specific puroses.</p>
<p>The first layer of input management handled the data provided by the user to make sure it was acceptable for the export and/or the import. For example, it ould make sure that only materials, overrides and passes could make it through for an export. The interface used to perform this task is an interface used throughout the entire framework to take care of all initial data inputs.</p>
<p>That interface, that I called the &#8220;Chapter&#8221; (as in a chapter in the biker world) would know everything about the context: is the user exporting or importing, is he exporting/importing in a shot or in an element, things like that. Through a system of promotion and demotion, the Chapter would store &#8220;suitable&#8221; primary data into structures that could be later accessed by everyone else.</p>
<p>The second layer of input management, &#8220;final data input management&#8221;, as well as &#8220;fetch data from structures&#8221;, used a more specialized interface, dedicated to shader export. Its job would be to hide all the complexities in the differences that exist between materials, overrides and shader stacks.</p>
<p>In &#8220;final data input management&#8221;, it would extract the final data from the &#8220;suitable&#8221; primary data coming from the Chapter, and store it in data structures. In &#8220;fetch data from structures&#8221;, it would read this final data on-demand as the script goes on. The interface would make the storing and reading of that data completely homogenous, no matter if dealing with a material, a pass, or an override. While resulting in very long code (the entire interface is implemented as nested classes), it made the overall data handling incredibly easy.</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=243&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/243/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Points of Light</title>
		<link>http://www.softimageblog.com/archives/219#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=points-of-light</link>
		<comments>http://www.softimageblog.com/archives/219#comments</comments>
		<pubDate>Wed, 06 Jun 2007 02:56:18 +0000</pubDate>
		<dc:creator>Vladimir Jankijevic</dc:creator>
				<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Rendering]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/219</guid>
		<description><![CDATA[Some time ago, I stumbled across the MapVIZ node in Maya and was surprised, that Softimage didn&#8217;t implement something like this in our beloved software package, XSI. Well, last weekend I decided to code it myself.
The first step to take, was to decipher the the file structure of the FG and Photon maps. Since the [...]]]></description>
			<content:encoded><![CDATA[<p>Some time ago, I stumbled across the MapVIZ node in Maya and was surprised, that <a href="http://www.softimage.com">Softimage</a> didn&#8217;t implement something like this in our beloved software package, XSI. Well, last weekend I decided to code it myself.</p>
<p>The first step to take, was to decipher the the file structure of the FG and Photon maps. Since the files are written in binary, this wasn&#8217;t that easy. But with the help of UltraEdit, a HEX editor for Windows, and some trial and error, the main file structure mystery was unveiled. Here is an example of how this looks like in a FG map:</p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/06/hex.gif' alt='hex.gif' width='600'/></p>
<p>The blue marked part is the file type and the version of MentalRay. The green part is a 32 bit integer value which is the total FG Point count. Next comes the FG Point itself. One FG point is represented in 104 bytes in total. That&#8217;s the first bright block. The first 12 bytes are the x, y and z coordinates in 32bit floating point numbers. And so on&#8230;</p>
<p><span id="more-219"></span>Final Gathering maps have another structure than Photon maps and thus it took me twice the time to build the file structure.</p>
<p>Once this was done, I had a little look into OpenGL programming and had a little talk to Andrea Interguglielmi. He helped me with the Logic behind the connection between XSI and OpenGL.</p>
<p>What this tool does is in fact pretty simple. When the plugin is loaded, it registers a new DisplayCallback and allocates some basic memory. Then, after you select a map and press on the &#8220;Show Map&#8221; button, it reads the chosen file and creates an array of position and color values which are stored in the memory and then displayed through some basic OpenGL functions.</p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/06/ppg.gif' alt='ppg.gif' /></p>
<p>With this tool you have the ability to view your saved FG and Photon maps in your 3D-Viewport of XSI. After installation, you can find it under Get-> Property-> JV_MapMiner. It is provided as is and nobody is responsible in any way for any possible damage.</p>
<p>I hope this tool can help people to optimize their renderings and make their lives a little bit more comfortable.</p>
<p>You can download the <a href="http://www.xsi-blog.com/userContent/upload/2007/06/mapMinerAddon.rar">.xsiaddon</a> (tested on XSI 5.x and 6.1) and a <a href="http://www.xsi-blog.com/userContent/upload/2007/06/mapMinerScene.rar">demo project</a> with one FG map, one GI map, one Caustic map and one GI+Caustic map combined (27mb).</p>
<p>You are welcome to send me bug-reports or suggestions.</p>
<p><a href='http://www.xsi-blog.com/userContent/upload/2007/06/fg_s.mov' title='fg_s.mov'>FG Quicktime</a><br />
<a href='http://www.xsi-blog.com/userContent/upload/2007/06/gi_s.mov' title='gi_s.mov'>GI Quicktime</a><br />
<a href='http://www.xsi-blog.com/userContent/upload/2007/06/caustics_s.mov' title='caustics_s.mov'>Caustics Quicktime</a></p>
<p><img src='http://www.xsi-blog.com/userContent/upload/2007/06/types.gif' alt='types.gif' /></p>
<p>Buddha 3D-Model courtesy of the <a href="http://graphics.stanford.edu/data/3Dscanrep">Stanford Computer Graphics Laboratory</a>.</p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=219&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/219/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
<enclosure url="http://www.xsi-blog.com/userContent/upload/2007/06/fg_s.mov" length="1318982" type="video/quicktime" />
<enclosure url="http://www.xsi-blog.com/userContent/upload/2007/06/gi_s.mov" length="1713249" type="video/quicktime" />
<enclosure url="http://www.xsi-blog.com/userContent/upload/2007/06/caustics_s.mov" length="1443768" type="video/quicktime" />
		</item>
		<item>
		<title>FCurves As Shading Tools&#8230;</title>
		<link>http://www.softimageblog.com/archives/200#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=fcurves-as-shaders</link>
		<comments>http://www.softimageblog.com/archives/200#comments</comments>
		<pubDate>Fri, 30 Mar 2007 04:33:35 +0000</pubDate>
		<dc:creator>Stefano Jannuzzo</dc:creator>
				<category><![CDATA[JScript]]></category>
		<category><![CDATA[Rendering]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/200</guid>
		<description><![CDATA[Let&#8217;s look at a short technique that will allow you to export any custom function curve or scripting math function to a texturable node to be used in the rendertree.
Let&#8217;s get a curve
We start by taking a poly strip, with a 100&#215;1 subdivisions. This will be our &#8220;drawing&#8221; table. We then make a cluster of [...]]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s look at a short technique that will allow you to export any custom function curve or scripting math function to a texturable node to be used in the rendertree.</p>
<p><strong>Let&#8217;s get a curve</strong></p>
<p>We start by taking a poly strip, with a 100&#215;1 subdivisions. This will be our &#8220;drawing&#8221; table. We then make a cluster of the whole set of points, and give it a weightmap. The range for the weights is set as wide as possible (-100, 100) to allow for both large positive and negative values. We then apply to the grid a custom property with a profile function curve, which you can then shape as you wish.</p>
<p>Here is a script that will do the steps mentioned above.</p>
<p><span id="more-200"></span></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
</pre></td><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> CreateGrid<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #006600; font-style: italic;">//create a poly strand, 100x1</span>
    <span style="color: #003366; font-weight: bold;">var</span> grid <span style="color: #339933;">=</span> CreatePrim<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;Grid&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;MeshSurface&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.grid.ulength&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">10</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.grid.vlength&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.geom.subdivu&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">100</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.geom.subdivv&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">//create the cluster</span>
    SelectGeometryComponents<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.pnt[*]&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    CreateCluster<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #006600; font-style: italic;">//apply a weightmap</span>
    CreateWeightMap<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;Weight_Map&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.cls.Point.Weight_Map.red&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.cls.Point.Weight_Map.blue&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.cls.Point.Weight_Map.green&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">//allow for large min and max weights</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.cls.Point.Weight_Map.weightmapop.wmin&quot;</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">100</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    SetValue<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.cls.Point.Weight_Map.weightmapop.wmax&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">100</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">return</span> grid<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> ApplyProfile<span style="color: #009900;">&#40;</span>grid<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #003366; font-weight: bold;">var</span> oCPSet <span style="color: #339933;">=</span> grid.<span style="color: #660066;">AddProperty</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;CustomProperty&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;ProfilePSet&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> oFCParam <span style="color: #339933;">=</span> oCPSet.<span style="color: #660066;">AddFCurveParameter</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;ProfileFC&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> oFCurve <span style="color: #339933;">=</span> oFCParam.<span style="color: #660066;">Value</span><span style="color: #339933;">;</span>
&nbsp;
    oFCurve.<span style="color: #660066;">SetKeys</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">new</span> Array<span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0.25</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0.5</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">0.5</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0.75</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0.8</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">var</span> grid <span style="color: #339933;">=</span> CreateGrid<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
ApplyProfile<span style="color: #009900;">&#40;</span>grid<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p><a href='/userContent/upload/2007/03/fm1.jpg' title='fm1.jpg'><img src='/userContent/upload/2007/03/fm1.jpg' alt='fm1.jpg' width='600' /></a></p>
<p><strong>Sampling the curve</strong></p>
<p>This is done by the following 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
</pre></td><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> SampleFunction<span style="color: #009900;">&#40;</span>pSet<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #003366; font-weight: bold;">var</span> par <span style="color: #339933;">=</span> pSet.<span style="color: #660066;">NestedObjects</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> fC <span style="color: #339933;">=</span> par.<span style="color: #660066;">value</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> pArray <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">,</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> vArray <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">,</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">//fcurve min and max range</span>
    <span style="color: #003366; font-weight: bold;">var</span> rangeMin <span style="color: #339933;">=</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> rangeMax <span style="color: #339933;">=</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> range <span style="color: #339933;">=</span> rangeMax <span style="color: #339933;">-</span> rangeMin<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> x<span style="color: #339933;">,</span> y<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">//number of samples, equal to the grid number of points along x</span>
    <span style="color: #003366; font-weight: bold;">var</span> nbSamples <span style="color: #339933;">=</span> <span style="color: #CC0000;">100</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #CC0000;">0</span><span style="color: #339933;">;</span> i<span style="color: #339933;">&lt;</span> <span style="color: #339933;">=</span>nbSamples<span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #006600; font-style: italic;">//i-th column of points</span>
        pArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> i<span style="color: #339933;">*</span><span style="color: #CC0000;">2</span><span style="color: #339933;">;</span>
        pArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> i<span style="color: #339933;">*</span><span style="color: #CC0000;">2</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
&nbsp;
        x <span style="color: #339933;">=</span> rangeMin <span style="color: #339933;">+</span> range <span style="color: #339933;">*</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">/</span>nbSamples<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        y <span style="color: #339933;">=</span> fC.<span style="color: #000066; font-weight: bold;">Eval</span><span style="color: #009900;">&#40;</span>x<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">//value of the function at x</span>
&nbsp;
        vArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> vArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> y<span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">//values for the weight map</span>
&nbsp;
        <span style="color: #006600; font-style: italic;">//set the weights for the i-th column of points</span>
        PaintWeights<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.cls.Point.Weight_Map&quot;</span><span style="color: #339933;">,</span> pArray<span style="color: #339933;">,</span> vArray<span style="color: #339933;">,</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">//select the ProfilePSet first</span>
<span style="color: #003366; font-weight: bold;">var</span> profilePSet <span style="color: #339933;">=</span> selection<span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
SampleFunction<span style="color: #009900;">&#40;</span>profilePSet<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>You just have to take care of setting the current range min and max correctly and then run it with the ProfilePSet. The script will simply sample the curve with the same rate as the grid subdivisions along x. The grid has 101 column of points along the x. We take the corresponding function values and stick them on the weightmap. Note that we&#8217;re using 100 samples, but we could have used much more for both the grid and the sampling rate.</p>
<p><strong>Plotting the weightmap</strong></p>
<p><a href='/userContent/upload/2007/03/fm2.jpg' title='fm2.jpg'><img src='/userContent/upload/2007/03/fm2.jpg' alt='fm2.jpg' width='600'/></a></p>
<p>We apply a color sampler lightmap shader. The input color to be sampled comes from a scalar lookup of the weightmap.</p>
<p>The writable map must be a hdr one (float .ct is my choice), in order to allow for large positive and negative values to be stored. Only the X resolution matters, since we are basically plotting a mono dimensional texture. The map is applied with a xz projection over the grid. You now just need to fire a tiny render region to have the lighmap written.</p>
<p><strong>Reusing the map</strong></p>
<p>As said, the map is one dimensional, so only the u matters when you look it up. So, if you pick a share vector node and connect it to an image lookup, the x coordinate of the vector acts as the input you are remapping by the original function curve. In this example, the lookup feeds the displacement, being the value to be remapped coming from a standard uv projection over the new grid.</p>
<p><a href='/userContent/upload/2007/03/fm3.jpg' title='fm3.jpg'><img src='/userContent/upload/2007/03/fm3.jpg' alt='fm3.jpg' width='600'/></a></p>
<p>What is important is to remember the range the function was originally sampled over, that must match the expected range of the value (the u coordinate in this example) you are going to remap.</p>
<p><strong>Sampling a scripting function</strong></p>
<p>As mentioned, you can also use this approach to paint any math function provided by your scripting function. Instead of using a function curve, you can simply loop and evaluate the desired function.</p>
<p>For instance, this is how you sample and weight paint a sin function in the -pi +pi range:</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="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> SampleSin<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #003366; font-weight: bold;">var</span> pArray <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">,</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> vArray <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">,</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> rangeMin <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">3.14159</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> rangeMax <span style="color: #339933;">=</span> <span style="color: #CC0000;">3.14159</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> range <span style="color: #339933;">=</span> rangeMax <span style="color: #339933;">-</span> rangeMin<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> x<span style="color: #339933;">,</span> y<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> nbSamples <span style="color: #339933;">=</span> <span style="color: #CC0000;">100</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #CC0000;">0</span><span style="color: #339933;">;</span> i<span style="color: #339933;">&lt;</span> <span style="color: #339933;">=</span>nbSamples<span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        pArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> i<span style="color: #339933;">*</span><span style="color: #CC0000;">2</span><span style="color: #339933;">;</span>
        pArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> i<span style="color: #339933;">*</span><span style="color: #CC0000;">2</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
&nbsp;
        x <span style="color: #339933;">=</span> rangeMin <span style="color: #339933;">+</span> range <span style="color: #339933;">*</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">/</span>nbSamples<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        y <span style="color: #339933;">=</span> Math.<span style="color: #660066;">sin</span><span style="color: #009900;">&#40;</span>x<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">//value of the function at x</span>
&nbsp;
        vArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> vArray<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> y<span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">//values for the weight map</span>
&nbsp;
        PaintWeights<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;grid.polymsh.cls.Point.Weight_Map&quot;</span><span style="color: #339933;">,</span> pArray<span style="color: #339933;">,</span> vArray<span style="color: #339933;">,</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

</pre>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=200&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/200/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Anisotropic Patterns</title>
		<link>http://www.softimageblog.com/archives/197#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=anisotropic-patterns</link>
		<comments>http://www.softimageblog.com/archives/197#comments</comments>
		<pubDate>Thu, 15 Mar 2007 03:44:11 +0000</pubDate>
		<dc:creator>Vladimir Jankijevic</dc:creator>
				<category><![CDATA[Rendering]]></category>

		<guid isPermaLink="false">http://www.xsi-blog.com/archives/197</guid>
		<description><![CDATA[Generating anisotropic highlights or reflections on polygon objects, was a real pain for many years. We always were jealous of NURBS surfaces which allowed artists to generate anisotropy along UV parametrization. Once polygons received the ability of anisotropic highlights, the users wanted more. Anisotropic reflections have the power to add great deal of realism to [...]]]></description>
			<content:encoded><![CDATA[<p>Generating anisotropic highlights or reflections on polygon objects, was a real pain for many years. We always were jealous of NURBS surfaces which allowed artists to generate anisotropy along UV parametrization. Once polygons received the ability of anisotropic highlights, the users wanted more. Anisotropic reflections have the power to add great deal of realism to renders and became a handy tool for everyday use.</p>
<p>In the past I have done jobs which required the use of anisotropic patterns and those had always been faked with maps and the like. But now there is no need for faking anymore. A very nice feature of the <a href="http://www.tek2shoot.com/knowledge-base/softimage-xsi/t2s-illumination.html">T2S_Illumination</a> shader enables the manipulation of the anisotropy vector orientation which makes it possible to generate realistic reflection patterns over the surface.</p>
<p>A big thanks to the folks at <a href="http://www.tek2shoot.com">Tek2Shoot</a> for that one.</p>
<p><span id="more-197"></span><strong>Reflect on this</strong></p>
<p>Obviously we&#8217;ll be building off T2S_Illumination.</p>
<p><a href='/userContent/upload/2007/03/main_shader.gif' title='Main Shader' style='float: right; margin: 10px;'><img src='/userContent/upload/2007/03/main_shader.thumbnail.gif' alt='Main Shader' /></a>Disable the Fresnel option under the Fresnel tab. This is done to see the full reflection strength of the material &#8211; metals often don&#8217;t have any or very slight Fresnel reflections. Enable the Reflections under the Scattering tab and switch the Glossy mode to <em>Anisotropic</em>. Set the Shiny U/V parameter to 1 and 100. Under the Anisotropy tab change the mode to <em>North Pole</em> to see how the reflections could look like. Later we will switch it back to <em>North Direction</em>.</p>
<p>To make the reflections visible, create an environment which will be reflected. You can setup some objects in the scene or just use a <a href='/userContent/upload/2007/03/environment.jpg' title='Environment'>spherical map</a>.</p>
<p>Applying this material to a grid with proper UVs (Planar XZ projection), shows the nice circular anisotropic reflections as seen in your kitchen on a <a href='/userContent/upload/2007/03/kitchen_pan.jpg' title='Kitchen Cauldron'>cauldron&#8217;s bottom</a>. 	</p>
[See post to watch QuickTime movie]
<p><a href='/userContent/upload/2007/03/vectors.gif' title='Vector Breakdown' style='float: right; margin: 10px;'><img src='/userContent/upload/2007/03/vectors.thumbnail.gif' alt='Vector Breakdown' /></a>The next step is to recreate that <em>North Pole</em> behavior in the direction vector. To achieve this we have to provide a vector map that manipulates the north direction, so that it points to the center of the grid.</p>
<p>Switch back the Anisotropy mode to <em>North Direction</em>. </p>
<p>In Photoshop, you can very easily create such a vector map. All you need to do is to make two grayscale gradients. One is from right to left in the red channel of the image and the other is from bottom to top in the blue channel. Make sure your gradients are linear and not cubic (smoothness 0 vs smoothness 100).</p>
<p><img src='/userContent/upload/2007/03/vector_colors.gif' alt='Vector Colors' /></p>
<p><a href='/userContent/upload/2007/03/middle_shader.gif' title='Middle Shader' style='float: right; margin: 10px;'><img src='/userContent/upload/2007/03/middle_shader.thumbnail.gif' alt='Middle Shader' /></a>These colors will then be interpreted by XSI so that the full red color will point to the positive x direction and black will be pointing to the negative x direction, pos z and neg z respectively for the blue color. To do this the color channels must be split apart and remapped to a vector. By using <em>Pick_Channel</em> and <em>Change_Range</em> nodes you can easily remap color values (0 -> 255) to a vector component (-1, 1). After having done this for both red and blue channels of your picture, you can recombine the scalars into a direction vector by using a <em>Scalars2Vector</em> node.</p>
<p>One last thing to note is that even if your texture is applied locally on the object, the vector that is built with the texture&#8217;s values is defined in world space. To get proper anisotropy that will follow your object&#8217;s every transform you need to convert the vector to object space by using a <em>Vector_Coordinate_Convertor</em> node. Switch the Transform to <em>to Object</em>.</p>
<p><img src='/userContent/upload/2007/03/vcc.gif' alt='Vector Coordinate Converter' /></p>
<p><strong>Having fun</strong></p>
<p>At the beginning of this article I talked about Anisotropic Patterns and their creation. Now that we know how to create custom vector direction maps there is no limit to the anisotropy patterns we can render. For example: a typical brushed finish of stainless steel.</p>
<p><img src='/userContent/upload/2007/03/circular_pattern.jpg' alt='Circle Patterns' /></p>
<p>A very simple map can generate the same effect.</p>
<p><img src='/userContent/upload/2007/03/circle_vectors.jpg' alt='Circle Vectors' /></p>
[See post to watch QuickTime movie]
<p>And the same thing for some really nice looking Carbon Fiber material. 	</p>
<p><img src='/userContent/upload/2007/03/carbon.jpg' alt='Carbon Fibre' /></p>
<p><img src='/userContent/upload/2007/03/carbon_vector.jpg' alt='Carbon Fibre Vectors' /></p>
[See post to watch QuickTime movie]
<p>I hope this article, beyond anisotropy, has helped a few people better understand the world of vector usage in the Rendertree.</p>
<p>You can download the <a href='/userContent/upload/2007/03/anisotropicpatternsfiles.zip' title='Anistropic Pattern Scene Files'>project files</a> or just the <a href='http://www.xsi-blog.com/userContent/upload/2007/03/anistropicpatternspreset.zip' title='Anistropic Patterns Preset'>material preset</a>.</p>
<p>Thank you and have fun with these tricks&#8230; </p>
<img src="http://www.softimageblog.com/?ak_action=api_record_view&id=197&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.softimageblog.com/archives/197/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
