Download shaders via XHR

This commit is contained in:
Patrick Walton 2017-08-11 21:26:25 -07:00
parent ebeaf119ba
commit ee9c1a7998
3 changed files with 143 additions and 9 deletions

View File

@ -8,6 +8,39 @@ const FONT_SIZE: number = 16.0;
const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font";
const SHADER_URLS: ShaderURLMap = {
directCurve: {
vertex: "/glsl/gles2/direct-curve.vs.glsl",
fragment: "/glsl/gles2/direct-curve.fs.glsl",
},
directInterior: {
vertex: "/glsl/gles2/direct-interior.vs.glsl",
fragment: "/glsl/gles2/direct-interior.fs.glsl",
},
};
interface ShaderURLMap {
[shaderName: string]: { 'vertex': string, 'fragment': string }
}
interface ShaderMap {
[shaderName: string]: { [shaderType: number]: WebGLShader };
}
interface ShaderProgramMap {
[shaderProgramName: string]: PathfinderShaderProgram;
}
type ShaderType = number;
type ShaderTypeName = 'vertex' | 'fragment';
function expect<T>(value: T | null, message: string): T {
if (value == null)
throw new Error(message);
return value;
}
class PathfinderMeshes {
constructor(encodedResponse: string) {
const response = JSON.parse(encodedResponse);
@ -35,7 +68,7 @@ class AppController {
}
loadFont() {
const file = this.loadFontButton.files[0];
const file = expect(this.loadFontButton.files, "No file selected!")[0];
const reader = new FileReader;
reader.addEventListener('loadend', () => {
this.fontData = reader.result;
@ -46,12 +79,10 @@ class AppController {
fontLoaded() {
this.font = opentype.parse(this.fontData);
if (!this.font.supported) {
window.alert("The font type is unsupported.");
return;
}
if (!this.font.supported)
throw new Error("The font type is unsupported.");
const glyphIDs = this.font.stringToGlyphs(TEXT).map(glyph => glyph.index);
const glyphIDs = this.font.stringToGlyphs(TEXT).map((glyph: any) => glyph.index);
const request = {
otf: base64js.fromByteArray(new Uint8Array(this.fontData)),
@ -60,7 +91,7 @@ class AppController {
pointSize: FONT_SIZE,
};
const xhr = new XMLHttpRequest();
const xhr = new XMLHttpRequest;
xhr.addEventListener('load', () => {
this.meshes = new PathfinderMeshes(xhr.responseText);
this.meshesReceived();
@ -84,9 +115,83 @@ class AppController {
class PathfinderView {
constructor(canvas: HTMLCanvasElement) {
this.canvas = canvas;
this.initContext();
this.loadShaders().then(shaders => this.shaderProgramsPromise = this.linkShaders(shaders));
window.addEventListener('resize', () => this.resizeToFit(), false);
this.resizeToFit();
}
initContext() {
this.gl = expect(this.canvas.getContext('webgl', { antialias: false, depth: true }),
"Failed to initialize WebGL! Check that your browser supports it.");
this.gl.clearColor(0.0, 0.0, 1.0, 1.0);
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
this.gl.flush();
}
loadShaders(): Promise<ShaderMap> {
return new Promise((resolve, reject) => {
let shaders: ShaderMap = {};
const shaderKeys = Object.keys(SHADER_URLS);
let shaderKeysLeft = shaderKeys.length;
let loaded = (type: ShaderType, shaderKey: string, source: string) => {
const shader = this.gl.createShader(type);
if (shader == null)
throw new Error("Failed to create shader!");
this.gl.shaderSource(shader, source);
this.gl.compileShader(shader);
if (!(shaderKey in shaders))
shaders[shaderKey] = {};
shaders[shaderKey][type] = shader;
shaderKeysLeft--;
if (shaderKeysLeft == 0)
resolve(shaders);
};
for (const shaderKey of shaderKeys) {
for (const typeName of ['vertex', 'fragment'] as Array<ShaderTypeName>) {
const type = {
vertex: this.gl.VERTEX_SHADER,
fragment: this.gl.FRAGMENT_SHADER,
}[typeName];
const xhr = new XMLHttpRequest;
xhr.addEventListener('load',
() => loaded(type, shaderKey, xhr.responseText),
false);
xhr.addEventListener('error', () => reject(), false);
xhr.open('GET', SHADER_URLS[shaderKey][typeName], true);
xhr.send();
}
}
});
}
linkShaders(shaders: ShaderMap): Promise<ShaderProgramMap> {
// TODO(pcwalton)
throw new Error("TODO");
}
resizeToFit() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight - this.canvas.scrollTop;
}
canvas: HTMLCanvasElement;
gl: WebGLRenderingContext;
shaderProgramsPromise: Promise<ShaderProgramMap>;
}
class PathfinderShaderProgram {
constructor(vertexShaderSource: string, fragmentShaderSource: string) {
// TODO(pcwalton)
}
}
function main() {

View File

@ -8,7 +8,9 @@
"typeRoots": [
"node_modules/@types"
],
"sourceMap": true
"sourceMap": true,
"strictNullChecks": true,
"noImplicitAny": true
},
"include": [
"src"

View File

@ -26,9 +26,12 @@ use euclid::{Point2D, Size2D};
use pathfinder_font_renderer::{FontContext, FontInstanceKey, FontKey};
use pathfinder_font_renderer::{GlyphKey, GlyphOutlineBuffer};
use pathfinder_partitioner::partitioner::Partitioner;
use rocket::response::NamedFile;
use rocket::http::{ContentType, Status};
use rocket::request::Request;
use rocket::response::{NamedFile, Responder, Response};
use rocket_contrib::json::Json;
use serde::Serialize;
use std::fs::File;
use std::io;
use std::mem;
use std::path::{Path, PathBuf};
@ -38,6 +41,7 @@ static STATIC_CSS_BOOTSTRAP_PATH: &'static str = "../client/node_modules/bootstr
static STATIC_JS_BOOTSTRAP_PATH: &'static str = "../client/node_modules/bootstrap/dist/js";
static STATIC_JS_JQUERY_PATH: &'static str = "../client/node_modules/jquery/dist";
static STATIC_JS_PATHFINDER_JS_PATH: &'static str = "../client/pathfinder.js";
static STATIC_GLSL_PATH: &'static str = "../../shaders";
#[derive(Clone, Copy, Serialize, Deserialize)]
struct IndexRange {
@ -304,6 +308,28 @@ fn static_js_bootstrap(file: PathBuf) -> Option<NamedFile> {
fn static_js_jquery(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new(STATIC_JS_JQUERY_PATH).join(file)).ok()
}
#[get("/glsl/<file..>")]
fn static_glsl(file: PathBuf) -> Option<Shader> {
Shader::open(Path::new(STATIC_GLSL_PATH).join(file)).ok()
}
struct Shader {
file: File,
}
impl Shader {
fn open(path: PathBuf) -> io::Result<Shader> {
File::open(path).map(|file| Shader {
file: file,
})
}
}
impl<'a> Responder<'a> for Shader {
fn respond_to(self, _: &Request) -> Result<Response<'a>, Status> {
Response::build().header(ContentType::Plain).streamed_body(self.file).ok()
}
}
fn main() {
rocket::ignite().mount("/", routes![
@ -313,5 +339,6 @@ fn main() {
static_css_bootstrap,
static_js_bootstrap,
static_js_jquery,
static_glsl,
]).launch();
}