# Random Flake Procedural

This is an example of making a geometry-generating procedural for Arnold. There are 2 files in this example; the code for the procedural, and a .ass scene file.

This procedural makes a random point cloud with fractal distribution in space. There are 5 controls for the generation of the cloud:

• count: the number of points per recursion to generate
• recursions: the number of recursive steps to descend
• flake_radius: the radius of the little spheres that make up the cloud
• seed: a random seed to generate a unique random cloud

The algorithm works like this: given a spherical region of radius R, scatter N points into it. For each point, scatter N random points within a radius of 1/2 the previous R, and so on, for X number of recursions, then hang a little sphere of radius S on the resulting points.

This algorithm creates poorly bound geometry and is likely to build a few points outside the bounding box. Sample render of a point cloud with 2,015,539 points

Here is the procedural code:

```#include <ai.h>
#include <cstring>
#include <cstdlib>

// Procedural parameters
struct RandomFlake
{
int   count;
int   recursions;
int   counter;
int   num_points;
};

// returns a random vector in a unit sphere with a
// power function to bias it towards the center
static AtVector random_vector(float power)
{
AtVector out(drand48() - 0.5, drand48() - 0.5, drand48() - 0.5);
return AiV3Normalize(out) * pow(drand48(), power);
}

// recursive function that creates random flake with fractal clumping
static void make_cloud(RandomFlake *flake, AtArray *point_array, AtArray *radius_array, AtVector center, float radius, int recursions)
{
for (int i = 0; i < flake->count; i++)
{
AtVector new_center = random_vector(0.5) * radius;
AiArraySetVec(point_array, flake->counter, new_center + center);
flake->counter++;
if (recursions > 1)
make_cloud(flake, point_array, radius_array, new_center + center, radius * 0.5, recursions - 1);
}
}

AI_PROCEDURAL_NODE_EXPORT_METHODS(RandomFlakeMtd);

node_parameters
{
AiParameterInt("count"        , 10);
AiParameterInt("recursions"   , 5);
AiParameterInt("seed"         , 0);
}

procedural_init
{
RandomFlake *flake = new RandomFlake();
*user_ptr = flake;

srand48(AiNodeGetInt(node, "seed"));

flake->count         = AiNodeGetInt(node, "count");
flake->recursions    = AiNodeGetInt(node, "recursions");
flake->counter       = 0;

flake->num_points = 0;
for (int i = 0; i < flake->recursions; i++)
flake->num_points += pow(flake->count, i);
AiMsgInfo("[random_flake] number of points: %d", flake->num_points);

return true;
}

procedural_cleanup
{
RandomFlake *flake = (RandomFlake*)user_ptr;
delete flake;
return true;
}

procedural_num_nodes
{
return 1;
}

procedural_get_node
{
RandomFlake *flake = (RandomFlake*)user_ptr;
AtArray *point_array    = AiArrayAllocate(flake->num_points, 1, AI_TYPE_VECTOR);
AtArray *radius_array   = AiArrayAllocate(flake->num_points, 1, AI_TYPE_FLOAT);

// create node with procedural node as parent
AtNode *points_node = AiNode("points", "flake", node);
AiNodeSetArray(points_node, "points", point_array);
AiNodeSetStr  (points_node, "mode"  , "sphere");

return points_node;
}

{
if (i>0)
return false;

node->methods      = RandomFlakeMtd;
node->output_type  = AI_TYPE_NONE;
node->name         = "random_flake";
node->node_type    = AI_NODE_SHAPE_PROCEDURAL;
strcpy(node->version, AI_VERSION);

return true;
}```

This is the .ass file that renders the above image:

```options
{
AA_samples 6
outputs "RGB RGB myfilter mydriver"
xres 640
yres 480
GI_diffuse_depth 1
}

driver_jpeg
{
name mydriver
filename "randflake.jpg"
}

gaussian_filter
{
name myfilter
}

plane
{
name myplane
point 0 -10 0
normal 0 1 0
}

random_flake
{
name myrandflake
count 6
recursions 9
seed 4
}

lambert
{
Kd 0.7
}

lambert
{
Kd_color .15 .2 .2
}

persp_camera
{
name mycamera
focus_distance 11
aperture_size .15
position -5 5 15
look_at 0 0 0
}

point_light
{
name key
position 100 200 100
color 1 0.6 0.4
}

spot_light
{
name kicker
position -200 200 -500
look_at 0 0 0
cone_angle 2
color .1 0.2 3
}

skydome_light
{
name mysky
color 0.6 0.85 1
intensity .5
}```

The compilation commands:

```c++ -o random_flake.os -c -fPIC -D_LINUX -fPIC -I. -I/opt/arnold/include random_flake.cpp
c++ -o librandom_flake.so -shared random_flake.os -L/opt/arnold/bin -lai```

See Creating a Simple Plugin for further information about compilation commands.

• No labels