Mask work
This commit is contained in:
parent
3c38405a71
commit
28f0ce3c83
|
@ -27,6 +27,9 @@ pub trait RenderBackend {
|
|||
fn end_frame(&mut self);
|
||||
fn draw_pause_overlay(&mut self);
|
||||
fn draw_letterbox(&mut self, letterbox: Letterbox);
|
||||
fn push_mask(&mut self);
|
||||
fn activate_mask(&mut self);
|
||||
fn pop_mask(&mut self);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -80,6 +83,9 @@ impl RenderBackend for NullRenderer {
|
|||
fn render_shape(&mut self, _shape: ShapeHandle, _transform: &Transform) {}
|
||||
fn draw_pause_overlay(&mut self) {}
|
||||
fn draw_letterbox(&mut self, _letterbox: Letterbox) {}
|
||||
fn push_mask(&mut self) {}
|
||||
fn activate_mask(&mut self) {}
|
||||
fn pop_mask(&mut self) {}
|
||||
}
|
||||
|
||||
pub fn glue_swf_jpeg_to_tables(jpeg_tables: &[u8], jpeg_data: &[u8]) -> Vec<u8> {
|
||||
|
|
|
@ -196,9 +196,7 @@ impl<'gc> DisplayObject<'gc> for Button<'gc> {
|
|||
fn render(&self, context: &mut RenderContext<'_, 'gc>) {
|
||||
context.transform_stack.push(self.transform());
|
||||
|
||||
for child in self.children.values() {
|
||||
child.read().render(context);
|
||||
}
|
||||
crate::display_object::render_children(context, &self.children);
|
||||
|
||||
context.transform_stack.pop();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ impl<'gc> Default for DisplayObjectBase<'gc> {
|
|||
}
|
||||
|
||||
impl<'gc> DisplayObject<'gc> for DisplayObjectBase<'gc> {
|
||||
fn depth(&self) -> Depth {
|
||||
self.depth
|
||||
}
|
||||
fn transform(&self) -> &Transform {
|
||||
&self.transform
|
||||
}
|
||||
|
@ -71,6 +74,7 @@ impl<'gc> DisplayObject<'gc> for DisplayObjectBase<'gc> {
|
|||
}
|
||||
|
||||
pub trait DisplayObject<'gc>: 'gc + Collect + Debug {
|
||||
fn depth(&self) -> Depth;
|
||||
fn local_bounds(&self) -> BoundingBox {
|
||||
BoundingBox::default()
|
||||
}
|
||||
|
@ -148,6 +152,9 @@ impl<'gc> Clone for Box<dyn DisplayObject<'gc>> {
|
|||
|
||||
macro_rules! impl_display_object {
|
||||
($field:ident) => {
|
||||
fn depth(&self) -> crate::prelude::Depth {
|
||||
self.$field.depth()
|
||||
}
|
||||
fn transform(&self) -> &crate::transform::Transform {
|
||||
self.$field.transform()
|
||||
}
|
||||
|
@ -190,6 +197,43 @@ macro_rules! impl_display_object {
|
|||
};
|
||||
}
|
||||
|
||||
/// Renders the children of a display object, taking masking into account.
|
||||
// TODO(Herschel): Move this into an IDisplayObject/IDisplayObjectContainer trait when
|
||||
// we figure out inheritance
|
||||
pub fn render_children<'gc>(
|
||||
context: &mut RenderContext<'_, 'gc>,
|
||||
children: &std::collections::BTreeMap<Depth, DisplayNode<'gc>>,
|
||||
) {
|
||||
let mut clip_depth = 0;
|
||||
let mut clip_depth_stack = vec![];
|
||||
for (&depth, &child) in children {
|
||||
// Check if we need to pop off a mask.
|
||||
// This must be a while loop because multiple masks can be popped
|
||||
// at the same dpeth.
|
||||
while clip_depth > 0 && depth >= clip_depth {
|
||||
context.renderer.pop_mask();
|
||||
clip_depth = clip_depth_stack.pop().unwrap();
|
||||
}
|
||||
let child = child.read();
|
||||
if child.clip_depth() > 0 {
|
||||
// Push and render the mask.
|
||||
clip_depth_stack.push(clip_depth);
|
||||
clip_depth = child.clip_depth();
|
||||
context.renderer.push_mask();
|
||||
child.render(context);
|
||||
context.renderer.activate_mask();
|
||||
} else {
|
||||
// Normal child.
|
||||
child.render(context);
|
||||
}
|
||||
}
|
||||
|
||||
while !clip_depth_stack.is_empty() {
|
||||
context.renderer.pop_mask();
|
||||
clip_depth_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/// `DisplayNode` is the garbage-collected pointer between display objects.
|
||||
/// TODO(Herschel): The extra Box here is necessary to hold the trait object inside a GC pointer,
|
||||
/// but this is an extra allocation... Can we avoid this, maybe with a DST?
|
||||
|
|
|
@ -330,11 +330,7 @@ impl<'gc> DisplayObject<'gc> for MovieClip<'gc> {
|
|||
|
||||
fn render(&self, context: &mut RenderContext<'_, 'gc>) {
|
||||
context.transform_stack.push(self.transform());
|
||||
|
||||
for child in self.children.values() {
|
||||
child.read().render(context);
|
||||
}
|
||||
|
||||
crate::display_object::render_children(context, &self.children);
|
||||
context.transform_stack.pop();
|
||||
}
|
||||
|
||||
|
@ -1008,6 +1004,9 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
character
|
||||
.write(context.gc_context)
|
||||
.set_color_transform(prev_character.read().color_transform());
|
||||
character
|
||||
.write(context.gc_context)
|
||||
.set_clip_depth(prev_character.read().clip_depth());
|
||||
}
|
||||
character
|
||||
}
|
||||
|
|
|
@ -431,6 +431,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
library: gc_root.library.read(),
|
||||
transform_stack,
|
||||
view_bounds,
|
||||
clip_depth_stack: vec![],
|
||||
};
|
||||
gc_root.root.read().render(&mut render_context);
|
||||
});
|
||||
|
@ -543,4 +544,5 @@ pub struct RenderContext<'a, 'gc> {
|
|||
pub library: std::cell::Ref<'a, Library<'gc>>,
|
||||
pub transform_stack: &'a mut TransformStack,
|
||||
pub view_bounds: BoundingBox,
|
||||
pub clip_depth_stack: Vec<Depth>,
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
|||
.with_vsync(true)
|
||||
.with_multisampling(4)
|
||||
.with_srgb(true)
|
||||
.with_stencil_buffer(8)
|
||||
.build_windowed(window_builder, &events_loop)?;
|
||||
let audio = audio::RodioAudioBackend::new()?;
|
||||
let renderer = GliumRenderBackend::new(windowed_context)?;
|
||||
|
|
|
@ -22,6 +22,12 @@ pub struct GliumRenderBackend {
|
|||
bitmap_shader_program: glium::Program,
|
||||
meshes: Vec<Mesh>,
|
||||
textures: Vec<(swf::CharacterId, Texture)>,
|
||||
num_masks: u32,
|
||||
num_masks_active: u32,
|
||||
write_stencil_mask: u32,
|
||||
test_stencil_mask: u32,
|
||||
next_stencil_mask: u32,
|
||||
mask_stack: Vec<(u32, u32)>,
|
||||
viewport_width: f32,
|
||||
viewport_height: f32,
|
||||
view_matrix: [[f32; 4]; 4],
|
||||
|
@ -87,6 +93,12 @@ impl GliumRenderBackend {
|
|||
viewport_width: 500.0,
|
||||
viewport_height: 500.0,
|
||||
view_matrix: [[0.0; 4]; 4],
|
||||
num_masks: 0,
|
||||
num_masks_active: 0,
|
||||
write_stencil_mask: 0,
|
||||
test_stencil_mask: 0,
|
||||
next_stencil_mask: 1,
|
||||
mask_stack: vec![],
|
||||
};
|
||||
renderer.build_matrices();
|
||||
Ok(renderer)
|
||||
|
@ -104,9 +116,6 @@ impl GliumRenderBackend {
|
|||
|
||||
let mut mesh = Mesh { draws: vec![] };
|
||||
|
||||
//let mut vertices: Vec<Vertex> = vec![];
|
||||
//let mut indices: Vec<u32> = vec![];
|
||||
|
||||
let mut fill_tess = FillTessellator::new();
|
||||
let mut stroke_tess = StrokeTessellator::new();
|
||||
let mut lyon_mesh: VertexBuffers<_, u32> = VertexBuffers::new();
|
||||
|
@ -571,22 +580,29 @@ impl RenderBackend for GliumRenderBackend {
|
|||
fn begin_frame(&mut self) {
|
||||
assert!(self.target.is_none());
|
||||
self.target = Some(self.display.draw());
|
||||
self.num_masks = 0;
|
||||
self.num_masks_active = 0;
|
||||
self.write_stencil_mask = 0;
|
||||
self.test_stencil_mask = 0;
|
||||
self.next_stencil_mask = 1;
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
assert!(self.target.is_some());
|
||||
|
||||
let target = self.target.take().unwrap();
|
||||
target.finish().unwrap();
|
||||
}
|
||||
|
||||
fn clear(&mut self, color: Color) {
|
||||
let target = self.target.as_mut().unwrap();
|
||||
target.clear_color_srgb(
|
||||
f32::from(color.r) / 255.0,
|
||||
f32::from(color.g) / 255.0,
|
||||
f32::from(color.b) / 255.0,
|
||||
f32::from(color.a) / 255.0,
|
||||
target.clear_color_srgb_and_stencil(
|
||||
(
|
||||
f32::from(color.r) / 255.0,
|
||||
f32::from(color.g) / 255.0,
|
||||
f32::from(color.b) / 255.0,
|
||||
f32::from(color.a) / 255.0,
|
||||
),
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -621,16 +637,27 @@ impl RenderBackend for GliumRenderBackend {
|
|||
transform.color_transform.a_add,
|
||||
];
|
||||
|
||||
let mut draw_parameters = DrawParameters::default();
|
||||
mask_draw_parameters(
|
||||
&mut draw_parameters,
|
||||
self.num_masks,
|
||||
self.num_masks_active,
|
||||
self.write_stencil_mask,
|
||||
self.test_stencil_mask,
|
||||
);
|
||||
|
||||
for draw in &mesh.draws {
|
||||
match &draw.draw_type {
|
||||
DrawType::Color => {
|
||||
draw_parameters.blend = color_blend();
|
||||
|
||||
target
|
||||
.draw(
|
||||
&draw.vertex_buffer,
|
||||
&draw.index_buffer,
|
||||
&self.shader_program,
|
||||
&uniform! { view_matrix: self.view_matrix, world_matrix: world_matrix, mult_color: mult_color, add_color: add_color },
|
||||
&color_draw_parameters()
|
||||
&draw_parameters
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -643,13 +670,15 @@ impl RenderBackend for GliumRenderBackend {
|
|||
gradient: gradient_uniforms.clone(),
|
||||
};
|
||||
|
||||
draw_parameters.blend = color_blend();
|
||||
|
||||
target
|
||||
.draw(
|
||||
&draw.vertex_buffer,
|
||||
&draw.index_buffer,
|
||||
&self.gradient_shader_program,
|
||||
&uniforms,
|
||||
&color_draw_parameters(),
|
||||
&draw_parameters,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -697,13 +726,15 @@ impl RenderBackend for GliumRenderBackend {
|
|||
texture,
|
||||
};
|
||||
|
||||
draw_parameters.blend = bitmap_blend();
|
||||
|
||||
target
|
||||
.draw(
|
||||
&draw.vertex_buffer,
|
||||
&draw.index_buffer,
|
||||
&self.bitmap_shader_program,
|
||||
&uniforms,
|
||||
&bitmap_draw_parameters(),
|
||||
&draw_parameters,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -772,6 +803,44 @@ impl RenderBackend for GliumRenderBackend {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_mask(&mut self) {
|
||||
// Desktop draws the masker to the stencil buffer, one bit per mask.
|
||||
// Masks-within-masks are handled as a bitmask.
|
||||
// This does unfortunately mean we are limited in the number of masks at once (usually 8 bits).
|
||||
if self.next_stencil_mask == 0 {
|
||||
// If we've reached the limit of masks, clear the stencil buffer and start over.
|
||||
// But this may not be correct if there is still a mask active (mask-within-mask).
|
||||
let target = self.target.as_mut().unwrap();
|
||||
if self.test_stencil_mask != 0 {
|
||||
log::warn!(
|
||||
"Too many masks active for stencil buffer; possibly inccorect rendering"
|
||||
);
|
||||
}
|
||||
self.next_stencil_mask = 0;
|
||||
target.clear_stencil(self.test_stencil_mask as i32);
|
||||
}
|
||||
self.num_masks += 1;
|
||||
self.mask_stack
|
||||
.push((self.write_stencil_mask, self.test_stencil_mask));
|
||||
self.write_stencil_mask = self.next_stencil_mask;
|
||||
self.test_stencil_mask |= self.next_stencil_mask;
|
||||
self.next_stencil_mask <<= 1;
|
||||
}
|
||||
fn activate_mask(&mut self) {
|
||||
self.num_masks_active += 1;
|
||||
}
|
||||
fn pop_mask(&mut self) {
|
||||
if !self.mask_stack.is_empty() {
|
||||
self.num_masks -= 1;
|
||||
self.num_masks_active -= 1;
|
||||
let (write, test) = self.mask_stack.pop().unwrap();
|
||||
self.write_stencil_mask = write;
|
||||
self.test_stencil_mask = test;
|
||||
} else {
|
||||
log::warn!("Mask stack underflow\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Texture {
|
||||
|
@ -1124,30 +1193,72 @@ fn swf_bitmap_to_gl_matrix(m: swf::Matrix, bitmap_width: u32, bitmap_height: u32
|
|||
[[a, d, 0.0], [b, e, 0.0], [c, f, 1.0]]
|
||||
}
|
||||
|
||||
/// Returns the drawing parameters for masking.
|
||||
#[inline]
|
||||
fn mask_draw_parameters(
|
||||
params: &mut DrawParameters,
|
||||
num_masks: u32,
|
||||
num_masks_active: u32,
|
||||
write_stencil_mask: u32,
|
||||
test_stencil_mask: u32,
|
||||
) {
|
||||
use glium::draw_parameters::{Stencil, StencilOperation, StencilTest};
|
||||
if num_masks > 0 {
|
||||
let (value, test, pass_op, color_mask, write_mask) = if num_masks_active < num_masks {
|
||||
(
|
||||
write_stencil_mask as i32,
|
||||
StencilTest::AlwaysPass,
|
||||
StencilOperation::Replace,
|
||||
(false, false, false, false),
|
||||
write_stencil_mask,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
test_stencil_mask as i32,
|
||||
StencilTest::IfEqual {
|
||||
mask: test_stencil_mask,
|
||||
},
|
||||
StencilOperation::Keep,
|
||||
(true, true, true, true),
|
||||
test_stencil_mask,
|
||||
)
|
||||
};
|
||||
params.color_mask = color_mask;
|
||||
params.stencil = Stencil {
|
||||
test_clockwise: test,
|
||||
reference_value_clockwise: value,
|
||||
write_mask_clockwise: write_mask,
|
||||
fail_operation_clockwise: StencilOperation::Keep,
|
||||
pass_depth_fail_operation_clockwise: StencilOperation::Keep,
|
||||
depth_pass_operation_clockwise: pass_op,
|
||||
test_counter_clockwise: test,
|
||||
reference_value_counter_clockwise: value,
|
||||
write_mask_counter_clockwise: write_mask,
|
||||
fail_operation_counter_clockwise: StencilOperation::Keep,
|
||||
pass_depth_fail_operation_counter_clockwise: StencilOperation::Keep,
|
||||
depth_pass_operation_counter_clockwise: pass_op,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the drawing parameters for standard color/gradient fills.
|
||||
#[inline]
|
||||
fn color_draw_parameters() -> DrawParameters<'static> {
|
||||
DrawParameters {
|
||||
blend: glium::Blend::alpha_blending(),
|
||||
..Default::default()
|
||||
}
|
||||
fn color_blend() -> glium::Blend {
|
||||
glium::Blend::alpha_blending()
|
||||
}
|
||||
|
||||
/// Returns the drawing parameters for bitmaps with pre-multipled alpha.
|
||||
#[inline]
|
||||
fn bitmap_draw_parameters() -> DrawParameters<'static> {
|
||||
fn bitmap_blend() -> glium::Blend {
|
||||
use glium::{BlendingFunction, LinearBlendingFactor};
|
||||
DrawParameters {
|
||||
blend: glium::Blend {
|
||||
color: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::One,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
alpha: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::SourceAlpha,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
..Default::default()
|
||||
glium::Blend {
|
||||
color: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::One,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
alpha: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::SourceAlpha,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
@ -440,6 +440,10 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_mask(&mut self) {}
|
||||
fn activate_mask(&mut self) {}
|
||||
fn pop_mask(&mut self) {}
|
||||
}
|
||||
|
||||
fn swf_shape_to_svg(
|
||||
|
|
Loading…
Reference in New Issue