use {
super::{LineVertex, Material},
crate::{
color::{AlphaColor, Color},
gpu::{MeshFilter, ModelRef, Pose},
math::{vec3_is_finite, CoordF, Mat4, Sphere, Vec3},
},
std::{marker::PhantomData, num::FpCategory, ops::Range},
};
pub type PointLightIter<'a> = CommandIter<'a, PointLightCommand>;
pub type RectLightIter<'a> = CommandIter<'a, RectLightCommand>;
pub type SpotlightIter<'a> = CommandIter<'a, SpotlightCommand>;
pub type SunlightIter<'a> = CommandIter<'a, SunlightCommand>;
pub enum Command {
Line(LineCommand),
Model(ModelCommand),
PointLight(PointLightCommand),
RectLight(RectLightCommand),
Spotlight(SpotlightCommand),
Sunlight(SunlightCommand),
}
impl Command {
pub(crate) fn as_line(&self) -> Option<&LineCommand> {
match self {
Self::Line(res) => Some(res),
_ => None,
}
}
pub(crate) fn as_model(&self) -> Option<&ModelCommand> {
match self {
Self::Model(res) => Some(res),
_ => None,
}
}
pub(crate) fn as_point_light(&self) -> Option<&PointLightCommand> {
match self {
Self::PointLight(res) => Some(res),
_ => None,
}
}
pub(crate) fn as_rect_light(&self) -> Option<&RectLightCommand> {
match self {
Self::RectLight(res) => Some(res),
_ => None,
}
}
pub(crate) fn as_spotlight(&self) -> Option<&SpotlightCommand> {
match self {
Self::Spotlight(res) => Some(res),
_ => None,
}
}
pub(crate) fn as_sunlight(&self) -> Option<&SunlightCommand> {
match self {
Self::Sunlight(res) => Some(res),
_ => None,
}
}
pub(crate) fn is_line(&self) -> bool {
self.as_line().is_some()
}
pub(crate) fn is_model(&self) -> bool {
self.as_model().is_some()
}
pub(crate) fn is_point_light(&self) -> bool {
self.as_point_light().is_some()
}
pub(crate) fn is_rect_light(&self) -> bool {
self.as_rect_light().is_some()
}
pub(crate) fn is_spotlight(&self) -> bool {
self.as_spotlight().is_some()
}
pub(crate) fn is_sunlight(&self) -> bool {
self.as_sunlight().is_some()
}
pub fn line<S: Into<Vec3>, SC: Into<AlphaColor>, E: Into<Vec3>, EC: Into<AlphaColor>>(
start: S,
start_color: SC,
end: E,
end_color: EC,
) -> Self {
Self::Line(LineCommand {
vertices: [
LineVertex {
color: start_color.into(),
pos: start.into(),
},
LineVertex {
color: end_color.into(),
pos: end.into(),
},
],
})
}
pub fn model<M: Into<Mesh>>(mesh: M, material: Material, transform: Mat4) -> Self {
let mesh = mesh.into();
Self::Model(ModelCommand {
camera_order: f32::NAN,
material,
mesh_filter: mesh.filter,
model: mesh.model,
pose: mesh.pose,
transform,
})
}
pub fn point_light(center: Vec3, color: Color, lumens: f32, radius: f32) -> Self {
Self::PointLight(PointLightCommand {
center,
color,
lumens,
radius,
})
}
pub fn spotlight(
color: Color,
lumens: f32,
position: Vec3,
normal: Vec3,
range: Range<f32>,
cone_angle: f32,
penumbra_angle: f32,
) -> Self {
assert_eq!(lumens.classify(), FpCategory::Normal);
assert!(vec3_is_finite(position));
assert!(vec3_is_finite(normal));
assert_eq!(range.start.classify(), FpCategory::Normal);
assert_eq!(range.end.classify(), FpCategory::Normal);
assert!(range.start < range.end);
assert_eq!(cone_angle.classify(), FpCategory::Normal);
assert!(penumbra_angle >= 0.0);
assert!(cone_angle + penumbra_angle < 180.0);
let half = cone_angle * 0.5;
let cone_radius = half.tan() * range.end;
let penumbra_radius = (half + penumbra_angle).tan() * range.end;
let top_radius = 0.0;
Self::Spotlight(SpotlightCommand {
color,
cone_radius,
lumens,
normal,
penumbra_radius,
position,
range,
top_radius,
})
}
}
pub struct CommandIter<'a, T> {
__: PhantomData<T>,
cmds: &'a [Command],
idx: usize,
}
impl<'a, T> CommandIter<'a, T> {
pub fn new(cmds: &'a [Command]) -> Self {
Self {
__: PhantomData,
cmds,
idx: 0,
}
}
}
impl<'a> Iterator for CommandIter<'a, PointLightCommand> {
type Item = &'a PointLightCommand;
fn next(&mut self) -> Option<Self::Item> {
self.cmds
.get(self.idx)
.map(|cmd| {
self.idx += 1;
cmd.as_point_light()
})
.unwrap_or_default()
}
}
impl<'a> Iterator for CommandIter<'a, RectLightCommand> {
type Item = &'a RectLightCommand;
fn next(&mut self) -> Option<Self::Item> {
self.cmds
.get(self.idx)
.map(|cmd| {
self.idx += 1;
cmd.as_rect_light()
})
.unwrap_or_default()
}
}
impl<'a> Iterator for CommandIter<'a, SpotlightCommand> {
type Item = &'a SpotlightCommand;
fn next(&mut self) -> Option<Self::Item> {
self.cmds
.get(self.idx)
.map(|cmd| {
self.idx += 1;
cmd.as_spotlight()
})
.unwrap_or_default()
}
}
impl<'a> Iterator for CommandIter<'a, SunlightCommand> {
type Item = &'a SunlightCommand;
fn next(&mut self) -> Option<Self::Item> {
self.cmds
.get(self.idx)
.map(|cmd| {
self.idx += 1;
cmd.as_sunlight()
})
.unwrap_or_default()
}
}
#[derive(Clone, Debug)]
pub struct LineCommand {
pub vertices: [LineVertex; 2],
}
#[derive(Debug)]
pub struct Mesh {
pub filter: Option<MeshFilter>,
pub model: ModelRef,
pub pose: Option<Pose>,
}
impl From<ModelRef> for Mesh {
fn from(model: ModelRef) -> Self {
Self {
filter: None,
model,
pose: None,
}
}
}
impl From<(ModelRef, MeshFilter)> for Mesh {
fn from((model, filter): (ModelRef, MeshFilter)) -> Self {
Self {
filter: Some(filter),
model,
pose: None,
}
}
}
impl From<(ModelRef, Option<MeshFilter>)> for Mesh {
fn from((model, filter): (ModelRef, Option<MeshFilter>)) -> Self {
Self {
filter,
model,
pose: None,
}
}
}
impl From<(ModelRef, Pose)> for Mesh {
fn from((model, pose): (ModelRef, Pose)) -> Self {
Self {
filter: None,
model,
pose: Some(pose),
}
}
}
impl From<(ModelRef, Option<Pose>)> for Mesh {
fn from((model, pose): (ModelRef, Option<Pose>)) -> Self {
Self {
filter: None,
model,
pose,
}
}
}
impl From<(ModelRef, MeshFilter, Pose)> for Mesh {
fn from((model, filter, pose): (ModelRef, MeshFilter, Pose)) -> Self {
Self {
filter: Some(filter),
model,
pose: Some(pose),
}
}
}
impl From<(ModelRef, Option<MeshFilter>, Option<Pose>)> for Mesh {
fn from((model, filter, pose): (ModelRef, Option<MeshFilter>, Option<Pose>)) -> Self {
Self {
filter,
model,
pose,
}
}
}
#[derive(Debug)]
pub struct ModelCommand {
pub(super) camera_order: f32,
pub material: Material,
pub mesh_filter: Option<MeshFilter>,
pub model: ModelRef,
pub pose: Option<Pose>,
pub transform: Mat4,
}
#[derive(Clone, Debug)]
pub struct PointLightCommand {
pub center: Vec3,
pub color: Color,
pub lumens: f32,
pub radius: f32,
}
#[derive(Clone, Debug)]
pub struct RectLightCommand {
pub color: Color,
pub dims: CoordF,
pub lumens: f32,
pub normal: Vec3,
pub position: Vec3,
pub radius: f32,
pub range: f32,
}
impl RectLightCommand {
pub(self) fn bounds(&self) -> Sphere {
todo!();
}
}
#[derive(Clone, Debug)]
pub struct SpotlightCommand {
pub color: Color,
pub cone_radius: f32,
pub lumens: f32,
pub normal: Vec3,
pub penumbra_radius: f32,
pub position: Vec3,
pub range: Range<f32>,
pub top_radius: f32,
}
#[derive(Clone, Debug)]
pub struct SunlightCommand {
pub color: Color,
pub lumens: f32,
pub normal: Vec3,
}