use arrayvec::ArrayVec;
use unlit_ecs::Entity;
use unlit_wgpu::mesh::JointMatrix;
use unlit_wgpu::offset_allocator::Allocation;
use unlit_wgpu::render_attachments::{color_clear, depth_clear, stencil_clear};
use unlit_wgpu::resources::ResourceId;
use unlit_wgpu::scene::MAX_VERTEX_BUFFERS;
use unlit_wgpu::specialize::VertexBufferLayoutDesc;
use crate::bounds::Aabb;
use crate::mesh_source::UnlitPipelineKey;
#[derive(Clone, Debug, PartialEq)]
pub struct Transform {
pub translation: glam::Vec3,
pub rotation: glam::Quat,
pub scale: glam::Vec3,
}
impl Default for Transform {
fn default() -> Self {
Self {
translation: glam::Vec3::ZERO,
rotation: glam::Quat::IDENTITY,
scale: glam::Vec3::ONE,
}
}
}
impl Transform {
#[must_use]
pub fn compute_matrix(&self) -> glam::Affine3A {
glam::Affine3A::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Camera {
pub clip_from_world: glam::Mat4,
pub position: glam::Vec3,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RenderLoadOps {
pub color: wgpu::LoadOp<wgpu::Color>,
pub depth: wgpu::LoadOp<f32>,
pub stencil: wgpu::LoadOp<u32>,
}
impl Default for RenderLoadOps {
fn default() -> Self {
Self {
color: color_clear(),
depth: depth_clear(),
stencil: stencil_clear(),
}
}
}
#[derive(Clone, Debug)]
pub struct GpuMesh {
pub root: ResourceId,
pub vertex_buffers: ArrayVec<(u32, ResourceId), MAX_VERTEX_BUFFERS>,
pub vertex_layout: ArrayVec<(u32, VertexBufferLayoutDesc), MAX_VERTEX_BUFFERS>,
pub index_buffer: Option<(ResourceId, wgpu::IndexFormat)>,
pub count: u32,
pub first: u32,
pub base_vertex: u32,
pub indexed: bool,
pub aabb: Aabb,
pub(crate) vertex_allocation: Option<Allocation>,
pub(crate) index_allocation: Option<Allocation>,
pub metadata_index: u32,
pub bind_group_id: Option<ResourceId>,
pub morph_targets: u32,
pub skinned: bool,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SkinPose {
pub matrices: Vec<JointMatrix>,
}
impl SkinPose {
pub fn new(matrices: impl Into<Vec<JointMatrix>>) -> Self {
Self {
matrices: matrices.into(),
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct MorphWeights {
pub weights: Vec<f32>,
}
impl MorphWeights {
pub fn new(weights: impl Into<Vec<f32>>) -> Self {
Self {
weights: weights.into(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SkinBinding {
pub pose: Entity,
}
impl SkinBinding {
pub const fn new(pose: Entity) -> Self {
Self { pose }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MorphBinding {
pub weights: Entity,
}
impl MorphBinding {
pub const fn new(weights: Entity) -> Self {
Self { weights }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GpuPipeline<Key> {
pub(crate) key: Key,
}
impl<Key> GpuPipeline<Key> {
pub fn new(key: Key) -> Self {
Self { key }
}
pub(crate) fn key(&self) -> &Key {
&self.key
}
}
pub type UnlitPipeline = GpuPipeline<UnlitPipelineKey>;
#[derive(Clone, Debug)]
pub struct GpuMaterial {
pub bind_group_id: ResourceId,
}
impl GpuMaterial {
pub fn sort_key(&self) -> u64 {
self.bind_group_id.index() as u64
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSortedDrawing;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InstanceColor {
pub color: glam::Vec4,
}
impl Default for InstanceColor {
fn default() -> Self {
Self {
color: glam::Vec4::ONE,
}
}
}
impl InstanceColor {
pub const fn new(color: glam::Vec4) -> Self {
Self { color }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_transform_is_identity() {
let t = Transform::default();
assert_eq!(t.compute_matrix(), glam::Affine3A::IDENTITY);
}
#[test]
fn transform_matrix_combines_components() {
let t = Transform {
translation: glam::Vec3::new(1.0, 2.0, 3.0),
rotation: glam::Quat::from_rotation_y(1.5),
scale: glam::Vec3::splat(2.0),
};
let expected = glam::Affine3A::from_scale_rotation_translation(
glam::Vec3::splat(2.0),
glam::Quat::from_rotation_y(1.5),
glam::Vec3::new(1.0, 2.0, 3.0),
);
assert_eq!(t.compute_matrix(), expected);
}
#[test]
fn default_instance_color_is_white() {
assert_eq!(InstanceColor::default().color, glam::Vec4::ONE);
}
#[test]
fn camera_fields_round_trip() {
let c = Camera {
clip_from_world: glam::camera::rh::view::look_at_mat4(
glam::Vec3::new(0.0, 5.0, 10.0),
glam::Vec3::ZERO,
glam::Vec3::Y,
),
position: glam::Vec3::new(0.0, 5.0, 10.0),
};
assert_eq!(c.position, glam::Vec3::new(0.0, 5.0, 10.0));
}
}