Last post, I talked about the motion of the squab, and the points created for the tubers sprouting from its back.
This post will be about the creating the tuber geometry and motion.
A series of tubes
Here’s the tuber network:
The Object Merge at the top is bringing in the tuber start points, which I talked a bit about last post.
I’m pushing those points along their normals by some random amount, to find the end points.
I could have done this with a Peak SOP, but wrangles are far more fun!
float length = fit(rand(@ptnum), 0, 1, 1.3, 2.2); @P += @N*length;
Sometimes I just find it a lot quicker to do things in VEX, especially when I want to do value randomization within a range like this.
The points chopnet, near the top, is straight out of the “Introduction To CHOPs” by Ari Danesh. I highly recommend watching that series for more detailed information on CHOPs.
The chopnet takes the end point positions, adds noise, uses spring and lag CHOPs to delay the motion of the end points so they feel like they are dragging behind a bit:
After that, I create a polygon between each end and start point using an Add sop:
I’m then using Refine to add a bunch of in-between points.
In the middle right of the network, there are two network boxes that are used to add more lag to the centre of each wire, and also to create a pulse that animates down the wires.
When the pulse gets to the bulb at the end of the tuber, it emits a burst of particles and smoke, but more on that in a later post.
Here is the chopnet that handles the pulse, the lag on the centre of the wires, and a slightly modified version of the pulse value that is transferred onto the bulbs:
The wire lag is pretty simple, I’m just adding some more lag to the movement (which I latter mask out towards both ends of the wire so it just affects the centre).
The pulse source is a little more interesting.
Before this network, I’ve already created the pulse attribute, initialized to 0. I’ve also created a “class” attribute using connectivity, giving each wire primitive it’s own id.
When I import the geometry, I’m using the class attribute in the field “organize by Attribute”:
This creates a channel for each wire.
I also have a Wave CHOP with the type set to “ramp”, and in this CHOP I’m referencing the number of channels created by the geometry import, and then using that channel id to vary the period and phase.
Here are the wave settings:
And here is where I set up the Channels to reference the Geometry CHOP:
To visualize how this is creating periodic pulses, it’s easier if I add a 0-1 clamp on the channels and remove a bunch of channels:
So hopefully this shows that each wire gets a pulse value that is usually 0, but at some point in the animation might slowly ramp up to 1, and then immediately drop to 0.
To demonstrate what we have so far, I’ve put a polywire on the wires to thicken them out, and coloured the pulse red so it’s easier to see:
It’s also sped up because I’m dropping most of the frames out of the gif, but you get the idea 🙂
The “Pulse Source Bulbs” section of the chopnet is a copy of the pulse, but with some lag applied (so the pulse lasts longer), and multiplied up a bit.
The remaining part of the network is for creating the tuber geometry, here is that section of the network zoomed in from the screenshot early in this post:
I’m creating the geometry around a time shifted version of the wires (back to the first frame), and then using a lattice to deform the geometry each frame to the animated wires.
By triangulating a bunch of scattered points, dividing them with “Compute Dual” enabled, convert them to nurbs and closing them, I get a bunch of circle-ish shapes packed together.
There are probably better ways to do this, but it created a cross section I liked once I’d deleted the outside shapes that were distorted:
To kill those exterior shapes, I used a Delete SOP with an expression that removes any primitive with a centre that was a certain distance from the centre of the original circle:
length($CEX, $CEY, $CEZ) > (ch(“../circle4/radx”) * 0.6)
This cross-section is then run up the wire with a Sweep:
The Sweep SOP is in a foreach, and there are a few different attributes that I’m varying for each of the wires, such as the number of twists, the scale of the cross-section.
The twisting took a little bit of working out, but the Sweep sop will orient the cross section towards the Up attribute of each point.
I already have an Up attribute, created using “Add Edge Force” with the old Point SOP, it points along the length of the wire.
The normals are pointing out from the wire already, so I rotate the normals around the up vector along the length of the wire:
The Sweep SOP expects the Up vector to be pointing out from the wire, so I swap the Normal and Up in a wrangle before the Sweep.
So, now I have the resting tuber geometry:
To skin these straight tubers to the bent wires, I use a Lattice SOP:
I Lattice each wire separately, because sometimes they get quite close to each other.
Last thing I do with the tubers is push the normals out a bit as the pulse runs through.
I already have an attribute that increases from 0 – 1 along each tuber, called “rootToTip”.
On the wrangle, I added a ramp parameter that describes the width of the tuber along the length (so the tuber starts and ends flared out a bit).
The ramp value for the tuber shape is fit into a range I like through trial and error, and I add the pulse amount to it, then use that to push the points out along their normals
This is the wrangle with the ramp parameter for the shape:
@tuberProfile = chramp("profile", @rootToTip, 0); float tuberWidth = fit(@tuberProfile, 0, 1, 0.0, .04); float pulseBulge = ((@pulse)*0.1*(1-@rootToTip)); @P = @P + (@N * (tuberWidth + pulseBulge));
This gives me the pulse bulge for the tubers:
That does it for the tubers!
In future posts, I’ll cover the stalk bulbs, particles, pyro, rendering and audio.