web: Add WebGL render backend
This commit is contained in:
parent
e9a03859ca
commit
fa5c09b0cd
|
@ -1842,6 +1842,22 @@ dependencies = [
|
|||
"wgpu-native 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruffle_render_webgl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jpeg-decoder 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ruffle_core 0.1.0",
|
||||
"ruffle_render_common_tess 0.1.0",
|
||||
"wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruffle_scanner"
|
||||
version = "0.1.0"
|
||||
|
@ -1870,6 +1886,7 @@ dependencies = [
|
|||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ruffle_core 0.1.0",
|
||||
"ruffle_render_canvas 0.1.0",
|
||||
"ruffle_render_webgl 0.1.0",
|
||||
"ruffle_web_common 0.1.0",
|
||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -10,6 +10,7 @@ members = [
|
|||
"render/canvas",
|
||||
"render/wgpu",
|
||||
"render/common_tess",
|
||||
"render/webgl",
|
||||
]
|
||||
|
||||
# Don't optimize build scripts and macros.
|
||||
|
|
|
@ -294,6 +294,12 @@ where
|
|||
mesh
|
||||
}
|
||||
|
||||
impl Default for ShapeTessellator {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
type Mesh = Vec<Draw>;
|
||||
|
||||
pub struct Draw {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
[package]
|
||||
name = "ruffle_render_webgl"
|
||||
version = "0.1.0"
|
||||
authors = ["Mike Welsh <mwelsh@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
fnv = "1.0.3"
|
||||
js-sys = "0.3.25"
|
||||
log = "0.4"
|
||||
percent-encoding = "2.1.0"
|
||||
png = "0.16.3"
|
||||
ruffle_render_common_tess = { path = "../common_tess" }
|
||||
ruffle_web_common = { path = "../../web/common" }
|
||||
wasm-bindgen = "0.2.57"
|
||||
|
||||
[dependencies.jpeg-decoder]
|
||||
version = "0.1.18"
|
||||
default-features = false
|
||||
|
||||
[dependencies.ruffle_core]
|
||||
path = "../../core"
|
||||
default-features = false
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.34"
|
||||
features = ["HtmlCanvasElement", "HtmlElement", "Node", "WebGlBuffer", "WebGlProgram", "WebGlRenderingContext",
|
||||
"WebGlShader", "WebGlTexture", "WebGlUniformLocation"]
|
|
@ -0,0 +1,25 @@
|
|||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
uniform mat4 view_matrix;
|
||||
uniform mat4 world_matrix;
|
||||
uniform vec4 mult_color;
|
||||
uniform vec4 add_color;
|
||||
uniform mat3 u_matrix;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
varying vec2 frag_uv;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture2D(u_texture, frag_uv);
|
||||
|
||||
// Unmultiply alpha before apply color transform.
|
||||
if( color.a > 0.0 ) {
|
||||
color.rgb /= color.a;
|
||||
color = mult_color * color + add_color;
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
uniform mat4 view_matrix;
|
||||
uniform mat4 world_matrix;
|
||||
uniform vec4 mult_color;
|
||||
uniform vec4 add_color;
|
||||
|
||||
varying vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = frag_color;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
uniform mat4 view_matrix;
|
||||
uniform mat4 world_matrix;
|
||||
uniform vec4 mult_color;
|
||||
uniform vec4 add_color;
|
||||
|
||||
attribute vec2 position;
|
||||
attribute vec4 color;
|
||||
varying vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = color * mult_color + add_color;
|
||||
gl_Position = view_matrix * world_matrix * vec4(position, 0.0, 1.0);
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
uniform mat4 view_matrix;
|
||||
uniform mat4 world_matrix;
|
||||
uniform vec4 mult_color;
|
||||
uniform vec4 add_color;
|
||||
uniform mat3 u_matrix;
|
||||
|
||||
uniform int u_gradient_type;
|
||||
uniform float u_ratios[8];
|
||||
uniform vec4 u_colors[8];
|
||||
uniform int u_num_colors;
|
||||
uniform int u_repeat_mode;
|
||||
uniform float u_focal_point;
|
||||
|
||||
varying vec2 frag_uv;
|
||||
|
||||
void main() {
|
||||
float t;
|
||||
if( u_gradient_type == 0 )
|
||||
{
|
||||
t = frag_uv.x;
|
||||
}
|
||||
else if( u_gradient_type == 1 )
|
||||
{
|
||||
t = length(frag_uv * 2.0 - 1.0);
|
||||
}
|
||||
else if( u_gradient_type == 2 )
|
||||
{
|
||||
vec2 uv = frag_uv * 2.0 - 1.0;
|
||||
vec2 d = vec2(u_focal_point, 0.0) - uv;
|
||||
float l = length(d);
|
||||
d /= l;
|
||||
t = l / (sqrt(1.0 - u_focal_point*u_focal_point*d.y*d.y) + u_focal_point*d.x);
|
||||
}
|
||||
if( u_repeat_mode == 0 )
|
||||
{
|
||||
// Clamp
|
||||
t = clamp(t, 0.0, 1.0);
|
||||
}
|
||||
else if( u_repeat_mode == 1 )
|
||||
{
|
||||
// Repeat
|
||||
t = fract(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mirror
|
||||
if( t < 0.0 )
|
||||
{
|
||||
t = -t;
|
||||
}
|
||||
|
||||
if( int(mod(t, 2.0)) == 0 ) {
|
||||
t = fract(t);
|
||||
} else {
|
||||
t = 1.0 - fract(t);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: No non-constant array access in WebGL 1, so the following is kind of painful.
|
||||
// We'd probably be better off passing in the gradient as a texture and sampling from there.
|
||||
vec4 color;
|
||||
float a;
|
||||
if( t <= u_ratios[0] ) {
|
||||
color = u_colors[0];
|
||||
} else if( t <= u_ratios[1] ) {
|
||||
a = (t - u_ratios[0]) / (u_ratios[1] - u_ratios[0]);
|
||||
color = mix(u_colors[0], u_colors[1], a);
|
||||
} else if( t <= u_ratios[2] ) {
|
||||
a = (t - u_ratios[1]) / (u_ratios[2] - u_ratios[1]);
|
||||
color = mix(u_colors[1], u_colors[2], a);
|
||||
} else if( t <= u_ratios[3] ) {
|
||||
a = (t - u_ratios[2]) / (u_ratios[3] - u_ratios[2]);
|
||||
color = mix(u_colors[2], u_colors[3], a);
|
||||
} else if( t <= u_ratios[4] ) {
|
||||
a = (t - u_ratios[3]) / (u_ratios[4] - u_ratios[3]);
|
||||
color = mix(u_colors[3], u_colors[4], a);
|
||||
} else if( t <= u_ratios[5] ) {
|
||||
a = (t - u_ratios[4]) / (u_ratios[5] - u_ratios[4]);
|
||||
color = mix(u_colors[4], u_colors[5], a);
|
||||
} else if( t <= u_ratios[6] ) {
|
||||
a = (t - u_ratios[5]) / (u_ratios[6] - u_ratios[5]);
|
||||
color = mix(u_colors[5], u_colors[6], a);
|
||||
} else if( t <= u_ratios[7] ) {
|
||||
a = (t - u_ratios[6]) / (u_ratios[7] - u_ratios[6]);
|
||||
color = mix(u_colors[6], u_colors[7], a);
|
||||
} else {
|
||||
color = u_colors[7];
|
||||
}
|
||||
|
||||
gl_FragColor = mult_color * color + add_color;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
uniform mat4 view_matrix;
|
||||
uniform mat4 world_matrix;
|
||||
uniform vec4 mult_color;
|
||||
uniform vec4 add_color;
|
||||
uniform mat3 u_matrix;
|
||||
|
||||
attribute vec2 position;
|
||||
attribute vec4 color;
|
||||
|
||||
varying vec2 frag_uv;
|
||||
|
||||
void main() {
|
||||
frag_uv = vec2(u_matrix * vec3(position, 1.0));
|
||||
gl_Position = view_matrix * world_matrix * vec4(position, 0.0, 1.0);
|
||||
}
|
|
@ -0,0 +1,955 @@
|
|||
use ruffle_core::backend::render::swf::{self, FillStyle};
|
||||
use ruffle_core::backend::render::{
|
||||
BitmapHandle, BitmapInfo, Color, Letterbox, RenderBackend, ShapeHandle, Transform,
|
||||
};
|
||||
use ruffle_web_common::JsResult;
|
||||
use std::convert::TryInto;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{
|
||||
HtmlCanvasElement, WebGlBuffer, WebGlProgram, WebGlRenderingContext as Gl, WebGlShader,
|
||||
WebGlTexture, WebGlUniformLocation,
|
||||
};
|
||||
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
||||
const COLOR_VERTEX_GLSL: &str = include_str!("../shaders/color.vert");
|
||||
const COLOR_FRAGMENT_GLSL: &str = include_str!("../shaders/color.frag");
|
||||
const TEXTURE_VERTEX_GLSL: &str = include_str!("../shaders/texture.vert");
|
||||
const GRADIENT_FRAGMENT_GLSL: &str = include_str!("../shaders/gradient.frag");
|
||||
const BITMAP_FRAGMENT_GLSL: &str = include_str!("../shaders/bitmap.frag");
|
||||
|
||||
pub struct WebGlRenderBackend {
|
||||
gl: Gl,
|
||||
|
||||
vertex_position_location: u32,
|
||||
vertex_color_location: u32,
|
||||
|
||||
color_program: ShaderProgram,
|
||||
bitmap_program: ShaderProgram,
|
||||
gradient_program: ShaderProgram,
|
||||
|
||||
textures: Vec<(swf::CharacterId, Texture)>,
|
||||
meshes: Vec<Mesh>,
|
||||
|
||||
quad_shape: ShapeHandle,
|
||||
|
||||
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],
|
||||
}
|
||||
|
||||
impl WebGlRenderBackend {
|
||||
pub fn new(canvas: &HtmlCanvasElement) -> Result<Self, Error> {
|
||||
// Create WebGL context.
|
||||
let context_options = js_sys::Object::new();
|
||||
js_sys::Reflect::set(
|
||||
&context_options,
|
||||
&"stencil".into(),
|
||||
&wasm_bindgen::JsValue::TRUE,
|
||||
)
|
||||
.warn_on_error();
|
||||
js_sys::Reflect::set(
|
||||
&context_options,
|
||||
&"alpha".into(),
|
||||
&wasm_bindgen::JsValue::FALSE,
|
||||
)
|
||||
.warn_on_error();
|
||||
|
||||
let gl = canvas
|
||||
.get_context_with_context_options("webgl", &context_options)
|
||||
.into_js_result()?
|
||||
.ok_or("No context returned")?
|
||||
.dyn_into::<Gl>()
|
||||
.map_err(|_| "Expected GL context")?;
|
||||
|
||||
let color_vertex = Self::compile_shader(&gl, Gl::VERTEX_SHADER, COLOR_VERTEX_GLSL)?;
|
||||
let texture_vertex = Self::compile_shader(&gl, Gl::VERTEX_SHADER, TEXTURE_VERTEX_GLSL)?;
|
||||
let color_fragment = Self::compile_shader(&gl, Gl::FRAGMENT_SHADER, COLOR_FRAGMENT_GLSL)?;
|
||||
let bitmap_fragment = Self::compile_shader(&gl, Gl::FRAGMENT_SHADER, BITMAP_FRAGMENT_GLSL)?;
|
||||
let gradient_fragment =
|
||||
Self::compile_shader(&gl, Gl::FRAGMENT_SHADER, GRADIENT_FRAGMENT_GLSL)?;
|
||||
|
||||
let color_program = ShaderProgram::new(&gl, &color_vertex, &color_fragment)?;
|
||||
let bitmap_program = ShaderProgram::new(&gl, &texture_vertex, &bitmap_fragment)?;
|
||||
let gradient_program = ShaderProgram::new(&gl, &texture_vertex, &gradient_fragment)?;
|
||||
|
||||
// This assumes we are using the same vertex format for all shaders.
|
||||
let vertex_position_location =
|
||||
gl.get_attrib_location(&color_program.program, "position") as u32;
|
||||
let vertex_color_location = gl.get_attrib_location(&color_program.program, "color") as u32;
|
||||
|
||||
let quad_mesh = Self::build_quad_mesh(&gl)?;
|
||||
let quad_shape = ShapeHandle(0);
|
||||
|
||||
// Enable vertex attributes.
|
||||
// Because we currently only use on vertex format, we can do this once on init.
|
||||
gl.enable_vertex_attrib_array(vertex_position_location as u32);
|
||||
gl.enable_vertex_attrib_array(vertex_color_location as u32);
|
||||
gl.vertex_attrib_pointer_with_i32(
|
||||
vertex_position_location,
|
||||
2,
|
||||
Gl::FLOAT,
|
||||
false,
|
||||
24, //std::mem::size_of::<Vertex>() as i32,
|
||||
0,
|
||||
);
|
||||
gl.vertex_attrib_pointer_with_i32(
|
||||
vertex_color_location,
|
||||
4,
|
||||
Gl::FLOAT,
|
||||
false,
|
||||
24, //std::mem::size_of::<Vertex>() as i32,
|
||||
8,
|
||||
);
|
||||
|
||||
gl.enable(Gl::BLEND);
|
||||
|
||||
// Necessary to load RGB textures (alignment defaults to 4).
|
||||
gl.pixel_storei(Gl::UNPACK_ALIGNMENT, 1);
|
||||
|
||||
let mut renderer = Self {
|
||||
gl,
|
||||
|
||||
color_program,
|
||||
gradient_program,
|
||||
bitmap_program,
|
||||
|
||||
meshes: vec![quad_mesh],
|
||||
quad_shape,
|
||||
textures: vec![],
|
||||
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![],
|
||||
|
||||
vertex_position_location,
|
||||
vertex_color_location,
|
||||
};
|
||||
|
||||
renderer.build_matrices();
|
||||
Ok(renderer)
|
||||
}
|
||||
|
||||
// Builds the quad mesh that is used for rendering bitmap display objects.
|
||||
fn build_quad_mesh(gl: &Gl) -> Result<Mesh, Error> {
|
||||
let quad_mesh = unsafe {
|
||||
let vertex_buffer = gl.create_buffer().unwrap();
|
||||
gl.bind_buffer(Gl::ARRAY_BUFFER, Some(&vertex_buffer));
|
||||
|
||||
let verts = [
|
||||
Vertex {
|
||||
position: [0.0, 0.0],
|
||||
color: [1.0, 1.0, 1.0, 1.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [1.0, 0.0],
|
||||
color: [1.0, 1.0, 1.0, 1.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [1.0, 1.0],
|
||||
color: [1.0, 1.0, 1.0, 1.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [0.0, 1.0],
|
||||
color: [1.0, 1.0, 1.0, 1.0],
|
||||
},
|
||||
];
|
||||
let verts_bytes = std::slice::from_raw_parts(
|
||||
verts.as_ptr() as *const u8,
|
||||
std::mem::size_of_val(&verts),
|
||||
);
|
||||
gl.buffer_data_with_u8_array(Gl::ARRAY_BUFFER, verts_bytes, Gl::STATIC_DRAW);
|
||||
|
||||
let index_buffer = gl.create_buffer().unwrap();
|
||||
gl.bind_buffer(Gl::ELEMENT_ARRAY_BUFFER, Some(&index_buffer));
|
||||
let indices = [0u16, 1, 2, 0, 2, 3];
|
||||
let indices_bytes = std::slice::from_raw_parts(
|
||||
indices.as_ptr() as *const u8,
|
||||
std::mem::size_of::<u16>() * indices.len(),
|
||||
);
|
||||
gl.buffer_data_with_u8_array(Gl::ELEMENT_ARRAY_BUFFER, indices_bytes, Gl::STATIC_DRAW);
|
||||
|
||||
Mesh {
|
||||
draws: vec![Draw {
|
||||
draw_type: DrawType::Bitmap(Bitmap {
|
||||
matrix: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
|
||||
id: 0,
|
||||
|
||||
is_smoothed: true,
|
||||
is_repeating: false,
|
||||
}),
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
num_indices: 6,
|
||||
}],
|
||||
}
|
||||
};
|
||||
|
||||
Ok(quad_mesh)
|
||||
}
|
||||
|
||||
fn compile_shader(gl: &Gl, shader_type: u32, glsl_src: &str) -> Result<WebGlShader, Error> {
|
||||
let shader = gl.create_shader(shader_type).unwrap();
|
||||
gl.shader_source(&shader, glsl_src);
|
||||
gl.compile_shader(&shader);
|
||||
let log = gl.get_shader_info_log(&shader).unwrap_or_default();
|
||||
if !log.is_empty() {
|
||||
log::error!("{}", log);
|
||||
}
|
||||
Ok(shader)
|
||||
}
|
||||
|
||||
fn register_shape_internal(&mut self, shape: &swf::Shape) -> ShapeHandle {
|
||||
use ruffle_render_common_tess::DrawType as TessDrawType;
|
||||
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
|
||||
let lyon_mesh = ruffle_render_common_tess::tessellate_shape(shape, |id| {
|
||||
self.textures
|
||||
.iter()
|
||||
.find(|(other_id, _tex)| *other_id == id)
|
||||
.map(|tex| (tex.1.width, tex.1.height))
|
||||
});
|
||||
|
||||
let mut draws = Vec::with_capacity(lyon_mesh.len());
|
||||
|
||||
for draw in lyon_mesh {
|
||||
let num_indices = draw.indices.len() as i32;
|
||||
|
||||
let (vertex_buffer, index_buffer) = unsafe {
|
||||
let vertex_buffer = self.gl.create_buffer().unwrap();
|
||||
self.gl.bind_buffer(Gl::ARRAY_BUFFER, Some(&vertex_buffer));
|
||||
|
||||
let verts_bytes = std::slice::from_raw_parts(
|
||||
draw.vertices.as_ptr() as *const u8,
|
||||
draw.vertices.len() * std::mem::size_of::<Vertex>(),
|
||||
);
|
||||
self.gl
|
||||
.buffer_data_with_u8_array(Gl::ARRAY_BUFFER, verts_bytes, Gl::STATIC_DRAW);
|
||||
|
||||
let indices: Vec<_> = draw.indices.into_iter().map(|n| n as u16).collect();
|
||||
let index_buffer = self.gl.create_buffer().unwrap();
|
||||
self.gl
|
||||
.bind_buffer(Gl::ELEMENT_ARRAY_BUFFER, Some(&index_buffer));
|
||||
let indices_bytes = std::slice::from_raw_parts(
|
||||
indices.as_ptr() as *const u8,
|
||||
indices.len() * std::mem::size_of::<u16>(),
|
||||
);
|
||||
self.gl.buffer_data_with_u8_array(
|
||||
Gl::ELEMENT_ARRAY_BUFFER,
|
||||
indices_bytes,
|
||||
Gl::STATIC_DRAW,
|
||||
);
|
||||
|
||||
(vertex_buffer, index_buffer)
|
||||
};
|
||||
|
||||
let out_draw = match draw.draw_type {
|
||||
TessDrawType::Color => Draw {
|
||||
draw_type: DrawType::Color,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
num_indices,
|
||||
},
|
||||
TessDrawType::Gradient(gradient) => {
|
||||
let mut ratios = [0.0; 8];
|
||||
let mut colors = [[0.0; 4]; 8];
|
||||
let num_colors = gradient.num_colors as usize;
|
||||
ratios[..num_colors].copy_from_slice(&gradient.ratios[..num_colors]);
|
||||
colors[..num_colors].copy_from_slice(&gradient.colors[..num_colors]);
|
||||
let out_gradient = Gradient {
|
||||
matrix: gradient.matrix,
|
||||
gradient_type: gradient.gradient_type,
|
||||
ratios,
|
||||
colors,
|
||||
num_colors: gradient.num_colors,
|
||||
repeat_mode: gradient.repeat_mode,
|
||||
focal_point: gradient.focal_point,
|
||||
};
|
||||
Draw {
|
||||
draw_type: DrawType::Gradient(Box::new(out_gradient)),
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
num_indices,
|
||||
}
|
||||
}
|
||||
TessDrawType::Bitmap(bitmap) => Draw {
|
||||
draw_type: DrawType::Bitmap(Bitmap {
|
||||
matrix: bitmap.matrix,
|
||||
id: bitmap.id,
|
||||
is_smoothed: bitmap.is_smoothed,
|
||||
is_repeating: bitmap.is_repeating,
|
||||
}),
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
num_indices,
|
||||
},
|
||||
};
|
||||
|
||||
draws.push(out_draw);
|
||||
}
|
||||
|
||||
self.meshes.push(Mesh { draws });
|
||||
|
||||
handle
|
||||
}
|
||||
|
||||
fn build_matrices(&mut self) {
|
||||
self.view_matrix = [
|
||||
[1.0 / (self.viewport_width as f32 / 2.0), 0.0, 0.0, 0.0],
|
||||
[0.0, -1.0 / (self.viewport_height as f32 / 2.0), 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[-1.0, 1.0, 0.0, 1.0],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBackend for WebGlRenderBackend {
|
||||
fn set_viewport_dimensions(&mut self, width: u32, height: u32) {
|
||||
self.viewport_width = width as f32;
|
||||
self.viewport_height = height as f32;
|
||||
self.gl.viewport(0, 0, width as i32, height as i32);
|
||||
self.build_matrices();
|
||||
}
|
||||
|
||||
fn register_shape(&mut self, shape: &swf::Shape) -> ShapeHandle {
|
||||
self.register_shape_internal(shape)
|
||||
}
|
||||
|
||||
fn register_glyph_shape(&mut self, glyph: &swf::Glyph) -> ShapeHandle {
|
||||
let shape = swf::Shape {
|
||||
version: 2,
|
||||
id: 0,
|
||||
shape_bounds: Default::default(),
|
||||
edge_bounds: Default::default(),
|
||||
has_fill_winding_rule: false,
|
||||
has_non_scaling_strokes: false,
|
||||
has_scaling_strokes: true,
|
||||
styles: swf::ShapeStyles {
|
||||
fill_styles: vec![FillStyle::Color(Color {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255,
|
||||
a: 255,
|
||||
})],
|
||||
line_styles: vec![],
|
||||
},
|
||||
shape: glyph.shape_records.clone(),
|
||||
};
|
||||
self.register_shape_internal(&shape)
|
||||
}
|
||||
|
||||
fn register_bitmap_jpeg(
|
||||
&mut self,
|
||||
id: swf::CharacterId,
|
||||
data: &[u8],
|
||||
jpeg_tables: Option<&[u8]>,
|
||||
) -> BitmapInfo {
|
||||
let data = ruffle_core::backend::render::glue_tables_to_jpeg(data, jpeg_tables);
|
||||
self.register_bitmap_jpeg_2(id, &data[..])
|
||||
}
|
||||
|
||||
fn register_bitmap_jpeg_2(&mut self, id: swf::CharacterId, data: &[u8]) -> BitmapInfo {
|
||||
let data = ruffle_core::backend::render::remove_invalid_jpeg_data(data);
|
||||
|
||||
let mut decoder = jpeg_decoder::Decoder::new(&data[..]);
|
||||
decoder.read_info().unwrap();
|
||||
let metadata = decoder.info().unwrap();
|
||||
let decoded_data = decoder.decode().expect("failed to decode image");
|
||||
|
||||
let texture = self.gl.create_texture().unwrap();
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
||||
self.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
0,
|
||||
Gl::RGB as i32,
|
||||
metadata.width.into(),
|
||||
metadata.height.into(),
|
||||
0,
|
||||
Gl::RGB,
|
||||
Gl::UNSIGNED_BYTE,
|
||||
Some(&decoded_data),
|
||||
)
|
||||
.warn_on_error();
|
||||
|
||||
let handle = BitmapHandle(self.textures.len());
|
||||
self.textures.push((
|
||||
id,
|
||||
Texture {
|
||||
texture,
|
||||
width: metadata.width.into(),
|
||||
height: metadata.height.into(),
|
||||
},
|
||||
));
|
||||
|
||||
BitmapInfo {
|
||||
handle,
|
||||
width: metadata.width,
|
||||
height: metadata.height,
|
||||
}
|
||||
}
|
||||
|
||||
fn register_bitmap_jpeg_3(
|
||||
&mut self,
|
||||
id: swf::CharacterId,
|
||||
jpeg_data: &[u8],
|
||||
alpha_data: &[u8],
|
||||
) -> BitmapInfo {
|
||||
let (width, height, rgba) =
|
||||
ruffle_core::backend::render::define_bits_jpeg_to_rgba(jpeg_data, alpha_data)
|
||||
.expect("Error decoding DefineBitsJPEG3");
|
||||
|
||||
let texture = self.gl.create_texture().unwrap();
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
||||
self.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
0,
|
||||
Gl::RGBA as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
Gl::RGBA,
|
||||
Gl::UNSIGNED_BYTE,
|
||||
Some(&rgba),
|
||||
)
|
||||
.warn_on_error();
|
||||
|
||||
let handle = BitmapHandle(self.textures.len());
|
||||
self.textures.push((
|
||||
id,
|
||||
Texture {
|
||||
texture,
|
||||
width,
|
||||
height,
|
||||
},
|
||||
));
|
||||
|
||||
BitmapInfo {
|
||||
handle,
|
||||
width: width.try_into().unwrap(),
|
||||
height: height.try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_bitmap_png(&mut self, swf_tag: &swf::DefineBitsLossless) -> BitmapInfo {
|
||||
let decoded_data = ruffle_core::backend::render::define_bits_lossless_to_rgba(swf_tag)
|
||||
.expect("Error decoding DefineBitsLossless");
|
||||
|
||||
let texture = self.gl.create_texture().unwrap();
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
||||
self.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
0,
|
||||
Gl::RGBA as i32,
|
||||
swf_tag.width.into(),
|
||||
swf_tag.height.into(),
|
||||
0,
|
||||
Gl::RGBA,
|
||||
Gl::UNSIGNED_BYTE,
|
||||
Some(&decoded_data),
|
||||
)
|
||||
.warn_on_error();
|
||||
|
||||
// You must set the texture parameters for non-power-of-2 textures to function in WebGL.
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_S, Gl::CLAMP_TO_EDGE as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_T, Gl::CLAMP_TO_EDGE as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MIN_FILTER, Gl::LINEAR as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MAG_FILTER, Gl::LINEAR as i32);
|
||||
|
||||
let handle = BitmapHandle(self.textures.len());
|
||||
self.textures.push((
|
||||
swf_tag.id,
|
||||
Texture {
|
||||
texture,
|
||||
width: swf_tag.width.into(),
|
||||
height: swf_tag.height.into(),
|
||||
},
|
||||
));
|
||||
|
||||
BitmapInfo {
|
||||
handle,
|
||||
width: swf_tag.width,
|
||||
height: swf_tag.height,
|
||||
}
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self) {
|
||||
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) {}
|
||||
|
||||
fn clear(&mut self, color: Color) {
|
||||
self.gl.clear_color(
|
||||
color.r as f32 / 255.0,
|
||||
color.g as f32 / 255.0,
|
||||
color.b as f32 / 255.0,
|
||||
color.a as f32 / 255.0,
|
||||
);
|
||||
self.gl.clear(Gl::COLOR_BUFFER_BIT | Gl::STENCIL_BUFFER_BIT);
|
||||
}
|
||||
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform) {
|
||||
// TODO: Might be better to make this separate code to render the bitmap
|
||||
// instead of going through render_shape. But render_shape already handles
|
||||
// masking etc.
|
||||
if let Some((id, bitmap)) = self.textures.get(bitmap.0) {
|
||||
// Adjust the quad draw to use the target bitmap.
|
||||
let mesh = &mut self.meshes[self.quad_shape.0];
|
||||
let draw = &mut mesh.draws[0];
|
||||
let width = bitmap.width as f32;
|
||||
let height = bitmap.height as f32;
|
||||
if let DrawType::Bitmap(Bitmap { id: draw_id, .. }) = &mut draw.draw_type {
|
||||
*draw_id = *id;
|
||||
}
|
||||
|
||||
// Scale the quad to the bitmap's dimensions.
|
||||
use ruffle_core::matrix::Matrix;
|
||||
let scale_transform = Transform {
|
||||
matrix: transform.matrix
|
||||
* Matrix {
|
||||
a: width,
|
||||
d: height,
|
||||
..Default::default()
|
||||
},
|
||||
..*transform
|
||||
};
|
||||
|
||||
// Render the quad.
|
||||
self.render_shape(self.quad_shape, &scale_transform);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) {
|
||||
let mesh = &self.meshes[shape.0];
|
||||
|
||||
let world_matrix = [
|
||||
[transform.matrix.a, transform.matrix.b, 0.0, 0.0],
|
||||
[transform.matrix.c, transform.matrix.d, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[
|
||||
transform.matrix.tx.to_pixels() as f32,
|
||||
transform.matrix.ty.to_pixels() as f32,
|
||||
0.0,
|
||||
1.0,
|
||||
],
|
||||
];
|
||||
|
||||
let mult_color = [
|
||||
transform.color_transform.r_mult,
|
||||
transform.color_transform.g_mult,
|
||||
transform.color_transform.b_mult,
|
||||
transform.color_transform.a_mult,
|
||||
];
|
||||
|
||||
let add_color = [
|
||||
transform.color_transform.r_add,
|
||||
transform.color_transform.g_add,
|
||||
transform.color_transform.b_add,
|
||||
transform.color_transform.a_add,
|
||||
];
|
||||
|
||||
// Set masking state.
|
||||
// TODO: Keep a dirty flag and minimize state changes.
|
||||
if self.num_masks > 0 {
|
||||
self.gl.enable(Gl::STENCIL_TEST);
|
||||
if self.num_masks_active < self.num_masks {
|
||||
self.gl.stencil_mask(self.write_stencil_mask);
|
||||
self.gl
|
||||
.stencil_func(Gl::ALWAYS, self.write_stencil_mask as i32, 0xff);
|
||||
self.gl.stencil_op(Gl::KEEP, Gl::KEEP, Gl::REPLACE);
|
||||
self.gl.color_mask(false, false, false, false);
|
||||
} else {
|
||||
self.gl.stencil_mask(0);
|
||||
self.gl.stencil_func(
|
||||
Gl::EQUAL,
|
||||
self.test_stencil_mask as i32,
|
||||
self.test_stencil_mask,
|
||||
);
|
||||
self.gl.stencil_op(Gl::KEEP, Gl::KEEP, Gl::KEEP);
|
||||
self.gl.color_mask(true, true, true, true);
|
||||
}
|
||||
} else {
|
||||
self.gl.disable(Gl::STENCIL_TEST);
|
||||
self.gl.color_mask(true, true, true, true);
|
||||
}
|
||||
|
||||
for draw in &mesh.draws {
|
||||
self.gl
|
||||
.bind_buffer(Gl::ARRAY_BUFFER, Some(&draw.vertex_buffer));
|
||||
self.gl
|
||||
.bind_buffer(Gl::ELEMENT_ARRAY_BUFFER, Some(&draw.index_buffer));
|
||||
self.gl.vertex_attrib_pointer_with_i32(
|
||||
self.vertex_position_location,
|
||||
2,
|
||||
Gl::FLOAT,
|
||||
false,
|
||||
24, //std::mem::size_of::<Vertex>() as i32,
|
||||
0,
|
||||
);
|
||||
self.gl.vertex_attrib_pointer_with_i32(
|
||||
self.vertex_color_location,
|
||||
4,
|
||||
Gl::FLOAT,
|
||||
false,
|
||||
24, //std::mem::size_of::<Vertex>() as i32,
|
||||
8,
|
||||
);
|
||||
|
||||
let program = match &draw.draw_type {
|
||||
DrawType::Color => &self.color_program,
|
||||
DrawType::Gradient(_) => &self.gradient_program,
|
||||
DrawType::Bitmap { .. } => &self.bitmap_program,
|
||||
};
|
||||
self.gl.use_program(Some(&program.program));
|
||||
|
||||
// Set common uniforms.
|
||||
program.uniform_matrix4fv(&self.gl, ShaderUniform::WorldMatrix, &world_matrix);
|
||||
program.uniform_matrix4fv(&self.gl, ShaderUniform::ViewMatrix, &self.view_matrix);
|
||||
program.uniform4fv(&self.gl, ShaderUniform::MultColor, &mult_color);
|
||||
program.uniform4fv(&self.gl, ShaderUniform::AddColor, &add_color);
|
||||
|
||||
// Set shader specific uniforms.
|
||||
match &draw.draw_type {
|
||||
DrawType::Color => {
|
||||
self.gl.blend_func(Gl::SRC_ALPHA, Gl::ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
DrawType::Gradient(gradient) => {
|
||||
self.gl.blend_func(Gl::SRC_ALPHA, Gl::ONE_MINUS_SRC_ALPHA);
|
||||
program.uniform_matrix3fv(
|
||||
&self.gl,
|
||||
ShaderUniform::TextureMatrix,
|
||||
&gradient.matrix,
|
||||
);
|
||||
program.uniform1i(
|
||||
&self.gl,
|
||||
ShaderUniform::GradientType,
|
||||
gradient.gradient_type,
|
||||
);
|
||||
program.uniform1fv(&self.gl, ShaderUniform::GradientRatios, &gradient.ratios);
|
||||
let colors =
|
||||
unsafe { std::slice::from_raw_parts(gradient.colors[0].as_ptr(), 32) };
|
||||
program.uniform4fv(&self.gl, ShaderUniform::GradientColors, &colors);
|
||||
program.uniform1i(
|
||||
&self.gl,
|
||||
ShaderUniform::GradientNumColors,
|
||||
gradient.num_colors as i32,
|
||||
);
|
||||
program.uniform1i(
|
||||
&self.gl,
|
||||
ShaderUniform::GradientRepeatMode,
|
||||
gradient.repeat_mode,
|
||||
);
|
||||
program.uniform1f(
|
||||
&self.gl,
|
||||
ShaderUniform::GradientFocalPoint,
|
||||
gradient.focal_point,
|
||||
);
|
||||
}
|
||||
DrawType::Bitmap(bitmap) => {
|
||||
// Bitmaps use pre-multiplied alpha.
|
||||
self.gl.blend_func(Gl::ONE, Gl::ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
let texture = &self
|
||||
.textures
|
||||
.iter()
|
||||
.find(|(id, _tex)| *id == bitmap.id)
|
||||
.unwrap()
|
||||
.1;
|
||||
|
||||
program.uniform_matrix3fv(
|
||||
&self.gl,
|
||||
ShaderUniform::TextureMatrix,
|
||||
&bitmap.matrix,
|
||||
);
|
||||
|
||||
// Bind texture.
|
||||
self.gl.active_texture(Gl::TEXTURE0);
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture.texture));
|
||||
program.uniform1i(&self.gl, ShaderUniform::BitmapTexture, 0);
|
||||
|
||||
// Set texture parameters.
|
||||
let filter = if bitmap.is_smoothed {
|
||||
Gl::LINEAR as i32
|
||||
} else {
|
||||
Gl::NEAREST as i32
|
||||
};
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MAG_FILTER, filter);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MIN_FILTER, filter);
|
||||
let wrap = if bitmap.is_repeating {
|
||||
Gl::MIRRORED_REPEAT as i32
|
||||
} else {
|
||||
Gl::CLAMP_TO_EDGE as i32
|
||||
};
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_S, wrap);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_T, wrap);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the triangles.
|
||||
self.gl
|
||||
.draw_elements_with_i32(Gl::TRIANGLES, draw.num_indices, Gl::UNSIGNED_SHORT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_letterbox(&mut self, letterbox: Letterbox) {
|
||||
self.gl.clear_color(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
match letterbox {
|
||||
Letterbox::None => (),
|
||||
Letterbox::Letterbox(margin_height) => {
|
||||
self.gl.enable(Gl::SCISSOR_TEST);
|
||||
self.gl
|
||||
.scissor(0, 0, self.viewport_width as i32, margin_height as i32);
|
||||
self.gl.clear(Gl::COLOR_BUFFER_BIT);
|
||||
self.gl.scissor(
|
||||
0,
|
||||
(self.viewport_height - margin_height) as i32,
|
||||
self.viewport_width as i32,
|
||||
margin_height as i32 + 1,
|
||||
);
|
||||
self.gl.clear(Gl::COLOR_BUFFER_BIT);
|
||||
self.gl.disable(Gl::SCISSOR_TEST);
|
||||
}
|
||||
Letterbox::Pillarbox(margin_width) => {
|
||||
self.gl.enable(Gl::SCISSOR_TEST);
|
||||
self.gl
|
||||
.scissor(0, 0, margin_width as i32, self.viewport_height as i32);
|
||||
self.gl.clear(Gl::COLOR_BUFFER_BIT);
|
||||
self.gl.scissor(
|
||||
(self.viewport_width - margin_width) as i32,
|
||||
0,
|
||||
margin_width as i32 + 1,
|
||||
self.viewport_height as i32,
|
||||
);
|
||||
self.gl.clear(Gl::COLOR_BUFFER_BIT);
|
||||
self.gl.scissor(
|
||||
0,
|
||||
0,
|
||||
self.viewport_width as i32,
|
||||
self.viewport_height as i32,
|
||||
);
|
||||
self.gl.disable(Gl::SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 >= 0x100 {
|
||||
// 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).
|
||||
if self.test_stencil_mask != 0 {
|
||||
log::warn!(
|
||||
"Too many masks active for stencil buffer; possibly incorrect rendering"
|
||||
);
|
||||
}
|
||||
self.next_stencil_mask = 1;
|
||||
self.gl.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 {
|
||||
width: u32,
|
||||
height: u32,
|
||||
texture: WebGlTexture,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Vertex {
|
||||
position: [f32; 2],
|
||||
color: [f32; 4],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Gradient {
|
||||
matrix: [[f32; 3]; 3],
|
||||
gradient_type: i32,
|
||||
ratios: [f32; 8],
|
||||
colors: [[f32; 4]; 8],
|
||||
num_colors: u32,
|
||||
repeat_mode: i32,
|
||||
focal_point: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Bitmap {
|
||||
matrix: [[f32; 3]; 3],
|
||||
id: swf::CharacterId,
|
||||
is_repeating: bool,
|
||||
is_smoothed: bool,
|
||||
}
|
||||
|
||||
struct Mesh {
|
||||
draws: Vec<Draw>,
|
||||
}
|
||||
|
||||
struct Draw {
|
||||
draw_type: DrawType,
|
||||
vertex_buffer: WebGlBuffer,
|
||||
index_buffer: WebGlBuffer,
|
||||
num_indices: i32,
|
||||
}
|
||||
|
||||
enum DrawType {
|
||||
Color,
|
||||
Gradient(Box<Gradient>),
|
||||
Bitmap(Bitmap),
|
||||
}
|
||||
|
||||
// Because the shaders are currently simple and few in number, we are using a
|
||||
// straightforward shader model. We maintain an enum of every possible uniform,
|
||||
// and each shader tries to grab the location of each uniform.
|
||||
struct ShaderProgram {
|
||||
program: WebGlProgram,
|
||||
uniforms: [Option<WebGlUniformLocation>; NUM_UNIFORMS],
|
||||
}
|
||||
|
||||
// These should match the uniform names in the shaders.
|
||||
const NUM_UNIFORMS: usize = 12;
|
||||
const UNIFORM_NAMES: [&str; NUM_UNIFORMS] = [
|
||||
"world_matrix",
|
||||
"view_matrix",
|
||||
"mult_color",
|
||||
"add_color",
|
||||
"u_matrix",
|
||||
"u_gradient_type",
|
||||
"u_ratios",
|
||||
"u_colors",
|
||||
"u_num_colors",
|
||||
"u_repeat_mode",
|
||||
"u_focal_point",
|
||||
"u_texture",
|
||||
];
|
||||
|
||||
enum ShaderUniform {
|
||||
WorldMatrix = 0,
|
||||
ViewMatrix,
|
||||
MultColor,
|
||||
AddColor,
|
||||
TextureMatrix,
|
||||
GradientType,
|
||||
GradientRatios,
|
||||
GradientColors,
|
||||
GradientNumColors,
|
||||
GradientRepeatMode,
|
||||
GradientFocalPoint,
|
||||
BitmapTexture,
|
||||
}
|
||||
|
||||
impl ShaderProgram {
|
||||
fn new(
|
||||
gl: &Gl,
|
||||
vertex_shader: &WebGlShader,
|
||||
fragment_shader: &WebGlShader,
|
||||
) -> Result<Self, Error> {
|
||||
let program = gl.create_program().ok_or("Unable to create program")?;
|
||||
gl.attach_shader(&program, &vertex_shader);
|
||||
gl.attach_shader(&program, &fragment_shader);
|
||||
|
||||
gl.link_program(&program);
|
||||
if !gl
|
||||
.get_program_parameter(&program, Gl::LINK_STATUS)
|
||||
.as_bool()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let msg = format!(
|
||||
"Error linking shader program: {:?}",
|
||||
gl.get_program_info_log(&program)
|
||||
);
|
||||
log::error!("{}", msg);
|
||||
return Err(msg.into());
|
||||
}
|
||||
|
||||
// Find uniforms.
|
||||
let mut uniforms: [Option<WebGlUniformLocation>; NUM_UNIFORMS] = Default::default();
|
||||
for i in 0..NUM_UNIFORMS {
|
||||
uniforms[i] = gl.get_uniform_location(&program, UNIFORM_NAMES[i]);
|
||||
}
|
||||
|
||||
Ok(ShaderProgram { program, uniforms })
|
||||
}
|
||||
|
||||
fn uniform1f(&self, gl: &Gl, uniform: ShaderUniform, value: f32) {
|
||||
gl.uniform1f(self.uniforms[uniform as usize].as_ref(), value);
|
||||
}
|
||||
|
||||
fn uniform1fv(&self, gl: &Gl, uniform: ShaderUniform, values: &[f32]) {
|
||||
gl.uniform1fv_with_f32_array(self.uniforms[uniform as usize].as_ref(), values);
|
||||
}
|
||||
|
||||
fn uniform1i(&self, gl: &Gl, uniform: ShaderUniform, value: i32) {
|
||||
gl.uniform1i(self.uniforms[uniform as usize].as_ref(), value);
|
||||
}
|
||||
|
||||
fn uniform4fv(&self, gl: &Gl, uniform: ShaderUniform, values: &[f32]) {
|
||||
gl.uniform4fv_with_f32_array(self.uniforms[uniform as usize].as_ref(), values);
|
||||
}
|
||||
|
||||
fn uniform_matrix3fv(&self, gl: &Gl, uniform: ShaderUniform, values: &[[f32; 3]; 3]) {
|
||||
gl.uniform_matrix3fv_with_f32_array(
|
||||
self.uniforms[uniform as usize].as_ref(),
|
||||
false,
|
||||
unsafe { std::slice::from_raw_parts(values[0].as_ptr(), 9) },
|
||||
);
|
||||
}
|
||||
|
||||
fn uniform_matrix4fv(&self, gl: &Gl, uniform: ShaderUniform, values: &[[f32; 4]; 4]) {
|
||||
gl.uniform_matrix4fv_with_f32_array(
|
||||
self.uniforms[uniform as usize].as_ref(),
|
||||
false,
|
||||
unsafe { std::slice::from_raw_parts(values[0].as_ptr(), 16) },
|
||||
);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ js-sys = "0.3.25"
|
|||
log = "0.4"
|
||||
ruffle_render_canvas = { path = "../render/canvas" }
|
||||
ruffle_web_common = { path = "common" }
|
||||
ruffle_render_webgl = { path = "../render/webgl" }
|
||||
url = "2.1.1"
|
||||
wasm-bindgen = "0.2.57"
|
||||
wasm-bindgen-futures = "0.4.4"
|
||||
|
|
|
@ -8,6 +8,7 @@ use generational_arena::{Arena, Index};
|
|||
use js_sys::Uint8Array;
|
||||
use ruffle_core::PlayerEvent;
|
||||
use ruffle_render_canvas::WebCanvasRenderBackend;
|
||||
use ruffle_render_webgl::WebGlRenderBackend;
|
||||
use std::mem::drop;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{cell::RefCell, error::Error, num::NonZeroI32};
|
||||
|
@ -101,7 +102,7 @@ impl Ruffle {
|
|||
swf_data.copy_to(&mut data[..]);
|
||||
|
||||
let window = web_sys::window().ok_or_else(|| "Expected window")?;
|
||||
let renderer = Box::new(WebCanvasRenderBackend::new(&canvas)?);
|
||||
let renderer = Box::new(WebGlRenderBackend::new(&canvas)?);
|
||||
let audio = Box::new(WebAudioBackend::new()?);
|
||||
let navigator = Box::new(WebNavigatorBackend::new());
|
||||
let input = Box::new(WebInputBackend::new(&canvas));
|
||||
|
|
Loading…
Reference in New Issue