Select Page

Adding Colour to Volume Noise in Maya

Suppose, for whatever reason, you want colourised noise in Maya; there are some 3d shaders that are colour based, like Crater, or Mountain, but if you want a colour version of Volume noise for example, there’s no support for that out the box, so in order to achieve this, you need to remap tones of grey into colour. To do this you simply add a remapColor node.

Here’s an example of how a finished setup would look (this is the setup for the above image):

Using Velocity in MASH

Using velocity to scale MASH points

MASH measures the velocity of all the points travelling in a network. To see the speed of your objects you can add a Points node and set the mode to Velocity, you will then see each point’s velocity printed in the viewport:

 

So how about a practical use for this, well, the Colour node has a build in ‘Use Velocity’ checkbox, which will use an object’s velocity as a multiplier for the colour, so slow objects are dark, and fast objects are bright.

But if you want to move on to something more complex, you need to add the Python node, rest assured, this couldn’t be easier.
The following script will scale objects according to their speed, it was used to produce the above animation.

  1. Add a Python node
  2. Copy and paste the script into it.
 

The available channels are:

  • md.velocity: A list of translational magnitudes (the speed of the point’s movement)
  • md.angularVelocity: The same thing but for rotation.
  • md.velocityVec: A tuple containing the magnitudes for each axis (x, y, z)
  • md.angularVelocityVec: The same thing but for rotation.

Their corresponding output arrays (though I don’t know why you’d want to set these!) are:

  • md.outVelocity
  • md.outAngularVelocity
  • md.outVelocityVec
  • md.outAngularVelocityVec

The Papers Behind The World Node

The World Node has three distinct types of distribution.

  • Clusters – where points are placed in clusters around other points.
  • Map based – where points are distributed according to a density in a map.
  • Terrestrial ecosystems – where an ecosystem is simulated.

I’ve been asked a number of times which paper I based the terrestrial ecosystems simulations on, so this post will look into that. The truth is that I read around 20 papers when creating this node (23 to be exact!), but one in particular kicked it all off.

Where it started

It all started when Andrew Camenisch, of Mudbox fame, sent me this paper: Guided Ecological Simulation For Artistic Editing. It’s a great document that talks about the implementation of cutting edge ecosystem simulation algorithms. The algorithms mentioned in that paper can be found here: Ground Cover and Vegetation in Level Editors. This really is an excellent, clear and complete paper which can be summarised, haphazardly, like so:

  1. Put some seeds on the ground.
  2. These seeds grow up over time.
  3. At a certain age they start dropping their own seeds.
  4. These seeds may or may not survive depending on where they land/ what resources are available.
  5. The surviving seeds grow up, and at a certain point drop their own seeds.
  6. Plants can die if they are starved of resources.
  7. Plants die when they get too old.

This is a major over simplification, but you get the picture. I decided to start doing some experiments on implementing these ideas — done on my commute to work of course — and these produced some really cool results, so I decided to just go ahead and turn it into a fully fledged MASH node.

Additions

In order to provide artistic control over the simulation, and to make it production worthy, it needed a few extra controls, these included:

  • Avoidance meshes and curves.
  • Id maps – for dictating what type of seed was planted where.
  • Maps for soil conditions (such as moisture and soil quality).
  • Mesh normal sampling for slope awareness (some genotypes might not like ground that is too steep).
  • Sparcity controls.
  • Sun/ shade controls.

On top of those there were a few additions that resulted in more realistic outcomes, including using sigmoidal curves for the growth rate and various quadratic curves for resilience and other genotype characteristics.

Almost everything in the above list is optional, meaning you can go from an empty scene to a basic forest in about 30 seconds. All the advanced stuff needs turning on.

Faster, faster, faster

There was however a problem; once you enabled all these things, everything started taking a bit more time then I deemed acceptable – and once I’d optimised all the additional features as much as possible, there was only one place to go, the algorithm itself. So I started experimenting with which bits of the simulation I could approximate, or what, given certain circumstances, could be skipped. The result is what we’ve ended up with in the shipping product, which gives a result of virtually the same quality and is about 2.5x faster then the original algorithm, so while it isn’t an exact implementation of the above paper, you shouldn’t really notice the difference.

Additional reading

To anyone who’s interested in this topic, content in these papers also resulted in code additions/ changes:

Effects of size, competition and altitude on tree growth 
David A. Coomes and Robert B. Allen

Extended Competition Rules for Interacting Plants
Monssef Alsweis

Generating Spatial Distributions for Multilevel Models of Plant Communities
Brendan Lane and Przemyslaw Prusinkiewicz

 

Setting MASH Point positions with JSON

A while ago I was asked by how you can set MASH points using JSON – a fairly specific request, but it’s pretty easy because the MASH Placer node uses a JSON dictionary to store it’s painted points, so we can tap into that to set the positions (and scale, and rotation and Id).

The JSON data is just some nonsense I made up, you can obviously use whatever you’d like here and make this as complex or simple as you’d like.

An alternative approach to this would be using the Python node and translating Python to MASH points in there, the disadvantage of this though is that the Python node recalculates every frame, which is unnecessary for this task (note: you can disconnect the Python node from the Time node to stop this behaviour so as is often the case with MASH, there’s more then one way to do this).

 

 

MASH & Python & meshes – Part 2: Colour Sets

Following on from part 1, in this example we take things a small step further. Instead of just sampling the mesh for the closest normal to the MASH point, we sample the mesh for the closest point and normal, this call has the added benefit of also returning the closest face to the MASH point. Once we have the closest face we can use the MItMeshPolygon class to give us colour information about that face. I appreciate MFnMesh can also give us the information we want here, I’m just showing one of many ways of doing this.

This is a fairly simplistic example of using colour sets, meshes of course can contain multiple colour sets, and these can be set via methods on MFnMesh so with a little extra work (left to the reader) you can read multiple colour sets if you wish.

This script also includes the normal sampling from the previous script, and as before, you can just copy and paste this Python script onto the Python node and hit the run button, make sure your MASH network has a mesh based distribution, otherwise there will be no mesh to sample.

 

MASH & Python & meshes – Part 1: Normals

There are many reasons you might want to access mesh information in a MASH network, we’ll take a look at two of those reasons here.

  • In part 1 we’ll look at turning points off based on mesh normals.
  • In part 2 we’ll look at setting point Ids based on mesh vertex colours.

For the mesh normals, the process is fairly easy, we ask the MASH API to give us the Distribute node of the network, we then ask the Distribute node for the mesh you’re distributing on, once we have that mesh, we query the closest normal to the MASH point. The end goal here is to say ‘if the mesh normal is pointing more then 20 degrees away from up, turn the point off’. In the above images this has the effect of turning the trees off on steep slopes.

Check out the script. You can copy and paste this onto any Python node (make sure your MASH network has a mesh distribution, otherwise there will be no normals to sample!).

The World node has this ‘slope’ filtering built in, but this kind of technique is useful if you aren’t using that node (for whatever reason, e.g. you want to have a completely manual placement via the Placer node, or you want a more random scattering via the Distribute node).

If performance is important to you, I highly recommend investigating the MMeshIntersector class and using that instead of MFnMesh’s closest normal command as the mesh intersector is a lot faster at this task (it takes a bit more setup which would confuse the example, so I haven’t used it here).

 

Accessing MASH Point Data with the Maya API

Good news, MASH only uses Maya’s default data types, so all MASH data can be accessed via normal Maya APIs.

Ordinarily this isn’t needed as MASH provides two built in ways to extract data:

  1. For the non scripter there’s the Breakout node, which can feed MASH data into float/vector attributes in Maya via connections.
  2. The Python node gives users access to the raw data which they can extract and manipulate in any way they like.

However, for the performance conscious, or for those wanting to write their own MASH nodes or export MASH data in some custom/ game engine format, it might help to know a few things about Maya’s data types and APIs, so here are some simple recipes to get you going. In the following example we’ll get a MASH node (a Waiter), get it’s output data, and print a list of all the channels it contains along with the MASH point positions.

Before we get started, please remember, data getting/setting in Maya should only be done with connections otherwise Parallel Evaluation performance will suffer, so what I’m about to show you should never be run in an expression node.

MASH Data can only be extracted with either the CAPI or PyAPI 1.0. The PyAPI 2.0 isn’t finished yet and thus can’t be used with MASH.

Step 1: Get the MObject for the MASH node.

Step 2: Get a list of channel data and print it.

Step 3: Get a specific channel. In C++ getting the channel data type is trivial, however I’ve never managed to do this in Python (it seems the data type enum get’s mangled when the Python API is auto-generated), so a bit of educated guessing on your part may be needed.

A. Double channel eg. Visibility.

B. Vector array, e.g. Position.
First we need a function to copy the MVectorArray into a Python list:

Now we can use that to get the data:

And you’re done. In C++, everything’s easier, you would check the channel data type first like so and then just copy the array:

Any questions let me know.

 

 

 

 

 

 

MASH API Examples

Maya 2017 Update 3 includes a new API to help quickly create MASH networks with Python. Below are 4 examples that will hopefully get you started. To use them, copy and paste the Python into the Script Editor, and then run the script. signalcolour

Morphing from a Torus to a Sphere using a Volume Axis Field for Turbulence. This project is pretty cool as it shows how to use Flight to do something completely unrelated to flocking, it’s essentially being hijacked and turned into a particle system (the align/cohere/separate/ gravitate strengths are all set to 0). The ability to plug a particle field into Flight is also new in Update 3. merge-flight-field

colouroffsetmesh

Parquet Flooring. There are many ways to do this kind of thing with MASH (I can think of at least 8), this method uses the Symmetry and Replicator nodes. It’s not really that useful, but it might inspire. symmetryreplicator

Shuffling MASH Points with the Python node

Say you want to create the above animation of random spokes moving about; to do this you’ll need to shuffle the MASH points, read on to find out why, and how.

Let’s start with a grid distribution:

As you can see, when MASH creates a grid distribution, all the points are in an order (0, 1, 2, 3, 4 across the top here). I’ve used the Points node to visualise the numbers here.

If we add a Trails node to create some lines between a single point and say, 30 MASH points, it will connect to the first 30 points. See below.

This is obviously not what we’re after so we need to add a Python node to shuffle our MASH points and put them into a random order. This means that even though the first 30 points are connected, they’re all in a random order.

You can copy and paste the below into a Python node. It will shuffle all the points in any MASH network.

As you can see, our ids are now shuffled.

You can animate this by ’rounding’ the seed. In this example, every 10 frames we change the seed (it’s rounded up in 10s). I’ve commented out a version that will change the seed every 20 frames so you can see how it works.

So there are actually 2 MASH networks here. The first one just has a grid of little dots. The second one is more interesting; the Instancer is empty, but it has the same grid in the Distribute node as the first network. The second network is the one with the Python and Trails nodes.

The reason there are two different networks is because in the animation the Trails wander about but the grid stays still. So, on the network with the Trails on it, as you’ve probably guessed, I added a Signal node to make the trails animate about.

The finishing touch was adding an Offset node and using the Low Clamp and High Clamp settings to clip the point positions and keep them inside the grid. Before and after below:

On the left are the clamp settings.

On the right is how the MASH Editor looks when we’re finished (ignore the Id node!).

Multiple looping animations with the Instancer

The Time node in MASH is really useful for creating looping or offset animations inherited from an input model. Check out this tutorial for more on that.

The problem is that the Time node is only available when MASH is creating an output mesh via the Repro node (this is MASH’s default mode). This mode can be impractical if you want to create thousands high polygon objects all with looping input animations. So, how do you do this if you’re using the Instancer with MASH?

If you’ve only got one model, the answer is simple, id cycling.

The Id node in MASH can do id cycling which is really useful for looping animations where each frame of the animation is a separate model on the Instancer (you set this up by creating an Animation Snapshot of your model). This is an age old technique that will be familiar to anyone who’s used the Particle Instancer with Maya’s nParticles.

However, what if you’ve got multiple models with different loop lengths (like different models of people cheering in a crowd). The answer here, ladies and gentleman, as with everything else so far on this blog, is the Python node which you would use instead of the Id node. The script is pretty short, take a look: