Non Local Geometry Operations

Until now we have focuses on attribute wranglers for points and volumes where we only handled each point individually. Next we are going to write code that requires the use of the whole geometry at once.

In particular we will try to compute the mean of an attribute and compute a surface area on each triangle and also try to blur a volume.

Summing Over the Geometry

Next we want to illustrate with a single example how to sum over each element of a geometry. Let us assume that we are given a system with random points spread over space. Each point is given a particular mass as a floating point attribute. This could for example model planets in space.

We model them by uniformly randomly distributed points in the $[0,1]^2$ box in the XZ-plane. The number of points is read from an integer parameter in the node interface that we created just like in previous tutorials.

Example: total mass
This task is very simple. We run our index from 0 to the (total number of points-1) on each point and sum up the masses we find. This happens in a attribute wrangle running over “detail“.

Example: center of mass
This task can be done quite similarly by running through every point index  again reading the mass “mass” and positions “P“. We then perform a weighted averaging with these values.

Note that we set our attribute wrangle node to run only once. You can define this inside the node’s parameter controls by switching to Detail (only once). We store the result in an extra attribute to be able access it later.

If you now look at the geometry spreadsheet of the final node you can see that the total mass is roughly half the number of points and that the center of mass is near (0.5, 0, 0.5). We can expect these results when using uniformly distributed values in the interval [0,1].

Neighbor Operations

Next we will see how to work with the neighboring points of a single point or with the neighboring faces of a single face. Accessing the right neighbors can be tricky at first. We work with a triangulated version of the surface, and if it is not yet triangulated, we can us a remesh node to archive that.

Let us try it out on a sphere.

Example: edge lengths
Here we want to know the distance between to neighboring points. One could compute the distances between all points to archive that too but that would be computationally too expensive. Instead we can do the following:

Points describe the position of the junctions of our meshes. The GPU however works with vertices and needs 3 vertices to render a triangle even if the points appeared beforehand in another triangle. We have far more vertices than points and we can use this to our advantage. Each vertex is linked to a halfedge (read here to know what halfedges are) from which we can determine the next vertex. We can access the halfedges using vertexhedge(0,@vtnum) and then read the next point in it’s attribute. We do this in a single vertex wrangle node and store the edge lengths multiple times this way.

Example: triangle areas
Once we know the the edge lengths we can compute the triangle area. Given edge lengths $a,b,c$ we can use the handy Heron’s formula to compute the enclosed area:

$$p:= \frac{a+b+c}{2} , \ \ \ A=\sqrt{p(p-a)(p-b)(p-c)}$$

This means that we can split this problem into finding the edge lengths first and then using the above formula. We can use the same node as above to compute the edge lengths and then attach another node below to it that computes the area. The primitive wrangle will run on each triangle and extract the edge lengths around it by grabbing any one of the half edges around it and then reading the vertexes through them.

Example: total surface area
This is just like the total mass example above. Instead of summing over the points we now sum over the triangles (primitives) using a attribute wrangle node that runs only once.

Volume Operations

A volume is a 3D grid of values. As such, we would like to know how to access neighbours inside it. Volumes come with their own special kind of attributes, important ones being @resx, @resy, @resz to encode the resolution of the grid.

Analogous to the tutorial of the advection, we will create a scalar volume and name its scalar f. We will later blur the function f by its surrounding values. Be sure to set “Uniform sampling Divisions” to be around 50.

Let us make the volume a interesting by editing f inside a volume wrangle node. The next code will define a non-zero constant field inside a sphere.

Then inside the repeating loop of the blurring, we need the following code:

@ix, @iy, @iz refer to the index of the piece of volume (the grid box) inside the volume. Using volumeindex(0,”f”,set(jx,jy,jz)) we can then access the volume values by index. The way we perform modular division with % @resx is justified by thinking that the volume is periodically repeating like a torus. Yes, there is a Houdini node that does exactly this job, but writing the code our self is a valuable exercise.