aboutsummaryrefslogtreecommitdiff
path: root/stockton-render
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:22 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:22 +0100
commit8004132c918a52af3b2e7b32b4fc72c65fde9e3c (patch)
tree9e57c80762cfbb50f98bacfb745bbb988acc22a7 /stockton-render
parent8c1406f7a42822d1b020cc672ed99d02eb34d22f (diff)
feat(draw): attach camera position/settings to entity
Diffstat (limited to 'stockton-render')
-rw-r--r--stockton-render/src/draw/camera.rs237
-rw-r--r--stockton-render/src/draw/context.rs38
-rw-r--r--stockton-render/src/draw/mod.rs1
-rw-r--r--stockton-render/src/draw/target.rs8
-rw-r--r--stockton-render/src/lib.rs16
5 files changed, 61 insertions, 239 deletions
diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs
index 11bf617..aa5efac 100644
--- a/stockton-render/src/draw/camera.rs
+++ b/stockton-render/src/draw/camera.rs
@@ -17,19 +17,14 @@
//! Things related to converting 3D world space to 2D screen space
-use stockton_types::{Matrix4, Vector3};
+use legion::maybe_changed;
-use na::{look_at_lh, perspective_lh_zo, Mat4, Vec4};
-use std::f32::consts::PI;
+use nalgebra_glm::look_at_lh;
+use nalgebra_glm::perspective_lh_zo;
-/// 90 degrees in radians
-const R89: f32 = (PI / 180.0) * 89.0;
-
-/// 90 degrees in radians
-const R90: f32 = PI / 2.0;
-
-/// 180 degrees in radians
-const R180: f32 = PI;
+use crate::Renderer;
+use stockton_types::components::{CameraSettings, Transform};
+use stockton_types::Vector3;
fn euler_to_direction(euler: &Vector3) -> Vector3 {
let pitch = euler.x;
@@ -43,193 +38,35 @@ fn euler_to_direction(euler: &Vector3) -> Vector3 {
)
}
-pub struct CameraSettings {
- /// Position of the camera (world units)
- pub position: Vector3,
-
- /// Rotation of the camera (euler angles in radians)
- pub rotation: Vector3,
-
- /// The up direction (normalized)
- pub up: Vector3,
-
- /// FOV (radians)
- pub fov: f32,
-
- /// Near clipping plane (world units)
- pub near: f32,
-
- /// Far clipping plane (world units)
- pub far: f32,
-}
-
-/// Holds settings related to the projection of world space to screen space
-/// Also holds maths for generating important matrices
-pub struct WorkingCamera {
- /// Settings for the camera
- settings: CameraSettings,
-
- /// Aspect ratio as a fraction
- aspect_ratio: f32,
-
- /// Cached view projection matrix
- vp_matrix: Mat4,
-
- /// If true, cached value needs updated
- is_dirty: bool,
-}
-
-impl WorkingCamera {
- /// Return a camera with default settings
- pub fn defaults(aspect_ratio: f32) -> WorkingCamera {
- WorkingCamera::with_settings(
- CameraSettings {
- position: Vector3::new(0.0, 0.0, 0.0),
- rotation: Vector3::new(0.0, R90, 0.0),
- up: Vector3::new(0.0, 1.0, 0.0),
- fov: f32::to_radians(90.0),
- near: 0.1,
- far: 1024.0,
- },
- aspect_ratio,
- )
- }
-
- /// Return a camera with the given settings
- pub fn with_settings(settings: CameraSettings, aspect_ratio: f32) -> WorkingCamera {
- WorkingCamera {
- aspect_ratio,
- settings,
- vp_matrix: Mat4::identity(),
- is_dirty: true,
- }
- }
-
- /// Get the VP matrix, updating cache if needed
- pub fn get_matrix(&mut self) -> &Mat4 {
- // Update matrix if needed
- if self.is_dirty {
- self.vp_matrix = self.calc_vp_matrix();
- self.is_dirty = false;
- }
-
- // Return the matrix
- &self.vp_matrix
- }
-
- /// Returns a matrix that transforms from world space to screen space
- fn calc_vp_matrix(&self) -> Matrix4 {
- // Get look direction from euler angles
- let direction = euler_to_direction(&self.settings.rotation);
-
- // Converts world space to camera space
- let view_matrix = look_at_lh(
- &self.settings.position,
- &(self.settings.position + direction),
- &self.settings.up,
- );
-
- // Converts camera space to screen space
- let projection_matrix = {
- let mut temp = perspective_lh_zo(
- self.aspect_ratio,
- self.settings.fov,
- self.settings.near,
- self.settings.far,
- );
-
- // Vulkan's co-ord system is different from OpenGLs
- temp[(1, 1)] *= -1.0;
-
- temp
- };
-
- // Chain them together into a single matrix
- projection_matrix * view_matrix
- }
-
- /// Update the aspect ratio
- pub fn update_aspect_ratio(&mut self, new: f32) {
- self.aspect_ratio = new;
- self.is_dirty = true;
- }
-
- /// Apply rotation of the camera
- /// `euler` should be euler angles in degrees
- pub fn rotate(&mut self, euler: Vector3) {
- // TODO
- self.settings.rotation += euler;
-
- // Clamp -pi/2 < pitch < pi/2
- if self.settings.rotation.x > R89 {
- self.settings.rotation.x = R89;
- } else if self.settings.rotation.x <= -R89 {
- self.settings.rotation.x = -R89;
- }
-
- // -pi < yaw <= pi
- if self.settings.rotation.y <= -R180 {
- self.settings.rotation.y = R180 - self.settings.rotation.y % -R180;
- } else if self.settings.rotation.y > 180.0 {
- self.settings.rotation.y = -R180 + self.settings.rotation.y % R180;
- }
-
- self.is_dirty = true;
- }
-
- /// Move the camera by `delta`, relative to the camera's rotation
- pub fn move_camera_relative(&mut self, delta: Vector3) {
- let rot_matrix = Mat4::from_euler_angles(
- -self.settings.rotation.x,
- self.settings.rotation.y,
- self.settings.rotation.z,
- );
-
- let new = rot_matrix * Vec4::new(delta.x, delta.y, delta.z, 1.0);
- self.settings.position.x += new.x;
- self.settings.position.y += new.y;
- self.settings.position.z += new.z;
-
- self.is_dirty = true;
- }
-
- pub fn camera_pos(&self) -> Vector3 {
- self.settings.position
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::draw::camera::WorkingCamera;
- use stockton_types::Matrix4;
- use stockton_types::Vector3;
-
- fn contains_nan(mat: &Matrix4) -> bool {
- for x in mat.iter() {
- if (*x).is_nan() {
- return true;
- }
- }
- false
- }
-
- #[test]
- fn camera_vp() {
- let mut camera = WorkingCamera::defaults(16.0 / 9.0);
-
- let old = camera.calc_vp_matrix();
- println!("initial vp matrix: {:?}", old);
-
- assert!(!contains_nan(&old), "No NaNs for initial matrix");
-
- // Do a 180
- camera.rotate(Vector3::new(0.0, 180.0, 0.0));
-
- let new = camera.calc_vp_matrix();
- assert!(!contains_nan(&new), "No NaNs after rotating");
-
- println!("new vp matrix: {:?}", new);
-
- assert!(old != new, "VP Matrix changes when camera rotates");
- }
+#[system(for_each)]
+#[filter(maybe_changed::<Transform>() | maybe_changed::<CameraSettings>())]
+pub fn calc_vp_matrix(
+ transform: &Transform,
+ settings: &CameraSettings,
+ #[resource] renderer: &mut Renderer<'static>,
+) {
+ let ratio = renderer.context.target_chain.properties.extent.width as f32
+ / renderer.context.target_chain.properties.extent.height as f32;
+ // Get look direction from euler angles
+ let direction = euler_to_direction(&transform.rotation);
+
+ // Converts world space to camera space
+ let view_matrix = look_at_lh(
+ &transform.position,
+ &(transform.position + direction),
+ &Vector3::new(0.0, 1.0, 0.0), // TODO
+ );
+
+ // Converts camera space to screen space
+ let projection_matrix = {
+ let mut temp = perspective_lh_zo(ratio, settings.fov, settings.near, settings.far);
+
+ // Vulkan's co-ord system is different from OpenGLs
+ temp[(1, 1)] *= -1.0;
+
+ temp
+ };
+
+ // Chain them together into a single matrix
+ renderer.context.vp_matrix = projection_matrix * view_matrix
}
diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs
index 777a3a9..b569ced 100644
--- a/stockton-render/src/draw/context.rs
+++ b/stockton-render/src/draw/context.rs
@@ -29,6 +29,7 @@ use std::{
use arrayvec::ArrayVec;
use hal::{pool::CommandPoolCreateFlags, prelude::*};
use log::debug;
+use na::Mat4;
use winit::window::Window;
use stockton_levels::prelude::*;
@@ -37,7 +38,6 @@ use stockton_types::{Vector2, Vector3};
use super::{
buffer::ModifiableBuffer,
- camera::WorkingCamera,
draw_buffers::{DrawBuffers, INITIAL_INDEX_SIZE, INITIAL_VERT_SIZE},
target::{SwapchainProperties, TargetChain},
texture::TextureStore,
@@ -76,7 +76,7 @@ pub struct RenderingContext<'a> {
surface: ManuallyDrop<Surface>,
/// Swapchain and stuff
- target_chain: ManuallyDrop<TargetChain>,
+ pub(crate) target_chain: ManuallyDrop<TargetChain>,
// Pipeline
/// Our main render pass
@@ -101,8 +101,8 @@ pub struct RenderingContext<'a> {
/// Buffers used for drawing
draw_buffers: ManuallyDrop<DrawBuffers<'a>>,
- /// Our camera settings
- camera: WorkingCamera,
+ /// View projection matrix
+ pub(crate) vp_matrix: Mat4,
/// The vertex shader module
vs_module: ManuallyDrop<ShaderModule>,
@@ -246,12 +246,6 @@ impl<'a> RenderingContext<'a> {
main_pass: &renderpass,
};
- // Camera
- // TODO: Settings
- let ratio =
- swapchain_properties.extent.width as f32 / swapchain_properties.extent.height as f32;
- let camera = WorkingCamera::defaults(ratio);
-
// Vertex and index buffers
let draw_buffers = DrawBuffers::new(&mut device, &adapter)?;
@@ -309,7 +303,7 @@ impl<'a> RenderingContext<'a> {
vs_module: ManuallyDrop::new(vs_module),
fs_module: ManuallyDrop::new(fs_module),
- camera,
+ vp_matrix: Mat4::identity(),
})
}
@@ -322,10 +316,6 @@ impl<'a> RenderingContext<'a> {
let properties = SwapchainProperties::find_best(&self.adapter, &self.surface)
.map_err(|_| error::CreationError::BadSurface)?;
- // Camera settings (aspect ratio)
- self.camera
- .update_aspect_ratio(properties.extent.width as f32 / properties.extent.height as f32);
-
use core::ptr::read;
// Graphics pipeline
@@ -572,7 +562,7 @@ impl<'a> RenderingContext<'a> {
&self.renderpass,
&self.pipeline,
&self.pipeline_layout,
- &mut self.camera,
+ &self.vp_matrix,
)?;
// Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once.
@@ -683,22 +673,6 @@ impl<'a> RenderingContext<'a> {
Ok(())
}
-
- /// Get current position of camera
- pub fn camera_pos(&self) -> Vector3 {
- self.camera.camera_pos()
- }
-
- /// Move the camera by `delta` relative to its rotation
- pub fn move_camera_relative(&mut self, delta: Vector3) {
- self.camera.move_camera_relative(delta)
- }
-
- /// Rotate the camera
- /// `euler` should be euler angles in radians
- pub fn rotate(&mut self, euler: Vector3) {
- self.camera.rotate(euler)
- }
}
impl<'a> core::ops::Drop for RenderingContext<'a> {
diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs
index 744ad96..88381c5 100644
--- a/stockton-render/src/draw/mod.rs
+++ b/stockton-render/src/draw/mod.rs
@@ -27,5 +27,6 @@ mod context;
mod draw_buffers;
mod texture;
+pub use self::camera::calc_vp_matrix_system;
pub use self::context::RenderingContext;
pub use self::context::UVPoint;
diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs
index 65e03a2..19f41dc 100644
--- a/stockton-render/src/draw/target.rs
+++ b/stockton-render/src/draw/target.rs
@@ -16,7 +16,7 @@
*/
//! Resources needed for drawing on the screen, including sync objects
-use super::{camera::WorkingCamera, texture::image::LoadedImage};
+use super::texture::image::LoadedImage;
use crate::types::*;
use core::{iter::once, mem::ManuallyDrop};
@@ -32,6 +32,7 @@ use hal::{
queue::Submission,
window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig},
};
+use na::Mat4;
/// Defines the colour range we use.
const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange {
@@ -266,7 +267,7 @@ impl TargetChain {
renderpass: &RenderPass,
pipeline: &GraphicsPipeline,
pipeline_layout: &PipelineLayout,
- camera: &mut WorkingCamera,
+ vp: &Mat4,
) -> Result<&'a mut crate::types::CommandBuffer, &'static str> {
self.last_drawn = (self.last_drawn + 1) % self.targets.len();
@@ -337,8 +338,7 @@ impl TargetChain {
target.cmd_buffer.bind_graphics_pipeline(&pipeline);
// VP Matrix
- let vp = camera.get_matrix().as_slice();
- let vp = &*(vp as *const [f32] as *const [u32]);
+ let vp = &*(vp.data.as_slice() as *const [f32] as *const [u32]);
target.cmd_buffer.push_graphics_constants(
&pipeline_layout,
diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs
index 4d10ff8..8bdca89 100644
--- a/stockton-render/src/lib.rs
+++ b/stockton-render/src/lib.rs
@@ -31,12 +31,16 @@ pub mod window;
use culling::get_visible_faces;
use draw::RenderingContext;
+use legion::world::SubWorld;
+use legion::IntoQuery;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::Arc;
use std::sync::RwLock;
pub use window::WindowEvent;
use stockton_levels::prelude::*;
+use stockton_types::components::{CameraSettings, Transform};
+use stockton_types::Vector3;
use winit::event_loop::ControlFlow;
use winit::window::Window;
@@ -75,9 +79,9 @@ impl<'a> Renderer<'a> {
}
/// Render a single frame of the given map.
- fn render<T: MinBSPFeatures<VulkanSystem>>(&mut self, map: &T) {
+ fn render<T: MinBSPFeatures<VulkanSystem>>(&mut self, map: &T, pos: Vector3) {
// Get visible faces
- let faces = get_visible_faces(self.context.camera_pos(), map);
+ let faces = get_visible_faces(pos, map);
// Then draw them
if self.context.draw_vertices(map, &faces).is_err() {
@@ -95,9 +99,15 @@ impl<'a> Renderer<'a> {
/// A system that just renders the world.
#[system]
+#[read_component(Transform)]
+#[read_component(CameraSettings)]
pub fn do_render<T: 'static + MinBSPFeatures<VulkanSystem>>(
#[resource] renderer: &mut Renderer<'static>,
#[resource] map: &T,
+ world: &SubWorld,
) {
- renderer.render(map);
+ let mut query = <(&Transform, &CameraSettings)>::query();
+ for (transform, _) in query.iter(world) {
+ renderer.render(map, transform.position);
+ }
}