use crate::gl;
use crate::context;
use crate::ToGlEnum;
#[derive(Copy, Clone, Debug)]
pub struct Stencil {
pub test_clockwise: StencilTest,
pub reference_value_clockwise: i32,
pub write_mask_clockwise: u32,
pub fail_operation_clockwise: StencilOperation,
pub pass_depth_fail_operation_clockwise: StencilOperation,
pub depth_pass_operation_clockwise: StencilOperation,
pub test_counter_clockwise: StencilTest,
pub reference_value_counter_clockwise: i32,
pub write_mask_counter_clockwise: u32,
pub fail_operation_counter_clockwise: StencilOperation,
pub pass_depth_fail_operation_counter_clockwise: StencilOperation,
pub depth_pass_operation_counter_clockwise: StencilOperation,
}
impl Default for Stencil {
#[inline]
fn default() -> Stencil {
Stencil {
test_clockwise: StencilTest::AlwaysPass,
reference_value_clockwise: 0,
write_mask_clockwise: 0xffffffff,
fail_operation_clockwise: StencilOperation::Keep,
pass_depth_fail_operation_clockwise: StencilOperation::Keep,
depth_pass_operation_clockwise: StencilOperation::Keep,
test_counter_clockwise: StencilTest::AlwaysPass,
reference_value_counter_clockwise: 0,
write_mask_counter_clockwise: 0xffffffff,
fail_operation_counter_clockwise: StencilOperation::Keep,
pass_depth_fail_operation_counter_clockwise: StencilOperation::Keep,
depth_pass_operation_counter_clockwise: StencilOperation::Keep,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StencilTest {
AlwaysPass,
AlwaysFail,
IfLess {
mask: u32
},
IfLessOrEqual {
mask: u32,
},
IfMore {
mask: u32,
},
IfMoreOrEqual {
mask: u32,
},
IfEqual {
mask: u32,
},
IfNotEqual {
mask: u32,
},
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)] pub enum StencilOperation {
Keep = gl::KEEP,
Zero = gl::ZERO,
Replace = gl::REPLACE,
Increment = gl::INCR,
IncrementWrap = gl::INCR_WRAP,
Decrement = gl::DECR,
DecrementWrap = gl::DECR_WRAP,
Invert = gl::INVERT,
}
impl ToGlEnum for StencilOperation {
#[inline]
fn to_glenum(&self) -> gl::types::GLenum {
*self as gl::types::GLenum
}
}
pub fn sync_stencil(ctxt: &mut context::CommandContext<'_>, params: &Stencil) {
if params.test_clockwise == StencilTest::AlwaysPass &&
params.test_counter_clockwise == StencilTest::AlwaysPass &&
params.fail_operation_clockwise == StencilOperation::Keep &&
params.pass_depth_fail_operation_clockwise == StencilOperation::Keep &&
params.depth_pass_operation_clockwise == StencilOperation::Keep &&
params.fail_operation_counter_clockwise == StencilOperation::Keep &&
params.pass_depth_fail_operation_counter_clockwise == StencilOperation::Keep &&
params.depth_pass_operation_counter_clockwise == StencilOperation::Keep
{
if ctxt.state.enabled_stencil_test {
unsafe { ctxt.gl.Disable(gl::STENCIL_TEST) };
ctxt.state.enabled_stencil_test = false;
}
return;
}
if !ctxt.state.enabled_stencil_test {
unsafe { ctxt.gl.Enable(gl::STENCIL_TEST) };
ctxt.state.enabled_stencil_test = true;
}
let (test_cw, read_mask_cw) = match params.test_clockwise {
StencilTest::AlwaysPass => (gl::ALWAYS, 0),
StencilTest::AlwaysFail => (gl::NEVER, 0),
StencilTest::IfLess { mask } => (gl::LESS, mask),
StencilTest::IfLessOrEqual { mask } => (gl::LEQUAL, mask),
StencilTest::IfMore { mask } => (gl::GREATER, mask),
StencilTest::IfMoreOrEqual { mask } => (gl::GEQUAL, mask),
StencilTest::IfEqual { mask } => (gl::EQUAL, mask),
StencilTest::IfNotEqual { mask } => (gl::NOTEQUAL, mask),
};
let (test_ccw, read_mask_ccw) = match params.test_counter_clockwise {
StencilTest::AlwaysPass => (gl::ALWAYS, 0),
StencilTest::AlwaysFail => (gl::NEVER, 0),
StencilTest::IfLess { mask } => (gl::LESS, mask),
StencilTest::IfLessOrEqual { mask } => (gl::LEQUAL, mask),
StencilTest::IfMore { mask } => (gl::GREATER, mask),
StencilTest::IfMoreOrEqual { mask } => (gl::GEQUAL, mask),
StencilTest::IfEqual { mask } => (gl::EQUAL, mask),
StencilTest::IfNotEqual { mask } => (gl::NOTEQUAL, mask),
};
let ref_cw = params.reference_value_clockwise;
let ref_ccw = params.reference_value_counter_clockwise;
if (test_cw, ref_cw, read_mask_cw) == (test_ccw, ref_ccw, read_mask_ccw) {
if ctxt.state.stencil_func_back != (test_cw, ref_cw, read_mask_cw) ||
ctxt.state.stencil_func_front != (test_ccw, ref_ccw, read_mask_ccw)
{
unsafe { ctxt.gl.StencilFunc(test_cw, ref_cw, read_mask_cw) };
ctxt.state.stencil_func_back = (test_cw, ref_cw, read_mask_cw);
ctxt.state.stencil_func_front = (test_ccw, ref_ccw, read_mask_ccw);
}
} else {
if ctxt.state.stencil_func_back != (test_cw, ref_cw, read_mask_cw) {
unsafe { ctxt.gl.StencilFuncSeparate(gl::BACK, test_cw, ref_cw, read_mask_cw) };
ctxt.state.stencil_func_back = (test_cw, ref_cw, read_mask_cw);
}
if ctxt.state.stencil_func_front != (test_ccw, ref_ccw, read_mask_ccw) {
unsafe { ctxt.gl.StencilFuncSeparate(gl::FRONT, test_ccw, ref_ccw, read_mask_ccw) };
ctxt.state.stencil_func_front = (test_ccw, ref_ccw, read_mask_ccw);
}
}
if params.write_mask_clockwise == params.write_mask_counter_clockwise {
if ctxt.state.stencil_mask_back != params.write_mask_clockwise ||
ctxt.state.stencil_mask_front != params.write_mask_clockwise
{
unsafe { ctxt.gl.StencilMask(params.write_mask_clockwise) };
ctxt.state.stencil_mask_back = params.write_mask_clockwise;
ctxt.state.stencil_mask_front = params.write_mask_clockwise;
}
} else {
if ctxt.state.stencil_mask_back != params.write_mask_clockwise {
unsafe { ctxt.gl.StencilMaskSeparate(gl::BACK, params.write_mask_clockwise) };
ctxt.state.stencil_mask_back = params.write_mask_clockwise;
}
if ctxt.state.stencil_mask_front != params.write_mask_clockwise {
unsafe { ctxt.gl.StencilMaskSeparate(gl::FRONT, params.write_mask_clockwise) };
ctxt.state.stencil_mask_front = params.write_mask_clockwise;
}
}
let op_back = (params.fail_operation_clockwise.to_glenum(),
params.pass_depth_fail_operation_clockwise.to_glenum(),
params.depth_pass_operation_clockwise.to_glenum());
let op_front = (params.fail_operation_counter_clockwise.to_glenum(),
params.pass_depth_fail_operation_counter_clockwise.to_glenum(),
params.depth_pass_operation_counter_clockwise.to_glenum());
if op_back == op_front {
if ctxt.state.stencil_op_back != op_back || ctxt.state.stencil_op_front != op_front {
unsafe { ctxt.gl.StencilOp(op_back.0, op_back.1, op_back.2) };
ctxt.state.stencil_op_back = op_back;
ctxt.state.stencil_op_front = op_front;
}
} else {
if ctxt.state.stencil_op_back != op_back {
unsafe { ctxt.gl.StencilOpSeparate(gl::BACK, op_back.0, op_back.1, op_back.2) };
ctxt.state.stencil_op_back = op_back;
}
if ctxt.state.stencil_op_front != op_front {
unsafe { ctxt.gl.StencilOpSeparate(gl::FRONT, op_front.0, op_front.1, op_front.2) };
ctxt.state.stencil_op_front = op_front;
}
}
}