diff --git a/game/src/engine/game.rs b/game/src/engine/game.rs index ea2a310..90f8e97 100644 --- a/game/src/engine/game.rs +++ b/game/src/engine/game.rs @@ -1,4 +1,7 @@ -use crate::engine::math::{V2, V3}; +use crate::engine::{ + math::{V2, V3}, + Triangle2, +}; use std::{marker::PhantomData, time::Duration}; use super::error::Error; @@ -16,15 +19,17 @@ pub trait Renderer { fn draw_rect(&mut self, pos: V2, size: V2, color: Color); fn draw_point(&mut self, pos: V2, color: Color); fn draw_line(&mut self, from: V2, to: V2, color: Color); + fn draw_triangles(&mut self, triangles: &[Triangle2], color: Color); } #[derive(Clone, Copy)] pub enum Color { - HEX(u32), - WHITE, - GREEN, - RED, - CYAN, + Hex(u32), + White, + Green, + Red, + Cyan, + Black, } pub enum Event { diff --git a/game/src/engine/math.rs b/game/src/engine/math.rs index d75d056..9d55fb5 100644 --- a/game/src/engine/math.rs +++ b/game/src/engine/math.rs @@ -38,6 +38,11 @@ impl V3 { pub fn map f64>(&self, func: F) -> Self { V3(func(self.0), func(self.1), func(self.2)) } + + pub fn rotate(&self, rot: V3) -> Self { + M3x3::new_rotate_z(rot.2) + * (M3x3::new_rotate_y(rot.1) * (M3x3::new_rotate_x(rot.0) * *self)) + } } impl Add for V3 { @@ -94,6 +99,79 @@ impl MulAssign for V3 { } } +#[derive(Clone, PartialEq, Debug)] +pub struct Triangle2(pub V2, pub V2, pub V2); + +#[derive(Clone, PartialEq, Debug)] +pub struct Triangle3(pub V3, pub V3, pub V3); + +impl Triangle3 { + pub fn normal(&self) -> V3 { + (self.1 - self.0).cross(self.2 - self.1) + } + + pub fn translate(&self, offset: V3) -> Self { + Self(self.0 + offset, self.1 + offset, self.2 + offset) + } + + pub fn project_2d(&self) -> Triangle2 { + Triangle2( + self.0.project_2d(), + self.1.project_2d(), + self.2.project_2d(), + ) + } + + pub fn middle(&self) -> V3 { + (self.0 + self.1 + self.2).map(|v| v / 3.0) + } +} + +struct M3x3([f64; 9]); + +impl M3x3 { + #[rustfmt::skip] + pub fn new_rotate_x(angle: f64) -> Self { + Self([ + 1.0, 0.0, 0.0, + 0.0, f64::cos(angle), -f64::sin(angle), + 0.0, f64::sin(angle), f64::cos(angle), + ]) + } + + #[rustfmt::skip] + pub fn new_rotate_y(angle: f64) -> Self { + Self([ + f64::cos(angle), 0.0, f64::sin(angle), + 0.0, 1.0, 0.0, + -f64::sin(angle), 0.0, f64::cos(angle), + ]) + } + + #[rustfmt::skip] + pub fn new_rotate_z(angle: f64) -> Self { + Self([ + f64::cos(angle), -f64::sin(angle), 0.0, + f64::sin(angle), f64::cos(angle), 0.0, + 0.0, 0.0, 1.0, + ]) + } +} + +impl Mul for M3x3 { + type Output = V3; + + fn mul(self, rhs: V3) -> Self::Output { + let V3(x, y, z) = rhs; + let [xx, xy, xz, yx, yy, yz, zx, zy, zz] = self.0; + V3( + x * xx + y * xy + z * xz, + x * yx + y * yy + z * yz, + x * zx + y * zy + z * zz, + ) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/game/src/engine/r3d.rs b/game/src/engine/r3d.rs index 5031ad3..fe09817 100644 --- a/game/src/engine/r3d.rs +++ b/game/src/engine/r3d.rs @@ -1,43 +1,18 @@ -use crate::engine::{game::Renderer, Color, Shape, V2, V3}; - -#[derive(Clone, PartialEq, Debug)] -pub struct Triangle2(pub V2, pub V2, pub V2); - -#[derive(Clone, PartialEq, Debug)] -pub struct Triangle3(pub V3, pub V3, pub V3); - -impl Triangle3 { - pub fn normal_vector(&self) -> V3 { - (self.1 - self.0).cross(self.2 - self.1) - } - - pub fn translate(&self, offset: V3) -> Self { - Self(self.0 + offset, self.1 + offset, self.2 + offset) - } - - pub fn project_2d(&self) -> Triangle2 { - Triangle2( - self.0.project_2d(), - self.1.project_2d(), - self.2.project_2d(), - ) - } - - pub fn middle(&self) -> V3 { - let m1 = self.0 + (self.1 - self.0).map(|v| v / 2.0); - m1 + (self.2 - m1).map(|v| v / 2.0) - } -} +use crate::engine::{game::Renderer, Color, Shape, Triangle2, Triangle3, V2, V3}; pub struct R3d<'r, R: Renderer> { r: &'r mut R, + triangle_buffer: Vec, } static CAMERA_POS: V3 = V3(0.0, 0.0, -1.0); impl<'r, R: Renderer> R3d<'r, R> { pub fn new(r: &'r mut R) -> Self { - Self { r } + Self { + r, + triangle_buffer: Vec::new(), + } } pub fn draw_line(&mut self, from: V3, to: V3, color: Color) { @@ -53,25 +28,45 @@ impl<'r, R: Renderer> R3d<'r, R> { } pub fn draw_shape(&mut self, pos: V3, shape: &Shape, outline_color: Color, fill_color: Color) { + self.triangle_buffer.clear(); + self.triangle_buffer + .extend(shape.faces().map(|tri| tri.translate(pos).project_2d())); + + self.r.draw_triangles(&self.triangle_buffer, fill_color); + for face in shape.faces() { - let normal_vector = face.normal_vector(); - let pxyz = face.middle() + normal_vector; - self.draw_line(face.middle() + pos, pxyz + pos, Color::RED); + if face.normal().dot(face.0 - CAMERA_POS + pos) >= 0.0 { + continue; + } + // let pxyz = face.middle() + face.normal(); // self.draw_line( // face.middle() + pos, - // face.middle() + face.0.unit() * 0.1 + pos, - // Color::HEX(0x33dd33), + // face.middle() + (face.1 - face.0).unit() * 0.02 + pos, + // Color::HEX(0xffaa00), // ); + // self.draw_line( + // face.middle() + pos, + // face.middle() + (face.2 - face.1).unit() * 0.02 + pos, + // Color::HEX(0x00ffaa), + // ); + // self.draw_line( + // face.middle() + pos, + // face.middle() + (face.0 - face.2).unit() * 0.02 + pos, + // Color::HEX(0xaa00ff), + // ); + // self.draw_line(face.middle() + pos, pxyz + pos, Color::HEX(0xffffff)); - let bingbongvector = face.0 - CAMERA_POS + pos; - if normal_vector.dot(bingbongvector) < 0.0 { - self.draw_triangle(face.translate(pos - CAMERA_POS), outline_color); - } + // self.draw_triangle(face.translate(pos - CAMERA_POS), outline_color); + self.r.draw_line( + (face.0 + pos).project_2d(), + (face.1 + pos).project_2d(), + outline_color, + ); + self.r.draw_line( + (face.2 + pos).project_2d(), + (face.0 + pos).project_2d(), + outline_color, + ); } - - // for vertex in shape.vertices() { - // self.r - // .draw_point((vertex + pos).project_2d(), outline_color); - // } } } diff --git a/game/src/engine/sdl_io.rs b/game/src/engine/sdl_io.rs index e9d7ac7..227057d 100644 --- a/game/src/engine/sdl_io.rs +++ b/game/src/engine/sdl_io.rs @@ -3,9 +3,9 @@ use std::time::{Duration, Instant}; use sdl3::{ event::Event, keyboard::Keycode, - pixels::{Color as SdlColor, PixelFormat}, + pixels::{Color as SdlColor, FColor, PixelFormat}, rect::Point, - render::{Canvas, FPoint, FRect}, + render::{Canvas, FPoint, FRect, Vertex, VertexIndices}, video::Window, Sdl, VideoSubsystem, }; @@ -14,7 +14,7 @@ use crate::engine::{ error::Error, game, math::{V2, V3}, - Color, Renderer, + Color, Renderer, Triangle2, }; pub static WIDTH: f64 = 1280.0; @@ -137,6 +137,23 @@ impl Renderer for SdlIo { .draw_line(self.point_w2s(from), self.point_w2s(to)) .unwrap(); } + + fn draw_triangles(&mut self, triangles: &[Triangle2], color: Color) { + let vertices = triangles + .iter() + .flat_map(|t| [t.0, t.1, t.2]) + .map(|p| self.point_w2s(p)) + .map(|p| Vertex { + position: p.into(), + color: color.into(), + tex_coord: FPoint::new(0.0, 0.0), + }) + .collect::>(); + + self.canvas + .render_geometry(&vertices, None, VertexIndices::Sequential) + .unwrap(); + } } impl From for FPoint { @@ -148,11 +165,22 @@ impl From for FPoint { impl From for SdlColor { fn from(value: Color) -> Self { match value { - Color::HEX(v) => SdlColor::from_u32(&PixelFormat::BGR24, v), - Color::WHITE => SdlColor::WHITE, - Color::GREEN => SdlColor::GREEN, - Color::RED => SdlColor::RED, - Color::CYAN => SdlColor::CYAN, + Color::Hex(v) => SdlColor::RGB( + ((v >> 16) & 0xff) as u8, + ((v >> 8) & 0xff) as u8, + (v & 0xff) as u8, + ), + Color::White => SdlColor::WHITE, + Color::Green => SdlColor::GREEN, + Color::Red => SdlColor::RED, + Color::Cyan => SdlColor::CYAN, + Color::Black => SdlColor::BLACK, } } } + +impl From for FColor { + fn from(value: Color) -> Self { + FColor::from(>::into(value)) + } +} diff --git a/game/src/engine/shapes.rs b/game/src/engine/shapes.rs index 8f01f33..dc3f0cf 100644 --- a/game/src/engine/shapes.rs +++ b/game/src/engine/shapes.rs @@ -54,10 +54,9 @@ static PLANE_VERTICES: [(i8, i8, i8); 4] = [ (1, 0, 0), (1, 0, 1), ]; -static PLANE_EDGES: [(usize, usize); 5] = [ +static PLANE_EDGES: [(usize, usize); 4] = [ // (0, 1), - (1, 2), (2, 3), (0, 2), (1, 3), @@ -101,12 +100,33 @@ impl Shape { self.vertices.iter().cloned() } + pub fn edges<'a>(&'a self) -> impl Iterator + 'a { + let verts: &[V3] = &self.vertices; + self.edges.iter().map(|(a, b)| (verts[*a], verts[*b])) + } + pub fn faces<'a>(&'a self) -> impl Iterator + 'a { let verts: &[V3] = &self.vertices; self.faces .iter() .map(|(a, b, c)| Triangle3(verts[*a], verts[*b], verts[*c])) } + + pub fn rotate(&self, rot: V3) -> Self { + Self { + vertices: self.vertices.iter().map(|v| v.rotate(rot)).collect(), + edges: self.edges.clone(), + faces: self.faces.clone(), + } + } + + pub fn translate(&self, trans: V3) -> Self { + Self { + vertices: self.vertices.iter().map(|v| *v + trans).collect(), + edges: self.edges.clone(), + faces: self.faces.clone(), + } + } } fn scale_i8_vertex(p: (i8, i8, i8), s: &V3) -> V3 { diff --git a/game/src/main.rs b/game/src/main.rs index dd9c5ed..6154362 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -1,8 +1,8 @@ #![allow(dead_code)] -use std::time::Duration; +use std::{f64, time::Duration}; -use crate::engine::{Color, R3d, Shape, Triangle3, V3}; +use crate::engine::{Color, R3d, Shape, V3}; mod engine; @@ -15,8 +15,8 @@ impl engine::Object for MyObject { match self { MyObject::Player { pos, vel } => { *pos += *vel * delta_time.as_secs_f64(); - if pos.0 >= 1.0 { - pos.0 = -1.0; + if pos.0 >= 2.1 { + pos.0 = -2.0; } } } @@ -32,8 +32,8 @@ impl engine::Object for MyObject { r3d.draw_shape( V3(x as f64 * 0.1, -0.5, z as f64 * 0.1), &Shape::new_plane(V3(0.1, 0.0, 0.1)), - Color::CYAN, - Color::WHITE, + Color::Cyan, + Color::Black, ); } } @@ -41,14 +41,16 @@ impl engine::Object for MyObject { r3d.draw_shape( *pos + V3(0.0, 0.2, 0.0), &Shape::new_cube(V3(0.2, 0.1, 0.2)), - Color::GREEN, - Color::WHITE, + Color::Green, + Color::Black, ); r3d.draw_shape( - *pos + V3(-0.4, -0.2, 0.0), - &Shape::new_cube(V3(0.2, 0.2, 0.2)), - Color::GREEN, - Color::WHITE, + *pos + V3(-0.8, -0.2, 0.0), + &Shape::new_cube(V3(0.2, 0.2, 0.2)) + .translate(V3(-0.1, -0.1, -0.1)) + .rotate(V3(pos.0 * 5.0, pos.0 * 5.0, pos.0 * 5.0)), + Color::Green, + Color::Black, ); } } @@ -60,7 +62,7 @@ fn main() -> Result<(), Box> { let mut game = engine::Game::::new()?; let player = MyObject::Player { - pos: V3(0.0, -0.1, 0.0), + pos: V3(-1.0, -0.1, 0.0), vel: V3(0.4, 0.0, 0.0), }; game.spawn(player);