Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cliffhall/fa82a03874a8d45c2227bea7375d5960 to your computer and use it in GitHub Desktop.
Save cliffhall/fa82a03874a8d45c2227bea7375d5960 to your computer and use it in GitHub Desktop.

The Sinewav3 Plugin example 'Sparks Fly' creates a particle system simulating sparks which dim as they age, while moving in a realistic fashion.

A number of settings are exposed for the particle system emitter and its movement as well as the protoypical spark particle and its movement. The code demonstrates the use of audio modulation and easing.

Learn more about this plugin here: https://medium.com/sinewav3/make-sparks-fly-c48e94219d4f

// The body of the plugin's render() function,
// which takes a single argument, the Visualizer context,
// and is called for each frame.
context.memory.spawn(); // Create a spark
context.memory.age(); // Age all the sparks
// The body of the plugin's setup() function,
// which takes a single argument, the Visualizer context,
// and is called once, prior to entering the render loop.
// Initialize particle count and array in context memory
context.memory.spark_count = 0;
context.memory.sparks = [];
// Create a function to get the current emitter swing settings
context.memory.getSwingInfo = function() {
// Base position
let x_base = context.plugin.getSettingValue('Emitter', 'X Pos');
let y_base = context.plugin.getSettingValue('Emitter', 'Y Pos');
let z_base = context.plugin.getSettingValue('Emitter', 'Z Pos');
// Swing params
let modulator = context.plugin.getSettingValue('Emitter', 'Swing Mod');
let mod_amount = context.modulators.getModulatorValue( modulator ) || (Math.random() * 1);
let radius = context.plugin.getSettingValue('Emitter', 'Swing Radius');
let duration = context.plugin.getSettingValue('Emitter', 'Swing Duration');
let curve = context.plugin.getSettingValue('Emitter', 'Swing Curve');
let x_min = x_base - radius, x_max = x_base + radius;
let y_min = y_base - radius, y_max = y_base + radius;
let z_min = z_base - radius, z_max = z_base + radius;
// Bundle and return the settings
return {
radius: radius,
curve: curve,
duration: duration,
x_base: x_base,
y_base: y_base,
z_base: z_base,
x_min: x_min * mod_amount,
x_max: x_max * mod_amount,
y_min: y_min * mod_amount,
y_max: y_max * mod_amount,
z_min: z_min * mod_amount,
z_max: z_max * mod_amount
};
};
// Create a function to setup the swing motion for the emitter
context.memory.setupSwing = function() {
let swing = context.memory.getSwingInfo();
swing.x_ease = context.modulators.getEaser(swing.curve, swing.x_max, swing.x_min, swing.duration * (Math.random() * 1), Setting.LOOPS.PING_PONG);
swing.y_ease = context.modulators.getEaser(swing.curve, swing.y_min, swing.y_max, swing.duration * (Math.random() * 1), Setting.LOOPS.PING_PONG);
swing.z_ease = context.modulators.getEaser(swing.curve, swing.z_max, swing.z_min, swing.duration * (Math.random() * 1), Setting.LOOPS.PING_PONG);
context.memory.swing = swing;
};
// Create a function to check if the emitter swing info changed since last frame.
// Remember, the user can change the setting values during visualization.
context.memory.swingChanged = function() {
let old = context.memory.swing;
let current = context.memory.getSwingInfo();
let changed = (
current.curve !== old.curve ||
current.duration !== old.duration ||
current.radius !== old.radius ||
current.x_base !== old.x_base ||
current.y_base !== old.y_base ||
current.z_base !== old.z_base
);
return changed;
}
// Create a function to get the current emitter location
context.memory.getEmitterLocation = function() {
// Build/rebuild the swing eases if need be
if (!context.memory.swing || context.memory.swingChanged()) context.memory.setupSwing();
// Get the current position of the emitter
let x = context.memory.swing.x_ease();
let y = context.memory.swing.y_ease();
let z = context.memory.swing.z_ease();
return new THREE.Vector3(x,y,z);
};
// Create a funcion to get a random target position for a new particle, within the min and max distance
context.memory.getSparkTarget = function(emitter_loc) {
let min_distance = context.plugin.getSettingValue('Particle Motion', 'Min Distance');
let max_distance = context.plugin.getSettingValue('Particle Motion', 'Max Distance');
let dx = Math.floor(Math.random() * (max_distance - min_distance)) + min_distance;
let dy = Math.floor(Math.random() * (max_distance - min_distance)) + min_distance;
let dz = Math.floor(Math.random() * (max_distance - min_distance)) + min_distance;
dx = (Math.random() > 0.5) ? dx : -dx;
dy = (Math.random() > 0.5) ? dy : -dy;
dz = (Math.random() > 0.5) ? dz : -dz;
return new THREE.Vector3(emitter_loc.x + dx, emitter_loc.y + dy, emitter_loc.z + dz);
};
// Create a function to get the lifespan for a spark based on current setting and modulation
context.memory.getSparkLifeSpan = function() {
let max_life = context.plugin.getSettingValue('Particle', 'Life Span');
let modulator = context.plugin.getSettingValue('Particle','Life Span Mod');
let life_span = context.modulators.getModulatorValue( modulator ) * max_life || (Math.random() * max_life) + 1;
return life_span;
};
// Create a function to get a random size for a spark
context.memory.getSparkSize = function() {
let max_size = context.plugin.getSettingValue('Particle', 'Max Size');
let modulator = context.plugin.getSettingValue('Particle','Size Mod');
let spark_size = context.modulators.getModulatorValue( modulator ) * max_size || (Math.random() * max_size) + 1;
return spark_size;
};
// Create a function to build the material for a new spark (which will be modified over time)
context.memory.getSparkMaterial = function() {
let color = context.plugin.getSettingValue('Particle', 'Color');
let material = new THREE.MeshLambertMaterial({color: color, emissive: color, emissiveIntensity:1, side:THREE.DoubleSide});
return material;
};
// Create a function to spawn a new particle
context.memory.spawn = function() {
// Get the key attributes of the spark
let spark_num = context.memory.spark_count;
let spark_size = context.memory.getSparkSize();
let life_span = context.memory.getSparkLifeSpan();
let emitter_loc = context.memory.getEmitterLocation();
// Create and register material
let color = context.plugin.getSettingValue('Particle', 'Color');
let spark_mat = context.memory.getSparkMaterial();
context.registerMaterial(`spark-material-${spark_num}`, spark_mat);
// Create and register the spark geometry
let spark_geo = new THREE.DodecahedronGeometry(spark_size);
context.registerGeometry(`spark-geo-${spark_num}`, spark_geo);
// Create and regsister the mesh
let spark_mesh = new THREE.Mesh(spark_geo, spark_mat);
spark_mesh.position.set(emitter_loc.x, emitter_loc.y, emitter_loc.z);
context.addToScene(spark_mesh);
// Create the target for the spark and create motion eases
let spark_target = context.memory.getSparkTarget(emitter_loc);
// Vary the easing curves for each
let x_curve = context.plugin.getSettingValue('Particle Motion', 'X Curve');
let y_curve = context.plugin.getSettingValue('Particle Motion', 'Y Curve');
let z_curve = context.plugin.getSettingValue('Particle Motion', 'Z Curve');
let x_ease = context.modulators.getEaser(x_curve, emitter_loc.x, spark_target.x, life_span);
let y_ease = context.modulators.getEaser(y_curve, emitter_loc.y, spark_target.y, life_span);
let z_ease = context.modulators.getEaser(z_curve, emitter_loc.z, spark_target.z, life_span);
let s_ease = context.modulators.getEaser(Setting.EASES.LINEAR, 1, 0, life_span); // scale
let e_ease = context.modulators.getEaser(Setting.EASES.EXP_OUT, 1, 0, life_span); // emissive intensity
// Create and store the particle
let spark = {
age: 0,
mesh: spark_mesh,
material: spark_mat,
life_span: life_span,
x_ease: x_ease,
y_ease: y_ease,
z_ease: z_ease,
s_ease: s_ease,
e_ease: e_ease
};
context.memory.sparks.push(spark);
context.memory.spark_count++;
};
// Create a function to age all the living particles
context.memory.age = function() {
context.memory.sparks.forEach( spark => {
if (spark.age <= spark.life_span) {
let scale = spark.s_ease();
if (scale > 0) spark.mesh.scale.set(scale, scale, scale);
spark.mesh.position.set(spark.x_ease(), spark.y_ease(), spark.z_ease());
spark.material.emissiveIntenstity = spark.e_ease();
spark.age++;
if (spark.age > spark.life_span) {
context.removeFromScene(spark.mesh);
spark.mesh = null;
}
}
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment