Skip to content

Commit

Permalink
feat: add camera motion controls
Browse files Browse the repository at this point in the history
  • Loading branch information
cbgbt committed Dec 2, 2024
1 parent 2b9cfdd commit c271199
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 47 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.vox filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ steal his words (because this port isn't theft enough):
This repository has added support for screen-space hatching based on lights
placed within the scene.

![](/raydeon/examples/cityscape.png)
![](/raydeon/examples/cityscape.png) ![](/raydeon/examples/castle_expected.svg)

## Example

Expand Down
1 change: 1 addition & 0 deletions pyraydeon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ name = "pyraydeon"
crate-type = ["cdylib"]

[dependencies]
euclid.workspace = true
pyo3 = { workspace = true, features = ["extension-module"] }
raydeon.workspace = true
numpy.workspace = true
5 changes: 3 additions & 2 deletions pyraydeon/examples/py_sphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ def paths(self, cam):
)


eye = Point3(0, 0, 5)
focus = Vec3(0, 0, 0)
eye = Point3(0, 0, 0)
focus = Point3(0, 0, -1)
up = Vec3(0, 1, 0)

fovy = 50.0
Expand All @@ -110,6 +110,7 @@ def paths(self, cam):
.perspective(fovy, width, height, znear, zfar)
.render_options(render_opts)
)
cam.translate((0, 0, 5))

paths = scene.render_with_lighting(cam, seed=5)

Expand Down
40 changes: 33 additions & 7 deletions pyraydeon/src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use pyo3::prelude::*;
use crate::linear::{Point3, Vec3};

#[derive(Debug, Clone)]
#[pyclass(frozen)]
#[pyclass]
pub(crate) struct Camera(pub(crate) raydeon::Camera);

impl ::std::ops::Deref for Camera {
Expand Down Expand Up @@ -55,19 +55,45 @@ impl Camera {
ncam.into()
}

fn translate(&mut self, trans: &Bound<'_, PyAny>) -> PyResult<()> {
let trans = Vec3::try_from(trans)?;
self.0.translate(trans.0.cast_unit());
Ok(())
}

fn adjust_yaw(&mut self, yaw: f64) -> PyResult<()> {
self.0.adjust_yaw(euclid::Angle::degrees(yaw));
Ok(())
}

fn adjust_pitch(&mut self, pitch: f64) -> PyResult<()> {
self.0.adjust_pitch(euclid::Angle::degrees(pitch));
Ok(())
}

fn adjust_roll(&mut self, roll: f64) -> PyResult<()> {
self.0.adjust_roll(euclid::Angle::degrees(roll));
Ok(())
}

#[getter]
fn eye<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<f64, Ix1>> {
PyArray::from_slice_bound(py, &self.0.observation.eye.to_array())
PyArray::from_slice_bound(py, &self.0.observation.eye().to_array())
}

#[getter]
fn focus<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<f64, Ix1>> {
PyArray::from_slice_bound(py, &self.0.observation.center.to_array())
fn up<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<f64, Ix1>> {
PyArray::from_slice_bound(py, &self.0.observation.up().to_array())
}

#[getter]
fn up<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<f64, Ix1>> {
PyArray::from_slice_bound(py, &self.0.observation.up.to_array())
fn right<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<f64, Ix1>> {
PyArray::from_slice_bound(py, &self.0.observation.right().to_array())
}

#[getter]
fn look<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<f64, Ix1>> {
PyArray::from_slice_bound(py, &self.0.observation.look().to_array())
}

#[getter]
Expand Down Expand Up @@ -102,7 +128,7 @@ impl Camera {

fn __repr__(slf: &Bound<'_, Self>) -> PyResult<String> {
let class_name = slf.get_type().qualname()?;
Ok(format!("{}<{:?}>", class_name, slf.borrow().0))
Ok(format!("{}<{:#?}>", class_name, slf.borrow().0))
}
}

Expand Down
2 changes: 1 addition & 1 deletion raydeon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ authors = ["cbgbt <[email protected]>"]
edition = "2021"

[dependencies]
anyhow.workspace = true
bon.workspace = true
cgmath.workspace = true
collision.workspace = true
Expand All @@ -18,5 +17,6 @@ rayon.workspace = true
tracing = { workspace = true, features = ["log"] }

[dev-dependencies]
dot_vox.workspace = true
env_logger.workspace = true
svg.workspace = true
3 changes: 3 additions & 0 deletions raydeon/examples/assets/castle.vox
Git LFS file not shown
112 changes: 112 additions & 0 deletions raydeon/examples/castle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use dot_vox;
use euclid::Angle;
use raydeon::lights::PointLight;
use raydeon::shapes::AxisAlignedCuboid;
use raydeon::{Camera, CameraOptions, DrawableShape, Material, Scene, SceneLighting};
use std::sync::Arc;

const CASTLE_VOX: &[u8] = include_bytes!("./assets/castle.vox");

fn main() {
env_logger::Builder::from_default_env()
.format_timestamp_nanos()
.init();

let castle_vox = dot_vox::load_bytes(CASTLE_VOX).expect("Could not load castle.vox");

let geometry: Vec<_> = castle_vox.models[0]
.voxels
.iter()
.map(|v| {
let x = v.x as f64;
let y = v.y as f64;
let z = v.z as f64;
DrawableShape::new()
.geometry(Arc::new(
AxisAlignedCuboid::new()
.min((x + 0.05, y + 0.05, z + 0.05))
.max((x + 0.95, y + 0.95, z + 0.95))
.build(),
))
.material(Material::new().diffuse(5.0).build())
.build()
})
.collect();

let eye = (10.0, -20.0, 0.0);
let focus = (10.0, 0.0, 0.0);
let up = (0.0, 0.0, 1.0);

let fovy = 40.0;
let width = 2048;
let height = 2048;
let znear = 0.1;
let zfar = 200.0;

let mut camera = Camera::configure()
.observation(Camera::look_at(eye, focus, up))
.perspective(Camera::perspective(fovy, width, height, znear, zfar))
.render_options(CameraOptions::configure().pen_px_size(4.0).build())
.build();

camera.translate((-15.0, 10.75, 0.0));
camera.adjust_yaw(Angle::degrees(-45.0));
camera.translate((-10.5, 0.0, 12.0));

let scene = Scene::new()
.geometry(geometry)
.lighting(
SceneLighting::new()
.with_ambient_lighting(0.37)
.with_lights(vec![Arc::new(PointLight::new(
55.0,
10.0,
(-10.81, -20.0, 30.0),
0.0,
0.13,
0.19,
))]),
)
.construct();

let paths = scene
.attach_camera(camera)
.with_seed(0)
.render_with_lighting();

let mut svg_doc = svg::Document::new()
.set("width", "8in")
.set("height", "8in")
.set("viewBox", (0, 0, width, height))
.set("stroke-width", "0.7mm")
.set("stroke", "black")
.set("fill", "none")
.add(
svg::node::element::Group::new().add(
svg::node::element::Rectangle::new()
.set("x", 0)
.set("y", 0)
.set("width", "100%")
.set("height", "100%")
.set("fill", "white"),
),
);

// We have to flip the y-axis in our svg...
let mut item_group = svg::node::element::Group::new()
.set("transform", format!("translate(0, {}) scale(1,-1)", height));

for path in paths {
let (p1, p2) = (path.p1, path.p2);
item_group = item_group.add(
svg::node::element::Line::new()
.set("x1", p1.x)
.set("y1", p1.y)
.set("x2", p2.x)
.set("y2", p2.y),
);
}

svg_doc = svg_doc.add(item_group);
println!("{}", svg_doc);
}
Binary file modified raydeon/examples/cityscape.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit c271199

Please sign in to comment.