first commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
2324
Cargo.lock
generated
Normal file
2324
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
9
Cargo.toml
Normal file
9
Cargo.toml
Normal 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
248
src/main.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user