2024-01-27 20:21:10 +00:00
|
|
|
# A particles generator in rust for laser shows
|
|
|
|
|
|
|
|
|
|
|
|
## Philosophy
|
|
|
|
|
|
|
|
This library should allow users to define their own particle behaviours
|
|
|
|
|
|
|
|
- how many particles at start? min/max at any time? how many to add at any time?
|
|
|
|
- how do particles behave? Are there different types? Different shapes?
|
|
|
|
- how do particles react with real world? To audio? To a controller?
|
|
|
|
|
|
|
|
## Constraints
|
|
|
|
|
2024-01-27 21:29:18 +00:00
|
|
|
- Render as lists of points in the form [x,y,color] with color encoded as 12 bits (r,g,b)
|
2024-01-27 20:21:10 +00:00
|
|
|
- i.e. it doesn't speak to the laser directly, we use a middleware for that part (see. xxx)
|
|
|
|
- it can output to STDOUT or send to redis
|
|
|
|
- Obviously be as fast as possible
|
|
|
|
- Use a physics engine as a tool for collision detection, velocity and more
|
2024-01-27 21:29:18 +00:00
|
|
|
- Be easy to use: good defaults for everything
|
|
|
|
- Handle bounding and drawing boxes
|
|
|
|
- Handle user custom particle properties (ex: decay, frequency, personality, etc.)
|
|
|
|
- Handle additional drawings for the current frame due to custom events (ex: random edge between two particles)
|
|
|
|
|
|
|
|
## Questions
|
|
|
|
|
|
|
|
### Working with the Physics engine
|
|
|
|
|
2024-01-27 20:21:10 +00:00
|
|
|
- Q: how do we enable custom behaviours (ex: reacting to audio)
|
|
|
|
- Q: how do we map physics engine object (ex: ball, square) for users as concept
|
|
|
|
- Q: how do we convert the physics engine objets to 2D points
|
|
|
|
- Q: how do we handle 3D? On by default?
|
|
|
|
|
2024-01-27 21:25:36 +00:00
|
|
|
## How to use in a project
|
2024-01-27 20:21:10 +00:00
|
|
|
|
|
|
|
1. Clone lj_rust_template and edit draw.rs
|
|
|
|
2. add lj_particle library crate
|
|
|
|
3. edit the draw function
|
2024-01-27 21:25:36 +00:00
|
|
|
4. compile
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:25:36 +00:00
|
|
|
## Fundamental Features
|
|
|
|
- [ ] Use a 3D physics engine
|
|
|
|
- [ ] Provide hooks to create and update the simulation (onInit, onTick, onCollide, etc.)
|
|
|
|
- [ ] Render using box clipping for out of bound particles points
|
|
|
|
- [ ] Use an out-of-simulation list of additional points
|
|
|
|
- [ ] Render to STDOUT or Redis
|
|
|
|
|
|
|
|
## Future Features
|
|
|
|
|
|
|
|
- [ ] Use a 2D physics engine
|
|
|
|
- [ ] Use attractors/repulsors objects
|
|
|
|
- [ ] Use OSC to update configuration
|
2024-01-27 20:21:10 +00:00
|
|
|
|
|
|
|
## Library struct/objects
|
|
|
|
|
2024-01-27 21:29:18 +00:00
|
|
|
```
|
2024-01-27 20:21:10 +00:00
|
|
|
Particle
|
2024-01-27 21:25:36 +00:00
|
|
|
- PhysicsBody
|
|
|
|
- Shape : Square, Triangle, Circle, 3DSphere
|
2024-01-27 20:21:10 +00:00
|
|
|
- UserData // custom ex: life decay, seed, created_at, frequency
|
|
|
|
- Group
|
|
|
|
|
|
|
|
PhysicsEngine
|
|
|
|
PhysicsEngineConfig
|
2024-01-27 21:25:36 +00:00
|
|
|
|
|
|
|
RenderEngine
|
|
|
|
RenderEngineConfig
|
|
|
|
|
2024-01-27 20:21:10 +00:00
|
|
|
|
|
|
|
Config
|
|
|
|
- ParticlesGroups: vec<<Particle>
|
2024-01-27 21:25:36 +00:00
|
|
|
- RedisConfig
|
|
|
|
- PhysicsEngineConfig
|
|
|
|
- RenderEngineConfig
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:25:36 +00:00
|
|
|
// future feature
|
2024-01-27 20:21:10 +00:00
|
|
|
OSCConfig
|
|
|
|
- OSCCache
|
|
|
|
- OSCServer <IP,Port>
|
2024-01-27 21:29:18 +00:00
|
|
|
````
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:29:18 +00:00
|
|
|
## API example
|
2024-01-27 20:21:10 +00:00
|
|
|
|
|
|
|
```rust
|
|
|
|
|
2024-01-27 21:25:36 +00:00
|
|
|
/***
|
|
|
|
An idea of the crate API
|
|
|
|
***/
|
|
|
|
|
2024-01-27 20:21:10 +00:00
|
|
|
use LJParticleSystem as ParticleSystem;
|
|
|
|
use LJRedilysis as Redilysis;
|
|
|
|
use Rapier as PhysicsEngine;
|
|
|
|
|
|
|
|
// Configure
|
|
|
|
let boundingbox = BoundingBox();
|
|
|
|
let clipping_box = ClippingBox();
|
|
|
|
|
|
|
|
let particle_system = ParticleSystem( ParticleSystemConfig, PhysicsEngine, ClippingBox, BoundingBox );
|
|
|
|
let redilysis = Redilysis ( RedilysisConfig( file_path ) );
|
|
|
|
|
|
|
|
|
|
|
|
// by default, equiprobability to generate a particle from any group
|
|
|
|
particle_system.new_particle( lambda() => { ... })
|
|
|
|
|
|
|
|
// some way to init the particles
|
|
|
|
world.init();
|
|
|
|
|
|
|
|
particle_system.tick = lambda(){
|
|
|
|
|
|
|
|
// Configuration update if read via OSC
|
|
|
|
current_config = self->getConfig();
|
|
|
|
|
|
|
|
// Retrieve bandwidth/bpm/rms info (with caching)
|
|
|
|
analysis = redilysis.update();
|
|
|
|
|
|
|
|
// Manage
|
|
|
|
self.boundingBox();
|
|
|
|
|
|
|
|
self.physicsEngine();
|
|
|
|
|
|
|
|
for( particle in self.getParticles() ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
n_particles = current_config.particles_amount;
|
|
|
|
|
|
|
|
return self.clippingBox();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw() -> Result<Vec<Point>, Box<dyn std::error::Error>> {
|
|
|
|
let mut v: Vec<Point> = vec![];
|
|
|
|
v.push( particle_system.tick() );
|
|
|
|
Ok(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## OSC CONFIG
|
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
### Actors and notations
|
|
|
|
|
|
|
|
```
|
|
|
|
- [S] Server / (LJ) OSC Server and client
|
2024-01-27 20:21:10 +00:00
|
|
|
eventual config
|
|
|
|
channel 1 : IP
|
|
|
|
channel X : IP
|
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
- [R] Rust = (LJ Particle) OSC Server and Client
|
|
|
|
|
|
|
|
- [U] User Interface = (Tablet) HTML Interface
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
- [c] Program Channel = configurable, uint ex: 1
|
|
|
|
- [n] Programe name = configurable, string ex: particle_foo_square
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
```
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
### Sequence Diagram
|
2024-01-27 20:21:10 +00:00
|
|
|
|
|
|
|
```
|
2024-01-27 21:33:04 +00:00
|
|
|
# Prerequisite : [S] is running and available and U is connected to [S]
|
|
|
|
# [R] starts
|
|
|
|
# [R]: Do you have my config already ?
|
|
|
|
[R] -> [S] "/program/${c}/${n}/configure"
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
# [S] needs to stock which program is currently used for ${n} channel and its parameters
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
# If no, [S] manifests the need to init the configuration
|
|
|
|
[S] -> [R] "/program/${c}/${n}/no-config"
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
# If no-config, [R] sends its default config for [S] to store
|
|
|
|
[R] -> [S] "/program/${c}/${n}/var1 default_value1" # etc.
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
# In any case, [S] sends to [R] the current config
|
|
|
|
[S] -> [R] "/program/${c}/${n}/var1 value1" # etc.
|
2024-01-27 20:21:10 +00:00
|
|
|
|
2024-01-27 21:33:04 +00:00
|
|
|
# [S] creates an event for the User to change
|
|
|
|
[S] -> U "/program/${c}/${n}"
|
2024-01-27 20:21:10 +00:00
|
|
|
|
|
|
|
# U can change the program configuration
|
2024-01-27 21:33:04 +00:00
|
|
|
U -> [S] "/program/${c}/${n}"
|
2024-01-27 20:21:10 +00:00
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|