more geometry stuff, shapes
This commit is contained in:
parent
48abb19464
commit
38e28a2d57
7 changed files with 190 additions and 13 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -20,6 +20,18 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum_dispatch"
|
||||||
|
version = "0.3.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glam"
|
name = "glam"
|
||||||
version = "0.30.2"
|
version = "0.30.2"
|
||||||
|
@ -52,6 +64,7 @@ name = "mechthild_core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
|
"enum_dispatch",
|
||||||
"glam",
|
"glam",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck = { version = "1.22.0", optional = true }
|
bytemuck = { version = "1.22.0", optional = true }
|
||||||
|
enum_dispatch = "0.3.13"
|
||||||
glam = "0.30.2"
|
glam = "0.30.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
use std::ops::{Add, Div, Mul, Sub};
|
use std::{
|
||||||
|
fmt,
|
||||||
|
ops::{Add, Div, Mul, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod shapes;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Vector(glam::Vec3A);
|
pub struct Vector(glam::Vec3A);
|
||||||
|
|
||||||
|
impl fmt::Debug for Vector {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple(stringify!(Vector))
|
||||||
|
.field(&self.0.x)
|
||||||
|
.field(&self.0.y)
|
||||||
|
.field(&self.0.z)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vector> for glam::Vec3 {
|
impl From<Vector> for glam::Vec3 {
|
||||||
fn from(value: Vector) -> Self {
|
fn from(value: Vector) -> Self {
|
||||||
value.0.into()
|
value.0.into()
|
||||||
|
@ -30,6 +45,10 @@ impl Vector {
|
||||||
self.0.length()
|
self.0.length()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn length_squared(self) -> f32 {
|
||||||
|
self.0.length_squared()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dot(self, other: Vector) -> f32 {
|
pub fn dot(self, other: Vector) -> f32 {
|
||||||
self.0.dot(other.0)
|
self.0.dot(other.0)
|
||||||
}
|
}
|
||||||
|
@ -72,9 +91,19 @@ impl Add for Vector {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Point(glam::Vec3A);
|
pub struct Point(glam::Vec3A);
|
||||||
|
|
||||||
|
impl fmt::Debug for Point {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple(stringify!(Point))
|
||||||
|
.field(&self.0.x)
|
||||||
|
.field(&self.0.y)
|
||||||
|
.field(&self.0.z)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Point> for glam::Vec3 {
|
impl From<Point> for glam::Vec3 {
|
||||||
fn from(value: Point) -> Self {
|
fn from(value: Point) -> Self {
|
||||||
value.0.into()
|
value.0.into()
|
||||||
|
@ -93,6 +122,10 @@ impl Point {
|
||||||
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||||
Self(glam::vec3a(x, y, z))
|
Self(glam::vec3a(x, y, z))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_vector(self) -> Vector {
|
||||||
|
Vector(self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<Vector> for Point {
|
impl Add<Vector> for Point {
|
||||||
|
@ -111,10 +144,19 @@ impl Sub for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Transform(glam::Affine3A);
|
pub struct Transform(glam::Affine3A);
|
||||||
|
|
||||||
|
impl fmt::Debug for Transform {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct(stringify!(Transform))
|
||||||
|
.field(stringify!(matrix3), &self.0.matrix3)
|
||||||
|
.field(stringify!(translation), &self.0.translation)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Transform {
|
impl Transform {
|
||||||
pub fn scale(x: f32, y: f32, z: f32) -> Self {
|
pub fn scale(x: f32, y: f32, z: f32) -> Self {
|
||||||
Self(glam::Affine3A::from_scale(glam::vec3(x, y, z)))
|
Self(glam::Affine3A::from_scale(glam::vec3(x, y, z)))
|
||||||
|
@ -149,6 +191,14 @@ impl Mul<Vector> for Transform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul<Point> for Transform {
|
||||||
|
type Output = Point;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Point) -> Self::Output {
|
||||||
|
Point(self.0.transform_point3a(rhs.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
origin: Point,
|
origin: Point,
|
||||||
direction: Vector,
|
direction: Vector,
|
||||||
|
|
90
core/src/geometry/shapes.rs
Normal file
90
core/src/geometry/shapes.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
|
use super::{Point, Ray, Vector};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Hit {
|
||||||
|
point: Point,
|
||||||
|
normal: Vector,
|
||||||
|
t: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[enum_dispatch]
|
||||||
|
pub(crate) trait Hittable {
|
||||||
|
fn intersect(&self, ray: Ray, t_max: f32) -> Option<Hit>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Sphere {
|
||||||
|
center: Point,
|
||||||
|
radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hittable for Sphere {
|
||||||
|
fn intersect(&self, ray: Ray, t_max: f32) -> Option<Hit> {
|
||||||
|
let oc = self.center - ray.origin;
|
||||||
|
let a = ray.direction.length_squared();
|
||||||
|
let h = ray.direction.dot(oc);
|
||||||
|
let c = oc.length_squared() - self.radius * self.radius;
|
||||||
|
let d = h * h - a * c;
|
||||||
|
|
||||||
|
(d > 0.0)
|
||||||
|
.then(|| (h - d.sqrt()) / a)
|
||||||
|
.filter(|t| *t < t_max && *t > 0.0)
|
||||||
|
.map(|t| {
|
||||||
|
let point = ray.at(t);
|
||||||
|
let normal = point - self.center;
|
||||||
|
Hit { point, normal, t }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Plane {
|
||||||
|
normal: Vector,
|
||||||
|
d: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hittable for Plane {
|
||||||
|
fn intersect(&self, ray: Ray, t_max: f32) -> Option<Hit> {
|
||||||
|
let n_dot_r = self.normal.dot(ray.direction);
|
||||||
|
(n_dot_r.abs() > f32::EPSILON)
|
||||||
|
.then(|| {
|
||||||
|
let n_dot_o = self.normal.dot(ray.origin.to_vector());
|
||||||
|
-(n_dot_o + self.d) / n_dot_r
|
||||||
|
})
|
||||||
|
.filter(|t| *t < t_max && *t > 0.0)
|
||||||
|
.map(|t| Hit {
|
||||||
|
point: ray.at(t),
|
||||||
|
normal: self.normal,
|
||||||
|
t,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[enum_dispatch(Hittable)]
|
||||||
|
pub enum Shape {
|
||||||
|
Sphere,
|
||||||
|
Plane,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Shape {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Sphere(s) => s.fmt(f),
|
||||||
|
Self::Plane(p) => p.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shape {
|
||||||
|
pub fn sphere(center: Point, radius: f32) -> Shape {
|
||||||
|
Shape::from(Sphere { center, radius })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plane(normal: Vector, d: f32) -> Shape {
|
||||||
|
Shape::from(Plane { normal, d })
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,11 @@
|
||||||
pub mod impl_bytemuck;
|
pub mod impl_bytemuck;
|
||||||
|
|
||||||
pub mod color;
|
pub mod color;
|
||||||
|
|
||||||
mod geometry;
|
mod geometry;
|
||||||
|
|
||||||
|
pub use geometry::{Point, Transform, Vector, shapes::Shape};
|
||||||
|
|
||||||
use color::Rgba;
|
use color::Rgba;
|
||||||
|
|
||||||
pub struct Scene;
|
pub struct Scene;
|
||||||
|
|
15
examples/basic.py
Normal file → Executable file
15
examples/basic.py
Normal file → Executable file
|
@ -1,11 +1,10 @@
|
||||||
from PIL import Image
|
#!python
|
||||||
from mechthild import Scene
|
|
||||||
|
|
||||||
WIDTH = 800
|
from mechthild import Shape
|
||||||
HEIGHT = 600
|
|
||||||
|
|
||||||
render_result = Scene().render(WIDTH, HEIGHT)
|
s1 = Shape.sphere((0.0, 0.5, 0.0), 0.5)
|
||||||
rgba = bytes([int(v * 255) for v in render_result])
|
s2 = Shape.sphere((1.5, 2.0, -1.0), 2.0)
|
||||||
|
|
||||||
img = Image.frombuffer('RGBA', (WIDTH, HEIGHT), rgba)
|
ground = Shape.plane((0.0, 1.0, 0.0), 0.0)
|
||||||
img.save("test.png")
|
|
||||||
|
print(f"{s1}\n{s2}\n{ground}")
|
||||||
|
|
|
@ -1,7 +1,27 @@
|
||||||
use bytemuck::allocation::cast_slice_box;
|
use bytemuck::allocation::cast_slice_box;
|
||||||
use mechthild_core::{Scene, color::Rgba};
|
use mechthild_core::{Scene, Shape, color::Rgba};
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pyclass(name = "Shape")]
|
||||||
|
struct PyShape(Shape);
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PyShape {
|
||||||
|
#[staticmethod]
|
||||||
|
pub fn sphere(center: (f32, f32, f32), radius: f32) -> Self {
|
||||||
|
Self(Shape::sphere(center.into(), radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[staticmethod]
|
||||||
|
pub fn plane(normal: (f32, f32, f32), d: f32) -> Self {
|
||||||
|
Self(Shape::plane(normal.into(), d))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __str__(&self) -> PyResult<String> {
|
||||||
|
Ok(format!("{:?}", self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[pyclass(name = "Scene")]
|
#[pyclass(name = "Scene")]
|
||||||
struct PyScene(Scene);
|
struct PyScene(Scene);
|
||||||
|
|
||||||
|
@ -22,6 +42,7 @@ impl PyScene {
|
||||||
/// A Python module implemented in Rust.
|
/// A Python module implemented in Rust.
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn mechthild(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
fn mechthild(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
m.add_class::<PyShape>()?;
|
||||||
m.add_class::<PyScene>()?;
|
m.add_class::<PyScene>()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue