rafx-api 0.0.16

Rendering framework built on an extensible asset pipeline
Documentation
use crate::gles3::gles3_bindings;
use crate::gles3::gles3_bindings::types::GLenum;
use crate::{
    RafxAddressMode, RafxBlendFactor, RafxBlendOp, RafxBlendState, RafxColorFlags, RafxCompareOp,
    RafxCullMode, RafxDepthState, RafxFilterType, RafxFrontFace, RafxMemoryUsage,
    RafxPrimitiveTopology, RafxRasterizerState, RafxResult, RafxStencilOp,
};

impl RafxFilterType {
    pub fn gles3_filter_type(self) -> GLenum {
        match self {
            RafxFilterType::Nearest => gles3_bindings::NEAREST,
            RafxFilterType::Linear => gles3_bindings::LINEAR,
        }
    }
}

impl RafxAddressMode {
    pub fn gles3_address_mode(self) -> Option<GLenum> {
        match self {
            RafxAddressMode::Mirror => Some(gles3_bindings::MIRRORED_REPEAT),
            RafxAddressMode::Repeat => Some(gles3_bindings::REPEAT),
            RafxAddressMode::ClampToEdge => Some(gles3_bindings::CLAMP_TO_EDGE),
            //TODO: GL ES 3.0 support
            // requires GL_OES_texture_border_clamp
            //RafxAddressMode::ClampToBorder => gles30::CLAMP_TO_BORDER,
            RafxAddressMode::ClampToBorder => None,
        }
    }
}

impl RafxPrimitiveTopology {
    pub fn gles3_topology(self) -> Option<GLenum> {
        match self {
            RafxPrimitiveTopology::PointList => Some(gles3_bindings::POINTS),
            RafxPrimitiveTopology::LineList => Some(gles3_bindings::LINES),
            RafxPrimitiveTopology::LineStrip => Some(gles3_bindings::LINE_STRIP),
            RafxPrimitiveTopology::TriangleList => Some(gles3_bindings::TRIANGLES),
            RafxPrimitiveTopology::TriangleStrip => Some(gles3_bindings::TRIANGLE_STRIP),
            RafxPrimitiveTopology::PatchList => None,
        }
    }
}

impl RafxMemoryUsage {
    pub fn gles3_usage(self) -> Option<GLenum> {
        match self {
            RafxMemoryUsage::Unknown => None,
            RafxMemoryUsage::GpuOnly => Some(gles3_bindings::STATIC_DRAW),
            RafxMemoryUsage::CpuOnly => Some(gles3_bindings::NONE),
            RafxMemoryUsage::CpuToGpu => Some(gles3_bindings::DYNAMIC_DRAW),
            RafxMemoryUsage::GpuToCpu => Some(gles3_bindings::STREAM_DRAW),
        }
    }
}

impl RafxCullMode {
    pub fn gles3_cull_mode(self) -> GLenum {
        match self {
            RafxCullMode::None => gles3_bindings::NONE,
            RafxCullMode::Back => gles3_bindings::BACK,
            RafxCullMode::Front => gles3_bindings::FRONT,
        }
    }
}

impl RafxFrontFace {
    pub fn gles3_front_face(
        self,
        reversed: bool,
    ) -> GLenum {
        if reversed {
            match self {
                RafxFrontFace::CounterClockwise => gles3_bindings::CW,
                RafxFrontFace::Clockwise => gles3_bindings::CCW,
            }
        } else {
            match self {
                RafxFrontFace::CounterClockwise => gles3_bindings::CCW,
                RafxFrontFace::Clockwise => gles3_bindings::CW,
            }
        }
    }
}

impl RafxCompareOp {
    pub fn gles3_compare_op(self) -> GLenum {
        match self {
            RafxCompareOp::Never => gles3_bindings::NEVER,
            RafxCompareOp::Less => gles3_bindings::LESS,
            RafxCompareOp::Equal => gles3_bindings::EQUAL,
            RafxCompareOp::LessOrEqual => gles3_bindings::LEQUAL,
            RafxCompareOp::Greater => gles3_bindings::GREATER,
            RafxCompareOp::NotEqual => gles3_bindings::NOTEQUAL,
            RafxCompareOp::GreaterOrEqual => gles3_bindings::GEQUAL,
            RafxCompareOp::Always => gles3_bindings::ALWAYS,
        }
    }
}

impl RafxStencilOp {
    pub fn gles3_stencil_op(self) -> GLenum {
        match self {
            RafxStencilOp::Keep => gles3_bindings::KEEP,
            RafxStencilOp::Zero => gles3_bindings::ZERO,
            RafxStencilOp::Replace => gles3_bindings::REPLACE,
            RafxStencilOp::IncrementAndClamp => gles3_bindings::INCR,
            RafxStencilOp::DecrementAndClamp => gles3_bindings::DECR,
            RafxStencilOp::Invert => gles3_bindings::INVERT,
            RafxStencilOp::IncrementAndWrap => gles3_bindings::INCR_WRAP,
            RafxStencilOp::DecrementAndWrap => gles3_bindings::INCR_WRAP,
        }
    }
}

//TODO: Some fields in RafxRasterizerState are not handled!
#[derive(Debug)]
pub struct Gles3RasterizerState {
    pub cull_mode: GLenum,
    pub front_face: GLenum,
    pub scissor_test: bool,
}

impl From<&RafxRasterizerState> for Gles3RasterizerState {
    fn from(state: &RafxRasterizerState) -> Self {
        Gles3RasterizerState {
            cull_mode: state.cull_mode.gles3_cull_mode(),
            front_face: state.front_face.gles3_front_face(false),
            scissor_test: state.scissor,
        }
    }
}

//TODO: Some fields in RafxDepthState are not handled!
#[derive(Debug)]
pub struct Gles3DepthStencilState {
    pub depth_test_enable: bool,
    pub depth_write_enable: bool,
    pub depth_compare_op: GLenum,
    pub stencil_test_enable: bool,
    //pub stencil_read_mask: u8,
    pub stencil_write_mask: u8,
    pub front_depth_fail_op: GLenum,
    pub front_stencil_compare_op: GLenum,
    pub front_stencil_fail_op: GLenum,
    pub front_stencil_pass_op: GLenum,
    pub back_depth_fail_op: GLenum,
    pub back_stencil_compare_op: GLenum,
    pub back_stencil_fail_op: GLenum,
    pub back_stencil_pass_op: GLenum,
}

impl From<&RafxDepthState> for Gles3DepthStencilState {
    fn from(state: &RafxDepthState) -> Self {
        Gles3DepthStencilState {
            depth_test_enable: state.depth_test_enable,
            depth_write_enable: state.depth_write_enable,
            depth_compare_op: state.depth_compare_op.gles3_compare_op(),
            stencil_test_enable: state.stencil_test_enable,
            //stencil_read_mask: state.stencil_read_mask,
            stencil_write_mask: state.stencil_write_mask,
            front_depth_fail_op: state.front_depth_fail_op.gles3_stencil_op(),
            front_stencil_compare_op: state.front_stencil_compare_op.gles3_compare_op(),
            front_stencil_fail_op: state.front_stencil_fail_op.gles3_stencil_op(),
            front_stencil_pass_op: state.front_stencil_pass_op.gles3_stencil_op(),
            back_depth_fail_op: state.back_depth_fail_op.gles3_stencil_op(),
            back_stencil_compare_op: state.back_stencil_compare_op.gles3_compare_op(),
            back_stencil_fail_op: state.back_stencil_fail_op.gles3_stencil_op(),
            back_stencil_pass_op: state.back_stencil_pass_op.gles3_stencil_op(),
        }
    }
}

impl RafxBlendFactor {
    pub fn gles3_blend_factor(self) -> GLenum {
        match self {
            RafxBlendFactor::Zero => gles3_bindings::ZERO,
            RafxBlendFactor::One => gles3_bindings::ONE,
            RafxBlendFactor::SrcColor => gles3_bindings::SRC_COLOR,
            RafxBlendFactor::OneMinusSrcColor => gles3_bindings::ONE_MINUS_SRC_COLOR,
            RafxBlendFactor::DstColor => gles3_bindings::DST_COLOR,
            RafxBlendFactor::OneMinusDstColor => gles3_bindings::ONE_MINUS_DST_COLOR,
            RafxBlendFactor::SrcAlpha => gles3_bindings::SRC_ALPHA,
            RafxBlendFactor::OneMinusSrcAlpha => gles3_bindings::ONE_MINUS_SRC_ALPHA,
            RafxBlendFactor::DstAlpha => gles3_bindings::DST_ALPHA,
            RafxBlendFactor::OneMinusDstAlpha => gles3_bindings::ONE_MINUS_DST_ALPHA,
            RafxBlendFactor::SrcAlphaSaturate => gles3_bindings::SRC_ALPHA_SATURATE,
            RafxBlendFactor::ConstantColor => gles3_bindings::CONSTANT_COLOR,
            RafxBlendFactor::OneMinusConstantColor => gles3_bindings::ONE_MINUS_CONSTANT_COLOR,
        }
    }
}

impl RafxBlendOp {
    pub fn gles3_blend_op(self) -> Option<GLenum> {
        match self {
            RafxBlendOp::Add => Some(gles3_bindings::FUNC_ADD),
            RafxBlendOp::Subtract => Some(gles3_bindings::FUNC_SUBTRACT),
            RafxBlendOp::ReverseSubtract => Some(gles3_bindings::FUNC_REVERSE_SUBTRACT),

            // min/max are GLES 3.2
            RafxBlendOp::Min => None,
            RafxBlendOp::Max => None,
        }
    }
}

#[derive(Debug)]
pub struct Gles3BlendState {
    pub enabled: bool,
    pub src_factor: GLenum,
    pub dst_factor: GLenum,
    pub src_factor_alpha: GLenum,
    pub dst_factor_alpha: GLenum,
    pub blend_op: GLenum,
    pub blend_op_alpha: GLenum,
    pub color_flags: RafxColorFlags,
}

impl RafxBlendState {
    pub fn gles3_blend_state(&self) -> RafxResult<Gles3BlendState> {
        if self.independent_blend {
            unimplemented!("GL ES 2.0 does not support independent blend states");
        }

        let rt_state = self
            .render_target_blend_states
            .get(0)
            .ok_or("RafxBlendState has no render target blend states")?;
        let blend_state = Gles3BlendState {
            enabled: rt_state.blend_enabled(),
            src_factor: rt_state.src_factor.gles3_blend_factor(),
            dst_factor: rt_state.dst_factor.gles3_blend_factor(),
            src_factor_alpha: rt_state.src_factor_alpha.gles3_blend_factor(),
            dst_factor_alpha: rt_state.dst_factor_alpha.gles3_blend_factor(),
            blend_op: rt_state.blend_op.gles3_blend_op().ok_or_else(|| {
                format!(
                    "GL ES 2.0 does not support blend op {:?}",
                    rt_state.blend_op
                )
            })?,
            blend_op_alpha: rt_state.blend_op.gles3_blend_op().ok_or_else(|| {
                format!(
                    "GL ES 2.0 does not support blend op {:?}",
                    rt_state.blend_op
                )
            })?,
            color_flags: rt_state.masks,
        };

        Ok(blend_state)
    }
}

pub const GL_CUBE_MAP_TARGETS: [GLenum; 6] = [
    gles3_bindings::TEXTURE_CUBE_MAP_POSITIVE_X,
    gles3_bindings::TEXTURE_CUBE_MAP_NEGATIVE_X,
    gles3_bindings::TEXTURE_CUBE_MAP_POSITIVE_Y,
    gles3_bindings::TEXTURE_CUBE_MAP_NEGATIVE_Y,
    gles3_bindings::TEXTURE_CUBE_MAP_POSITIVE_Z,
    gles3_bindings::TEXTURE_CUBE_MAP_NEGATIVE_Z,
];

pub fn array_layer_to_cube_map_target(array_layer: u16) -> GLenum {
    if array_layer > 5 {
        unimplemented!("GL ES 2.0 does not support more than 6 images for a cubemap")
    }

    GL_CUBE_MAP_TARGETS[array_layer as usize]
}