Welcome to the Stash
// Input 1 - Pts for Attribute | Inputer 2 - BoundingGeo
// float blur_radius — how far (in world units) to look for neighbors
// int blur_maxpts — max neighbors to sample
// 1️⃣ Get the box’s bounds (input 1)
vector bbmin = getbbox_min(1);
vector bbmax = getbbox_max(1);
// 2️⃣ Raw inside test: 1 if P is within [bbmin,bbmax], else 0
int raw = (@P.x >= bbmin.x && @P.x <= bbmax.x
&& @P.y >= bbmin.y && @P.y <= bbmax.y
&& @P.z >= bbmin.z && @P.z <= bbmax.z)
? 1 : 0;
// 3️⃣ Blur: average raw over neighbors on input 0
float radius = chf("blur_radius");
int maxpts = chi("blur_maxpts");
int handle = pcopen(0, "P", @P, radius, maxpts);
float sum = 0;
int count = 0;
while (pciterate(handle)) {
int pt;
pcimport(handle, "point.number", pt);
vector P2 = point(0, "P", pt);
// test neighbor against same bounds
int raw2 = (P2.x >= bbmin.x && P2.x <= bbmax.x
&& P2.y >= bbmin.y && P2.y <= bbmax.y
&& P2.z >= bbmin.z && P2.z <= bbmax.z)
? 1 : 0;
sum += raw2;
count++;
}
float blurred = count ? sum / count : raw;
// 4️⃣ Output final attribute
f@inside = blurred;
// Parameters: search radius and maximum number of neighbor points
float rad = chf("srchrad");
int maxpts = chi("maxpts");
// Find neighbors and remove the point itself
int npts[] = nearpoints(0, @P, rad, maxpts);
pop(npts, 0);
// Compute density as the ratio of found points to maxpts
int pcd = len(npts);
f@density = float(pcd) / float(maxpts);
// Initialize arrays to store differences and weighted directions
float diffs[];
vector weightedDirs[];
foreach (int npt; npts)
{
float ndensity = point(0, "density", npt);
vector npos = point(0, "P", npt);
ndensity = sin(ndensity*chf("sindensitymult"));
// Compute the density difference for this neighbor
float diff = ndensity - f@density;
// Append the difference to an array
append(diffs, diff);
// Multiply the direction vector by this difference and append
append(weightedDirs, (npos - @P) * diff);
}
// Compute a gradient by averaging the weighted directions
vector grad = {0, 0, 0};
if (len(weightedDirs) > 0)
grad = sum(weightedDirs) / float(len(weightedDirs));
// Optionally, invert the gradient if you want to push from high-density to low-density regions
// grad = -grad;
// Update the position
float stepsize = 0.9;
@P += grad * stepsize;
// Store the gradient for debugging/visualization
v@grad = grad;
// Color based on density
float hue = f@density;
@Cd = hsvtorgb(set(hue, 1, 1));
bbox(opinputpath('..',3),D_XSIZE) bbox(opinputpath('..',3),D_YSIZE)
bbox(opinputpath('..',3),D_ZSIZE)
centroid(opinputpath('..',3), 0)
centroid(opinputpath('..',3), 1)
centroid(opinputpath('..',3), 2)
// Pts input 0 // Geo (with normals) input 1 // // Find closest point on geometry in input 1
int prim;
vector uv;
vector surface_pos = xyzdist(1, @P, prim, uv);
// Get normal at that location
vector nml = primuv(1, "N", prim, uv);
// Move the point slightly along the normal
float move_amount = chf("move"); // Add a slider for control
vector moved_pos = @P + nml * move_amount;
// Reproject it back to the closest point on the surface
xyzdist(1, moved_pos, prim, uv);
@P = primuv(1, "P", prim, uv); ------------------------------------------------------------------------------------------------------------------------
///////WITH RANDOM SPEED/////////// float randval = rand(@ptnum);
f@speed = fit01(randval, chf("speed_min"), chf("speed_max")); // Range control
// Find closest point on geometry in input 1
int prim;
vector uv;
vector surface_pos = xyzdist(1, @P, prim, uv);
// Get normal at that location
vector nml = primuv(1, "N", prim, uv);
// Move amount = base move slider * speed attribute
float move_amount = chf("move") * f@speed;
// Move point along the normal
vector moved_pos = @P + nml * move_amount;
// Reproject it back to the closest point on the surface
xyzdist(1, moved_pos, prim, uv);
@P = primuv(1, "P", prim, uv);
// Random speed per point
float randval = rand(@ptnum);
float bias = chramp("speed_ramp", randval);
f@speed = fit01(bias, chf("speed_min"), chf("speed_max"));
// Y ramp based on current position
float maxY = chf("max_y");
float normalized_y = clamp(@P.y / maxY, 0.0, 1.0);
float y_ramp_val = chramp("y_gradient_ramp", normalized_y);
// Final movement amount
float move_amount = chf("move") * f@speed * y_ramp_val;
// Store original position
vector old_pos = @P;
// --- Get normal from rest geo (Input 2) ---
int rest_prim;
vector rest_uv;
xyzdist(2, @P, rest_prim, rest_uv);
vector nml = primuv(2, "N", rest_prim, rest_uv);
// Offset along normal
vector moved_pos = old_pos + nml * move_amount;
// --- Project back onto moving geo (Input 3) ---
int mov_prim;
vector mov_uv;
xyzdist(3, moved_pos, mov_prim, mov_uv);
@P = primuv(3, "P", mov_prim, mov_uv);
// --- pscale falloff based on distance moved ---
float dist_moved = length(@P - old_pos);
float max_dist = chf("max_expected_distance");
float move_factor = clamp(dist_moved / max_dist, 0.0, 1.0);
float scale_multiplier = chramp("scale_falloff", move_factor);
@pscale *= lerp(1.0, chf("min_pscale_mult"), scale_multiplier);
// Define a consistent reference vector (typically "up")
vector up = {0,1,0};
// Make sure N exists (calculate if missing)
vector N = @N;
if (length(N) < 0.001) {
N = normalize(@P - point(0, "P", nearpoint(0, @P + {0.01,0,0})));
}
// Calculate the tangent/along vector using the cross product
vector along = normalize(cross(N, up));
// In case N and up are aligned (resulting in a zero vector), pick a different reference
if (length(along) < 0.001) {
up = {1,0,0};
along = normalize(cross(N, up));
}
// Store the result
v@along = along;
i@found_overlap = 1;
// PARAMETERS
float max_wet = chf("max_wet");
float intensity = chf("intensity");
int decay_frames = chi("decay");
float density_threshold = chf("density_threshold"); // Volume threshold
// --- Previous frame wetmap ---
float prev_wet = point(0, "wetmap", @ptnum);
// --- Sample density volume from Input 2 ---
float density = volumesample(1, 0, @P); // Input 2, volume 0 (usually "density")
// --- Determine if affected by volume ---
int is_touched = (density > density_threshold);
// --- Apply wetmap logic ---
if (is_touched) {
f@wetmap = clamp(prev_wet + intensity, 0, max_wet);
} else {
float decay_rate = 1.0 / max(float(decay_frames), 1.0);
f@wetmap = max(prev_wet - decay_rate, 0.0);
}
// PARAMETERS
float threshold = chf("distance_threshold"); // How close geo2 must be to 'wet'
float max_wet = chf("max_wet");
float intensity = chf("intensity");
int decay_frames = chi("decay");
// --- Pull previous wetmap from last frame ---
float prev_wet = point(0, "wetmap", @ptnum); // from solver’s 1st input (history)
// --- Check proximity to external mesh ---
int prim;
vector uv;
float dist = xyzdist(1, @P, prim, uv); // input 2: meshed trail geo
// --- Determine if touched ---
int is_touched = (dist >= 0 && dist < threshold);
// --- Apply logic ---
if (is_touched) {
f@wetmap = clamp(prev_wet + intensity, 0, max_wet);
} else {
float decay_rate = 1.0 / max(float(decay_frames), 1.0);
f@wetmap = max(prev_wet - decay_rate, 0.0);
}
`opname(".")`
(@Frame == 1 || @Frame == 5 || @Frame == 10) ? 1 : 0