1#![allow(dead_code, unused_variables, unused_imports, unused_mut, unused_parens, non_snake_case, unreachable_patterns, unused_assignments, unused_labels, unused_doc_comments, private_interfaces, clippy::all)]
2
3pub mod math;
32pub mod glyph;
33pub mod entity;
34pub mod particle;
35pub mod scene;
36pub mod render;
37pub mod audio;
38pub mod integration;
39pub mod input;
40pub mod config;
41pub mod tween;
42pub mod debug;
43pub mod ui;
44pub mod timeline;
45pub mod procedural;
46pub mod physics;
47pub mod combat;
48pub mod spatial;
49pub mod effects;
50pub mod anim;
51pub mod animation;
52pub mod ai;
53pub mod networking;
54pub mod replay;
55pub mod scripting;
56pub mod terrain;
57pub mod ecs;
58pub mod editor;
59pub mod asset;
60pub mod save;
61pub mod character;
62pub mod dsp;
63pub mod game;
64pub mod profiler;
65pub mod vfx;
66pub mod netcode;
67pub mod network;
68pub mod world;
69pub mod crafting;
70pub mod pathfinding;
71pub mod economy;
72pub mod behavior;
73pub mod weather;
74pub mod deferred;
75pub mod shader_graph;
76pub mod surfaces;
77pub mod rendergraph;
78pub mod compute;
79pub mod lighting;
80pub mod number_theory;
81pub mod graph;
82pub mod topology;
83pub mod stochastic;
84pub mod ml;
85pub mod wgpu_backend;
86pub mod geometry;
87pub mod symbolic;
88pub mod solver;
89pub mod fractal;
90pub mod metaball;
91pub mod worldgen;
92pub mod ecology;
93pub mod narrative;
94pub mod electromagnetic;
95pub mod relativistic;
96pub mod quantum;
97pub mod svogi;
98pub mod curves;
99pub mod nishita_sky;
100pub mod volumetric_fog;
101pub mod tiled_lighting;
102
103pub use config::EngineConfig;
104pub use math::{MathFunction, ForceField, Falloff, AttractorType};
105pub use glyph::{Glyph, RenderLayer, BlendMode};
106pub use entity::AmorphousEntity;
107pub use particle::{MathParticle, ParticleInteraction};
108pub use scene::SceneGraph;
109pub use render::camera::ProofCamera;
110pub use input::{InputState, Key};
111pub use render::pipeline::FrameStats;
112pub use audio::AudioEvent;
113
114pub struct ProofEngine {
116 pub config: EngineConfig,
117 pub scene: SceneGraph,
118 pub camera: ProofCamera,
119 pub input: InputState,
120 pub ui: render::ui_layer::UiLayer,
123 pub fx: render::screen_fx::ScreenFx,
126 density_queue: Vec<particle::gpu_density::GpuDensityEntityData>,
128 density_budget: u32,
130 pub audio: Option<audio::AudioEngine>,
132 pipeline: Option<render::Pipeline>,
134}
135
136impl ProofEngine {
137 pub fn new(config: EngineConfig) -> Self {
138 let audio = if config.audio.enabled {
139 audio::AudioEngine::try_new()
140 } else {
141 None
142 };
143 Self {
144 camera: ProofCamera::new(&config),
145 scene: SceneGraph::new(),
146 input: InputState::new(),
147 ui: render::ui_layer::UiLayer::new(
148 config.window_width as f32,
149 config.window_height as f32,
150 ),
151 fx: render::screen_fx::ScreenFx::new(),
152 density_queue: Vec::new(),
153 density_budget: 0,
154 audio,
155 config,
156 pipeline: None,
157 }
158 }
159
160 pub fn init_gpu_density(&mut self, particles: u32) {
168 let cap = particle::gpu_density::MAX_PARTICLES_PER_ENTITY;
169 if particles > cap {
170 log::info!("gpu density: {particles} particles requested, drawing {cap} per entity");
171 }
172 self.density_budget = particles.min(cap);
173 }
174
175 pub fn queue_gpu_density_entity(&mut self, entity: particle::gpu_density::GpuDensityEntityData) {
177 self.density_queue.push(entity);
178 }
179
180 pub fn emit_audio(&self, event: audio::AudioEvent) {
182 if let Some(ref a) = self.audio {
183 a.emit(event);
184 }
185 }
186
187 pub fn run<F>(&mut self, mut update: F)
190 where
191 F: FnMut(&mut ProofEngine, f32),
192 {
193 self.run_with_overlay(move |engine, dt, _gl| {
194 update(engine, dt);
195 });
196 }
197
198 pub fn run_with_overlay<F>(&mut self, mut update: F)
202 where
203 F: FnMut(&mut ProofEngine, f32, &glow::Context),
204 {
205 let pipeline = render::Pipeline::init(&self.config);
206 self.pipeline = Some(pipeline);
207
208 let mut last = std::time::Instant::now();
209 loop {
210 let now = std::time::Instant::now();
211 let dt = now.duration_since(last).as_secs_f32().min(0.1);
212 last = now;
213
214 if let Some(ref mut p) = self.pipeline {
216 if !p.poll_events(&mut self.input) {
217 break;
218 }
219 }
220
221 self.scene.tick(dt);
223
224 let gl_ptr = self.pipeline.as_ref().map(|p| p.gl() as *const glow::Context);
228
229 if let Some(ref mut p) = self.pipeline {
232 p.update_render_config(&self.config.render);
233 }
234
235 if let Some(ref mut p) = self.pipeline {
237 p.set_density_entities(&self.density_queue, self.density_budget);
238 p.render(&self.scene, &self.camera);
239 }
240 self.density_queue.clear();
241 self.fx.lights.clear();
242
243 if let Some(ptr) = gl_ptr {
245 let gl_ref = unsafe { &*ptr };
246 update(self, dt, gl_ref);
247 }
248
249 if let Some(ref mut p) = self.pipeline {
251 if !p.swap() {
252 break;
253 }
254 }
255 }
256 }
257
258 pub fn run_ui<F>(&mut self, mut update: F)
268 where
269 F: FnMut(&mut ProofEngine, f32),
270 {
271 let pipeline = render::Pipeline::init(&self.config);
272 self.pipeline = Some(pipeline);
273
274 let (w, h) = self.render_size();
277 self.ui.resize(w as f32, h as f32);
278
279 let mut last = std::time::Instant::now();
280 let mut last_size = (w, h);
281 loop {
282 let now = std::time::Instant::now();
283 let dt = now.duration_since(last).as_secs_f32().min(0.1);
284 last = now;
285
286 if let Some(ref mut p) = self.pipeline {
287 if !p.poll_events(&mut self.input) {
288 break;
289 }
290 }
291
292 let size = self.render_size();
294 if size != last_size {
295 last_size = size;
296 self.ui.resize(size.0 as f32, size.1 as f32);
297 }
298
299 self.scene.tick(dt);
300
301 self.ui.begin_frame();
303 update(self, dt);
304
305 if self.input.quit_requested {
313 break;
314 }
315
316 self.camera.shake.tick(dt);
319 self.fx.tick(dt);
320
321 if let Some(ref mut p) = self.pipeline {
322 p.update_render_config(&self.config.render);
323 p.set_density_entities(&self.density_queue, self.density_budget);
324 p.render_frame(&self.scene, &self.camera, Some(&self.ui), &self.fx);
327 p.render_ui(&self.ui);
329 }
330 self.density_queue.clear();
331 self.fx.lights.clear();
332
333 if let Some(ref mut p) = self.pipeline {
334 if !p.swap() {
335 break;
336 }
337 }
338 }
339 }
340
341 pub fn add_field(&mut self, field: ForceField) -> scene::FieldId {
343 self.scene.add_field(field)
344 }
345
346 pub fn remove_field(&mut self, id: scene::FieldId) {
348 self.scene.remove_field(id)
349 }
350
351 pub fn spawn_glyph(&mut self, glyph: Glyph) -> glyph::GlyphId {
353 self.scene.spawn_glyph(glyph)
354 }
355
356 pub fn spawn_entity(&mut self, mut entity: AmorphousEntity) -> entity::EntityId {
358 if entity.formation.is_empty() {
360 use entity::formation::Formation;
361 let f = Formation::diamond(2);
362 entity.formation = f.positions;
363 entity.formation_chars = f.chars;
364 }
365 while entity.formation_colors.len() < entity.formation.len() {
367 entity.formation_colors.push(glam::Vec4::ONE);
368 }
369 for i in 0..entity.formation.len() {
371 let offset = entity.formation[i];
372 let ch = entity.formation_chars.get(i).copied().unwrap_or('◆');
373 let color = entity.formation_colors.get(i).copied().unwrap_or(glam::Vec4::ONE);
374 let id = self.scene.spawn_glyph(Glyph {
375 character: ch,
376 position: entity.position + offset,
377 color,
378 emission: 0.8,
379 glow_color: glam::Vec3::new(color.x, color.y, color.z),
380 glow_radius: 1.2,
381 mass: entity.entity_mass / entity.formation.len().max(1) as f32,
382 layer: RenderLayer::Entity,
383 ..Default::default()
384 });
385 entity.glyph_ids.push(id);
386 }
387 self.scene.spawn_entity(entity)
388 }
389
390 pub fn emit_particles(&mut self, emitter: particle::EmitterPreset, origin: glam::Vec3) {
392 particle::emit(&mut self.scene, emitter, origin);
393 }
394
395 pub fn add_trauma(&mut self, amount: f32) {
397 self.camera.add_trauma(amount);
398 }
399}
400
401impl ProofEngine {
403 pub fn request_quit(&mut self) {
404 self.input.quit_requested = true;
405 }
406
407 pub fn gl(&self) -> Option<&glow::Context> {
410 self.pipeline.as_ref().map(|p| p.gl())
411 }
412
413 pub fn window(&self) -> Option<&winit::window::Window> {
415 self.pipeline.as_ref().map(|p| p.window())
416 }
417
418 pub fn window_size(&self) -> (u32, u32) {
420 self.pipeline.as_ref().map(|p| p.window_size()).unwrap_or((1600, 1000))
421 }
422
423 pub fn render_size(&self) -> (u32, u32) {
428 self.pipeline.as_ref().map(|p| p.render_size()).unwrap_or((1600, 1000))
429 }
430
431 pub fn save_frame(&self, path: &str) -> std::io::Result<()> {
442 use std::io::Write;
443 let Some(p) = self.pipeline.as_ref() else {
444 return Err(std::io::Error::new(
445 std::io::ErrorKind::Other,
446 "no pipeline to read from",
447 ));
448 };
449 let (w, h, rgba) = p.read_frame();
450 if w == 0 || h == 0 {
451 return Err(std::io::Error::new(
452 std::io::ErrorKind::Other,
453 "empty framebuffer",
454 ));
455 }
456
457 let stride = ((w as usize * 3) + 3) & !3;
459 let pixels = stride * h as usize;
460 let mut out = Vec::with_capacity(54 + pixels);
461 out.extend_from_slice(b"BM");
462 out.extend_from_slice(&((54 + pixels) as u32).to_le_bytes());
463 out.extend_from_slice(&0u32.to_le_bytes());
464 out.extend_from_slice(&54u32.to_le_bytes());
465 out.extend_from_slice(&40u32.to_le_bytes());
466 out.extend_from_slice(&(w as i32).to_le_bytes());
467 out.extend_from_slice(&(h as i32).to_le_bytes());
468 out.extend_from_slice(&1u16.to_le_bytes());
469 out.extend_from_slice(&24u16.to_le_bytes());
470 for _ in 0..6 {
471 out.extend_from_slice(&0u32.to_le_bytes());
472 }
473
474 for y in 0..h as usize {
475 let row = y * w as usize * 4;
476 for x in 0..w as usize {
477 let i = row + x * 4;
478 out.push(rgba[i + 2]);
480 out.push(rgba[i + 1]);
481 out.push(rgba[i]);
482 }
483 for _ in 0..(stride - w as usize * 3) {
484 out.push(0);
485 }
486 }
487
488 let mut f = std::fs::File::create(path)?;
489 f.write_all(&out)
490 }
491}
492
493pub mod prelude {
495 pub use crate::{
496 ProofEngine, EngineConfig,
497 MathFunction, ForceField, Falloff, AttractorType,
498 Glyph, RenderLayer, BlendMode,
499 AmorphousEntity,
500 MathParticle, ParticleInteraction,
501 AudioEvent,
502 particle::EmitterPreset,
503 render::camera::ProofCamera,
504 input::{InputState, Key},
505 scene::{SceneGraph, FieldId},
506 audio::MusicVibe,
507 tween::{Tween, Easing, TweenState, Tweens, AnimationGroup},
508 tween::easing::Easing as EasingFn,
509 tween::keyframe::{KeyframeTrack, Keyframe, CameraPath, ExtrapolateMode},
510 tween::sequence::{TweenSequence, TweenTimeline, SequenceBuilder},
511 debug::DebugOverlay,
512 render::pipeline::FrameStats,
513 render::screen_fx::{ScreenFx, ScreenLight, Shockwave},
514 render::ui_layer::UiPass,
515 };
516 pub use glam::{Mat4, Quat, Vec2, Vec3, Vec4};
520}