use std::convert::TryFrom;
use std::ops::Deref;
use web_sys::WebGl2RenderingContext as Gl;
use crate::runtime::state::ContextUpdate;
use crate::runtime::Connection;
#[derive(Clone, PartialEq, Debug)]
pub enum PrimitiveAssembly {
Points,
Lines(LineWidth),
LineStrip(LineWidth),
LineLoop(LineWidth),
Triangles {
winding_order: WindingOrder,
face_culling: CullingMode,
},
TriangleStrip {
winding_order: WindingOrder,
face_culling: CullingMode,
},
TriangleFan {
winding_order: WindingOrder,
face_culling: CullingMode,
},
}
impl PrimitiveAssembly {
pub(crate) fn transform_feedback_mode(&self) -> u32 {
match self {
PrimitiveAssembly::Points => Gl::POINTS,
PrimitiveAssembly::Lines(_) => Gl::LINES,
PrimitiveAssembly::LineStrip(_) => Gl::LINES,
PrimitiveAssembly::LineLoop(_) => Gl::LINES,
PrimitiveAssembly::Triangles { .. } => Gl::TRIANGLES,
PrimitiveAssembly::TriangleStrip { .. } => Gl::TRIANGLES,
PrimitiveAssembly::TriangleFan { .. } => Gl::TRIANGLES,
}
}
pub(crate) fn topology(&self) -> Topology {
match self {
PrimitiveAssembly::Points => Topology::Point,
PrimitiveAssembly::Lines(_) => Topology::Line,
PrimitiveAssembly::LineStrip(_) => Topology::LineStrip,
PrimitiveAssembly::LineLoop(_) => Topology::LineLoop,
PrimitiveAssembly::Triangles { .. } => Topology::Triangle,
PrimitiveAssembly::TriangleStrip { .. } => Topology::TriangleStrip,
PrimitiveAssembly::TriangleFan { .. } => Topology::TriangleFan,
}
}
pub(crate) fn line_width(&self) -> Option<LineWidth> {
match self {
PrimitiveAssembly::Lines(line_width) => Some(*line_width),
PrimitiveAssembly::LineStrip(line_width) => Some(*line_width),
PrimitiveAssembly::LineLoop(line_width) => Some(*line_width),
_ => None,
}
}
pub(crate) fn face_culling(&self) -> Option<CullingMode> {
match self {
PrimitiveAssembly::Triangles { face_culling, .. } => Some(*face_culling),
PrimitiveAssembly::TriangleStrip { face_culling, .. } => Some(*face_culling),
PrimitiveAssembly::TriangleFan { face_culling, .. } => Some(*face_culling),
_ => None,
}
}
pub(crate) fn winding_order(&self) -> Option<WindingOrder> {
match self {
PrimitiveAssembly::Triangles { winding_order, .. } => Some(*winding_order),
PrimitiveAssembly::TriangleStrip { winding_order, .. } => Some(*winding_order),
PrimitiveAssembly::TriangleFan { winding_order, .. } => Some(*winding_order),
_ => None,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum Topology {
Point,
Line,
Triangle,
LineStrip,
LineLoop,
TriangleStrip,
TriangleFan,
}
impl Topology {
pub(crate) fn id(&self) -> u32 {
match self {
Topology::Point => Gl::POINTS,
Topology::Line => Gl::LINES,
Topology::Triangle => Gl::TRIANGLES,
Topology::LineStrip => Gl::LINE_STRIP,
Topology::LineLoop => Gl::LINE_LOOP,
Topology::TriangleStrip => Gl::TRIANGLE_STRIP,
Topology::TriangleFan => Gl::TRIANGLE_FAN,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct LineWidth {
value: f32,
}
impl LineWidth {
pub(crate) fn apply(&self, connection: &mut Connection) {
let (gl, state) = unsafe { connection.unpack_mut() };
state.set_line_width(self.value).apply(gl).unwrap();
}
}
impl TryFrom<f32> for LineWidth {
type Error = InvalidLineWidth;
fn try_from(value: f32) -> Result<Self, Self::Error> {
if value == std::f32::NAN {
Err(InvalidLineWidth::NaN)
} else if value < 0.0 {
Err(InvalidLineWidth::Negative)
} else {
Ok(LineWidth { value })
}
}
}
impl Default for LineWidth {
fn default() -> Self {
LineWidth { value: 1.0 }
}
}
impl Deref for LineWidth {
type Target = f32;
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Debug)]
pub enum InvalidLineWidth {
NaN,
Negative,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum WindingOrder {
Clockwise,
CounterClockwise,
}
impl WindingOrder {
pub(crate) fn apply(&self, connection: &mut Connection) {
let (gl, state) = unsafe { connection.unpack_mut() };
state.set_front_face(*self).apply(gl).unwrap();
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CullingMode {
None,
Front,
Back,
Both,
}
impl CullingMode {
pub(crate) fn apply(&self, connection: &mut Connection) {
let (gl, state) = unsafe { connection.unpack_mut() };
state.set_cull_face(*self).apply(gl).unwrap();
}
}