Your online Softimage Educational Resource

The blog has been online for more than 4 years and there’s still not a single post even remotely related to the delicious brew called coffee… Perhaps it will someday, but in the meantime you can read the articles about Softimage. Most of the material are tutorials and Q&As I’ve written for 3D World Magazine sometime between today and 2003. If you have any questions please don’t hesitate sending me an email.


Thanks to Letterbox Animation Studios for hosting all the scene files.

Make sure you visit their Redi-Vivus.com for 100s of hours of free XSI video tutorials.

Friday, July 11, 2014

Example using the shockwave deformer



Here’s a quick example of deforming a water surface instead of a wall, as described in the tutorial.


Read the full post>>

Build your own Shockwave deformer using ICE

The wave function
The shockwave will be constructed by two sets of waves - a somewhat smaller, condensed part followed by the larger more intense pulse. Even though a shockwave is an ideal candidate for simulation, it has a slight drawback - namely that it is simulated. Whenever you want to change a value or reposition the shockwave, you have to rewind and run the simulation again. An alternative approach is to do a bit of high school math. Don’t worry; it’s not as frightening as it sounds. The waveform is essentially nothing more than a sine wave; altered over time and its distance from the origin (technically, the equation actually uses the outer edge as the base for the calculation rather than the centre)  




The math
The animated sine wave consist of 3 building blocks. The first set of nodes uses the time attribute to change the values for each point over time and the distance from the deformation centre. The next set of nodes handles the frequency and the last set controls the amplitude or strength of the wave.

Open the Shockwave_start.scn scene from this issues CD. Select the Deform_Me geometry and press Alt + [9] to open an ICE Tree and from the Create menu choose ICE Tree. To make the following steps a bit easier to follow you’ll define all the parameters at the top of the tree and then simply getting those values as you need them as the Tree evolves. Get a Set Data node and connect it to Port1 of the ICETree. Get a Scalar node and connect it and then disconnect it to the New (Value) input of Set Data node 10 times to create 10 separate attributes. Open the Set Data PPG and enter the following attributes as the Reference. Use the number inside the parentheses as a starting point.  
self.Amplitude  (1)
self.SmallWaveRadius (11)
self.SmallWaveAmp (1)
self.SmallWaveLenght (4)
self.LargeWaveRadius (12)
self.LargeWaveAmp (2)
self.LargeWaveLenght (8)
self.Speed (0)
self.Distance (0)
self.Duration (40)

To set the speed of the wave you can get the current time in your scene and multiply it to increase or decrease the speed. So get a Current Time (which will return the time in seconds rather than frames) and a Multiply by Scalar node. Connect the Current Time to the Value input, set the factor to 10 and connect the Result output to the self.Speed input of the Set Data node. 
Rather than using the object’s actual centre point, you’ll use a separate null object and calculate the distance between each point of the geometry and the null. This enables you to reposition the null wherever you want the shockwave to start and the ICE tree will automatically take care of the rest. Get a Get Data node and enter Self.PointPosition as the Reference. Get another Get Data node and enter Impact_Center.kine.global.pos as the Reference. Get a Get Distance Between node and connect the self.PointPosition to its First input and the Impact_Center to the Second. Connect the Result output to the self.Distance input of the Set Data node.
While the Sin node will indeed give you a sine wave, it will be a static wave. In order to create an animated wave you need to change the value you feed into it over time, which is why you created the Speed attribute. Get a Get Data node and enter self.Speed as the Reference. Get another Get Data node and enter self.Distance as the Reference. Get a Subtract node and subtract the self.Distance from the self.Speed. The result of the subtraction will assign an incrementing value to each point over time and based on its distance from the centre. The value will however continue to increase to infinity, which will result in a continuous sine wave. An easy way to fix this is by clamping the output to 0. Get a Clamp node and connect the Result of the Subtraction to the Value input. Open the Clamp PPG and set the Limit 1 to 0 and the Limit 2 to a really high negative number, such as -1000000. 
The next step is to add control for the frequency (the number of waves). Get a Get Data node and enter self.SmallWaveLenght as the Reference. Get a Divide by Scalar node and connect the WaveLenght to the Value input. Open the PPG and set the Divide By to 360 and convert the output to Softimage units instead of degrees. Get another Divide by Scalar and connect the Clamp node to the Value input and Divide by Scalar to its Divided By input. Get a Sin node and connect the result of the division to its Value input. 
In addition to the frequency you also want to control the amplitude of the wave. Or the amplitude based on the waves distance to the center to be more precise. Get 3 Get Data nodes and enter self.Distance, self.Speed and self.SmallWaveRadius as their references. Get a Subtract node and subtract the self.Speed from the self.Distance. Get a Divide by Scalar and divide the result of the subtraction by the value output of the self.SmallWaveRadius. Get a Calmp node, set the Limit 1 to 0, Limit 2 to 1 and connect the result of the division to the Value input. The output of the Clamp node will output a decreasing value rather than increasing. To fix this, get a Subtract node, open the PPG and enter 1 as the First value. Connect the result of the Clamp node to the Second input. Since you’re gradually subtracting less from 1 for every frame, the output from the subtraction will obviously get closer to 1 over time. Get a Get Data node and enter self.SmallWaveAmp as the Reference. Get a Multiply by Scalar node and connect the WaveAmp to the Value input and the result from the Subtract node to the Factor. To bring the first wave together you just need to multiply the sine wave with the amplitude, so get another Multiply by Scalar node. Connect the Result of Sin node to the Value input and Result from the previous Multiply by Scalar node to its Factor input. 

The second wave
The math behind the large wave is identical to the first. The only thing you need to change is negating the sine wave and reversing the subtraction of self.Distance and self.Speed. Once this is done you can combine the 2 waves with a simple add node.

 With a few exceptions, the large wave uses the same math as the small wave. Select all the nodes constructing the small wave and press Ctrl + [C] and Ctrl [V] to copy and paste them. Replace all the ‘small’ with ‘large’ in the Get Data nodes (for example, self.SmallWaveLenght to self.LargeWaveLenght). Get a Negate node and intersect the connection from the Sin to the Multiply by Scalar node. You also need to reverse the subtraction of the self.Speed and the self.Distance, both for the frequency and for the amplitude. For the frequency the self.Speed should be subtracted from (Second input) the self.Distance (First input) and in for the amplitude the self.Distance should be subtracted from the self.Speed.

To add the two sets of waves together, get an Add node and connect the Result from the Multiply by Scalar from the small wave to Value1 and the Result from the large wave to Value2.
Get 2 Get Data nodes and enter self.Speed and self.Duration as their References. Get a Divide by Scalar and connect the self.Speed to the Value input and the self.Duration to the Divide By. Get an FCurve and connect the Result of the Division to its input. Open the FCurve PPG and add a keyframe with a value of 1 approximately at frame 0.2. Change the value of the keyframe at frame 1 to 0. This will scale the effect of the wave from 0 to 1 and back to 0 over its lifespan (which is defined by the duration attribute). Get a Multiply by Scalar and connect the Result of the Add node to the Value and the FCurve the Factor. 
The last step is to add a multiplication to scale the overall amplitude of the wave. Get a Get Data node and enter self.Amplitude as the Reference. Get a Multiply by Scalar and connect the Result from the previous multiplication to the Value and the self.Amplitude to the Factor.
The final step is to actually deform the geometry. Get a Get Data node and enter self.PointPosition as the Reference. This will return the X, Y and Z position for each of the points of the geometry. However, you’re only interested in changing the Y position (since the disc lies flat on the ground) so you need to separate the vectors. Get a 3D Vector to Scalar node and connect the self.PointPosition to its input. Get a Scalar to 3D Vector node and connect the X and Z output of the 3D Vector to Scalar to its corresponding inputs. Connect the Result output from the Multiply by Scalar node to the Y input of the Scalar to 3D Vector. Get a Set Data node and enter self.PointPosition as the Reference and connect the Scalar to 3D Vector to its input. Complete the ICE Tree by connect the Set Data node to the New (Port1)… input of the ICE Tree node.  

The complete ICE Tree


Read the full post>>

Monday, June 9, 2014

Future of the blog and completely unrelated cloud R&D



I haven’t been very active on the blog lately, which some of you may have noticed J I’ve got a just-about-finished tutorial describing how to create a ripple deformer in ICE with a “simple” equation. So, no simulation a fancy stuff like that. Just a couple of well-placed nodes J I also have a half-finished piece about creating a weightmap brush, with dispersion, decay etc, which I might finish if anyone asks for it.  In the meantime I’ll post some wip from a few projects I’ve been working on lately.

If you’re wondering about the future for the blog (given the depressing decision from Autodesk earlier this year) I can tell you that the blog will continue just as usual. Well, maybe not exactly as usual as I’m hoping to start adding content more frequently. In a not too foreseen future, the plan is to write articles and tutorials about another 3d application as well…  I am  very much in the learning process myself so I can’t say when I got any useful knowledge to pass on.


Read the full post>>

Monday, July 29, 2013

Automatic weightmaps for the left and right side of a head using ICE



Start by downloading and opening the scene ICE_Weightmaps.scn from https://dl.dropboxusercontent.com/u/3834689/CaffeineAbuse/LeftAndRight_Weightmap.zip. Before you can assign any weights use ICE you need an actual weightmap to assign them to. Select the Head object and from the Get > Property menu choose Weight Map. In the PPG, click the Rename button and rename it to Weight_Map_Left. Create a second WeightMap and rename it Weight_Map_Right. With the head still selected, press [Alt] + 9 to open an ICE Tree and from the Create menu choose ICE Tree. Get a Get Data node and enter self.PointPosition as the reference. To assign the proper weights to the weightmap you need to find where along the x-axis each of the points is located. If it’s higher a certain value (zero in this case) it’s part of the left side of the head, and if it’s lower (a negative number in this case) it’s part of the right side. While the get PointPosition node does get the position of the points on the geometry, it outputs them as a 3D vector (the X, Y and Z position and direction per point). So you’ll need to convert the 3D Vector to output the X, Y and Z component separately.

Since your calculating the distance between the points at the furthers left and right side, it doesn’t matter if the object is placed at the center of the scene or not. The distance between the minimum and maximum is always the same either way.

Get a 3D Vector to Scalar node and connect the Get PointPosition node to its input. By getting the position at the furthest right (maximum) and furthest left (minimum) and interpolate between the two you can assign a value to each point based on where it is located within that range. Sort of a percentage or gradient from furthest left to right if you will.

Get a Get Array Maximum and a Get Array Minimum node and connect the X output of 3D Vector to Scalar node to their respective Array input. Get a Linear Interpolate node and connect the Array Minimum to the First input and the Array Maximum to the Second input. The weights in the weightmap ranges from 0 to 1 so you need to rescale the current values which spans from -3.230 to 3.230 (their global x positions) to fit this range. Get a Rescale node and connect the Linear Interpolation node to its input. While you could enter the current minimum and maximum positions manually as the Source Start and End, your ICE Tree would only work on this specific model which really isn’t that useful. However, you already know the position of all the points so you can automatically get the highest and lowest number and plug that into the Rescale node. To do so, get a Get Maximum in Set and a Get Minimum in Set node and connect the X output of the 3D Vector to Scalar node to their respective Value inputs. Then connect the Maximum in Set to the Source End of the Rescale node and the Minimum in Set to the Source Start.

The FCurve node does not only enables you to create a smooth transition between the left and right weightmap, it also defines where each side start and ends. If your head is asymmetrical, simply nudge the keyframes to the left or right to aligned them with the geometry.

Get an FCurve node and connect the output of the Rescale node to its In input. Open the Fcurve PPG and select the Key at the left. Right-click on the Key and from the menu, choose Key Properties. Set the Frame value to 0.49. Click the Next Key button and change the Frame value to 0.51. This will create a smooth transition between the points of the left (with a value of 0) and right side (with a value of 1) of the head. Get a Set Data node, enter self.MyWeights as the reference and connect the output of the FCurve to its input. Then connect the Set Data node to the Port1 of the ICETree.

Get a Get Data node and enter self.MyWeights as the reference. Get a Set Data node, open its PPG and click the Explore button. Expand the tree in the explorer Head > Polygon Mesh > Clusters > WeightMap Cls > Weight_Map_Left and choose weights. Close the PPG and connect the Get self.MyWeights node the weights input of the Set Data node. Connect the Set Data node to the New (Port2)… input of the ICETree. To assign the weights for the right side weight map, all you have to do is to reverse the values. Get a Rescale node and connect the output of the self.MyWeights node to its Value input. Open the PPG and change the Target Start to 1 and Target End to 0. Get a Set Data node and repute the previous step but select the Weights for the Weight_Map_Right. Connect the Set Data node to New (Port3)… of the ICETree.

Quick tip
Whenever modifying or assign weights to weightmaps using ICE, you should always use a custom “buffer attribute” to do all your calculations. Then get the custom attribute and set the actual weights of the weightmap at the bottom of your ICETree.

Once you’re happy with the weightmaps it’s a good idea to freeze the geometry to avoid accidentally changing the weights. Please note that this will delete your ICE Tree so you might want to save the scene under a separate name for future reference.


Read the full post>>

Friday, April 12, 2013

Creating HDR environment maps from a 3D scene

Generating a HDR environment from your 3d scene is in fact not that much different from how you would go about in real life. The two most common options are to either shoot a chrome ball or to use multiple images to construct a spherical panorama. While there are 3rd party plugin which enables you to render the scene trough a spherical lens, you can just as easily create a virtual chrome ball to serve your needs.

The project files used in this tutorial can be found here: https://dl.dropboxusercontent.com/u/3834689/CaffeineAbuse/HDR_ProjectFiles.zip



Create the Chrome Ball 
Open the HDR_Environment.scn scene from this issues CD. Select the ChromeBall and from the Get > Material menu choose Constant. In the Constant Material PPG, switch to the Transparency/Reflection tab and set the Reflection Mix Color to pure white (R:1, G:1, B:1). This will turn the sphere into a perfect 100% reflective chrome ball. However, as the chrome ball is part of the scene it also means it will interfere with the lights, shadows, and so on, which probably is not what you want. 


Exclude the Chrome ball
With the chrome ball still selected, press [F3] to open a mini browser and click on the Visibility icon to open the PPG.  Uncheck the Shadow Caster and Shadow Receiver checkboxes.  While this will exclude it from casting and receiving shadows it still affects the final gathering in the scene. To avoid this, uncheck the Caster and Visible in Sampling attributes in the Final Gathering section of the PPG.

Generate the environment map
With the ball still selected, from the Get > Property menu choose Render Map.  In the Format section of the PPG, uncheck the Square checkbox and set the resolution to 1024 x 512. Click the New button next to the UV title and select Spherical to create a texture projection. Since you’re generating an HDR image you obviously need to use an image format supporting it. Change the output format to OpenEXR. By default reflection are disabled from the render map generation and while this is something you normally want it does counteract the sole purpose the chrome ball. In the Disable Surface Properties section, make sure uncheck the Reflection checkbox. Click the Regenerate Maps… button and you’re done.



Read the full post>>

Sunday, January 20, 2013

Creating strands between two different objects

Once completed the following steps you’ve effectively generated strands between the objects, though they won’t show up in a rendered image as you haven’t  defined any size or shape yet. To do this simply get a Set Data node, right click on the Value Port of the node and choose Add Port After two times so you have three values. Open the PPG, enter self.Size as Reference and set the size to 0.1 or so. Enter self.Shape as Reference1 and choose Cylinder. To loft the shape along the strand rather than using individual shapes you’ll need to add one last attribute. Enter self.StrandDeform as Reference2, press enter and check the self.StrandDeform checkbox. Finally connect the Execute output of the Set Data node to the Port2 of the ICETree node.

The project files used in this tutorial can be found at: http://dl.dropbox.com/u/3834689/CaffeineAbuse/Strands_between_points.zip


Set the starting points
Open the scene Strands_between_points_Start.scn. From the Get > Primitive > Point Cloud menu choose Empty and press [Alt] + [9] to open an ICE Tree View. From the Create menu choose ICE Tree. Press [8] to open an Explorer and drag and drop the StartPosition grid into the ICE Tree. Get a get data node, open its PPG and enter .PointPosition as the Reference. Get another Get Data node and .kine.global as the Reference. Connect the Out Name output of the Get StartPosition node to In Name input of the two Get Data nodes. The point positions are stored in local space, meaning that you have to add the object’s transformation in order to retrieve the actual position of each point.  Get a Multiply Vector by Matrix node and connect the Value output of Get .PointPosition to the Vector input and the Value output .kine.global node to the Matrix input. 


The end point
Get an Add Point node and connect the Result output of the Multiply Vector by Matrix node to the Positions1 input. Then connect the Add output of the Add Point node to the Port1 of the ICETree node. This will create a point at each of the grids vertices and will be used to create the strands. Get a Build Linearly Interpolated Array node and a Get Data node. Enter Self.PointPosition as the Reference and connect the Value output to the Start input of the Build Array node. Open an Explorer and drag and drop the EndPosition grid into the ICE Tree.  Repeat the previous step and connect the PointPosition, kine.global and Multiply Vector by Matrix nodes.



Create the strands
While you’ve just defined the end position for the strands you can’t feed this information directly into Build Array node because the point positions derive from different component types (points vs. vertices).  To fix this, get a Switch Context node and connect Multiply Vector by Matrix node to its Value input. Then connect the Result of the Switch Context to the End Value of the Build Array node. Get a Set Data node, enter self.StrandPosition as the Reference and connect the Result of the Build Array node to the input. Connect the Execute output of the Set Data node to the On Creation1 input of the Add Point node.


Read the full post>>

Thursday, December 6, 2012

Using an animated map to define particle goals in Softimage

There’s a slight difference depending on what map you’re using. A texture map is essentially an image file connected to an object, and as such you need to tell ICE what to do with this information before you can make use of it. First you need to convert the RGB color of the image to a scalar value and store this information as a custom attribute. Weightmaps on the other hand are already stored as scalar values, and as such you can skip the first step and use its Weights attribute directly in step 2. Start by opening the scene Goal_using_TextureMap.scn. 

The project files used in this tutorial can be found at:http://dl.dropbox.com/u/3834689/CaffeineAbuse/GoalLocation_using_AnimatedMaps.zip
Create a custom goal attribute
Select the Goal_Object and press [Alt] + [9] to open an ICE Tree. From the Create menu choose ICE Tree. Get a Get Data node, open its PPG and enter Self.NodeLocation as the reference. Get another Get Data node and enter Texture_Map as the reference. Connect the Value output of the Get Self.NodeLocation to the Source input. Get a Color to Brightness node and connect the value of the Get Texture_Map node to its Color input. Get a Set Data node and enter Self.GoalTextureMap as the reference and connect the Brightness output of the Color to Brightness node to the input. Connect the Set Data node to Port1 of the ICE Tree.
Set the GoalLocation
Select the pointcloud and update the ICE Tree view. Get a Get Data node and enter Goal_Object as the reference. Get a Get Geometry Sample node and connect the Value output of the Get Goal_Object node to the Geometry Input and then open its PPG. This node enables you to filter which part of the surface to use as the goal by extracting the weights from either the weightmap or the custom attribute created for the texture map.  Click the Explore button in the filter section of the PPG, expand the Input> Polygon Mesh tree and select the GoalTextureMap attribute. 
Move towards goal
Get a Set Data node and enter Self.GoalLocation as the reference.  Connect the Samples output of the Get Geometry Sample node to Self.GoalLocation input of the Set Data node. Connect the Set Data node to the Execute on Emit1 input of the Emit from Geometry. This will define the goal for each particle as it’s born. Once defined, you’ll use another node to actually move them there. Get a Move Towards Goal node and connect it to the Execute1 input of the Simulation Root node. Playback the animation and you’re done.



Read the full post>>