use crate::camera::Camera3d;
use crate::color::Color;
use crate::light::LightCollection;
use crate::resource::vertex_index::VertexIndex;
use crate::resource::{
AllocationType, BufferType, GPUVec, GpuData, GpuMesh3d, Material3d, RenderContext, Texture,
TextureManager,
};
use glamx::{Mat3, Pose3, Vec2, Vec3};
use std::any::Any;
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
pub struct ObjectData3d {
material: Rc<RefCell<Box<dyn Material3d + 'static>>>,
texture: Arc<Texture>,
color: Color,
lines_color: Option<Color>,
points_color: Option<Color>,
wlines: f32,
wpoints: f32,
lines_use_perspective: bool,
points_use_perspective: bool,
draw_surface: bool,
cull: bool,
user_data: Box<dyn Any + 'static>,
metallic: f32,
roughness: f32,
emissive: Color,
normal_map: Option<Arc<Texture>>,
metallic_roughness_map: Option<Arc<Texture>>,
ao_map: Option<Arc<Texture>>,
emissive_map: Option<Arc<Texture>>,
}
impl ObjectData3d {
#[inline]
pub fn texture(&self) -> &Arc<Texture> {
&self.texture
}
#[inline]
pub fn color(&self) -> Color {
self.color
}
#[inline]
pub fn lines_width(&self) -> f32 {
self.wlines
}
#[inline]
pub fn lines_color(&self) -> Option<Color> {
self.lines_color
}
#[inline]
pub fn points_size(&self) -> f32 {
self.wpoints
}
#[inline]
pub fn points_color(&self) -> Option<Color> {
self.points_color
}
#[inline]
pub fn lines_use_perspective(&self) -> bool {
self.lines_use_perspective
}
#[inline]
pub fn points_use_perspective(&self) -> bool {
self.points_use_perspective
}
#[inline]
pub fn surface_rendering_active(&self) -> bool {
self.draw_surface
}
#[inline]
pub fn backface_culling_enabled(&self) -> bool {
self.cull
}
#[inline]
pub fn user_data(&self) -> &dyn Any {
&*self.user_data
}
#[inline]
pub fn metallic(&self) -> f32 {
self.metallic
}
#[inline]
pub fn roughness(&self) -> f32 {
self.roughness
}
#[inline]
pub fn emissive(&self) -> Color {
self.emissive
}
#[inline]
pub fn normal_map(&self) -> Option<&Arc<Texture>> {
self.normal_map.as_ref()
}
#[inline]
pub fn metallic_roughness_map(&self) -> Option<&Arc<Texture>> {
self.metallic_roughness_map.as_ref()
}
#[inline]
pub fn ao_map(&self) -> Option<&Arc<Texture>> {
self.ao_map.as_ref()
}
#[inline]
pub fn emissive_map(&self) -> Option<&Arc<Texture>> {
self.emissive_map.as_ref()
}
}
pub struct InstanceData3d {
pub position: Vec3,
pub deformation: Mat3,
pub color: Color,
pub lines_color: Option<Color>,
pub lines_width: Option<f32>,
pub points_color: Option<Color>,
pub points_size: Option<f32>,
}
impl Default for InstanceData3d {
fn default() -> Self {
Self {
position: Vec3::ZERO,
deformation: Mat3::IDENTITY,
color: crate::color::WHITE,
lines_color: None, lines_width: None, points_color: None, points_size: None, }
}
}
pub const LINES_WIDTH_USE_OBJECT: f32 = -1.0;
pub const LINES_COLOR_USE_OBJECT: Color = Color::new(0.0, 0.0, 0.0, 0.0);
pub const POINTS_SIZE_USE_OBJECT: f32 = -1.0;
pub const POINTS_COLOR_USE_OBJECT: Color = Color::new(0.0, 0.0, 0.0, 0.0);
pub struct InstancesBuffer3d {
pub positions: GPUVec<Vec3>,
pub deformations: GPUVec<Vec3>,
pub colors: GPUVec<[f32; 4]>,
pub lines_colors: GPUVec<[f32; 4]>,
pub lines_widths: GPUVec<f32>,
pub points_colors: GPUVec<[f32; 4]>,
pub points_sizes: GPUVec<f32>,
}
#[inline]
pub(crate) fn color_to_array(color: Color) -> [f32; 4] {
[color.r, color.g, color.b, color.a]
}
impl Default for InstancesBuffer3d {
fn default() -> Self {
InstancesBuffer3d {
positions: GPUVec::new(
vec![Vec3::ZERO],
BufferType::Array,
AllocationType::StreamDraw,
),
deformations: GPUVec::new(
vec![Vec3::X, Vec3::Y, Vec3::Z],
BufferType::Array,
AllocationType::StreamDraw,
),
colors: GPUVec::new(
vec![[1.0; 4]],
BufferType::Array,
AllocationType::StreamDraw,
),
lines_colors: GPUVec::new(
vec![color_to_array(LINES_COLOR_USE_OBJECT)], BufferType::Array,
AllocationType::StreamDraw,
),
lines_widths: GPUVec::new(
vec![LINES_WIDTH_USE_OBJECT], BufferType::Array,
AllocationType::StreamDraw,
),
points_colors: GPUVec::new(
vec![color_to_array(POINTS_COLOR_USE_OBJECT)], BufferType::Array,
AllocationType::StreamDraw,
),
points_sizes: GPUVec::new(
vec![POINTS_SIZE_USE_OBJECT], BufferType::Array,
AllocationType::StreamDraw,
),
}
}
}
impl InstancesBuffer3d {
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.positions.len()
}
pub fn any_instance_has_wireframe(&self) -> bool {
if let Some(widths) = self.lines_widths.data() {
widths.iter().any(|&w| w >= 0.0)
} else {
false
}
}
pub fn all_use_object_wireframe(&self) -> bool {
if let Some(widths) = self.lines_widths.data() {
widths.iter().all(|&w| w < 0.0)
} else {
true
}
}
}
pub struct Object3d {
data: ObjectData3d,
instances: Rc<RefCell<InstancesBuffer3d>>,
mesh: Rc<RefCell<GpuMesh3d>>,
gpu_data: Box<dyn GpuData>,
}
impl Object3d {
#[doc(hidden)]
pub fn new(
mesh: Rc<RefCell<GpuMesh3d>>,
color: Color,
texture: Arc<Texture>,
material: Rc<RefCell<Box<dyn Material3d + 'static>>>,
) -> Object3d {
let gpu_data = material.borrow().create_gpu_data();
let user_data = ();
let data = ObjectData3d {
color,
lines_color: None,
points_color: None,
texture,
wlines: 0.0,
wpoints: 0.0,
lines_use_perspective: true,
points_use_perspective: true,
draw_surface: true,
cull: true,
material,
user_data: Box::new(user_data),
metallic: 0.0,
roughness: 0.5,
emissive: crate::color::BLACK,
normal_map: None,
metallic_roughness_map: None,
ao_map: None,
emissive_map: None,
};
let instances = Rc::new(RefCell::new(InstancesBuffer3d::default()));
Object3d {
data,
instances,
mesh,
gpu_data,
}
}
#[doc(hidden)]
pub fn prepare(
&mut self,
transform: Pose3,
scale: Vec3,
pass: usize,
camera: &mut dyn Camera3d,
lights: &LightCollection,
viewport_width: u32,
viewport_height: u32,
) {
self.data.material.borrow_mut().prepare(
pass,
transform,
scale,
camera,
lights,
&self.data,
&mut *self.gpu_data,
viewport_width,
viewport_height,
);
}
#[doc(hidden)]
pub fn render(
&mut self,
transform: Pose3,
scale: Vec3,
pass: usize,
camera: &mut dyn Camera3d,
lights: &LightCollection,
render_pass: &mut wgpu::RenderPass<'_>,
context: &RenderContext,
) {
self.data.material.borrow_mut().render(
pass,
transform,
scale,
camera,
lights,
&self.data,
&mut self.mesh.borrow_mut(),
&mut self.instances.borrow_mut(),
&mut *self.gpu_data,
render_pass,
context,
);
}
#[inline]
pub fn data(&self) -> &ObjectData3d {
&self.data
}
#[inline]
pub fn data_mut(&mut self) -> &mut ObjectData3d {
&mut self.data
}
#[inline]
pub fn instances(&self) -> &Rc<RefCell<InstancesBuffer3d>> {
&self.instances
}
pub fn set_instances(&mut self, instances: &[InstanceData3d]) {
let mut pos_data: Vec<_> = self
.instances
.borrow_mut()
.positions
.data_mut()
.take()
.unwrap_or_default();
let mut col_data: Vec<_> = self
.instances
.borrow_mut()
.colors
.data_mut()
.take()
.unwrap_or_default();
let mut def_data: Vec<_> = self
.instances
.borrow_mut()
.deformations
.data_mut()
.take()
.unwrap_or_default();
let mut lines_col_data: Vec<_> = self
.instances
.borrow_mut()
.lines_colors
.data_mut()
.take()
.unwrap_or_default();
let mut lines_width_data: Vec<_> = self
.instances
.borrow_mut()
.lines_widths
.data_mut()
.take()
.unwrap_or_default();
let mut points_col_data: Vec<_> = self
.instances
.borrow_mut()
.points_colors
.data_mut()
.take()
.unwrap_or_default();
let mut points_size_data: Vec<_> = self
.instances
.borrow_mut()
.points_sizes
.data_mut()
.take()
.unwrap_or_default();
pos_data.clear();
col_data.clear();
def_data.clear();
lines_col_data.clear();
lines_width_data.clear();
points_col_data.clear();
points_size_data.clear();
pos_data.extend(instances.iter().map(|i| i.position));
col_data.extend(instances.iter().map(|i| color_to_array(i.color)));
def_data.extend(instances.iter().flat_map(|i| {
[
i.deformation.x_axis,
i.deformation.y_axis,
i.deformation.z_axis,
]
}));
lines_col_data.extend(
instances
.iter()
.map(|i| color_to_array(i.lines_color.unwrap_or(LINES_COLOR_USE_OBJECT))),
);
lines_width_data.extend(
instances
.iter()
.map(|i| i.lines_width.unwrap_or(LINES_WIDTH_USE_OBJECT)),
);
points_col_data.extend(
instances
.iter()
.map(|i| color_to_array(i.points_color.unwrap_or(POINTS_COLOR_USE_OBJECT))),
);
points_size_data.extend(
instances
.iter()
.map(|i| i.points_size.unwrap_or(POINTS_SIZE_USE_OBJECT)),
);
*self.instances.borrow_mut().positions.data_mut() = Some(pos_data);
*self.instances.borrow_mut().colors.data_mut() = Some(col_data);
*self.instances.borrow_mut().deformations.data_mut() = Some(def_data);
*self.instances.borrow_mut().lines_colors.data_mut() = Some(lines_col_data);
*self.instances.borrow_mut().lines_widths.data_mut() = Some(lines_width_data);
*self.instances.borrow_mut().points_colors.data_mut() = Some(points_col_data);
*self.instances.borrow_mut().points_sizes.data_mut() = Some(points_size_data);
}
#[inline]
pub fn enable_backface_culling(&mut self, active: bool) {
self.data.cull = active;
}
#[inline]
pub fn set_user_data(&mut self, user_data: Box<dyn Any + 'static>) {
self.data.user_data = user_data;
}
#[inline]
pub fn material(&self) -> Rc<RefCell<Box<dyn Material3d + 'static>>> {
self.data.material.clone()
}
#[inline]
pub fn set_material(&mut self, material: Rc<RefCell<Box<dyn Material3d + 'static>>>) {
self.gpu_data = material.borrow().create_gpu_data();
self.data.material = material;
}
#[inline]
pub fn set_lines_width(&mut self, width: f32, use_perspective: bool) {
self.data.wlines = width;
self.data.lines_use_perspective = use_perspective;
}
#[inline]
pub fn lines_width(&self) -> f32 {
self.data.wlines
}
#[inline]
pub fn set_lines_color(&mut self, color: Option<Color>) {
self.data.lines_color = color
}
#[inline]
pub fn lines_color(&self) -> Option<Color> {
self.data.lines_color
}
#[inline]
pub fn set_points_size(&mut self, size: f32, use_perspective: bool) {
self.data.wpoints = size;
self.data.points_use_perspective = use_perspective;
}
#[inline]
pub fn points_size(&self) -> f32 {
self.data.wpoints
}
#[inline]
pub fn set_points_color(&mut self, color: Option<Color>) {
self.data.points_color = color
}
#[inline]
pub fn points_color(&self) -> Option<Color> {
self.data.points_color
}
#[inline]
pub fn set_surface_rendering_activation(&mut self, active: bool) {
self.data.draw_surface = active
}
#[inline]
pub fn surface_rendering_activation(&self) -> bool {
self.data.draw_surface
}
#[inline]
pub fn mesh(&self) -> &Rc<RefCell<GpuMesh3d>> {
&self.mesh
}
#[inline(always)]
pub fn modify_vertices<F: FnMut(&mut Vec<Vec3>)>(&mut self, f: &mut F) {
let bmesh = self.mesh.borrow_mut();
let _ = bmesh.coords().write().unwrap().data_mut().as_mut().map(f);
}
#[inline(always)]
pub fn read_vertices<F: FnMut(&[Vec3])>(&self, f: &mut F) {
let bmesh = self.mesh.borrow();
let _ = bmesh
.coords()
.read()
.unwrap()
.data()
.as_ref()
.map(|coords| f(&coords[..]));
}
#[inline]
pub fn recompute_normals(&mut self) {
self.mesh.borrow_mut().recompute_normals();
}
#[inline(always)]
pub fn modify_normals<F: FnMut(&mut Vec<Vec3>)>(&mut self, f: &mut F) {
let bmesh = self.mesh.borrow_mut();
let _ = bmesh.normals().write().unwrap().data_mut().as_mut().map(f);
}
#[inline(always)]
pub fn read_normals<F: FnMut(&[Vec3])>(&self, f: &mut F) {
let bmesh = self.mesh.borrow();
let _ = bmesh
.normals()
.read()
.unwrap()
.data()
.as_ref()
.map(|normals| f(&normals[..]));
}
#[inline(always)]
pub fn modify_faces<F: FnMut(&mut Vec<[VertexIndex; 3]>)>(&mut self, f: &mut F) {
let bmesh = self.mesh.borrow_mut();
let _ = bmesh.faces().write().unwrap().data_mut().as_mut().map(f);
}
#[inline(always)]
pub fn read_faces<F: FnMut(&[[VertexIndex; 3]])>(&self, f: &mut F) {
let bmesh = self.mesh.borrow();
let _ = bmesh
.faces()
.read()
.unwrap()
.data()
.as_ref()
.map(|faces| f(&faces[..]));
}
#[inline(always)]
pub fn modify_uvs<F: FnMut(&mut Vec<Vec2>)>(&mut self, f: &mut F) {
let bmesh = self.mesh.borrow_mut();
let _ = bmesh.uvs().write().unwrap().data_mut().as_mut().map(f);
}
#[inline(always)]
pub fn read_uvs<F: FnMut(&[Vec2])>(&self, f: &mut F) {
let bmesh = self.mesh.borrow();
let _ = bmesh
.uvs()
.read()
.unwrap()
.data()
.as_ref()
.map(|uvs| f(&uvs[..]));
}
#[inline]
pub fn set_color(&mut self, color: Color) {
self.data.color = color;
}
#[inline]
pub fn set_texture_from_file(&mut self, path: &Path, name: &str) {
let texture = TextureManager::get_global_manager(|tm| tm.add(path, name));
self.set_texture(texture)
}
#[inline]
pub fn set_texture_with_name(&mut self, name: &str) {
let texture = TextureManager::get_global_manager(|tm| {
tm.get(name).unwrap_or_else(|| {
panic!("Invalid attempt to use the unregistered texture: {}", name)
})
});
self.set_texture(texture)
}
#[inline]
pub fn set_texture(&mut self, texture: Arc<Texture>) {
self.data.texture = texture
}
#[inline]
pub fn set_metallic(&mut self, metallic: f32) {
self.data.metallic = metallic.clamp(0.0, 1.0);
}
#[inline]
pub fn set_roughness(&mut self, roughness: f32) {
self.data.roughness = roughness.clamp(0.0, 1.0);
}
#[inline]
pub fn set_emissive(&mut self, color: Color) {
self.data.emissive = color;
}
#[inline]
pub fn set_normal_map_from_file(&mut self, path: &Path, name: &str) {
let texture = TextureManager::get_global_manager(|tm| tm.add(path, name));
self.set_normal_map(texture);
}
#[inline]
pub fn set_normal_map(&mut self, texture: Arc<Texture>) {
self.data.normal_map = Some(texture);
}
#[inline]
pub fn clear_normal_map(&mut self) {
self.data.normal_map = None;
}
#[inline]
pub fn set_metallic_roughness_map_from_file(&mut self, path: &Path, name: &str) {
let texture = TextureManager::get_global_manager(|tm| tm.add(path, name));
self.set_metallic_roughness_map(texture);
}
#[inline]
pub fn set_metallic_roughness_map(&mut self, texture: Arc<Texture>) {
self.data.metallic_roughness_map = Some(texture);
}
#[inline]
pub fn clear_metallic_roughness_map(&mut self) {
self.data.metallic_roughness_map = None;
}
#[inline]
pub fn set_ao_map_from_file(&mut self, path: &Path, name: &str) {
let texture = TextureManager::get_global_manager(|tm| tm.add(path, name));
self.set_ao_map(texture);
}
#[inline]
pub fn set_ao_map(&mut self, texture: Arc<Texture>) {
self.data.ao_map = Some(texture);
}
#[inline]
pub fn clear_ao_map(&mut self) {
self.data.ao_map = None;
}
#[inline]
pub fn set_emissive_map_from_file(&mut self, path: &Path, name: &str) {
let texture = TextureManager::get_global_manager(|tm| tm.add(path, name));
self.set_emissive_map(texture);
}
#[inline]
pub fn set_emissive_map(&mut self, texture: Arc<Texture>) {
self.data.emissive_map = Some(texture);
}
#[inline]
pub fn clear_emissive_map(&mut self) {
self.data.emissive_map = None;
}
}