Download shaders via XHR
This commit is contained in:
parent
ebeaf119ba
commit
ee9c1a7998
|
@ -8,6 +8,39 @@ const FONT_SIZE: number = 16.0;
|
||||||
|
|
||||||
const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font";
|
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 {
|
class PathfinderMeshes {
|
||||||
constructor(encodedResponse: string) {
|
constructor(encodedResponse: string) {
|
||||||
const response = JSON.parse(encodedResponse);
|
const response = JSON.parse(encodedResponse);
|
||||||
|
@ -35,7 +68,7 @@ class AppController {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFont() {
|
loadFont() {
|
||||||
const file = this.loadFontButton.files[0];
|
const file = expect(this.loadFontButton.files, "No file selected!")[0];
|
||||||
const reader = new FileReader;
|
const reader = new FileReader;
|
||||||
reader.addEventListener('loadend', () => {
|
reader.addEventListener('loadend', () => {
|
||||||
this.fontData = reader.result;
|
this.fontData = reader.result;
|
||||||
|
@ -46,12 +79,10 @@ class AppController {
|
||||||
|
|
||||||
fontLoaded() {
|
fontLoaded() {
|
||||||
this.font = opentype.parse(this.fontData);
|
this.font = opentype.parse(this.fontData);
|
||||||
if (!this.font.supported) {
|
if (!this.font.supported)
|
||||||
window.alert("The font type is unsupported.");
|
throw new Error("The font type is unsupported.");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const glyphIDs = this.font.stringToGlyphs(TEXT).map(glyph => glyph.index);
|
const glyphIDs = this.font.stringToGlyphs(TEXT).map((glyph: any) => glyph.index);
|
||||||
|
|
||||||
const request = {
|
const request = {
|
||||||
otf: base64js.fromByteArray(new Uint8Array(this.fontData)),
|
otf: base64js.fromByteArray(new Uint8Array(this.fontData)),
|
||||||
|
@ -60,7 +91,7 @@ class AppController {
|
||||||
pointSize: FONT_SIZE,
|
pointSize: FONT_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest;
|
||||||
xhr.addEventListener('load', () => {
|
xhr.addEventListener('load', () => {
|
||||||
this.meshes = new PathfinderMeshes(xhr.responseText);
|
this.meshes = new PathfinderMeshes(xhr.responseText);
|
||||||
this.meshesReceived();
|
this.meshesReceived();
|
||||||
|
@ -84,9 +115,83 @@ class AppController {
|
||||||
class PathfinderView {
|
class PathfinderView {
|
||||||
constructor(canvas: HTMLCanvasElement) {
|
constructor(canvas: HTMLCanvasElement) {
|
||||||
this.canvas = canvas;
|
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;
|
canvas: HTMLCanvasElement;
|
||||||
|
gl: WebGLRenderingContext;
|
||||||
|
shaderProgramsPromise: Promise<ShaderProgramMap>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PathfinderShaderProgram {
|
||||||
|
constructor(vertexShaderSource: string, fragmentShaderSource: string) {
|
||||||
|
// TODO(pcwalton)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"node_modules/@types"
|
"node_modules/@types"
|
||||||
],
|
],
|
||||||
"sourceMap": true
|
"sourceMap": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noImplicitAny": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src"
|
||||||
|
|
|
@ -26,9 +26,12 @@ use euclid::{Point2D, Size2D};
|
||||||
use pathfinder_font_renderer::{FontContext, FontInstanceKey, FontKey};
|
use pathfinder_font_renderer::{FontContext, FontInstanceKey, FontKey};
|
||||||
use pathfinder_font_renderer::{GlyphKey, GlyphOutlineBuffer};
|
use pathfinder_font_renderer::{GlyphKey, GlyphOutlineBuffer};
|
||||||
use pathfinder_partitioner::partitioner::Partitioner;
|
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 rocket_contrib::json::Json;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
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_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_JQUERY_PATH: &'static str = "../client/node_modules/jquery/dist";
|
||||||
static STATIC_JS_PATHFINDER_JS_PATH: &'static str = "../client/pathfinder.js";
|
static STATIC_JS_PATHFINDER_JS_PATH: &'static str = "../client/pathfinder.js";
|
||||||
|
static STATIC_GLSL_PATH: &'static str = "../../shaders";
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
struct IndexRange {
|
struct IndexRange {
|
||||||
|
@ -304,6 +308,28 @@ fn static_js_bootstrap(file: PathBuf) -> Option<NamedFile> {
|
||||||
fn static_js_jquery(file: PathBuf) -> Option<NamedFile> {
|
fn static_js_jquery(file: PathBuf) -> Option<NamedFile> {
|
||||||
NamedFile::open(Path::new(STATIC_JS_JQUERY_PATH).join(file)).ok()
|
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() {
|
fn main() {
|
||||||
rocket::ignite().mount("/", routes![
|
rocket::ignite().mount("/", routes![
|
||||||
|
@ -313,5 +339,6 @@ fn main() {
|
||||||
static_css_bootstrap,
|
static_css_bootstrap,
|
||||||
static_js_bootstrap,
|
static_js_bootstrap,
|
||||||
static_js_jquery,
|
static_js_jquery,
|
||||||
|
static_glsl,
|
||||||
]).launch();
|
]).launch();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue