renders the diablo model
This commit is contained in:
parent
85f945aef7
commit
346e9aa85d
4 changed files with 165 additions and 32 deletions
33
src/main.rs
33
src/main.rs
|
@ -37,14 +37,23 @@ fn main() {
|
||||||
let mut framebuffer = TGAImage::new(w, h, TGAFormat::RGB);
|
let mut framebuffer = TGAImage::new(w, h, TGAFormat::RGB);
|
||||||
let model = Model::from_obj("diablo3_pose.obj");
|
let model = Model::from_obj("diablo3_pose.obj");
|
||||||
|
|
||||||
dbg!(model);
|
dbg!(model.verts.len());
|
||||||
|
let mut max_face = 0usize;
|
||||||
|
let mut min_face = usize::MAX;
|
||||||
|
|
||||||
framebuffer
|
for f in model.faces.iter() {
|
||||||
.write_file("framebuffer.tga", true, true)
|
for &f in f.iter() {
|
||||||
.unwrap();
|
max_face = f.max(max_face);
|
||||||
|
min_face = f.min(min_face);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn baseline(framebuffer: &mut TGAImage) {
|
model.render_wireframe(&mut framebuffer);
|
||||||
|
|
||||||
|
framebuffer.write_file("diablo.tga", true, true).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _baseline(framebuffer: &mut TGAImage) {
|
||||||
let ax = 7;
|
let ax = 7;
|
||||||
let ay = 3;
|
let ay = 3;
|
||||||
let bx = 12;
|
let bx = 12;
|
||||||
|
@ -56,9 +65,9 @@ fn baseline(framebuffer: &mut TGAImage) {
|
||||||
framebuffer.set(bx, by, WHITE);
|
framebuffer.set(bx, by, WHITE);
|
||||||
framebuffer.set(cx, cy, WHITE);
|
framebuffer.set(cx, cy, WHITE);
|
||||||
|
|
||||||
let a = Point::new(ax, ay);
|
let a = Point2i::newu(ax, ay);
|
||||||
let b = Point::new(bx, by);
|
let b = Point2i::newu(bx, by);
|
||||||
let c = Point::new(cx, cy);
|
let c = Point2i::newu(cx, cy);
|
||||||
|
|
||||||
line(a, b, framebuffer, BLUE);
|
line(a, b, framebuffer, BLUE);
|
||||||
line(c, b, framebuffer, GREEN);
|
line(c, b, framebuffer, GREEN);
|
||||||
|
@ -66,17 +75,17 @@ fn baseline(framebuffer: &mut TGAImage) {
|
||||||
line(a, c, framebuffer, RED);
|
line(a, c, framebuffer, RED);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench(fb: &mut TGAImage) {
|
fn _bench(fb: &mut TGAImage) {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
for _ in 0..1 << 24 {
|
for _ in 0..1 << 24 {
|
||||||
let ax = rng.random_range(0..fb.width);
|
let ax = rng.random_range(0..fb.width);
|
||||||
let ay = rng.random_range(0..fb.height);
|
let ay = rng.random_range(0..fb.height);
|
||||||
let a = Point::new(ax, ay);
|
let a = Point2i::newu(ax, ay);
|
||||||
|
|
||||||
let bx = rng.random_range(0..fb.width);
|
let bx = rng.random_range(0..fb.width);
|
||||||
let by = rng.random_range(0..fb.height);
|
let by = rng.random_range(0..fb.height);
|
||||||
let b = Point::new(bx, by);
|
let b = Point2i::newu(bx, by);
|
||||||
|
|
||||||
line(
|
line(
|
||||||
a,
|
a,
|
||||||
|
@ -87,7 +96,7 @@ fn bench(fb: &mut TGAImage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line(mut a: Point, mut b: Point, fb: &mut TGAImage, color: TGAColor) {
|
fn line(mut a: Point2i, mut b: Point2i, fb: &mut TGAImage, color: TGAColor) {
|
||||||
let is_steep = (a.x - b.x).abs() < (a.y - b.y).abs();
|
let is_steep = (a.x - b.x).abs() < (a.y - b.y).abs();
|
||||||
if is_steep {
|
if is_steep {
|
||||||
std::mem::swap(&mut a.x, &mut a.y);
|
std::mem::swap(&mut a.x, &mut a.y);
|
||||||
|
|
62
src/model.rs
62
src/model.rs
|
@ -1,13 +1,10 @@
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
|
use std::ops::{Deref, DerefMut};
|
||||||
pub struct Point3 {
|
|
||||||
store: [f32; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<[f32; 3]> for Point3 {
|
use crate::{
|
||||||
fn from(value: [f32; 3]) -> Self {
|
RED, line,
|
||||||
Self { store: value }
|
point::{Point2i, Point3f},
|
||||||
}
|
tga::TGAImage,
|
||||||
}
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
pub struct Face {
|
pub struct Face {
|
||||||
|
@ -20,9 +17,23 @@ impl From<[usize; 3]> for Face {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for Face {
|
||||||
|
type Target = [usize; 3];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Face {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
pub verts: Vec<Point3>,
|
pub verts: Vec<Point3f>,
|
||||||
pub faces: Vec<Face>,
|
pub faces: Vec<Face>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +60,15 @@ impl Model {
|
||||||
if *t == "f" {
|
if *t == "f" {
|
||||||
let f: Vec<usize> = line[1..4]
|
let f: Vec<usize> = line[1..4]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|l| l.split('/').take(1).next().unwrap().parse().unwrap())
|
.map(|l| {
|
||||||
|
l.split('/')
|
||||||
|
.take(1)
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.parse::<usize>()
|
||||||
|
.unwrap()
|
||||||
|
- 1
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let f: [usize; 3] = f.try_into().unwrap();
|
let f: [usize; 3] = f.try_into().unwrap();
|
||||||
faces.push(f.into());
|
faces.push(f.into());
|
||||||
|
@ -59,4 +78,25 @@ impl Model {
|
||||||
|
|
||||||
Self { verts, faces }
|
Self { verts, faces }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render_wireframe(&self, framebuffer: &mut TGAImage) {
|
||||||
|
let width = framebuffer.width;
|
||||||
|
let height = framebuffer.height;
|
||||||
|
|
||||||
|
for face in self.faces.iter() {
|
||||||
|
let a = world2view(self.verts[face[0]], width, height);
|
||||||
|
let b = world2view(self.verts[face[1]], width, height);
|
||||||
|
let c = world2view(self.verts[face[2]], width, height);
|
||||||
|
line(a, b, framebuffer, RED);
|
||||||
|
line(b, c, framebuffer, RED);
|
||||||
|
line(c, a, framebuffer, RED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn world2view(point: Point3f, width: u32, height: u32) -> Point2i {
|
||||||
|
let point = (point + 1.0) * 0.5;
|
||||||
|
let x = (point.x().min(1.0) * width as f32).round_ties_even() as i32;
|
||||||
|
let y = (point.y().min(1.0) * height as f32).round_ties_even() as i32;
|
||||||
|
Point2i::new(x, y)
|
||||||
}
|
}
|
||||||
|
|
98
src/point.rs
98
src/point.rs
|
@ -1,13 +1,16 @@
|
||||||
use std::ops::{Add, Mul, Sub};
|
use std::ops::{Add, Deref, DerefMut, Div, Mul, Sub};
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Point {
|
pub struct Point2i {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Point {
|
impl Point2i {
|
||||||
pub fn new(x: u32, y: u32) -> Self {
|
pub fn new(x: i32, y: i32) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
pub fn newu(x: u32, y: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
x: x as i32,
|
x: x as i32,
|
||||||
y: y as i32,
|
y: y as i32,
|
||||||
|
@ -15,7 +18,7 @@ impl Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for Point {
|
impl Add for Point2i {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
@ -26,7 +29,7 @@ impl Add for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub for Point {
|
impl Sub for Point2i {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
@ -37,7 +40,7 @@ impl Sub for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul for Point {
|
impl Mul for Point2i {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn mul(self, rhs: Self) -> Self::Output {
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
@ -47,3 +50,84 @@ impl Mul for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Point3f {
|
||||||
|
store: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[f32; 3]> for Point3f {
|
||||||
|
fn from(value: [f32; 3]) -> Self {
|
||||||
|
Self { store: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Point3f {
|
||||||
|
type Target = [f32; 3];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Point3f {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point3f {
|
||||||
|
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||||
|
Self { store: [x, y, z] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x(&self) -> f32 {
|
||||||
|
self.store[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y(&self) -> f32 {
|
||||||
|
self.store[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z(&self) -> f32 {
|
||||||
|
self.store[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<f32> for Point3f {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: f32) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
store: [self[0] + rhs, self[1] + rhs, self[2] + rhs],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Point3f {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
store: [self[0] * rhs, self[1] * rhs, self[2] * rhs],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for Point3f {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: f32) -> Self::Output {
|
||||||
|
self * (1.0 / rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Point3f> for Point2i {
|
||||||
|
fn from(p: Point3f) -> Self {
|
||||||
|
Self {
|
||||||
|
x: p.x().round_ties_even() as i32,
|
||||||
|
y: p.y().round_ties_even() as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -236,12 +236,12 @@ impl TGAImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, x: u32, y: u32, color: TGAColor) {
|
pub fn set(&mut self, x: u32, y: u32, color: TGAColor) {
|
||||||
if self.data.is_empty() || x > self.width || y > self.height {
|
if self.data.is_empty() || x >= self.width || y >= self.height {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bpp = self.bytes_per_pixel() as usize;
|
let bpp = self.bytes_per_pixel() as usize;
|
||||||
let start = (x + y * self.width) as usize * bpp;
|
let start = (x + (y * self.width)) as usize * bpp;
|
||||||
let end = start + bpp;
|
let end = start + bpp;
|
||||||
self.data[start..end].copy_from_slice(&color[0..bpp]);
|
self.data[start..end].copy_from_slice(&color[0..bpp]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue