/**
@page axexamples AX Code Examples
@section axexamplesintro AX Code Examples
@par
This page demonstrates a range of examples which use AX to manipulate OpenVDB
point and volume data and can be used as a quick start demonstration on the
capabilities of the software. These examples are constantly being updated but
do not cover all aspects of AX!
@section axexamplecontents Contents
- @ref axexamplepoints
- @ref axexamplepointbasic
- @ref axexamplepointdelete
- @ref axexamplepointdrag
- @ref axexamplepointcurlnoise
- @ref axexamplepointtransforms
- @ref axexamplevolumes
- @ref axexamplevolumeclamp
- @ref axexamplevolumevel
- @ref axexamplevolumeblend
@section axexamplepoints Points Examples
@par
These examples demonstrate how to use AX on OpenVDB points grids.
@subsection axexamplepointbasic Basic point attributes
@par
Below is a small example which demonstrates working with a few point attributes.
The @ symbol is the identifier for an AX attribute. The type of each attribute
is specified before the @ symbol and the name is specified afterwards. For
example: @b `int@count` would imply an @b integer attribute called @b count.
@par
In this example there are three point attributes: a @b float attribute @b speed,
a @b vec3f float attribute @b velocity and a @b vec3f float attribute @b colour.
@par
This snippet uses the functions @ref axlength "length" and @ref axfit "fit" to
give points a colour between black and white based on their speed.
@par
@code{.c}
// This statement writes to a new float attribute called speed and reads from a
// vector float attribute called velocity. The length function is used here to
// return the length of the point's velocity vector.
float@speed = length(vec3f@velocity);
// Points that move faster than this threshold will all be mapped to the same
// colour (white).
float fast_threshold = 6.f;
// Writing a single value to a vector will mean that all components of the
// vector are assigned the same value.
vec3f@colour = fit(float@speed, 0, fast_threshold, 0, 1);
@endcode
@par
The @b velocity attribute in this example is assumed to exist and have a range
of values to produce some visual variance in colour. The attributes @b speed and
@b colour do not need to exist before this snippet is run as the attributes are
only written to and will be created on the fly.
@par
@note
In Houdini (AX SOP) changing the name of the attribute from @b colour to @b Cd
will result in the points being given a colour in the viewport as Cd is a
special attribute.
@subsection axexamplepointdelete Deleting Points
@par
This example demonstrates a simple way of removing points from a VDB points grid.
It uses a combination of @ref axrand and @ref axdeletepoint to randomly delete
roughly half of the points in the input VDB points grid.
@par
@code{.c}
float threshold = 0.5f;
// The rand function takes a seed and produces a random value between 0 and 1.
// The integer attribute id from the points is used to seed the rand function.
// If the value from rand is greater than the threshold (0.5) then delete the point.
if (rand(int64@id) > threshold) {
deletepoint();
}
@endcode
@par
The @b id attribute in this example is assumed to be a unique @b integer for
each point in the VDB points grid such that @ref axrand will receive a
different seed value per point producing a good distribution of random values.
@subsection axexamplepointdrag Applying Drag
@par
This example shows something a bit more complex: modifying a point's velocity
with a drag force and then moving the point with the modified velocity.
@par
@code{.c}
float dt = 1.0f / (4.0f * 24.0f); // timestep
vec3f gravity = {0.0f, -9.81f, 0.0f}; // gravity
vec3f dV = {2.0f, 0.0f, 0.0f} - v@v; // drag
float lengthV = length(dV);
float Re = lengthV * @rad / 1.225f;
float C = 0.0f;
if (Re > 1000.0f) C = 24.0f / Re;
else C = 0.424f;
// calculate drag force
vec3f drag = 0.5f * 1.2f * C * lengthV * dV * 4.0f * 3.14f * pow(@rad, 2.0f);
// update velocity
v@v += (gravity - drag / ((4.0f / 3.0f) * 3.14f * pow(@rad, 3.0f))) * dt;
// update position
v@P += v@v * dt;
@endcode
@par
The final AX command, `v@P += v@v * dt;` writes to the position attribute of
the points @b `v@P`. Whenever position is written to in this way, the AX
`PointExecutable` will move (re-bucket) points in PointDataGrids.
@subsection axexamplepointcurlnoise Curl Noise
@par
The following example demonstrates using various native AX functions to
calculate a position based curl noise on points in a particular group, and to
use that calculation to update point velocities.
@par
@code{.c}
// Only calcualte noise and apply it to points if the current point is NOT in
// a group called "escaped". Note that the PointExecutable also has native
// support for group based execution.
if (!ingroup("escaped")) {
// Read custom data
float amplitude = $amplitude;
float persistence = $persistence;
float lacunarity = $lacunarity;
vec3f freq = vec3f$frequency;
vec3f offset = vec3f$offset;
vec3f noise = 0.0f;
// Position based curl simplex noise
for (int octave = 0; octave < int($octaves); ++octave) {
vec3f noisePos = vec3f@P * freq + offset;
noise += curlsimplexnoise(noisePos) * amplitude;
amplitude *= persistence;
freq *= lacunarity;
}
// Apply noise scaled by the current velocity length
vec3f@v += length(vec3f@v) * noise;
}
@endcode
@subsection axexamplepointtransforms Transformations
@par
AX supports 3x3 and 4x4 matrix types and various transformations. They can be
accessed via row, column operators or as a flat array and have defined
operators (such as matrix multiplication etc.). The following demonstrates
some trivial matrix math on VDB Point positions.
@par
@code{.c}
// get the 4x4 identity matrix. note that scalar->matrix promotion handles
// this, so writing 'mat4f transform = 1;' has the same effect.
mat4f transform = identity4();
// set the X transform component
transform[3,0] = 5;
// pre scale the matrix. this is equal to writing: 'transform = scale * transform;'
vec3f scale = { 1,2,3 };
prescale(transform, scale);
// double the value of the scale Y component
transform[5] *= 2;
// construct a basic rotation matrix, representing 90 degree rotation around Y
float degrees = 90.0f;
float rad = radians(degrees);
mat3f rotation = {
cos(rad), 0.0f, -sin(rad),
0.0f, 1.0f, 0.0f,
sin(rad), 0.0f, cos(rad)
};
// apply the rotation and transformation to the current points position
vec3f@P *= rotation;
vec3f@P *= transform;
@endcode
@section axexamplevolumes Volume Examples
@par
These examples demonstrate how to use AX on VDB volume grids.
@subsection axexamplevolumeclamp Volume Clamping
@par
Below is a small example of reading adn writing to voxels within a VDB volume.
As with points, the @ symbol specifies an access to a named volume, with the
type of each volume preceding it and the name appearing as a suffix. For
example: `float@density` would imply a @b float volume called @b density.
@par
This snippet uses @ref axclamp function to constrain the @b density attribute
between zero and one.
@par
@code{.c}
float@density = clamp(float@density, 0.f, 1.f);
@endcode
@subsection axexamplevolumevel Velocity Update
@par
AX can be used to read and write to multiple volumes. Here we have a typical
example of a eulerian velocity update which takes into account an updated
vector force and scalar mass.
@par
@code{.c}
// Access the current timestep - this could instead be provided by the AX
// integration and made accessible with custom data (i/e: float$timestep)
float dt = 1.0f / 24.0f;
// read from the mass grid the corresponding (matching index space) value
float mass = float@mass;
// safe divide, in case mass is zero
float massInverse = 0.0f;
if (mass > 0.0f) massInverse = 1.0f / mass;
// update the current velocity value by scaling the corresponding force
// by the timestep and inverse mass
vec3f@vel += dt * massInverse * vec3f@force;
@endcode
@subsection axexamplevolumeblend Linear Blending
@par
Here we demonstrate how AX can be used to blend two volumes together. Note that
only the volumes which are written to are executed over. In the below code,
only @b `@surface_a` is written to. This means that the final blended result,
stored in `surface_a`, will only be updated in `surface_a`'s topology. Should
we want a combined blend of @b non @b overlapping areas of both surface a and b,
we would need to activate a's topology with respect to b first.
@par
@code{.c}
// time constants for each run.
float time = float$time;
float maxDuration = 100.0f;
// read both surface a and surface b values
float a = @surface_a; // source value
float b = @surface_b; // target value
// calculate the fractional mask value in respect to time
float mask = time / maxDuration;
// optionally scale the mask by a per index space density. if
// $enable_density_mask is true, a density grid is used to vary the blend per
// voxel
if ($enable_density_mask) mask *= @density;
// finally, clamp the mask value and update the level set value in surface a.
// Note that this may produce a no longer valid level set and might need to be
// rebuilt!
mask = clamp(mask, 0.0f, 1.0f);
@surface_a = a + (b-a) * mask;
@endcode
*/