mirror of
https://github.com/Mercantec-GHC/h5-projekt-mst.git
synced 2026-08-26 21:27:38 +02:00
editor
This commit is contained in:
parent
04b1943040
commit
eb2bb12a8d
2
editor/.gitignore
vendored
Normal file
2
editor/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.vscode
|
||||||
46
editor/Cargo.lock
generated
Normal file
46
editor/Cargo.lock
generated
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.183"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sdl3"
|
||||||
|
version = "0.17.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dce0693cede7f7901e968e5539f87174d0fb5fac5d489a3e3be52b2b4a0b49c5"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"sdl3-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sdl3-sys"
|
||||||
|
version = "0.6.1+SDL-3.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d73fc820b0b6b9bd0557de7b799ec9da1ca4f35fcd1cb42f7ceb85e640d1477"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skate-slope"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"sdl3",
|
||||||
|
]
|
||||||
7
editor/Cargo.toml
Normal file
7
editor/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "skate-slope"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sdl3 = "0.17.3"
|
||||||
1
editor/src/engine/error.rs
Normal file
1
editor/src/engine/error.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub type Error = String;
|
||||||
53
editor/src/engine/game.rs
Normal file
53
editor/src/engine/game.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use crate::engine::{
|
||||||
|
math::{V2, V3},
|
||||||
|
Triangle2,
|
||||||
|
};
|
||||||
|
use std::{marker::PhantomData, time::Duration};
|
||||||
|
|
||||||
|
use super::error::Error;
|
||||||
|
|
||||||
|
pub trait Io<R: Renderer, G: Game<R>> {
|
||||||
|
fn run(&mut self, game: &mut G);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_triangle(&mut self, triangle: Triangle2, color: Color);
|
||||||
|
fn draw_triangles(&mut self, triangles: &[Triangle2], color: Color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Color {
|
||||||
|
Hex(u32),
|
||||||
|
White,
|
||||||
|
Green,
|
||||||
|
Red,
|
||||||
|
Cyan,
|
||||||
|
Black,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
|
pub enum Key {
|
||||||
|
R,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Down,
|
||||||
|
Up,
|
||||||
|
W,
|
||||||
|
A,
|
||||||
|
S,
|
||||||
|
D,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Event {
|
||||||
|
KeyUp { key: Key },
|
||||||
|
KeyDown { key: Key },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Game<R: Renderer> {
|
||||||
|
fn update(&mut self, delta_time: Duration);
|
||||||
|
fn render(&mut self, r: &mut R);
|
||||||
|
fn event(&mut self, event: Event);
|
||||||
|
}
|
||||||
211
editor/src/engine/math.rs
Normal file
211
editor/src/engine/math.rs
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub struct V2(pub f64, pub f64);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub struct V3(pub f64, pub f64, pub f64);
|
||||||
|
|
||||||
|
impl V3 {
|
||||||
|
pub fn project_2d(&self) -> V2 {
|
||||||
|
// V2(self.0 / self.2, self.1 / self.2)
|
||||||
|
let c = V3(0.0, 0.0, -1.0);
|
||||||
|
let d = *self - c;
|
||||||
|
let e = V3(0.0, 0.0, 0.0) - c;
|
||||||
|
V2(e.2 / d.2 * d.0 + e.0, e.2 / d.2 * d.1 + e.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cross(&self, rhs: Self) -> Self {
|
||||||
|
let V3(ax, ay, az) = self;
|
||||||
|
let V3(bx, by, bz) = rhs;
|
||||||
|
Self(ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dot(&self, rhs: Self) -> f64 {
|
||||||
|
let V3(ax, ay, az) = self;
|
||||||
|
let V3(bx, by, bz) = rhs;
|
||||||
|
ax * bx + ay * by + az * bz
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> f64 {
|
||||||
|
f64::sqrt(self.0.powi(2) + self.1.powi(2) + self.2.powi(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unit(&self) -> Self {
|
||||||
|
self.map(|v| v / self.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<F: Fn(f64) -> f64>(&self, func: F) -> Self {
|
||||||
|
V3(func(self.0), func(self.1), func(self.2))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate(&self, rot: Self) -> Self {
|
||||||
|
M3x3::new_rotate_z(rot.2)
|
||||||
|
* (M3x3::new_rotate_y(rot.1) * (M3x3::new_rotate_x(rot.0) * *self))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distance(&self, rhs: Self) -> f64 {
|
||||||
|
(*self - rhs).len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for V3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub for V3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 - rhs.0, self.1 - rhs.1, self.2 - rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for V3 {
|
||||||
|
type Output = V3;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
Self(self.0 * rhs, self.1 * rhs, self.2 * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Self> for V3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 * rhs.0, self.1 * rhs.1, self.2 * rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<&Self> for V3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: &Self) -> Self::Output {
|
||||||
|
Self(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub<&Self> for V3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: &Self) -> Self::Output {
|
||||||
|
Self(self.0 - rhs.0, self.1 - rhs.1, self.2 - rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for V3 {
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
*self = *self + rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SubAssign for V3 {
|
||||||
|
fn sub_assign(&mut self, rhs: Self) {
|
||||||
|
*self = *self - rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MulAssign<f64> for V3 {
|
||||||
|
fn mul_assign(&mut self, rhs: f64) {
|
||||||
|
*self = *self * rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn points(&self) -> [V3; 3] {
|
||||||
|
[self.0, self.1, self.2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<V3> 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::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn v3_ops() {
|
||||||
|
let v3 = |v: f64| V3(v, v, v);
|
||||||
|
|
||||||
|
assert_eq!(v3(1.0) + v3(2.0), v3(3.0));
|
||||||
|
assert_eq!(v3(3.0) - v3(2.0), v3(1.0));
|
||||||
|
assert_eq!(v3(2.0) * 2.0, v3(4.0));
|
||||||
|
|
||||||
|
let mut a = v3(1.0);
|
||||||
|
a += v3(2.0);
|
||||||
|
assert_eq!(a, v3(3.0));
|
||||||
|
|
||||||
|
let mut a = v3(2.0);
|
||||||
|
a *= 2.0;
|
||||||
|
assert_eq!(a, v3(4.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
17
editor/src/engine/mod.rs
Normal file
17
editor/src/engine/mod.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod game;
|
||||||
|
mod math;
|
||||||
|
mod r3d;
|
||||||
|
mod scene;
|
||||||
|
mod sdl_io;
|
||||||
|
mod shapes;
|
||||||
|
|
||||||
|
pub use error::*;
|
||||||
|
pub use game::*;
|
||||||
|
pub use math::*;
|
||||||
|
pub use r3d::*;
|
||||||
|
pub use scene::*;
|
||||||
|
pub use sdl_io::*;
|
||||||
|
pub use shapes::*;
|
||||||
72
editor/src/engine/r3d.rs
Normal file
72
editor/src/engine/r3d.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use crate::engine::{game::Renderer, Color, Shape, Triangle2, Triangle3, V2, V3};
|
||||||
|
|
||||||
|
pub struct R3d<'r, R: Renderer> {
|
||||||
|
r: &'r mut R,
|
||||||
|
triangle_buffer: Vec<Triangle2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
triangle_buffer: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_line(&mut self, from: V3, to: V3, color: Color) {
|
||||||
|
self.r.draw_line(from.project_2d(), to.project_2d(), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_triangle(&mut self, triangle: Triangle3, color: Color) {
|
||||||
|
let triangle = triangle.translate(CAMERA_POS).project_2d();
|
||||||
|
|
||||||
|
self.r.draw_line(triangle.0, triangle.1, color);
|
||||||
|
self.r.draw_line(triangle.1, triangle.2, color);
|
||||||
|
self.r.draw_line(triangle.2, triangle.0, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
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.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));
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
editor/src/engine/scene.rs
Normal file
68
editor/src/engine/scene.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use crate::engine::{Color, Renderer, Shape, Triangle3, V3};
|
||||||
|
|
||||||
|
pub struct DrawnTriangle {
|
||||||
|
triangle: Triangle3,
|
||||||
|
outline_color: Color,
|
||||||
|
fill_color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Scene {
|
||||||
|
objects: Vec<DrawnTriangle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scene {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
objects: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<R: Renderer>(&mut self, r: &mut R, camera: V3) {
|
||||||
|
let mut indices_with_scores = self
|
||||||
|
.objects
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, object)| {
|
||||||
|
let mut point_scores = object.triangle.points().map(|p| p.distance(camera));
|
||||||
|
point_scores.sort_by(|a, b| a.total_cmp(b));
|
||||||
|
|
||||||
|
(i, point_scores[0])
|
||||||
|
})
|
||||||
|
.rev()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
indices_with_scores.sort_by(|a, b| b.1.total_cmp(&a.1));
|
||||||
|
|
||||||
|
for (i, _) in indices_with_scores {
|
||||||
|
let object = &self.objects[i];
|
||||||
|
|
||||||
|
// check if behind camera
|
||||||
|
if !(object.triangle.0 .2 >= -1.0
|
||||||
|
&& object.triangle.1 .2 >= -1.0
|
||||||
|
&& object.triangle.2 .2 >= -1.0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if object.triangle.normal().dot(object.triangle.0 - camera) >= 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let triangle = object.triangle.project_2d();
|
||||||
|
|
||||||
|
r.draw_triangle(triangle.clone(), object.fill_color);
|
||||||
|
r.draw_line(triangle.0, triangle.1, object.outline_color);
|
||||||
|
r.draw_line(triangle.0, triangle.2, object.outline_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_shape(&mut self, pos: V3, shape: &Shape, outline_color: Color, fill_color: Color) {
|
||||||
|
for triangle in shape.faces() {
|
||||||
|
self.objects.push(DrawnTriangle {
|
||||||
|
triangle: triangle.translate(pos),
|
||||||
|
outline_color,
|
||||||
|
fill_color,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
280
editor/src/engine/sdl_io.rs
Normal file
280
editor/src/engine/sdl_io.rs
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use sdl3::{
|
||||||
|
event::Event,
|
||||||
|
keyboard::Keycode,
|
||||||
|
pixels::{Color as SdlColor, FColor, PixelFormat},
|
||||||
|
rect::Point,
|
||||||
|
render::{Canvas, FPoint, FRect, Vertex, VertexIndices},
|
||||||
|
video::Window,
|
||||||
|
Sdl, VideoSubsystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::engine::{
|
||||||
|
error::Error,
|
||||||
|
game,
|
||||||
|
math::{V2, V3},
|
||||||
|
Color, Renderer, Triangle2,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static WIDTH: f64 = 1280.0;
|
||||||
|
pub static HEIGHT: f64 = 720.0;
|
||||||
|
|
||||||
|
pub struct SdlIo {
|
||||||
|
sdl_context: Sdl,
|
||||||
|
video_subsystem: VideoSubsystem,
|
||||||
|
canvas: Canvas<Window>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdlIo {
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
let sdl_context = sdl3::init().unwrap();
|
||||||
|
let video_subsystem = sdl_context.video().unwrap();
|
||||||
|
let window = video_subsystem
|
||||||
|
.window("Game", WIDTH as u32, HEIGHT as u32)
|
||||||
|
.position_centered()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let mut canvas = window.into_canvas();
|
||||||
|
|
||||||
|
canvas.set_draw_color(SdlColor::BLACK);
|
||||||
|
canvas.clear();
|
||||||
|
canvas.present();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
sdl_context,
|
||||||
|
video_subsystem,
|
||||||
|
canvas,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, game: &mut impl game::Game<Self>) {
|
||||||
|
let mut event_pump = self.sdl_context.event_pump().unwrap();
|
||||||
|
|
||||||
|
let mut time_before = Instant::now();
|
||||||
|
let time_per_frame = Duration::from_secs_f64(1.0 / 60.0);
|
||||||
|
let mut print_fps_timer = Instant::now();
|
||||||
|
let mut fps_count = 0;
|
||||||
|
|
||||||
|
'running: loop {
|
||||||
|
for event in event_pump.poll_iter() {
|
||||||
|
match event {
|
||||||
|
Event::Quit { .. }
|
||||||
|
| Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::Escape),
|
||||||
|
..
|
||||||
|
} => break 'running,
|
||||||
|
Event::KeyDown { keycode, .. } => match keycode {
|
||||||
|
Some(key) => match key {
|
||||||
|
Keycode::R => {
|
||||||
|
game.event(game::Event::KeyDown { key: game::Key::R });
|
||||||
|
}
|
||||||
|
Keycode::W => {
|
||||||
|
game.event(game::Event::KeyDown { key: game::Key::W });
|
||||||
|
}
|
||||||
|
Keycode::A => {
|
||||||
|
game.event(game::Event::KeyDown { key: game::Key::A });
|
||||||
|
}
|
||||||
|
Keycode::S => {
|
||||||
|
game.event(game::Event::KeyDown { key: game::Key::S });
|
||||||
|
}
|
||||||
|
Keycode::D => {
|
||||||
|
game.event(game::Event::KeyDown { key: game::Key::D });
|
||||||
|
}
|
||||||
|
Keycode::Down => {
|
||||||
|
game.event(game::Event::KeyDown {
|
||||||
|
key: game::Key::Down,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Keycode::Up => {
|
||||||
|
game.event(game::Event::KeyDown { key: game::Key::Up });
|
||||||
|
}
|
||||||
|
Keycode::Left => {
|
||||||
|
game.event(game::Event::KeyDown {
|
||||||
|
key: game::Key::Left,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Keycode::Right => {
|
||||||
|
game.event(game::Event::KeyDown {
|
||||||
|
key: game::Key::Right,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
},
|
||||||
|
Event::KeyUp { keycode, .. } => match keycode {
|
||||||
|
Some(key) => match key {
|
||||||
|
Keycode::R => {
|
||||||
|
game.event(game::Event::KeyUp { key: game::Key::R });
|
||||||
|
}
|
||||||
|
Keycode::W => {
|
||||||
|
game.event(game::Event::KeyUp { key: game::Key::W });
|
||||||
|
}
|
||||||
|
Keycode::A => {
|
||||||
|
game.event(game::Event::KeyUp { key: game::Key::A });
|
||||||
|
}
|
||||||
|
Keycode::S => {
|
||||||
|
game.event(game::Event::KeyUp { key: game::Key::S });
|
||||||
|
}
|
||||||
|
Keycode::D => {
|
||||||
|
game.event(game::Event::KeyUp { key: game::Key::D });
|
||||||
|
}
|
||||||
|
Keycode::Down => {
|
||||||
|
game.event(game::Event::KeyUp {
|
||||||
|
key: game::Key::Down,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Keycode::Up => {
|
||||||
|
game.event(game::Event::KeyUp { key: game::Key::Up });
|
||||||
|
}
|
||||||
|
Keycode::Left => {
|
||||||
|
game.event(game::Event::KeyUp {
|
||||||
|
key: game::Key::Left,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Keycode::Right => {
|
||||||
|
game.event(game::Event::KeyUp {
|
||||||
|
key: game::Key::Right,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_now = Instant::now();
|
||||||
|
let delta_time = time_now - time_before;
|
||||||
|
time_before = time_now;
|
||||||
|
game.update(delta_time);
|
||||||
|
|
||||||
|
self.canvas.set_draw_color(SdlColor::BLACK);
|
||||||
|
self.canvas.clear();
|
||||||
|
game.render(self);
|
||||||
|
self.canvas.present();
|
||||||
|
|
||||||
|
fps_count += 1;
|
||||||
|
if (time_now - print_fps_timer).as_secs_f64() > 1.0 {
|
||||||
|
println!("fps: {fps_count}");
|
||||||
|
fps_count = 0;
|
||||||
|
print_fps_timer = time_now;
|
||||||
|
}
|
||||||
|
if delta_time < time_per_frame {
|
||||||
|
std::thread::sleep(time_per_frame - delta_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// world space to screen space
|
||||||
|
fn scale_w2s(&self, v: V2) -> V2 {
|
||||||
|
let factor = WIDTH / 2.0;
|
||||||
|
V2(v.0 * factor, v.1 * factor)
|
||||||
|
}
|
||||||
|
/// world space to screen space.
|
||||||
|
fn translate_w2s(&self, v: V2) -> V2 {
|
||||||
|
let middle = V2(WIDTH / 2.0, HEIGHT / 2.0);
|
||||||
|
V2(v.0 + middle.0, middle.1 - v.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// scale -> translate
|
||||||
|
fn point_w2s(&self, v: V2) -> V2 {
|
||||||
|
self.translate_w2s(self.scale_w2s(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderer for SdlIo {
|
||||||
|
fn draw_rect(&mut self, pos: V2, size: V2, color: Color) {
|
||||||
|
let pos = self.point_w2s(pos);
|
||||||
|
let size = self.scale_w2s(size);
|
||||||
|
self.canvas.set_draw_color(color);
|
||||||
|
self.canvas
|
||||||
|
.fill_rect(FRect::new(pos.0 as _, pos.1 as _, size.0 as _, size.1 as _))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_point(&mut self, pos: V2, color: Color) {
|
||||||
|
let pos = self.point_w2s(pos);
|
||||||
|
let size = 4.0;
|
||||||
|
self.canvas.set_draw_color(color);
|
||||||
|
self.canvas
|
||||||
|
.fill_rect(FRect::new(
|
||||||
|
(pos.0 - size / 2.0) as _,
|
||||||
|
(pos.1 - size / 2.0) as _,
|
||||||
|
size as _,
|
||||||
|
size as _,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_line(&mut self, from: V2, to: V2, color: Color) {
|
||||||
|
self.canvas.set_draw_color(color);
|
||||||
|
self.canvas
|
||||||
|
.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::<Vec<_>>();
|
||||||
|
|
||||||
|
self.canvas
|
||||||
|
.render_geometry(&vertices, None, VertexIndices::Sequential)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_triangle(&mut self, triangle: Triangle2, color: Color) {
|
||||||
|
let vertices = [triangle.0, triangle.1, triangle.2]
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| self.point_w2s(p))
|
||||||
|
.map(|p| Vertex {
|
||||||
|
position: p.into(),
|
||||||
|
color: color.into(),
|
||||||
|
tex_coord: FPoint::new(0.0, 0.0),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.canvas
|
||||||
|
.render_geometry(&vertices, None, VertexIndices::Sequential)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<V2> for FPoint {
|
||||||
|
fn from(value: V2) -> Self {
|
||||||
|
FPoint::new(value.0 as _, value.1 as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Color> for SdlColor {
|
||||||
|
fn from(value: Color) -> Self {
|
||||||
|
match value {
|
||||||
|
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<Color> for FColor {
|
||||||
|
fn from(value: Color) -> Self {
|
||||||
|
FColor::from(<Color as Into<SdlColor>>::into(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
134
editor/src/engine/shapes.rs
Normal file
134
editor/src/engine/shapes.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
use crate::engine::{math::V3, Triangle3, V2};
|
||||||
|
|
||||||
|
static CUBE_VERTICES: [(i8, i8, i8); 8] = [
|
||||||
|
(0, 1, 0), // 0 front top left
|
||||||
|
(1, 1, 0), // 1 front top right
|
||||||
|
(0, 0, 0), // 2 front bottom left
|
||||||
|
(1, 0, 0), // 3 front bottom right
|
||||||
|
(0, 1, 1), // 4 back top left
|
||||||
|
(1, 1, 1), // 5 back top right
|
||||||
|
(0, 0, 1), // 6 back bottom left
|
||||||
|
(1, 0, 1), // 7 back bottom right
|
||||||
|
];
|
||||||
|
|
||||||
|
static CUBE_EDGES: [(usize, usize); 12] = [
|
||||||
|
(0, 1), // ftl -> ftr
|
||||||
|
(2, 3), // fbl -> fbr
|
||||||
|
(4, 5), // btl -> btr
|
||||||
|
(6, 7), // bbl -> bbr
|
||||||
|
(0, 2), // ftl -> fbl
|
||||||
|
(1, 3), // ftr -> fbr
|
||||||
|
(4, 6), // btl -> bbl
|
||||||
|
(5, 7), // btr -> bbr
|
||||||
|
(0, 4), // ftl -> btl
|
||||||
|
(1, 5), // ftr -> btr
|
||||||
|
(2, 6), // fbl -> bbl
|
||||||
|
(3, 7), // fbr -> bbr
|
||||||
|
];
|
||||||
|
|
||||||
|
static CUBE_FACES: [(usize, usize, usize); 12] = [
|
||||||
|
// front
|
||||||
|
(0, 1, 2),
|
||||||
|
(3, 2, 1),
|
||||||
|
// top
|
||||||
|
(0, 4, 1),
|
||||||
|
(5, 1, 4),
|
||||||
|
// right
|
||||||
|
(7, 3, 5),
|
||||||
|
(1, 5, 3),
|
||||||
|
// back
|
||||||
|
(5, 4, 7),
|
||||||
|
(6, 7, 4),
|
||||||
|
// bottom
|
||||||
|
(3, 7, 2),
|
||||||
|
(6, 2, 7),
|
||||||
|
// left
|
||||||
|
(4, 0, 6),
|
||||||
|
(2, 6, 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
static PLANE_VERTICES: [(i8, i8, i8); 4] = [
|
||||||
|
//
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 1),
|
||||||
|
(1, 0, 0),
|
||||||
|
(1, 0, 1),
|
||||||
|
];
|
||||||
|
static PLANE_EDGES: [(usize, usize); 4] = [
|
||||||
|
//
|
||||||
|
(0, 1),
|
||||||
|
(2, 3),
|
||||||
|
(0, 2),
|
||||||
|
(1, 3),
|
||||||
|
];
|
||||||
|
static PLANE_FACES: [(usize, usize, usize); 2] = [
|
||||||
|
//
|
||||||
|
(0, 1, 2),
|
||||||
|
(3, 2, 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct Shape {
|
||||||
|
vertices: Vec<V3>,
|
||||||
|
edges: Vec<(usize, usize)>,
|
||||||
|
faces: Vec<(usize, usize, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shape {
|
||||||
|
pub fn new_plane(dim: V3) -> Self {
|
||||||
|
Self {
|
||||||
|
vertices: PLANE_VERTICES
|
||||||
|
.iter()
|
||||||
|
.map(|p| scale_i8_vertex(*p, &dim))
|
||||||
|
.collect(),
|
||||||
|
edges: Vec::from_iter(PLANE_EDGES),
|
||||||
|
faces: Vec::from_iter(PLANE_FACES),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_cube(dim: V3) -> Self {
|
||||||
|
Self {
|
||||||
|
vertices: CUBE_VERTICES
|
||||||
|
.iter()
|
||||||
|
.map(|p| scale_i8_vertex(*p, &dim))
|
||||||
|
.collect(),
|
||||||
|
edges: Vec::from_iter(CUBE_EDGES),
|
||||||
|
faces: Vec::from_iter(CUBE_FACES),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vertices<'a>(&'a self) -> impl Iterator<Item = V3> + 'a {
|
||||||
|
self.vertices.iter().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn edges<'a>(&'a self) -> impl Iterator<Item = (V3, V3)> + 'a {
|
||||||
|
let verts: &[V3] = &self.vertices;
|
||||||
|
self.edges.iter().map(|(a, b)| (verts[*a], verts[*b]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn faces<'a>(&'a self) -> impl Iterator<Item = Triangle3> + '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 {
|
||||||
|
V3(p.0 as f64 * s.0, p.1 as f64 * s.1, p.2 as f64 * s.2)
|
||||||
|
}
|
||||||
361
editor/src/main.rs
Normal file
361
editor/src/main.rs
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::{collections::HashSet, f64::consts::PI, time::Duration};
|
||||||
|
|
||||||
|
use crate::engine::{Color, Key, Renderer, Scene, Shape, V3};
|
||||||
|
|
||||||
|
mod engine;
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
objects: Vec<Object>,
|
||||||
|
next_object_id: u32,
|
||||||
|
keys_pressed: HashSet<Key>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
objects: Vec::new(),
|
||||||
|
next_object_id: 0,
|
||||||
|
keys_pressed: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn(&mut self, object_kind: ObjectKind) {
|
||||||
|
let object = Object {
|
||||||
|
kind: object_kind,
|
||||||
|
id: self.next_object_id,
|
||||||
|
};
|
||||||
|
self.objects.push(object);
|
||||||
|
self.next_object_id += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn despawn(&mut self, id: u32) {
|
||||||
|
let index = self
|
||||||
|
.objects
|
||||||
|
.iter()
|
||||||
|
.position(|o| o.id == id)
|
||||||
|
.expect("doesn't exist");
|
||||||
|
self.objects.remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Renderer> engine::Game<R> for Game {
|
||||||
|
fn update(&mut self, delta_time: Duration) {
|
||||||
|
for object in &mut self.objects {
|
||||||
|
if self.keys_pressed.contains(&Key::A) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { pivot_deg, .. } => {
|
||||||
|
*pivot_deg -= 18.0 * delta_time.as_secs_f64();
|
||||||
|
if *pivot_deg < -12.5 {
|
||||||
|
*pivot_deg = -12.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::D) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard {
|
||||||
|
pivot_deg: pivot_factor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
*pivot_factor += 18.0 * delta_time.as_secs_f64();
|
||||||
|
if *pivot_factor > 12.5 {
|
||||||
|
*pivot_factor = 12.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !(self.keys_pressed.contains(&Key::A) || self.keys_pressed.contains(&Key::D)) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard {
|
||||||
|
pivot_deg: pivot_factor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let decay_rate = 1.0 - (2.0 * delta_time.as_secs_f64());
|
||||||
|
*pivot_factor *= decay_rate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::R) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { rot, pos, .. } => {
|
||||||
|
*pos = V3(0.0, -0.1, -0.6);
|
||||||
|
*rot = V3(0.0, 0.5 * PI, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::W) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { rot, .. } => rot.0 += 1.0 * delta_time.as_secs_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::S) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { rot, .. } => rot.0 -= 1.0 * delta_time.as_secs_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::Left) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { rot, .. } => rot.1 -= 1.0 * delta_time.as_secs_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::Right) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { rot, .. } => rot.1 += 1.0 * delta_time.as_secs_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::Down) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { pos, .. } => pos.1 -= 0.5 * delta_time.as_secs_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.keys_pressed.contains(&Key::Up) {
|
||||||
|
match &mut object.kind {
|
||||||
|
ObjectKind::SkateBoard { pos, .. } => pos.1 += 0.5 * delta_time.as_secs_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object.update(delta_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, r: &mut R) {
|
||||||
|
let mut scene = Scene::new();
|
||||||
|
for object in &self.objects {
|
||||||
|
object.render(&mut scene);
|
||||||
|
}
|
||||||
|
scene.render(r, V3(0.0, 0.0, -1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, event: engine::Event) {
|
||||||
|
match event {
|
||||||
|
engine::Event::KeyDown { key } => self.keys_pressed.insert(key),
|
||||||
|
engine::Event::KeyUp { key } => self.keys_pressed.remove(&key),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Object {
|
||||||
|
kind: ObjectKind,
|
||||||
|
id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ObjectKind {
|
||||||
|
SkateBoard {
|
||||||
|
pos: V3,
|
||||||
|
vel: V3,
|
||||||
|
rot: V3,
|
||||||
|
nyoom_factor: f64,
|
||||||
|
pivot_deg: f64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ShapeGroupShape {
|
||||||
|
shape: Shape,
|
||||||
|
offset: V3,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ShapeGroup {
|
||||||
|
pub shapes: Vec<ShapeGroupShape>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShapeGroup {
|
||||||
|
pub fn new(shapes: Vec<ShapeGroupShape>) -> Self {
|
||||||
|
Self { shapes }
|
||||||
|
}
|
||||||
|
pub fn rotate(mut self, rot: V3) -> Self {
|
||||||
|
for shape in &mut self.shapes {
|
||||||
|
shape.shape = shape
|
||||||
|
.shape
|
||||||
|
.translate(shape.offset)
|
||||||
|
.rotate(rot)
|
||||||
|
.translate(shape.offset * -1.0);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn translate(mut self, offset: V3) -> Self {
|
||||||
|
for shape in &mut self.shapes {
|
||||||
|
shape.shape = shape.shape.translate(offset);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn draw(self, pos: V3, scene: &mut Scene, outline_color: Color, fill_color: Color) {
|
||||||
|
for shape in self.shapes {
|
||||||
|
scene.draw_shape(
|
||||||
|
pos,
|
||||||
|
&shape.shape.translate(shape.offset),
|
||||||
|
outline_color,
|
||||||
|
fill_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object {
|
||||||
|
fn update(&mut self, delta_time: Duration) {
|
||||||
|
match &mut self.kind {
|
||||||
|
ObjectKind::SkateBoard {
|
||||||
|
pos,
|
||||||
|
vel,
|
||||||
|
nyoom_factor,
|
||||||
|
pivot_deg: pivot_factor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
vel.0 = *pivot_factor * delta_time.as_secs_f64();
|
||||||
|
*pos += *vel * delta_time.as_secs_f64();
|
||||||
|
*nyoom_factor += 16.0 * delta_time.as_secs_f64();
|
||||||
|
|
||||||
|
// rot.0 += delta_time.as_secs_f64() * PI * 1.0;
|
||||||
|
// rot.1 += delta_time.as_secs_f64() * PI * 0.2;
|
||||||
|
// rot.2 += delta_time.as_secs_f64() * PI * 2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, scene: &mut Scene) {
|
||||||
|
match self.kind {
|
||||||
|
ObjectKind::SkateBoard {
|
||||||
|
pos,
|
||||||
|
rot,
|
||||||
|
nyoom_factor,
|
||||||
|
pivot_deg: pivot_factor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let board_size = V3(0.175, 0.005, 0.05);
|
||||||
|
let trucks = {
|
||||||
|
let anchor_size = V3(0.005, 0.01, 0.005);
|
||||||
|
let anchor_y = -anchor_size.1 - board_size.1 * 0.5;
|
||||||
|
let pivot = vec![
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(anchor_size),
|
||||||
|
offset: V3(
|
||||||
|
anchor_size.0 * -0.5 + board_size.0 * 0.4,
|
||||||
|
anchor_y,
|
||||||
|
anchor_size.2 * -0.5,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(anchor_size),
|
||||||
|
offset: V3(
|
||||||
|
anchor_size.0 * -0.5 - board_size.0 * 0.4,
|
||||||
|
anchor_y,
|
||||||
|
anchor_size.2 * -0.5,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let wheel_rail_size = V3(anchor_size.0, anchor_size.0, board_size.2 * 0.8);
|
||||||
|
let wheel_rail_y = anchor_y - wheel_rail_size.1;
|
||||||
|
let wheel_rail = vec![
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(wheel_rail_size),
|
||||||
|
offset: V3(
|
||||||
|
wheel_rail_size.0 * -0.5 + board_size.0 * 0.4,
|
||||||
|
wheel_rail_y,
|
||||||
|
wheel_rail_size.2 * -0.5,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(wheel_rail_size),
|
||||||
|
offset: V3(
|
||||||
|
wheel_rail_size.0 * -0.5 - board_size.0 * 0.4,
|
||||||
|
wheel_rail_y,
|
||||||
|
wheel_rail_size.2 * -0.5,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let wheel_size = V3(0.01, 0.01, 0.0025);
|
||||||
|
let wheel_y = wheel_rail_y - wheel_size.1 * 0.25;
|
||||||
|
let wheel_rot = V3(0.0, 0.0, nyoom_factor);
|
||||||
|
let wheel_rot_trans = V3(0.5, 0.5, 0.0);
|
||||||
|
let wheel = vec![
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(wheel_size)
|
||||||
|
.translate(wheel_size * wheel_rot_trans * -1.0)
|
||||||
|
.rotate(wheel_rot)
|
||||||
|
.translate(wheel_size * wheel_rot_trans),
|
||||||
|
offset: V3(
|
||||||
|
wheel_size.0 * -0.5 + board_size.0 * 0.4,
|
||||||
|
wheel_y,
|
||||||
|
wheel_rail_size.2 * -0.5 - wheel_size.2,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(wheel_size)
|
||||||
|
.translate(wheel_size * wheel_rot_trans * -1.0)
|
||||||
|
.rotate(wheel_rot)
|
||||||
|
.translate(wheel_size * wheel_rot_trans),
|
||||||
|
|
||||||
|
offset: V3(
|
||||||
|
wheel_size.0 * -0.5 - board_size.0 * 0.4,
|
||||||
|
wheel_y,
|
||||||
|
wheel_rail_size.2 * -0.5 - wheel_size.2,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(wheel_size)
|
||||||
|
.translate(wheel_size * wheel_rot_trans * -1.0)
|
||||||
|
.rotate(wheel_rot)
|
||||||
|
.translate(wheel_size * wheel_rot_trans),
|
||||||
|
|
||||||
|
offset: V3(
|
||||||
|
wheel_size.0 * -0.5 + board_size.0 * 0.4,
|
||||||
|
wheel_y,
|
||||||
|
wheel_rail_size.2 * 0.5,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(wheel_size)
|
||||||
|
.translate(wheel_size * wheel_rot_trans * -1.0)
|
||||||
|
.rotate(wheel_rot)
|
||||||
|
.translate(wheel_size * wheel_rot_trans),
|
||||||
|
|
||||||
|
offset: V3(
|
||||||
|
wheel_size.0 * -0.5 - board_size.0 * 0.4,
|
||||||
|
wheel_y,
|
||||||
|
wheel_rail_size.2 * 0.5,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
vec![pivot, wheel_rail, wheel].into_iter().flatten()
|
||||||
|
};
|
||||||
|
let board_pivot_trans = V3(0.0, 0.5, 0.5);
|
||||||
|
let mut board = vec![ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(board_size)
|
||||||
|
.translate(board_size * board_pivot_trans * -1.0)
|
||||||
|
.rotate(V3(pivot_factor * (PI / 180.0), 0.0, 0.0))
|
||||||
|
.translate(board_size * board_pivot_trans),
|
||||||
|
offset: board_size * -0.5,
|
||||||
|
}];
|
||||||
|
board.extend(trucks);
|
||||||
|
let board = ShapeGroup::new(board).rotate(rot);
|
||||||
|
board.draw(pos, scene, Color::Green, Color::Black);
|
||||||
|
scene.draw_shape(
|
||||||
|
V3(-0.5, -5.0, 0.0),
|
||||||
|
&Shape::new_cube(V3(1.0, 0.0, 100.0)),
|
||||||
|
Color::Red,
|
||||||
|
Color::Red,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut sdl_io = engine::SdlIo::new()?;
|
||||||
|
let mut game = Game::new();
|
||||||
|
let mut objects: Vec<ObjectKind> = Vec::new();
|
||||||
|
objects.push(ObjectKind::SkateBoard {
|
||||||
|
pos: V3(0.0, -0.1, -0.6),
|
||||||
|
vel: V3(0.0, 0.0, 0.0),
|
||||||
|
rot: V3(0.0, PI * 0.5, 0.0),
|
||||||
|
nyoom_factor: 0.0,
|
||||||
|
pivot_deg: 0.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
for object in objects {
|
||||||
|
game.spawn(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl_io.run(&mut game);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -98,6 +98,7 @@ enum ObjectKind {
|
|||||||
pos: V3,
|
pos: V3,
|
||||||
vel: V3,
|
vel: V3,
|
||||||
rot: V3,
|
rot: V3,
|
||||||
|
nyoom_factor: f64,
|
||||||
},
|
},
|
||||||
Obstacle {
|
Obstacle {
|
||||||
pos: V3,
|
pos: V3,
|
||||||
@ -158,11 +159,17 @@ impl ShapeGroup {
|
|||||||
impl Object {
|
impl Object {
|
||||||
fn update(&mut self, delta_time: Duration) {
|
fn update(&mut self, delta_time: Duration) {
|
||||||
match &mut self.kind {
|
match &mut self.kind {
|
||||||
ObjectKind::SkateBoard { pos, vel, rot } => {
|
ObjectKind::SkateBoard {
|
||||||
|
pos,
|
||||||
|
vel,
|
||||||
|
nyoom_factor,
|
||||||
|
rot,
|
||||||
|
} => {
|
||||||
*pos += *vel * delta_time.as_secs_f64();
|
*pos += *vel * delta_time.as_secs_f64();
|
||||||
|
*nyoom_factor += 16.0 * delta_time.as_secs_f64();
|
||||||
|
|
||||||
// rot.0 += delta_time.as_secs_f64() * PI * 1.0;
|
// rot.0 += delta_time.as_secs_f64() * PI * 1.0;
|
||||||
// rot.1 += delta_time.as_secs_f64() * PI * 1.0;
|
rot.1 += delta_time.as_secs_f64() * PI * 0.2;
|
||||||
// rot.2 += delta_time.as_secs_f64() * PI * 2.0;
|
// rot.2 += delta_time.as_secs_f64() * PI * 2.0;
|
||||||
}
|
}
|
||||||
ObjectKind::Obstacle { pos, vel } => {
|
ObjectKind::Obstacle { pos, vel } => {
|
||||||
@ -187,23 +194,41 @@ impl Object {
|
|||||||
|
|
||||||
fn render(&self, scene: &mut Scene) {
|
fn render(&self, scene: &mut Scene) {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ObjectKind::SkateBoard { pos, rot, .. } => {
|
ObjectKind::SkateBoard {
|
||||||
let camera_pos = V3(0.0, 0.0, -1.0);
|
pos,
|
||||||
let board = ShapeGroup::new(vec![
|
rot,
|
||||||
|
nyoom_factor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let trucks = {
|
||||||
|
vec![
|
||||||
ShapeGroupShape {
|
ShapeGroupShape {
|
||||||
shape: Shape::new_cube(V3(0.2, 0.01, 0.05)),
|
shape: Shape::new_cube(V3(0.005, 0.01, 0.005)),
|
||||||
|
offset: V3(0.04 - 0.0025, -0.005, 0.025 - 0.0025),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(V3(0.005, 0.01, 0.005)),
|
||||||
|
offset: V3(0.14 - 0.0025, -0.005, 0.025 - 0.0025),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(V3(0.0025, 0.0025, 0.05)),
|
||||||
|
offset: V3(0.04 - 0.0025, -0.005 - 0.01, 0.0),
|
||||||
|
},
|
||||||
|
ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(V3(0.0025, 0.0025, 0.05)),
|
||||||
|
offset: V3(0.14 - 0.0025, -0.005 - 0.01, 0.0),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
let wheels = {};
|
||||||
|
let mut board = vec![ShapeGroupShape {
|
||||||
|
shape: Shape::new_cube(V3(0.175, 0.005, 0.05)),
|
||||||
offset: V3(0.0, 0.0, 0.0),
|
offset: V3(0.0, 0.0, 0.0),
|
||||||
},
|
}];
|
||||||
ShapeGroupShape {
|
board.extend(trucks);
|
||||||
shape: Shape::new_cube(V3(0.02, 0.02, 0.05)),
|
board.extend(wheels);
|
||||||
offset: V3(0.04, -0.02, 0.0),
|
let board = ShapeGroup::new(board)
|
||||||
},
|
.translate(V3(-0.1, 0.005, -0.0025))
|
||||||
ShapeGroupShape {
|
|
||||||
shape: Shape::new_cube(V3(0.02, 0.02, 0.05)),
|
|
||||||
offset: V3(0.14, -0.02, 0.0),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.translate(V3(-0.1, -0.005, -0.0025))
|
|
||||||
.rotate(rot);
|
.rotate(rot);
|
||||||
board.draw(pos, scene, Color::Green, Color::Black);
|
board.draw(pos, scene, Color::Green, Color::Black);
|
||||||
}
|
}
|
||||||
@ -257,7 +282,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
objects.push(ObjectKind::SkateBoard {
|
objects.push(ObjectKind::SkateBoard {
|
||||||
pos: V3(0.0, -0.1, -0.7),
|
pos: V3(0.0, -0.1, -0.7),
|
||||||
vel: V3(0.0, 0.0, 0.0),
|
vel: V3(0.0, 0.0, 0.0),
|
||||||
rot: V3(0.0, PI * 0.5, 0.0),
|
rot: V3(0.0, PI * 0.33, 0.0),
|
||||||
|
nyoom_factor: 0.0,
|
||||||
});
|
});
|
||||||
objects.push(ObjectKind::Ground {
|
objects.push(ObjectKind::Ground {
|
||||||
original_pos: V3(0.0, -0.25, -0.6),
|
original_pos: V3(0.0, -0.25, -0.6),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user