Implement camera system

This commit is contained in:
2026-08-28 19:22:57 +02:00
parent 81736a4fab
commit 730ffb777e
8 changed files with 666 additions and 11 deletions
+67 -1
View File
@@ -10,6 +10,31 @@ use crate::{
};
use bevy::prelude::*;
/// Per-stage presentation tuning for the standard match camera.
///
/// Values use gameplay world units on the XY plane
#[derive(Component, Serialize, Deserialize, Clone, Reflect)]
#[reflect(opaque)]
pub struct StageCameraProfile {
pub left: f32,
pub right: f32,
pub bottom: f32,
pub top: f32,
pub origin: Vec2,
pub vertical_fov_degrees: f32,
pub min_depth: f32,
pub max_depth: f32,
pub subject_scale: f32,
pub tracking_smoothness: f32,
pub vertical_tilt_degrees: f32,
pub horizontal_pan_coefficient: f32,
pub vertical_pan_coefficient: f32,
pub max_horizontal_pan_degrees: f32,
pub max_upward_pan_degrees: f32,
pub max_downward_pan_degrees: f32,
pub max_downward_expansion: f32,
}
#[derive(Reflect, Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash, Debug, EnumIter)]
pub enum StageId {
TestStage,
@@ -26,6 +51,7 @@ pub struct StagePolyDefinition {
pub struct StageManifest {
pub id: StageId,
pub model_path: String,
pub camera: StageCameraProfile,
pub polygons: Vec<StagePolyDefinition>,
}
@@ -96,6 +122,13 @@ pub fn prepare_stage_manifests(
);
return;
}
if !manifest.camera.is_valid() {
fail(
&mut next_state,
&format!("{:?} has an invalid camera profile", manifest.id),
);
return;
}
if registry.insert(manifest.id, handle.clone()).is_some() {
fail(
&mut next_state,
@@ -129,4 +162,37 @@ impl StageManifest {
stage_polys
}
}
}
}
impl StageCameraProfile {
fn is_valid(&self) -> bool {
self.left.is_finite()
&& self.right.is_finite()
&& self.bottom.is_finite()
&& self.top.is_finite()
&& self.origin.is_finite()
&& self.vertical_fov_degrees.is_finite()
&& self.min_depth.is_finite()
&& self.max_depth.is_finite()
&& self.subject_scale.is_finite()
&& self.tracking_smoothness.is_finite()
&& self.vertical_tilt_degrees.is_finite()
&& self.horizontal_pan_coefficient.is_finite()
&& self.vertical_pan_coefficient.is_finite()
&& self.max_horizontal_pan_degrees.is_finite()
&& self.max_upward_pan_degrees.is_finite()
&& self.max_downward_pan_degrees.is_finite()
&& self.max_downward_expansion.is_finite()
&& self.left < self.right
&& self.bottom < self.top
&& (0.0..180.0).contains(&self.vertical_fov_degrees)
&& self.min_depth > 0.0
&& self.min_depth <= self.max_depth
&& self.subject_scale > 0.0
&& self.tracking_smoothness >= 0.0
&& self.max_horizontal_pan_degrees >= 0.0
&& self.max_upward_pan_degrees >= 0.0
&& self.max_downward_pan_degrees >= 0.0
&& self.max_downward_expansion >= 0.0
}
}
+9 -3
View File
@@ -10,7 +10,7 @@ pub mod manifest;
pub use line::{StagePlaneIntersectResult, StagePoly};
use crate::stage::line::StageCollision;
use crate::{stage::{line::StageCollision, manifest::StageCameraProfile}};
/// Owns the stage geometry: which scene gets spawned and how it is drawn.
#[derive(Default)]
@@ -25,7 +25,13 @@ impl Plugin for StagePlugin {
}
}
pub fn spawn_stage(commands: &mut Commands, match_root: Entity, visual: Handle<WorldAsset>, collision: StageCollision) {
pub fn spawn_stage(
commands: &mut Commands,
match_root: Entity,
visual: Handle<WorldAsset>,
collision: StageCollision,
camera_profile: StageCameraProfile,
) {
commands.entity(match_root).insert(WorldAssetRoot(visual));
scene::spawn_stage_support(commands, match_root, collision);
scene::spawn_stage_support(commands, match_root, collision, camera_profile);
}
+13 -1
View File
@@ -6,13 +6,19 @@ use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use crate::{
camera::MatchCamera,
math::{int::FGi32, vec::FGVec2}, stage::{
StagePoly,
line::{StageCollision, StagePolyLineSegmentType, StagePolyType},
},
};
pub fn spawn_stage_support(commands: &mut Commands, match_root: Entity, collision: StageCollision) {
pub fn spawn_stage_support(
commands: &mut Commands,
match_root: Entity,
collision: StageCollision,
camera_profile: crate::stage::manifest::StageCameraProfile,
) {
commands.insert_resource(collision);
commands.spawn((
DirectionalLight {
@@ -28,6 +34,12 @@ pub fn spawn_stage_support(commands: &mut Commands, match_root: Entity, collisio
));
commands.spawn((
Camera3d::default(),
Projection::Perspective(PerspectiveProjection {
fov: camera_profile.vertical_fov_degrees.to_radians(),
..default()
}),
MatchCamera::default(),
camera_profile,
Transform::from_xyz(0.0, 10.5, 150.0),
/*FreeCamera {
sensitivity: 0.2,