mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-26 11:07:54 -06:00
GPU render passes for CC Particle World / Systems II, Particle Playground, CC Ball Action, CC Pixel Polly, CC Scatterize and Curl Noise (fx_particles, chunked multi-pass raster); Shatter wireframe views, Foam extras, Timewarp with a Matte Layer, strong Warp Fisheye/Twist (CPU f64 inverse map) and custom .ocio configs no longer fall back to the CPU. CPU refactors pinned by particle_golden / timewarp_golden / sim_golden hashes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
146 lines
5.4 KiB
Rust
146 lines
5.4 KiB
Rust
//! GPU particle render passes (`fx_particles`) vs the CPU effects (the oracle): the frame's
|
|
//! plan is shared, the raster runs on each side. Direct on a buffer (full and half resolution)
|
|
//! and composited at 8 and 32 bpc (`tests_fx_sim`'s harness).
|
|
|
|
use effectcraft_keyframe::Value;
|
|
|
|
use crate::tests::n;
|
|
use crate::tests_fx_sim::{case, composited, e, off, on};
|
|
|
|
fn v2(x: f64, y: f64) -> Value {
|
|
Value::Vec2([x, y])
|
|
}
|
|
|
|
#[test]
|
|
fn particle_world_types_and_modes() {
|
|
for kind in 0..9 {
|
|
case(
|
|
"ec.sim.ccparticleworld",
|
|
&[("birthRate", n(1.5)), ("particle/particleType", e(kind)), ("particle/transferMode", e(kind % 4)), ("particle/birthSize", n(0.3))],
|
|
1.0,
|
|
0.0,
|
|
);
|
|
}
|
|
for anim in [1, 2, 5, 6, 8] {
|
|
case("ec.sim.ccparticleworld", &[("physics/animation", e(anim)), ("particle/particleType", e(1)), ("particle/sizeVariation", n(40.0))], 0.8, 0.0);
|
|
}
|
|
// Nothing born yet: the layer as is.
|
|
case("ec.sim.ccparticleworld", &[("birthRate", n(0.0))], 0.0, 0.0);
|
|
}
|
|
|
|
#[test]
|
|
fn particle_systems_ii_types_and_modes() {
|
|
for kind in 0..9 {
|
|
case("ec.sim.ccparticlesystems2", &[("birthRate", n(3.0)), ("particle/particleType", e(kind)), ("particle/transferMode", e(kind % 4))], 1.0, 0.0);
|
|
}
|
|
case("ec.sim.ccparticlesystems2", &[("physics/animation", e(4)), ("producer/radiusX", n(8.0)), ("physics/direction", n(70.0))], 1.3, 0.0);
|
|
}
|
|
|
|
/// A Particle Playground grid (cannon off) plus `more`.
|
|
fn grid(more: &[(&'static str, Value)]) -> Vec<(&'static str, Value)> {
|
|
let mut g = vec![
|
|
("cannonEnabled", off()),
|
|
("gridEnabled", on()),
|
|
("grid/gridPosition", v2(35.0, 22.0)),
|
|
("grid/gridWidth", n(40.0)),
|
|
("grid/gridHeight", n(20.0)),
|
|
("grid/particlesAcross", n(5.0)),
|
|
("grid/particlesDown", n(3.0)),
|
|
("grid/gridParticleRadius", n(3.0)),
|
|
("gravity/gravityForce", n(20.0)),
|
|
];
|
|
g.extend(more.iter().cloned());
|
|
g
|
|
}
|
|
|
|
#[test]
|
|
fn particle_playground() {
|
|
case("ec.sim.particleplayground", &[], 1.0, 0.0);
|
|
case(
|
|
"ec.sim.particleplayground",
|
|
&[("cannon/cannonPosition", v2(20.0, 40.0)), ("cannon/particlesPerSecond", n(150.0)), ("cannon/velocity", n(90.0)), ("gravity/gravityForce", n(40.0))],
|
|
1.2,
|
|
0.0,
|
|
);
|
|
case("ec.sim.particleplayground", &grid(&[]), 0.7, 0.0);
|
|
case(
|
|
"ec.sim.particleplayground",
|
|
&grid(&[("options/gridText", Value::Str("Ab C".into())), ("grid/gridParticleRadius", n(12.0)), ("options/autoOrientRotation", on())]),
|
|
0.6,
|
|
0.0,
|
|
);
|
|
case("ec.sim.particleplayground", &grid(&[("repel/repelForce", n(5.0)), ("repel/repelForceRadius", n(12.0))]), 0.9, 0.0);
|
|
composited(
|
|
"ec.sim.particleplayground",
|
|
&[
|
|
("cannonEnabled", off()),
|
|
("exploderEnabled", on()),
|
|
("layerExploder/explodeLayer", Value::Layer(Some(0))),
|
|
("layerExploder/radiusOfNewParticles", n(3.0)),
|
|
],
|
|
0.5,
|
|
0.0,
|
|
);
|
|
// Layer Map: every grid particle shows the other layer, at frames set by the offset type.
|
|
for (kind, offset) in [(0, 0.0), (2, 0.5), (3, 1.0)] {
|
|
composited(
|
|
"ec.sim.particleplayground",
|
|
&grid(&[
|
|
("layerMapEnabled", on()),
|
|
("layerMap/layerMapLayer", Value::Layer(Some(0))),
|
|
("layerMap/timeOffsetType", e(kind)),
|
|
("layerMap/timeOffset", n(offset)),
|
|
("grid/particlesAcross", n(3.0)),
|
|
("grid/particlesDown", n(2.0)),
|
|
]),
|
|
0.8,
|
|
0.0,
|
|
);
|
|
}
|
|
composited(
|
|
"ec.sim.particleplayground",
|
|
&grid(&[
|
|
("mapperEnabled", on()),
|
|
("persistentPropertyMapper/useLayerAsMap", Value::Layer(Some(0))),
|
|
("persistentPropertyMapper/mapRedTo", e(8)),
|
|
("persistentPropertyMapper/redMin", n(-50.0)),
|
|
("persistentPropertyMapper/redMax", n(50.0)),
|
|
]),
|
|
0.7,
|
|
0.0,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ball_action_pixel_polly_scatterize() {
|
|
case("ec.sim.ccballaction", &[], 0.0, 0.0);
|
|
case(
|
|
"ec.sim.ccballaction",
|
|
&[("scatter", n(20.0)), ("rotationAxis", e(6)), ("rotation", n(40.0)), ("twistProperty", e(3)), ("twistAngle", n(90.0))],
|
|
0.0,
|
|
0.0,
|
|
);
|
|
case("ec.sim.ccballaction", &[("gridSpacing", n(2.0)), ("ballSize", n(80.0)), ("instabilityState", n(45.0)), ("twistProperty", e(2))], 0.0, 0.0);
|
|
for object in 0..4 {
|
|
case("ec.sim.ccpixelpolly", &[("object", e(object)), ("gridSpacing", n(6.0))], 0.6, 0.0);
|
|
}
|
|
case("ec.sim.ccpixelpolly", &[("enableDepthSort", off()), ("spinning", n(90.0)), ("gravity", n(2.0))], 1.0, 0.0);
|
|
case("ec.sim.ccpixelpolly", &[("startTime", n(1.0))], 0.5, 0.0);
|
|
case("ec.sim.ccscatterize", &[("scatter", n(10.0))], 0.0, 0.0);
|
|
case("ec.sim.ccscatterize", &[("rightTwist", n(60.0)), ("leftTwist", n(-30.0)), ("transferMode", e(1)), ("scatter", n(4.0))], 0.0, 0.0);
|
|
case("ec.sim.ccscatterize", &[], 0.0, 0.0);
|
|
}
|
|
|
|
#[test]
|
|
fn curl_noise() {
|
|
case("ec.noise.curlnoise", &[], 0.0, 0.0);
|
|
case("ec.noise.curlnoise", &[("view", e(1))], 0.0, 0.0);
|
|
case(
|
|
"ec.noise.curlnoise",
|
|
&[("edgeBehavior", e(1)), ("steps", n(8.0)), ("displacementAmount", n(60.0)), ("scale", n(20.0)), ("rotation", n(30.0)), ("evolution", n(90.0))],
|
|
0.0,
|
|
0.0,
|
|
);
|
|
case("ec.noise.curlnoise", &[("displacementAmount", n(0.0))], 0.0, 0.0);
|
|
}
|