use crate::list::{ImageFormat, RenderPassRunRate};
use glam::{Mat3, Mat4, Vec2, Vec3, Vec3A, Vec4};
use itertools::Itertools;
use std::mem;
use wgpu::TextureFormat;
pub use wgpu::{Color as ClearColor, LoadOp as PipelineLoadOp};
#[macro_export]
#[doc(hidden)]
macro_rules! declare_handle {
($($name:ident),*) => {$(
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct $name(pub(crate) usize);
impl $name {
pub fn get(&self) -> usize {
self.0
}
}
)*};
}
declare_handle!(
MeshHandle,
TextureHandle,
MaterialHandle,
ObjectHandle,
DirectionalLightHandle,
ShaderHandle,
PipelineHandle
);
macro_rules! changeable_struct {
($(#[$outer:meta])* pub struct $name:ident <- nodefault $name_change:ident { $($field_vis:vis $field_name:ident : $field_type:ty),* $(,)? } ) => {
$(#[$outer])*
pub struct $name {
$(
$field_vis $field_name : $field_type
),*
}
impl $name {
pub fn update_from_changes(&mut self, change: $name_change) {
$(
if let Some(inner) = change.$field_name {
self.$field_name = inner;
}
);*
}
}
$(#[$outer])*
pub struct $name_change {
$(
$field_vis $field_name : Option<$field_type>
),*
}
};
($(#[$outer:meta])* pub struct $name:ident <- $name_change:ident { $($field_vis:vis $field_name:ident : $field_type:ty),* $(,)? } ) => {
$(#[$outer])*
pub struct $name {
$(
$field_vis $field_name : $field_type
),*
}
impl $name {
pub fn update_from_changes(&mut self, change: $name_change) {
$(
if let Some(inner) = change.$field_name {
self.$field_name = inner;
}
);*
}
}
$(#[$outer])*
#[derive(Default)]
pub struct $name_change {
$(
$field_vis $field_name : Option<$field_type>
),*
}
};
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct AffineTransform {
pub transform: Mat4,
}
unsafe impl bytemuck::Zeroable for AffineTransform {}
unsafe impl bytemuck::Pod for AffineTransform {}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RendererTextureFormat {
Rgba8Srgb,
Rgba8Linear,
Bc1Linear,
Bc1Srgb,
Bc3Linear,
Bc3Srgb,
Bc4Linear,
Bc5Normal,
Bc6Signed,
Bc6Unsigned,
Bc7Linear,
Bc7Srgb,
}
impl RendererTextureFormat {
pub fn pixels_per_block(&self) -> u32 {
match self {
RendererTextureFormat::Rgba8Srgb | RendererTextureFormat::Rgba8Linear => 1,
RendererTextureFormat::Bc1Linear
| RendererTextureFormat::Bc1Srgb
| RendererTextureFormat::Bc3Linear
| RendererTextureFormat::Bc3Srgb
| RendererTextureFormat::Bc4Linear
| RendererTextureFormat::Bc5Normal
| RendererTextureFormat::Bc6Signed
| RendererTextureFormat::Bc6Unsigned
| RendererTextureFormat::Bc7Linear
| RendererTextureFormat::Bc7Srgb => 4,
}
}
pub fn bytes_per_block(&self) -> u32 {
match self {
RendererTextureFormat::Rgba8Srgb | RendererTextureFormat::Rgba8Linear => 4,
RendererTextureFormat::Bc1Linear | RendererTextureFormat::Bc1Srgb | RendererTextureFormat::Bc4Linear => 8,
RendererTextureFormat::Bc3Linear
| RendererTextureFormat::Bc3Srgb
| RendererTextureFormat::Bc5Normal
| RendererTextureFormat::Bc6Signed
| RendererTextureFormat::Bc6Unsigned
| RendererTextureFormat::Bc7Linear
| RendererTextureFormat::Bc7Srgb => 16,
}
}
}
impl From<RendererTextureFormat> for wgpu::TextureFormat {
fn from(other: RendererTextureFormat) -> Self {
match other {
RendererTextureFormat::Rgba8Linear => TextureFormat::Rgba8Unorm,
RendererTextureFormat::Rgba8Srgb => TextureFormat::Rgba8UnormSrgb,
RendererTextureFormat::Bc1Linear => TextureFormat::Bc1RgbaUnorm,
RendererTextureFormat::Bc1Srgb => TextureFormat::Bc1RgbaUnormSrgb,
RendererTextureFormat::Bc3Linear => TextureFormat::Bc3RgbaUnorm,
RendererTextureFormat::Bc3Srgb => TextureFormat::Bc3RgbaUnormSrgb,
RendererTextureFormat::Bc4Linear => TextureFormat::Bc4RUnorm,
RendererTextureFormat::Bc5Normal => TextureFormat::Bc5RgUnorm,
RendererTextureFormat::Bc6Signed => TextureFormat::Bc6hRgbSfloat,
RendererTextureFormat::Bc6Unsigned => TextureFormat::Bc6hRgbUfloat,
RendererTextureFormat::Bc7Linear => TextureFormat::Bc7RgbaUnorm,
RendererTextureFormat::Bc7Srgb => TextureFormat::Bc7RgbaUnormSrgb,
}
}
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct InterleavedModelVertex {
pub position: Vec3,
pub normal: Vec3,
pub uv: Vec2,
pub color: [u8; 4],
pub material_index: u32,
}
#[derive(Debug, Default)]
pub struct MeshBuilder {
vertex_positions: Vec<Vec3>,
vertex_normals: Option<Vec<Vec3>>,
vertex_tangents: Option<Vec<Vec3>>,
vertex_uvs: Option<Vec<Vec2>>,
vertex_colors: Option<Vec<[u8; 4]>>,
vertex_material_indices: Option<Vec<u32>>,
vertex_count: usize,
indices: Option<Vec<u32>>,
right_handed: bool,
}
impl MeshBuilder {
pub fn new(vertex_positions: Vec<Vec3>) -> Self {
let me = Self {
vertex_count: vertex_positions.len(),
vertex_positions,
..Self::default()
};
assert_ne!(me.vertex_positions.len(), 0, "Cannot have a mesh with zero vertices");
me
}
fn validate_len(&self, len: usize) {
assert_eq!(self.vertex_count, len)
}
pub fn with_vertex_normals(mut self, normals: Vec<Vec3>) -> Self {
self.validate_len(normals.len());
self.vertex_normals = Some(normals);
self
}
pub fn with_vertex_tangents(mut self, tangents: Vec<Vec3>) -> Self {
self.validate_len(tangents.len());
self.vertex_tangents = Some(tangents);
self
}
pub fn with_vertex_uvs(mut self, uvs: Vec<Vec2>) -> Self {
self.validate_len(uvs.len());
self.vertex_uvs = Some(uvs);
self
}
pub fn with_vertex_colors(mut self, colors: Vec<[u8; 4]>) -> Self {
self.validate_len(colors.len());
self.vertex_colors = Some(colors);
self
}
pub fn with_vertex_material_indices(mut self, material_indices: Vec<u32>) -> Self {
self.validate_len(material_indices.len());
self.vertex_material_indices = Some(material_indices);
self
}
pub fn with_indices(mut self, indices: Vec<u32>) -> Self {
assert_ne!(indices.len(), 0, "Cannot have a mesh with zero indices");
self.indices = Some(indices);
self
}
pub fn with_right_handed(mut self) -> Self {
self.right_handed = true;
self
}
pub fn build(self) -> Mesh {
let length = self.vertex_count;
debug_assert_ne!(length, 0, "Length should be guarded by validation");
let has_normals = self.vertex_normals.is_some();
let has_tangents = self.vertex_tangents.is_some();
let mut mesh = Mesh {
vertex_positions: self.vertex_positions,
vertex_normals: self.vertex_normals.unwrap_or_else(|| vec![Vec3::ZERO; length]),
vertex_tangents: self.vertex_tangents.unwrap_or_else(|| vec![Vec3::ZERO; length]),
vertex_uvs: self.vertex_uvs.unwrap_or_else(|| vec![Vec2::ZERO; length]),
vertex_colors: self.vertex_colors.unwrap_or_else(|| vec![[0; 4]; length]),
vertex_material_indices: self.vertex_material_indices.unwrap_or_else(|| vec![0; length]),
indices: self.indices.unwrap_or_else(|| (0..length as u32).collect()),
};
if self.right_handed {
mesh.flip_winding_order();
}
if !has_normals {
mesh.calculate_normals();
}
if !has_tangents {
mesh.calculate_tangents();
}
mesh
}
}
#[derive(Debug, Default, Clone)]
pub struct Mesh {
pub vertex_positions: Vec<Vec3>,
pub vertex_normals: Vec<Vec3>,
pub vertex_tangents: Vec<Vec3>,
pub vertex_uvs: Vec<Vec2>,
pub vertex_colors: Vec<[u8; 4]>,
pub vertex_material_indices: Vec<u32>,
pub indices: Vec<u32>,
}
impl Mesh {
pub fn validate(&self) -> bool {
[
self.vertex_positions.len(),
self.vertex_normals.len(),
self.vertex_tangents.len(),
self.vertex_uvs.len(),
self.vertex_colors.len(),
self.vertex_material_indices.len(),
]
.iter()
.all_equal()
}
pub fn calculate_normals(&mut self) {
Self::calculate_normals_for_buffers(&mut self.vertex_normals, &self.vertex_positions, &self.indices);
}
pub fn calculate_normals_for_buffers(normals: &mut [Vec3], positions: &[Vec3], indices: &[u32]) {
assert_eq!(normals.len(), positions.len());
for norm in normals.iter_mut() {
*norm = Vec3::ZERO;
}
for idx in indices.chunks_exact(3) {
let (idx0, idx1, idx2) = match *idx {
[idx0, idx1, idx2] => (idx0, idx1, idx2),
_ => unsafe { std::hint::unreachable_unchecked() },
};
let pos1 = positions[idx0 as usize];
let pos2 = positions[idx1 as usize];
let pos3 = positions[idx2 as usize];
let edge1 = pos2 - pos1;
let edge2 = pos3 - pos1;
let normal = edge1.cross(edge2);
unsafe {
*normals.get_unchecked_mut(idx0 as usize) += normal;
*normals.get_unchecked_mut(idx1 as usize) += normal;
*normals.get_unchecked_mut(idx2 as usize) += normal;
}
}
for normal in normals.iter_mut() {
*normal = normal.normalize();
}
}
pub fn calculate_tangents(&mut self) {
Self::calculate_tangents_for_buffers(
&mut self.vertex_tangents,
&self.vertex_positions,
&self.vertex_normals,
&self.vertex_uvs,
&self.indices,
);
}
fn calculate_tangents_for_buffers(
tangents: &mut [Vec3],
positions: &[Vec3],
normals: &[Vec3],
uvs: &[Vec2],
indices: &[u32],
) {
assert_eq!(tangents.len(), positions.len());
assert_eq!(uvs.len(), positions.len());
for tan in tangents.iter_mut() {
*tan = Vec3::ZERO;
}
for idx in indices.chunks_exact(3) {
let (idx0, idx1, idx2) = match *idx {
[idx0, idx1, idx2] => (idx0, idx1, idx2),
_ => unsafe { std::hint::unreachable_unchecked() },
};
let pos1 = positions[idx0 as usize];
let pos2 = positions[idx1 as usize];
let pos3 = positions[idx2 as usize];
let (tex1, tex2, tex3) = unsafe {
(
*uvs.get_unchecked(idx0 as usize),
*uvs.get_unchecked(idx1 as usize),
*uvs.get_unchecked(idx2 as usize),
)
};
let edge1 = pos2 - pos1;
let edge2 = pos3 - pos1;
let uv1 = tex2 - tex1;
let uv2 = tex3 - tex1;
let r = 1.0 / (uv1.x * uv2.y - uv1.y * uv2.x);
let tangent = Vec3::new(
((edge1.x * uv2.y) - (edge2.x * uv1.y)) * r,
((edge1.y * uv2.y) - (edge2.y * uv1.y)) * r,
((edge1.z * uv2.y) - (edge2.z * uv1.y)) * r,
);
unsafe {
*tangents.get_unchecked_mut(idx0 as usize) += tangent;
*tangents.get_unchecked_mut(idx1 as usize) += tangent;
*tangents.get_unchecked_mut(idx2 as usize) += tangent;
}
}
for (tan, norm) in tangents.iter_mut().zip(normals) {
let t = *tan - (*norm * norm.dot(*tan));
*tan = t.normalize();
}
}
pub fn flip_winding_order(&mut self) {
for indices in self.indices.chunks_exact_mut(3) {
if let [left, _, right] = indices {
mem::swap(left, right);
} else {
unsafe { std::hint::unreachable_unchecked() }
}
}
}
}
#[derive(Debug, Clone)]
pub struct Texture {
pub data: Vec<u8>,
pub format: RendererTextureFormat,
pub width: u32,
pub height: u32,
pub label: Option<String>,
pub mip_levels: u32,
}
bitflags::bitflags! {
pub(crate) struct MaterialFlags : u32 {
const ALBEDO_ACTIVE = 0b0000_0000_0000_0001;
const ALBEDO_BLEND = 0b0000_0000_0000_0010;
const ALBEDO_VERTEX_SRGB = 0b0000_0000_0000_0100;
const ALPHA_CUTOUT = 0b0000_0000_0000_1000;
const BICOMPONENT_NORMAL = 0b0000_0000_0001_0000;
const SWIZZLED_NORMAL = 0b0000_0000_0010_0000;
const AOMR_GLTF_COMBINED = 0b0000_0000_0100_0000;
const AOMR_GLTF_SPLIT = 0b0000_0000_1000_0000;
const AOMR_BW_SPLIT = 0b0000_0001_0000_0000;
const CC_GLTF_COMBINED = 0b0000_0010_0000_0000;
const CC_GLTF_SPLIT = 0b0000_0100_0000_0000;
const CC_BW_SPLIT = 0b0000_1000_0000_0000;
const UNLIT = 0b0001_0000_0000_0000;
const NEAREST = 0b0010_0000_0000_0000;
}
}
#[derive(Debug, Copy, Clone)]
pub enum AlbedoComponent {
None,
Vertex {
srgb: bool,
},
Value(Vec4),
ValueVertex {
value: Vec4,
srgb: bool,
},
Texture(TextureHandle),
TextureVertex {
handle: TextureHandle,
srgb: bool,
},
TextureValue { handle: TextureHandle, value: Vec4 },
}
impl Default for AlbedoComponent {
fn default() -> Self {
Self::None
}
}
impl AlbedoComponent {
pub(crate) fn to_value(&self) -> Vec4 {
match *self {
Self::Value(value) => value,
Self::ValueVertex { value, .. } => value,
Self::TextureValue { value, .. } => value,
_ => Vec4::splat(1.0),
}
}
pub(crate) fn to_flags(&self) -> MaterialFlags {
match *self {
Self::None => MaterialFlags::empty(),
Self::Value(_) | Self::Texture(_) | Self::TextureValue { .. } => MaterialFlags::ALBEDO_ACTIVE,
Self::Vertex { srgb: false }
| Self::ValueVertex { srgb: false, .. }
| Self::TextureVertex { srgb: false, .. } => MaterialFlags::ALBEDO_ACTIVE | MaterialFlags::ALBEDO_BLEND,
Self::Vertex { srgb: true }
| Self::ValueVertex { srgb: true, .. }
| Self::TextureVertex { srgb: true, .. } => {
MaterialFlags::ALBEDO_ACTIVE | MaterialFlags::ALBEDO_BLEND | MaterialFlags::ALBEDO_VERTEX_SRGB
}
}
}
pub(crate) fn is_texture(&self) -> bool {
matches!(
*self,
Self::Texture(..) | Self::TextureVertex { .. } | Self::TextureValue { .. }
)
}
pub(crate) fn to_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::None | Self::Vertex { .. } | Self::Value(_) | Self::ValueVertex { .. } => None,
Self::Texture(handle) | Self::TextureVertex { handle, .. } | Self::TextureValue { handle, .. } => {
Some(func(handle))
}
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum MaterialComponent<T> {
None,
Value(T),
Texture(TextureHandle),
TextureValue { handle: TextureHandle, value: T },
}
impl<T> Default for MaterialComponent<T> {
fn default() -> Self {
Self::None
}
}
impl<T: Copy> MaterialComponent<T> {
pub(crate) fn to_value(&self, default: T) -> T {
match *self {
Self::Value(value) | Self::TextureValue { value, .. } => value,
Self::None | Self::Texture(_) => default,
}
}
pub(crate) fn is_texture(&self) -> bool {
matches!(*self, Self::Texture(..) | Self::TextureValue { .. })
}
pub(crate) fn to_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::None | Self::Value(_) => None,
Self::Texture(handle) | Self::TextureValue { handle, .. } => Some(func(handle)),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum NormalTexture {
None,
Tricomponent(TextureHandle),
Bicomponent(TextureHandle),
BicomponentSwizzled(TextureHandle),
}
impl Default for NormalTexture {
fn default() -> Self {
Self::None
}
}
impl NormalTexture {
pub(crate) fn to_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::None => None,
Self::Tricomponent(handle) | Self::Bicomponent(handle) | Self::BicomponentSwizzled(handle) => {
Some(func(handle))
}
}
}
pub(crate) fn to_flags(&self) -> MaterialFlags {
match self {
Self::None => MaterialFlags::empty(),
Self::Tricomponent(..) => MaterialFlags::empty(),
Self::Bicomponent(..) => MaterialFlags::BICOMPONENT_NORMAL,
Self::BicomponentSwizzled(..) => MaterialFlags::BICOMPONENT_NORMAL | MaterialFlags::SWIZZLED_NORMAL,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum AoMRTextures {
None,
GltfCombined {
texture: Option<TextureHandle>,
},
GltfSplit {
ao_texture: Option<TextureHandle>,
mr_texture: Option<TextureHandle>,
},
BWSplit {
ao_texture: Option<TextureHandle>,
m_texture: Option<TextureHandle>,
r_texture: Option<TextureHandle>,
},
}
impl AoMRTextures {
pub(crate) fn to_roughness_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::GltfCombined { texture: Some(texture) } => Some(func(texture)),
Self::GltfSplit {
mr_texture: Some(texture),
..
} => Some(func(texture)),
Self::BWSplit {
r_texture: Some(texture),
..
} => Some(func(texture)),
_ => None,
}
}
pub(crate) fn to_metallic_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::GltfCombined { .. } => None,
Self::GltfSplit { .. } => None,
Self::BWSplit {
m_texture: Some(texture),
..
} => Some(func(texture)),
_ => None,
}
}
pub(crate) fn to_ao_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::GltfCombined { .. } => None,
Self::GltfSplit {
ao_texture: Some(texture),
..
} => Some(func(texture)),
Self::BWSplit {
ao_texture: Some(texture),
..
} => Some(func(texture)),
_ => None,
}
}
pub(crate) fn to_flags(&self) -> MaterialFlags {
match self {
Self::GltfCombined { .. } => MaterialFlags::AOMR_GLTF_COMBINED,
Self::GltfSplit { .. } => MaterialFlags::AOMR_GLTF_SPLIT,
Self::BWSplit { .. } => MaterialFlags::AOMR_BW_SPLIT,
Self::None => MaterialFlags::AOMR_GLTF_COMBINED,
}
}
}
impl Default for AoMRTextures {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Copy, Clone)]
pub enum ClearcoatTextures {
GltfCombined {
texture: Option<TextureHandle>,
},
GltfSplit {
clearcoat_texture: Option<TextureHandle>,
clearcoat_roughness_texture: Option<TextureHandle>,
},
BWSplit {
clearcoat_texture: Option<TextureHandle>,
clearcoat_roughness_texture: Option<TextureHandle>,
},
None,
}
impl ClearcoatTextures {
pub(crate) fn to_clearcoat_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::GltfCombined { texture: Some(texture) } => Some(func(texture)),
Self::GltfSplit {
clearcoat_texture: Some(texture),
..
} => Some(func(texture)),
Self::BWSplit {
clearcoat_texture: Some(texture),
..
} => Some(func(texture)),
_ => None,
}
}
pub(crate) fn to_clearcoat_roughness_texture<Func, Out>(&self, func: Func) -> Option<Out>
where
Func: FnOnce(TextureHandle) -> Out,
{
match *self {
Self::GltfCombined { .. } => None,
Self::GltfSplit {
clearcoat_roughness_texture: Some(texture),
..
} => Some(func(texture)),
Self::BWSplit {
clearcoat_roughness_texture: Some(texture),
..
} => Some(func(texture)),
_ => None,
}
}
pub(crate) fn to_flags(&self) -> MaterialFlags {
match self {
Self::GltfCombined { .. } => MaterialFlags::CC_GLTF_COMBINED,
Self::GltfSplit { .. } => MaterialFlags::CC_GLTF_SPLIT,
Self::BWSplit { .. } => MaterialFlags::CC_BW_SPLIT,
Self::None => MaterialFlags::CC_GLTF_COMBINED,
}
}
}
impl Default for ClearcoatTextures {
fn default() -> Self {
Self::None
}
}
changeable_struct! {
#[derive(Debug, Default, Copy, Clone)]
pub struct Material <- nodefault MaterialChange {
pub albedo: AlbedoComponent,
pub normal: NormalTexture,
pub aomr_textures: AoMRTextures,
pub ao_factor: Option<f32>,
pub metallic_factor: Option<f32>,
pub roughness_factor: Option<f32>,
pub clearcoat_textures: ClearcoatTextures,
pub clearcoat_factor: Option<f32>,
pub clearcoat_roughness_factor: Option<f32>,
pub emissive: MaterialComponent<Vec3>,
pub reflectance: MaterialComponent<f32>,
pub anisotropy: MaterialComponent<f32>,
pub alpha_cutout: Option<f32>,
pub transform: Mat3,
pub unlit: bool,
pub nearest: bool,
}
}
#[derive(Debug, Clone)]
pub struct Object {
pub mesh: MeshHandle,
pub material: MaterialHandle,
pub transform: AffineTransform,
}
#[derive(Debug, Default, Copy, Clone)]
pub struct Camera {
pub projection: CameraProjection,
pub location: Vec3A,
}
#[derive(Debug, Copy, Clone)]
pub enum CameraProjection {
Orthographic {
size: Vec3A,
direction: Vec3A,
},
Projection {
vfov: f32,
near: f32,
pitch: f32,
yaw: f32,
},
}
impl CameraProjection {
pub fn from_orthographic_direction(direction: Vec3A) -> Self {
Self::Orthographic {
size: Vec3A::new(100.0, 100.0, 200.0),
direction,
}
}
}
impl Default for CameraProjection {
fn default() -> Self {
Self::Projection {
vfov: 60.0,
near: 0.1,
pitch: 0.0,
yaw: 0.0,
}
}
}
changeable_struct! {
#[derive(Debug, Copy, Clone)]
pub struct DirectionalLight <- DirectionalLightChange {
pub color: Vec3,
pub intensity: f32,
pub direction: Vec3,
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PipelineInputType {
FullscreenTriangle,
Models3d,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PipelineBindingType {
GeneralData,
ObjectData,
GPUMaterial,
CPUMaterial,
CameraData,
GPU2DTextures,
GPUCubeTextures,
ShadowTexture,
SkyboxTexture,
Custom2DTexture { count: usize },
CustomCubeTexture { count: usize },
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DepthCompare {
Closer,
CloserEqual,
Equal,
Further,
FurtherEqual,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PipelineOutputAttachment {
pub format: ImageFormat,
pub write: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PipelineDepthState {
pub format: ImageFormat,
pub compare: DepthCompare,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pipeline {
pub run_rate: RenderPassRunRate,
pub input: PipelineInputType,
pub outputs: Vec<PipelineOutputAttachment>,
pub depth: Option<PipelineDepthState>,
pub vertex: ShaderHandle,
pub fragment: Option<ShaderHandle>,
pub bindings: Vec<PipelineBindingType>,
pub samples: u8,
}