# NvParticles - User Guide for the NVIDIA Tech demo

    Version: public, Date: 31 July, 2012

##

## Loading the plug-in

You can load the plug-in by opening the Window --> Settings/Preferences --> Plug-in Manager dialog, and selecting the plug-in entitled, NvParticlesForMaya-arch. Or you can use the mel script window.

    loadPlugin "NvParticlesForMaya-linux64_gcc4.1";

You will be able to tell if the plug-in loaded successfully because the NvParticles menu item will appear.

Tip | If it fails to load then check the Output Window or command-line for information as to why. Usual cases are that the required dynamically loaded libraries are not in the path. See the INSTALL.TXT in the installation directory for details.

## Particles

First we need to create an nvParticlesParticle node. The fastest way is to simple create a standard particle type from the NvParticles menu...

    nvParticlesCreateParticleEmitterTank();

Now we can specify some solver parameters...

    // display as spheres with velocity...
    setAttr -type "string" "nvParticlesParticleShape1.renderer" "spheres"; // display spheres.

    setAttr "nvParticlesParticleShape1.solver_colorStyle" 3; // store the velocity into the color channel.
    setAttr "nvParticlesParticleShape1.solver_colorScale" 1.0; // scale the velocity value.

    // when the particles look chaotic then it's likely the speedOfSound is too high.
    setAttr "nvParticlesParticleShape1.solver_speedOfSound" 8;

##

## Emitters

Standard emitter nodes can be connected to an nvParticlesParticle node...

    // create an emitter with a specified rate of particles a second (too few particles produces a bad solve, too many also causes problems).
    emitter -pos 0 10 0 -type volume  -name "emitter1" -r 200 -vsh cube;
    // do the equivalent of connectDynamic.
    nvParticlesConnectEmitter emitter1 nvParticlesParticleShape1;

At this point we can test that it's working by setting the playback range and watching the emitter populate our nvParticles particles.
We must tell the particles when they reset by setting the startTime. When this time is hit, it will reset the particles to the initial condition. (Currently, the only initial condition supported is to destroy them all.)

    // set initial conditions at frame 0.
    setAttr "nvParticlesParticleShape1.startTime" 0;
    // loop.
    playbackOptions -loop continuous;
    playbackOptions -minTime 0 -maxTime 1000;
    play -forward true;

##

## Fields

We can add some of the maya fields to our particles...

    // create radial field...
    radial -pos 10 10 1 -name "radial1" -m -50 -att 0.3 -typ 1 -mxd 20;
    nvParticlesConnectField radial1 nvParticlesParticleShape1;

Tip | A subset of Maya's fields is supported. Gravity, Uniform, Drag, Radial and Vortex. Also, I do not yet support the volume control attributes, nor the falloff curves. Also, the radialField radialType attribute must be set to 1.

##

## Primitives

Collision handling is an area that needs improving. At the moment, we use special primitive shape, known as nvParticlesPrimitive.
Currently primitives can be defined as one of the following geometry types: Box, Sphere, Plane, Capsule.

A primitive can be use in different ways. It can be a collision primitive - whereupon the nvParticles particles will interact with it during simulation. Alternatively, it can be used as an emission source (only valid for the Box primitive type). The valid attributes on the nvParticlesPrimitive have different meanings depending on which way the node is being used.

### Primitive (Collision)

We can specify whether it is an external surface or an internal surface. (This effectively flips the normal to point inwards.)

You can transform the primitive by scaling, translating, and rotating its parent. Some primitives requiring extra control (such as the Capsule) can be modified using the extents vector. In the case of the capsule, you change the extent.z value to change its length.

    // create a sphere, scale it by 2, and move it up.
    createNode nvParticlesPrimitive -n nvParticlesPrimitiveShape3;
    setAttr "nvParticlesPrimitiveShape3.primitive" 1; // set to Sphere primitive.
    setAttr "nvParticlesPrimitiveShape3.type" 1;
    scale -r 4 4 4;
    setAttr "nvParticlesPrimitive3.translateX" -10;
    setAttr "nvParticlesPrimitive3.translateY" 5;
    setAttr "nvParticlesPrimitive3.translateZ" -2;
    nvParticlesConnectPrimitive nvParticlesPrimitive3 nvParticlesParticleShape1;

You can parent a primitive directly under a regular dag node. All you need to do is select the node you wish to be a parent and click the create primitive from the NvParticles menu.

Or you can use the mel function "nvParticlesCreatePrimitive(int $type)". But you must first select the would-be parent.

If you optionally select the nvParticlesParticle node, then it will automatically connect it. Otherwise you must connect it manually using "nvParticlesConnectPrimitive". And remember to change the type of the primitive to "collision".

Tip | If you feel the collisions cause too much water disturbance then you can turn down the solver_boundaryStiffness parameter.

### Primitive (Emission) - (Work-in-progress)

Currently only the Box primitive is supported for this.

    // create a solid box of particles.
    createNode nvParticlesPrimitive -n nvParticlesPrimitiveShape4;
    setAttr "nvParticlesPrimitiveShape4.primitive" 3; // set to Box primitive.
    setAttr "nvParticlesPrimitiveShape4.type" 2; // set to Emission type.
    scale -r 2 2 2;
    setAttr "nvParticlesPrimitive4.translateX" -10;
    setAttr "nvParticlesPrimitive4.translateY" 15;
    nvParticlesConnectPrimitive nvParticlesPrimitiveShape4 nvParticlesParticleShape1;

Tip | There is a limit to the number of primitives (128) that you can specify

##

## Matching a displaced ocean surface

The solver will warp the particles to fit a displacement map.

First you must attach the mesh geometry to the nvParticlesParticle.

    // create a periodic particle ocean
    nvParticlesCreateParticleOcean();

    // create a plane at the water level height. (NB. the height is estimated by eye).
    polyPlane -w 2 -h 2 -sx 20 -sy 20 -ax 0 1 0 -cuv 2 -ch 1 -name "oceanPlane";
    setAttr "oceanPlane.translateY" 0;
    parent -relative "oceanPlane" "domain";

    // make it more interesting.
    nonLinear -type wave -minRadius 0 -maxRadius 1 -amplitude 0.05 -wavelength 0.5 -dropoff 0 -offset 0;
    $expr = "wave1.offset = (time / 10);";
    expression -s $expr  -o wave1 -ae 1 -uc all;

    // connect the mesh and its matrix to the nvParticlesParticle
    connectAttr -f oceanPlaneShape.outMesh nvParticlesParticleShape1.inOceanGeometry;
    connectAttr -f oceanPlaneShape.worldMatrix[0] nvParticlesParticleShape1.inOceanGeometryMat;

    // create a simple cube with radius=1 (to act as a transform).
    // The simple rule is: every triangle of the inOceanGeometryMat that
    // falls within this cube will be captured using an orthogonal camera pointing down the z-axis.
    polyCube -w 2 -h 2 -d 2 -sx 1 -sy 1 -sz 1 -ax 0 1 0 -cuv 4 -ch 1 -name "oceanCamera";
    // rotate it so the z-axis is pointing at the surface we want to capture.
    setAttr "oceanCamera.rotateX" 90;

    // i.e. to make it fill the domain perfectly, I make this group a child of a "domain" group.
    // the domain group is then stretched and scaled to fit the scene.
    parent -relative oceanCamera "domain";

    // connect our "camera" to the nvParticlesParticle.
    connectAttr -f oceanCamera.worldInverseMatrix[0] nvParticlesParticleShape1.inOceanCameraInvMat;
    // tell the nvParticlesParticle that we have a displacement map:
    setAttr "nvParticlesParticleShape1.oceanTextureSize" 64;

You can see why the estimated surface height is important. The middle section of the periodic ocean will not use the displacement! You can adjust this height by changing the particle domain, in this case, adjusting the "domain" group

If you turn on the debug textures using "setAttr "nvParticlesParticleShape1.drawDebugTextures" 1;", then you will see the RGBA32F representation of the captured height-field.

Tip | If you want to export these "warped" particle positions to disk, then you must specify "warpedPosition" in the exported buffers.

## Closing Remarks

Please note that this is a technical demo and as a result is likely to have bugs.

## Known issues

There is a bug preventing multiple nvParticlesParticle nodes in one scene.

Collisions can be delicate. The collision maths could do with being improved.