Factor out the app controller logic so we can support multiple demos more easily
This commit is contained in:
parent
01d1382324
commit
9e7d922863
|
@ -0,0 +1,33 @@
|
||||||
|
// pathfinder/client/src/app-controller.ts
|
||||||
|
//
|
||||||
|
// Copyright © 2017 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader';
|
||||||
|
|
||||||
|
export default abstract class AppController<View> {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
const canvas = document.getElementById('pf-canvas') as HTMLCanvasElement;
|
||||||
|
|
||||||
|
const shaderLoader = new ShaderLoader;
|
||||||
|
shaderLoader.load();
|
||||||
|
|
||||||
|
this.view = Promise.all([shaderLoader.common, shaderLoader.shaders]).then(allShaders => {
|
||||||
|
return this.createView(canvas, allShaders[0], allShaders[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract createView(canvas: HTMLCanvasElement,
|
||||||
|
commonShaderSource: string,
|
||||||
|
shaderSources: ShaderMap<ShaderProgramSource>):
|
||||||
|
View;
|
||||||
|
|
||||||
|
view: Promise<View>;
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
// pathfinder/client/src/shader-loader.ts
|
||||||
|
//
|
||||||
|
// Copyright © 2017 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
const COMMON_SHADER_URL: string = '/glsl/gles2/common.inc.glsl';
|
||||||
|
|
||||||
|
export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
|
||||||
|
'blit',
|
||||||
|
'directCurve',
|
||||||
|
'directInterior',
|
||||||
|
'ecaaEdgeDetect',
|
||||||
|
'ecaaCover',
|
||||||
|
'ecaaLine',
|
||||||
|
'ecaaCurve',
|
||||||
|
'ecaaMonoResolve',
|
||||||
|
'ecaaMultiResolve',
|
||||||
|
];
|
||||||
|
|
||||||
|
const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
|
||||||
|
blit: {
|
||||||
|
vertex: "/glsl/gles2/blit.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/blit.fs.glsl",
|
||||||
|
},
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
ecaaEdgeDetect: {
|
||||||
|
vertex: "/glsl/gles2/ecaa-edge-detect.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/ecaa-edge-detect.fs.glsl",
|
||||||
|
},
|
||||||
|
ecaaCover: {
|
||||||
|
vertex: "/glsl/gles2/ecaa-cover.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/ecaa-cover.fs.glsl",
|
||||||
|
},
|
||||||
|
ecaaLine: {
|
||||||
|
vertex: "/glsl/gles2/ecaa-line.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/ecaa-line.fs.glsl",
|
||||||
|
},
|
||||||
|
ecaaCurve: {
|
||||||
|
vertex: "/glsl/gles2/ecaa-curve.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/ecaa-curve.fs.glsl",
|
||||||
|
},
|
||||||
|
ecaaMonoResolve: {
|
||||||
|
vertex: "/glsl/gles2/ecaa-mono-resolve.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/ecaa-mono-resolve.fs.glsl",
|
||||||
|
},
|
||||||
|
ecaaMultiResolve: {
|
||||||
|
vertex: "/glsl/gles2/ecaa-multi-resolve.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/ecaa-multi-resolve.fs.glsl",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ShaderMap<T> {
|
||||||
|
blit: T;
|
||||||
|
directCurve: T;
|
||||||
|
directInterior: T;
|
||||||
|
ecaaEdgeDetect: T;
|
||||||
|
ecaaCover: T;
|
||||||
|
ecaaLine: T;
|
||||||
|
ecaaCurve: T;
|
||||||
|
ecaaMonoResolve: T;
|
||||||
|
ecaaMultiResolve: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShaderProgramSource {
|
||||||
|
vertex: string;
|
||||||
|
fragment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShaderProgramURLs {
|
||||||
|
vertex: string;
|
||||||
|
fragment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ShaderLoader {
|
||||||
|
load() {
|
||||||
|
this.common = window.fetch(COMMON_SHADER_URL).then(response => response.text());
|
||||||
|
|
||||||
|
const shaderKeys = Object.keys(SHADER_URLS) as Array<keyof ShaderMap<string>>;
|
||||||
|
let promises = [];
|
||||||
|
for (const shaderKey of shaderKeys) {
|
||||||
|
promises.push(Promise.all([
|
||||||
|
window.fetch(SHADER_URLS[shaderKey].vertex).then(response => response.text()),
|
||||||
|
window.fetch(SHADER_URLS[shaderKey].fragment).then(response => response.text()),
|
||||||
|
]).then(results => { return { vertex: results[0], fragment: results[1] } }));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shaders = Promise.all(promises).then(promises => {
|
||||||
|
let shaderMap: Partial<ShaderMap<ShaderProgramSource>> = {};
|
||||||
|
for (let keyIndex = 0; keyIndex < shaderKeys.length; keyIndex++)
|
||||||
|
shaderMap[shaderKeys[keyIndex]] = promises[keyIndex];
|
||||||
|
return shaderMap as ShaderMap<ShaderProgramSource>;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
common: Promise<string>;
|
||||||
|
shaders: Promise<ShaderMap<ShaderProgramSource>>;
|
||||||
|
}
|
|
@ -13,6 +13,9 @@ import * as base64js from 'base64-js';
|
||||||
import * as glmatrix from 'gl-matrix';
|
import * as glmatrix from 'gl-matrix';
|
||||||
import * as opentype from 'opentype.js';
|
import * as opentype from 'opentype.js';
|
||||||
|
|
||||||
|
import AppController from './app-controller';
|
||||||
|
import {SHADER_NAMES, ShaderMap, ShaderProgramSource} from './shader-loader';
|
||||||
|
|
||||||
const TEXT: string =
|
const TEXT: string =
|
||||||
`’Twas brillig, and the slithy toves
|
`’Twas brillig, and the slithy toves
|
||||||
Did gyre and gimble in the wabe;
|
Did gyre and gimble in the wabe;
|
||||||
|
@ -57,8 +60,6 @@ const TIME_INTERVAL_DELAY: number = 32;
|
||||||
|
|
||||||
const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font";
|
const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font";
|
||||||
|
|
||||||
const COMMON_SHADER_URL: string = '/glsl/gles2/common.inc.glsl';
|
|
||||||
|
|
||||||
const UINT32_SIZE: number = 4;
|
const UINT32_SIZE: number = 4;
|
||||||
|
|
||||||
const B_POSITION_SIZE: number = 8;
|
const B_POSITION_SIZE: number = 8;
|
||||||
|
@ -75,45 +76,6 @@ const B_QUAD_LOWER_INDICES_OFFSET: number = 4 * 4;
|
||||||
|
|
||||||
const ATLAS_SIZE: glmatrix.vec2 = glmatrix.vec2.fromValues(3072, 3072);
|
const ATLAS_SIZE: glmatrix.vec2 = glmatrix.vec2.fromValues(3072, 3072);
|
||||||
|
|
||||||
const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
|
|
||||||
blit: {
|
|
||||||
vertex: "/glsl/gles2/blit.vs.glsl",
|
|
||||||
fragment: "/glsl/gles2/blit.fs.glsl",
|
|
||||||
},
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
ecaaEdgeDetect: {
|
|
||||||
vertex: "/glsl/gles2/ecaa-edge-detect.vs.glsl",
|
|
||||||
fragment: "/glsl/gles2/ecaa-edge-detect.fs.glsl",
|
|
||||||
},
|
|
||||||
ecaaCover: {
|
|
||||||
vertex: "/glsl/gles2/ecaa-cover.vs.glsl",
|
|
||||||
fragment: "/glsl/gles2/ecaa-cover.fs.glsl",
|
|
||||||
},
|
|
||||||
ecaaLine: {
|
|
||||||
vertex: "/glsl/gles2/ecaa-line.vs.glsl",
|
|
||||||
fragment: "/glsl/gles2/ecaa-line.fs.glsl",
|
|
||||||
},
|
|
||||||
ecaaCurve: {
|
|
||||||
vertex: "/glsl/gles2/ecaa-curve.vs.glsl",
|
|
||||||
fragment: "/glsl/gles2/ecaa-curve.fs.glsl",
|
|
||||||
},
|
|
||||||
ecaaMonoResolve: {
|
|
||||||
vertex: "/glsl/gles2/ecaa-mono-resolve.vs.glsl",
|
|
||||||
fragment: "/glsl/gles2/ecaa-mono-resolve.fs.glsl",
|
|
||||||
},
|
|
||||||
ecaaMultiResolve: {
|
|
||||||
vertex: "/glsl/gles2/ecaa-multi-resolve.vs.glsl",
|
|
||||||
fragment: "/glsl/gles2/ecaa-multi-resolve.fs.glsl",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
interface UnlinkedShaderProgram {
|
interface UnlinkedShaderProgram {
|
||||||
vertex: WebGLShader;
|
vertex: WebGLShader;
|
||||||
fragment: WebGLShader;
|
fragment: WebGLShader;
|
||||||
|
@ -130,28 +92,6 @@ interface Point2D {
|
||||||
|
|
||||||
type Size2D = glmatrix.vec2;
|
type Size2D = glmatrix.vec2;
|
||||||
|
|
||||||
interface ShaderProgramSource {
|
|
||||||
vertex: string;
|
|
||||||
fragment: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ShaderProgramURLs {
|
|
||||||
vertex: string;
|
|
||||||
fragment: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ShaderMap<T> {
|
|
||||||
blit: T;
|
|
||||||
directCurve: T;
|
|
||||||
directInterior: T;
|
|
||||||
ecaaEdgeDetect: T;
|
|
||||||
ecaaCover: T;
|
|
||||||
ecaaLine: T;
|
|
||||||
ecaaCurve: T;
|
|
||||||
ecaaMonoResolve: T;
|
|
||||||
ecaaMultiResolve: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UniformMap {
|
interface UniformMap {
|
||||||
[uniformName: string]: WebGLUniformLocation;
|
[uniformName: string]: WebGLUniformLocation;
|
||||||
}
|
}
|
||||||
|
@ -443,23 +383,20 @@ class PathfinderMeshBuffers implements Meshes<WebGLBuffer> {
|
||||||
readonly edgeLowerCurveIndices: WebGLBuffer;
|
readonly edgeLowerCurveIndices: WebGLBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppController {
|
class TextDemoController extends AppController<PathfinderView> {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this._atlas = new Atlas;
|
this._atlas = new Atlas;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
super.start();
|
||||||
|
|
||||||
this.fontSize = INITIAL_FONT_SIZE;
|
this.fontSize = INITIAL_FONT_SIZE;
|
||||||
|
|
||||||
this.fpsLabel = unwrapNull(document.getElementById('pf-fps-label'));
|
this.fpsLabel = unwrapNull(document.getElementById('pf-fps-label'));
|
||||||
|
|
||||||
const canvas = document.getElementById('pf-canvas') as HTMLCanvasElement;
|
const canvas = document.getElementById('pf-canvas') as HTMLCanvasElement;
|
||||||
const shaderLoader = new PathfinderShaderLoader;
|
|
||||||
shaderLoader.load();
|
|
||||||
this.view = Promise.all([shaderLoader.common, shaderLoader.shaders]).then(allShaders => {
|
|
||||||
return new PathfinderView(this, canvas, allShaders[0], allShaders[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loadFontButton = document.getElementById('pf-load-font-button') as HTMLInputElement;
|
this.loadFontButton = document.getElementById('pf-load-font-button') as HTMLInputElement;
|
||||||
this.loadFontButton.addEventListener('change', () => this.loadFont(), false);
|
this.loadFontButton.addEventListener('change', () => this.loadFont(), false);
|
||||||
|
|
||||||
|
@ -468,6 +405,12 @@ class AppController {
|
||||||
this.updateAALevel();
|
this.updateAALevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected createView(canvas: HTMLCanvasElement,
|
||||||
|
commonShaderSource: string,
|
||||||
|
shaderSources: ShaderMap<ShaderProgramSource>) {
|
||||||
|
return new PathfinderView(this, canvas, commonShaderSource, shaderSources);
|
||||||
|
}
|
||||||
|
|
||||||
private loadFont() {
|
private loadFont() {
|
||||||
const file = expectNotNull(this.loadFontButton.files, "No file selected!")[0];
|
const file = expectNotNull(this.loadFontButton.files, "No file selected!")[0];
|
||||||
const reader = new FileReader;
|
const reader = new FileReader;
|
||||||
|
@ -552,7 +495,6 @@ class AppController {
|
||||||
return this._atlas;
|
return this._atlas;
|
||||||
}
|
}
|
||||||
|
|
||||||
private view: Promise<PathfinderView>;
|
|
||||||
private loadFontButton: HTMLInputElement;
|
private loadFontButton: HTMLInputElement;
|
||||||
private aaLevelSelect: HTMLSelectElement;
|
private aaLevelSelect: HTMLSelectElement;
|
||||||
private fpsLabel: HTMLElement;
|
private fpsLabel: HTMLElement;
|
||||||
|
@ -572,33 +514,8 @@ class AppController {
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PathfinderShaderLoader {
|
|
||||||
load() {
|
|
||||||
this.common = window.fetch(COMMON_SHADER_URL).then(response => response.text());
|
|
||||||
|
|
||||||
const shaderKeys = Object.keys(SHADER_URLS) as Array<keyof ShaderMap<string>>;
|
|
||||||
let promises = [];
|
|
||||||
for (const shaderKey of shaderKeys) {
|
|
||||||
promises.push(Promise.all([
|
|
||||||
window.fetch(SHADER_URLS[shaderKey].vertex).then(response => response.text()),
|
|
||||||
window.fetch(SHADER_URLS[shaderKey].fragment).then(response => response.text()),
|
|
||||||
]).then(results => { return { vertex: results[0], fragment: results[1] } }));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.shaders = Promise.all(promises).then(promises => {
|
|
||||||
let shaderMap: Partial<ShaderMap<ShaderProgramSource>> = {};
|
|
||||||
for (let keyIndex = 0; keyIndex < shaderKeys.length; keyIndex++)
|
|
||||||
shaderMap[shaderKeys[keyIndex]] = promises[keyIndex];
|
|
||||||
return shaderMap as ShaderMap<ShaderProgramSource>;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
common: Promise<string>;
|
|
||||||
shaders: Promise<ShaderMap<ShaderProgramSource>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class PathfinderView {
|
class PathfinderView {
|
||||||
constructor(appController: AppController,
|
constructor(appController: TextDemoController,
|
||||||
canvas: HTMLCanvasElement,
|
canvas: HTMLCanvasElement,
|
||||||
commonShaderSource: string,
|
commonShaderSource: string,
|
||||||
shaderSources: ShaderMap<ShaderProgramSource>) {
|
shaderSources: ShaderMap<ShaderProgramSource>) {
|
||||||
|
@ -669,9 +586,8 @@ class PathfinderView {
|
||||||
private compileShaders(commonSource: string, shaderSources: ShaderMap<ShaderProgramSource>):
|
private compileShaders(commonSource: string, shaderSources: ShaderMap<ShaderProgramSource>):
|
||||||
ShaderMap<UnlinkedShaderProgram> {
|
ShaderMap<UnlinkedShaderProgram> {
|
||||||
let shaders: Partial<ShaderMap<Partial<UnlinkedShaderProgram>>> = {};
|
let shaders: Partial<ShaderMap<Partial<UnlinkedShaderProgram>>> = {};
|
||||||
const shaderKeys = Object.keys(SHADER_URLS) as Array<keyof ShaderMap<string>>;
|
|
||||||
|
|
||||||
for (const shaderKey of shaderKeys) {
|
for (const shaderKey of SHADER_NAMES) {
|
||||||
for (const typeName of ['vertex', 'fragment'] as Array<ShaderTypeName>) {
|
for (const typeName of ['vertex', 'fragment'] as Array<ShaderTypeName>) {
|
||||||
const type = {
|
const type = {
|
||||||
vertex: this.gl.VERTEX_SHADER,
|
vertex: this.gl.VERTEX_SHADER,
|
||||||
|
@ -1263,7 +1179,7 @@ class PathfinderView {
|
||||||
|
|
||||||
atlasTransformBuffer: PathfinderBufferTexture;
|
atlasTransformBuffer: PathfinderBufferTexture;
|
||||||
|
|
||||||
appController: AppController;
|
appController: TextDemoController;
|
||||||
|
|
||||||
private dirty: boolean;
|
private dirty: boolean;
|
||||||
}
|
}
|
||||||
|
@ -2250,7 +2166,7 @@ const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
const controller = new AppController;
|
const controller = new TextDemoController;
|
||||||
window.addEventListener('load', () => controller.start(), false);
|
window.addEventListener('load', () => controller.start(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue