first commit

This commit is contained in:
rattatwinko
2025-06-13 15:04:28 +02:00
commit 01ecab00a9
4 changed files with 2582 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

2324
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

9
Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "t3dcube"
version = "0.1.0"
edition = "2024"
[dependencies]
pixels = "0.12"
winit = "0.28"
winit_input_helper = "0.11"

248
src/main.rs Normal file
View File

@@ -0,0 +1,248 @@
use pixels::{Pixels, SurfaceTexture};
use std::time::{Duration, Instant};
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
// 3D Point structure
#[derive(Clone, Copy)]
struct Point3D {
x: f32,
y: f32,
z: f32,
}
// Cube structure
struct Cube {
vertices: [Point3D; 8],
edges: [(usize, usize); 12],
}
impl Cube {
fn new() -> Self {
// Define the 8 vertices of a cube
let vertices = [
Point3D {
x: -1.0,
y: -1.0,
z: -1.0,
}, // 0
Point3D {
x: 1.0,
y: -1.0,
z: -1.0,
}, // 1
Point3D {
x: 1.0,
y: 1.0,
z: -1.0,
}, // 2
Point3D {
x: -1.0,
y: 1.0,
z: -1.0,
}, // 3
Point3D {
x: -1.0,
y: -1.0,
z: 1.0,
}, // 4
Point3D {
x: 1.0,
y: -1.0,
z: 1.0,
}, // 5
Point3D {
x: 1.0,
y: 1.0,
z: 1.0,
}, // 6
Point3D {
x: -1.0,
y: 1.0,
z: 1.0,
}, // 7
];
// Define the 12 edges of the cube (vertex indices)
let edges = [
(0, 1),
(1, 2),
(2, 3),
(3, 0), // Front face
(4, 5),
(5, 6),
(6, 7),
(7, 4), // Back face
(0, 4),
(1, 5),
(2, 6),
(3, 7), // Connecting edges
];
Self { vertices, edges }
}
fn rotate(&mut self, angle_x: f32, angle_y: f32, angle_z: f32) {
for vertex in &mut self.vertices {
// Rotate around X axis
let y = vertex.y;
let z = vertex.z;
vertex.y = y * angle_x.cos() - z * angle_x.sin();
vertex.z = y * angle_x.sin() + z * angle_x.cos();
// Rotate around Y axis
let x = vertex.x;
let z = vertex.z;
vertex.x = x * angle_y.cos() + z * angle_y.sin();
vertex.z = -x * angle_y.sin() + z * angle_y.cos();
// Rotate around Z axis
let x = vertex.x;
let y = vertex.y;
vertex.x = x * angle_z.cos() - y * angle_z.sin();
vertex.y = x * angle_z.sin() + y * angle_z.cos();
}
}
}
// Project 3D point to 2D screen coordinates
fn project(point: Point3D, width: u32, height: u32) -> (i32, i32) {
let distance = 5.0;
let factor = distance / (distance + point.z);
let x = point.x * factor;
let y = point.y * factor;
let screen_x = (x * width as f32 * 0.25) as i32 + (width as i32 / 2);
let screen_y = (-y * height as f32 * 0.25) as i32 + (height as i32 / 2);
(screen_x, screen_y)
}
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Smoothly Spinning 3D Cube")
.with_inner_size(winit::dpi::LogicalSize::new(400, 400))
.build(&event_loop)
.unwrap();
let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
let mut pixels = Pixels::new(window_size.width, window_size.height, surface_texture).unwrap();
let mut cube = Cube::new();
let mut last_frame_time = Instant::now();
// Rotation speeds in radians per second
const ROTATION_SPEED_X: f32 = 0.6; // ~1 rotation every 12.57 seconds
const ROTATION_SPEED_Y: f32 = 0.5; // ~1 rotation every 15.71 seconds
const ROTATION_SPEED_Z: f32 = 0.4; // ~1 rotation every 20.94 seconds
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
pixels.resize_surface(size.width, size.height).unwrap();
}
Event::RedrawRequested(_) => {
// Calculate delta time
let now = Instant::now();
let delta_time = now - last_frame_time;
last_frame_time = now;
let delta_seconds = delta_time.as_secs_f32();
// Clear the screen
let frame = pixels.frame_mut();
for pixel in frame.chunks_exact_mut(4) {
pixel.copy_from_slice(&[0x10, 0x10, 0x10, 0xff]);
}
// Rotate based on actual elapsed time
cube.rotate(
ROTATION_SPEED_X * delta_seconds,
ROTATION_SPEED_Y * delta_seconds,
ROTATION_SPEED_Z * delta_seconds,
);
// Get dimensions
let width = window.inner_size().width;
let height = window.inner_size().height;
// Draw cube edges
for &(i, j) in &cube.edges {
let p1 = cube.vertices[i];
let p2 = cube.vertices[j];
let (x1, y1) = project(p1, width, height);
let (x2, y2) = project(p2, width, height);
draw_line_manual(frame, width, x1, y1, x2, y2, [0xff, 0xff, 0xff, 0xff]);
}
if pixels.render().is_err() {
*control_flow = ControlFlow::Exit;
}
// Limit frame rate
let elapsed = now.elapsed();
if elapsed < Duration::from_millis(16) {
std::thread::sleep(Duration::from_millis(16) - elapsed);
}
}
_ => (),
}
window.request_redraw();
});
}
// Bresenham's line algorithm
fn draw_line_manual(
frame: &mut [u8],
width: u32,
mut x0: i32,
mut y0: i32,
x1: i32,
y1: i32,
color: [u8; 4],
) {
let dx = (x1 - x0).abs();
let dy = -(y1 - y0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx + dy;
loop {
// Only draw if the point is within bounds
if x0 >= 0 && x0 < width as i32 && y0 >= 0 && y0 < width as i32 {
let idx = ((y0 as u32 * width + x0 as u32) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx..idx + 4].copy_from_slice(&color);
}
}
if x0 == x1 && y0 == y1 {
break;
}
let e2 = 2 * err;
if e2 >= dy {
err += dy;
x0 += sx;
}
if e2 <= dx {
err += dx;
y0 += sy;
}
}
}