use std::default::Default;
use std::path::Path;
use std::sync::Arc;
use gltf::camera::Projection;
use gltf::Document;
use crate::color::{Color, Texture};
use crate::math::{Mat4, Transform};
use crate::prelude::Error;
use crate::rendering::Camera;
use crate::shapes::Object;
pub struct Scene {
pub camera: Camera,
pub objects: Vec<Object>,
pub background_color: Color,
pub background_texture: Option<Arc<dyn Texture>>,
pub background_strength: f64,
pub background_transform: Transform,
}
impl Default for Scene {
fn default() -> Self {
Scene {
camera: Camera::default(),
objects: vec![],
background_color: Color::splat(0.8),
background_texture: None,
background_strength: 1.0,
background_transform: Transform::default(),
}
}
}
impl Scene {
pub fn load_gltf<P: AsRef<Path>>(path: P) -> Result<Scene, Error> {
let (document, buffers, images) = gltf::import(path)?;
let camera = Self::load_camera(&document).unwrap_or_default();
let objects = Object::load_gltf_document(&document, &buffers, &images)?;
Ok(Self {
camera,
objects,
..Default::default()
})
}
fn load_camera(document: &Document) -> Option<Camera> {
let camera = document.cameras().next()?;
if document.cameras().len() > 1 {
eprintln!("WARNING: multiple cameras found. Using the first one");
}
let projection = match camera.projection() {
Projection::Orthographic(_) => {
eprintln!("ERROR: orthographic camera projection is not supported");
return None;
}
Projection::Perspective(p) => p,
};
let default_camera = Camera::default();
let fov = projection.yfov() as f64;
let (width, height, fov) = if let Some(aspect_ratio) = projection.aspect_ratio() {
if aspect_ratio > 1.0 {
(
default_camera.width,
(default_camera.width as f32 / aspect_ratio) as u32,
fov,
)
} else {
(
(aspect_ratio * default_camera.height as f32) as u32,
default_camera.height,
fov * aspect_ratio as f64,
)
}
} else {
(default_camera.width, default_camera.height, fov)
};
let camera_node = document
.nodes()
.find(|n| {
if let Some(c) = n.camera() {
c.index() == camera.index()
} else {
false
}
})
.unwrap();
let mat4 =
Mat4::from(camera_node.transform().matrix().map(|r| r.map(f64::from))).transpose();
let mat4_inverse = mat4.inverse();
let transform = Transform::new(mat4, mat4_inverse);
Some(Camera {
transform,
width,
height,
fov,
..Default::default()
})
}
}