CParticleSpatialDescriptor

From PopcornFX
Jump to: navigation, search

Spatial layer Last update: v1.10.0 - Main page : Particle layers Particle layers


Available since v1.8.0
A spatial layer is a special container that stores particles and allows to make fast proximity queries.
It enables particle/particle interactions between different layers and effects.


Spatial layer Base concept

The idea is to have a regular layer whose particles insert themselves each frame into a spatial layer, through the Spatial insertion evolver Spatial-insertion evolver. Then, some other particles can query the spatial layer for information about the particles that fall inside a spherical volume.
This information can be the number of neighbors, the average value of a particle field, a the exact value of a field from the closest particle, etc..
Usually, the spherical query volume is centered around the querying particle, but you can specify a custom search center if necessary.

You can watch and listen to a video commentary of v1.8.0 release highlights where we talk about and describe the spatial layers feature:
https://youtu.be/0FFku3Hs17c?t=267


Full video: (spatial-layers highlight @ 4:27)


Spatial layer Node properties

Field name Default value Description
Spatial layer Action
LayerName <empty> Name of the spatial layer.

Must not contain special characters.Name must be non-empty and usable as a variable-name inside a script.
Must start with an alphabetical character, followed by any alpha-numerical character. Cannot contain spaces.

Global false if checked, other effects will be able to access and insert particles in this spatial layer.

activate if you want it to interact with other effects.

CellSize 4.25f Cell size. Should be set based on the scale of the effect and the average spatial query radius you'll be using to query this layer. Directly influences performance.

You can start with values around 1.5 to 2 times the average query radius you'll be using.

Spatial layer layout


Spatial layer Spatial queries

The spatial layers allow you to issue queries to get various informations about what's inside an arbitrary spherical volume

Spatial query base The query volume geometry is defined by:
  • a center (a float3 position in 3D space, usually the position of the particle who's launching the query)
  • a radius


For convenience and clarity, in the diagrams and examples below, the particles and query sphere will be represented in 2D, and the pseudocode examples will assume an array named 'neighborList' has already been populated with all the particles that are inside the query volume.


Field queries

These queries allow you to gather information about a specific field of the particles that fall inside the query region:


Closest

Searches for the closest particle and returns the value of its queried field.

Spatial query: closest Here, the query will return the values of particle 'D', as it is the closest to the search center.

spatialLayers.LayerName.Field.closest(QueryCenter, QueryRadius);

What the query does: (Pseudocode)

list neighborList = findAllNeighborsWithinRadius(QueryCenter, QueryRadius);
neighborList.sortByDistance();
particle closest = neighborList[0];
foreach (particle p in neighborList)
{
    if (p.distance < closest.distance)
    {
        closest = p;
    }
}
return p.Field;


N-Closest

Searches for the 'N'-th closest particle and returns the value of its queried field. (v1.9.0)

Spatial query: n-closest Here, a query with N=3 will return the values of particle 'B', as it is the 3rd closest to the search center:

spatialLayers.LayerName.Field.closest(QueryCenter, QueryRadius, N);

What the query does: (Pseudocode)

list neighborList = findAllNeighborsWithinRadius(QueryCenter, QueryRadius);
neighborList.sortByDistance();
particle closest = neighborList[N];
foreach (particle p in neighborList)
{
    if (p.distance < closest.distance)
    {
        closest = p;
    }
}
return p.Field;


Sum

Sums a specific field of all particles inside the query volume.

Spatial query: sum Here, the query will add together the desired field of particles 'A', 'B', 'C', 'D', and 'E':

spatialLayers.LayerName.Field.sum(QueryCenter, QueryRadius);

What the query does: (Pseudocode)

list neighborList = findAllNeighborsWithinRadius(QueryCenter, QueryRadius);
fieldType sum = 0;
foreach (particle p in neighborList)
{
    sum += p.Field;
}
return sum;


Sum with custom kernel

Sums a specific field of all particles inside the query volume, and uses the custom kernel to weight each value based on its distance to the query center.

Spatial query: sumKernel Here, the query will add together the desired field of particles 'A', 'B', 'C', 'D', and 'E', and scale each one of them by the value of the kernel sampled with the normalized distance of the particle to the query center.

spatialLayers.LayerName.Field.sumKernel(QueryCenter, QueryRadius, CurveSampler1D);

What the query does: (Pseudocode)

list neighborList = findAllNeighborsWithinRadius(QueryCenter, QueryRadius);
fieldType sum = 0;
foreach (particle p in neighborList)
{
    float weight = CurveSampler1D.sample(p.distance / searchRadius);
    sum += p.Field * weight;
}
return sum;


Average

Averages a specific field of all particles inside the query volume.

Spatial query: average Here, the query will add together the desired field of particles 'A', 'B', 'C', 'D', and 'E', and divide the result by the number of particles (5), to get the average value:

spatialLayers.LayerName.Field.average(QueryCenter, QueryRadius);

What the query does: (Pseudocode)

list neighborList = findAllNeighborsWithinRadius(QueryCenter, QueryRadius);
fieldType sum = 0;
foreach (particle p in neighborList)
{
    sum += p.Field;
}
return sum / neighborList.count;


Average with custom kernel

Averages a specific field of all particles inside the query volume, and uses the custom kernel to weight each value based on its distance to the query center.

Spatial query: averageKernel Here, the query will add together the desired field of particles 'A', 'B', 'C', 'D', and 'E', and scale each one of them by the value of the kernel sampled with the normalized distance of the particle to the query center. It will then return the sum divided by the sums of the kernel weights to compute the proper average.

spatialLayers.LayerName.Field.averageKernel(QueryCenter, QueryRadius, CurveSampler1D);

What the query does: (Pseudocode)

list neighborList = findAllNeighborsWithinRadius(QueryCenter, QueryRadius);
fieldType   sum = 0;
float       weightSum = 0;
foreach (particle p in neighborList)
{
    float weight = CurveSampler1D.sample(p.distance / searchRadius);
    sum += p.Field * weight;
    weightSum += weight;
}
return sum / weightSum;


Global queries

These queries are not tied to a specific field, but retreive informations about the geometry of the spatial neighborhood.


Neighbor count

Returns the number of particles inside the query volume.

Spatial query: neighbor count Here, the query will return the number of neighbors : 5

spatialLayers.LayerName.neighborCount(QueryCenter, QueryRadius);

What the query does: (Pseudocode)

list neighborList = findAllNeighborsWithinRadius(QueryCenter, QueryRadius);
return neighborList.count;


Spatial layer Script bindings

Each spatial layer of the effect will expose itself to all the effect's scripts, allowing you to use query functions.
All spatial layers declare themselves in the 'spatialLayers' namespace.

For example, if the effect contains a spatial layer named "Heat", and you want to query the number of neighboring particles around the current particle, inside a radius of 1.5 units, you'd perform a neighbor-count query like so:

int count = spatialLayers.Heat.neighborCount(Position, 1.5);

To query the number of neighbors in a sphere of radius 1.5 slightly ahead of the particle movement, you could do:

int count = spatialLayers.Heat.neighborCount(Position + 0.2 * Velocity, 1.5);


Field queries Prototypes & Notes
closest
fieldType   spatialLayers.LayerName.FieldName.closest(float3 center, float radius);

Returns the value of 'FieldName' of the closest particle inside the query volume.
If no particles are inside the query volume, returns infinity

n-closest
fieldType   spatialLayers.LayerName.FieldName.closest(float3 center, float radius, int N [, int cacheSize]);

(v1.9.0) Returns the value of 'FieldName' of the 'N' closest particle inside the query volume.
If no particles are inside the query volume, returns infinity
The optional 'cacheSize' defaults to 4. Can be tweaked to improve performance if you're making multiple queries with the same search radius and position.

sum
fieldTypeFloat  spatialLayers.LayerName.FieldName.sum(float3 center, float radius);

Returns the sum of 'FieldName' of all particles inside the query volume.
If no particles are inside the query volume, returns infinity
If the original field is int, int2, int3, or int4, the returned value will actually be a float vector of the same dimension: float, float2, float3 or float4.

sumKernel
fieldTypeFloat  spatialLayers.LayerName.FieldName.sumKernel(float3 center, float radius, samplerCurve1D kernelSampler);

Returns the sum of 'FieldName' of all particles inside the query volume, and uses the custom kernel (1D curve sampler curve sampler) to weight each value based on its distance to the query center.
If no particles are inside the query volume, returns infinity
If the original field is int, int2, int3, or int4, the returned value will actually be a float vector of the same dimension: float, float2, float3 or float4.

average
fieldTypeFloat  spatialLayers.LayerName.FieldName.average(float3 center, float radius);

Returns the average value of 'FieldName' of all particles inside the query volume.
If no particles are inside the query volume, returns infinity
If the original field is int, int2, int3, or int4, the returned value will actually be a float vector of the same dimension: float, float2, float3 or float4.

averageKernel
fieldTypeFloat  spatialLayers.LayerName.FieldName.averageKernel(float3 center, float radius, samplerCurve1D kernelSampler);

Returns the average value of 'FieldName' of all particles inside the query volume, and uses the custom kernel (1D curve sampler curve sampler) to weight each value based on its distance to the query center.
If no particles are inside the query volume, returns infinity
If the original field is int, int2, int3, or int4, the returned value will actually be a float vector of the same dimension: float, float2, float3 or float4.

Global queries Prototypes & Notes
neighborCount
int spatialLayers.LayerName.neighborCount(float3 center, float radius);

Returns the number of particles inside the query volume.
If no particles are inside the query volume, returns 0


Interactions between different effects

Different effects can share their spatial layers.
As long as two effects have a spatial layer with the same name, same cell size, and with its 'Global' property enable, then at runtime the same storage will be used for both effects.

This allows effects A and B to interact with each other.