use bevy::core_pipeline::prepass::DepthPrepass;
use bevy::prelude::*;
use bevy::time::TimeUpdateStrategy;
use bevy_carnage::{Bore, CarnageVfxPlugin, Pool};
use std::time::Duration;
mod common;
use common::body::{self, Chunk, ORIGIN};
use common::recorder::Recorder;
use common::{arg, light_and_floor};
const WIDTH: u32 = 960;
const HEIGHT: u32 = 680;
const GRANULARITY: usize = 0;
const SOFTEN: f32 = 0.0;
const CALIBRE: f32 = 0.05;
const SHATTER: u32 = 6;
const DT: f32 = 1.0 / 30.0 * 0.55;
const TAIL: u32 = 150;
const SHOTS: [(u32, Vec3); 3] =
[(8, Vec3::new(0.0, 0.10, 0.0)), (70, Vec3::new(0.04, 0.06, 0.0)), (132, Vec3::new(-0.03, 0.13, 0.0))];
fn main() {
let out = arg("--out").unwrap_or_else(|| "frames-pooling".to_string());
let camera = Transform::from_xyz(1.55, 1.55, 2.10).looking_at(ORIGIN - Vec3::Y * 0.70, Vec3::Y);
let Some(mut rec) = Recorder::new_with(WIDTH, HEIGHT, camera, &out, |app| {
app.add_plugins(CarnageVfxPlugin);
}) else {
return;
};
rec.world().insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_secs_f32(DT)));
rec.world().init_resource::<body::Thrown>();
rec.world().init_resource::<body::Pools>();
let cameras: Vec<Entity> =
rec.world().query_filtered::<Entity, With<Camera3d>>().iter(rec.world()).collect();
for entity in cameras {
rec.world().entity_mut(entity).insert(DepthPrepass);
}
light_and_floor(rec.world());
let mut bores: Vec<Bore> = Vec::new();
rebake(&mut rec, &bores);
rec.warm_up(4);
rec.app().main.add_systems(Update, (integrate, body::bleed).chain());
let last = SHOTS.iter().map(|(f, _)| *f).max().unwrap_or(0);
for frame in 0..last + TAIL {
for (at_frame, at) in SHOTS {
if frame == at_frame {
bores.push(body::bore_at(at, CALIBRE, SHATTER));
info!("capture_pooling: frame {frame} — bore at {at:?}");
rebake(&mut rec, &bores);
}
}
rec.shoot();
}
let pools: Vec<Pool> = rec.world().resource::<body::Pools>().0.clone();
let n = rec.finish();
let line = format!("pooling: frames={n} pools={} digest={:016x}", pools.len(), digest(&pools));
info!("{line}");
println!("{line}");
}
fn digest(pools: &[Pool]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
let mut eat = |x: u32| {
for byte in x.to_le_bytes() {
h ^= byte as u64;
h = h.wrapping_mul(0x100_0000_01b3);
}
};
for p in pools {
eat(p.at[0].to_bits());
eat(p.at[1].to_bits());
eat(p.at[2].to_bits());
eat(p.radius.to_bits());
eat(p.wetted.to_bits());
eat(p.seed);
}
h
}
fn rebake(rec: &mut Recorder, bores: &[Bore]) {
body::clear(rec.world());
let baked = body::Baked::bake(rec.world(), SOFTEN, bores, &[GRANULARITY]);
let damage = body::Damage::fresh(&baked, GRANULARITY);
rec.world().insert_resource(baked);
let materials = body::BodyMaterials::new(rec.world());
rec.world().insert_resource(materials);
rec.world().insert_resource(damage);
body::stand(rec.world(), GRANULARITY);
body::spawn_gore(rec.world());
}
fn integrate(mut chunks: Query<(&mut Chunk, &mut Transform)>) {
for (mut chunk, mut transform) in &mut chunks {
body::integrate(&mut chunk, &mut transform, DT);
}
}