Add all four sides of the Mozilla Monument to the 3D demo

This commit is contained in:
Patrick Walton 2017-09-07 19:01:55 -07:00
parent e34ca3d3e4
commit 57374e9f30
7 changed files with 134 additions and 79 deletions

View File

@ -23,6 +23,7 @@ import {PathfinderError, assert, panic, unwrapNull} from "./utils";
import {PathfinderDemoView, Timings} from "./view"; import {PathfinderDemoView, Timings} from "./view";
import SSAAStrategy from "./ssaa-strategy"; import SSAAStrategy from "./ssaa-strategy";
import * as _ from "lodash"; import * as _ from "lodash";
import PathfinderBufferTexture from "./buffer-texture";
const WIDTH: number = 150000; const WIDTH: number = 150000;
@ -34,9 +35,11 @@ const PIXELS_PER_UNIT: number = 1.0;
const FOV: number = 45.0; const FOV: number = 45.0;
const NEAR_CLIP_PLANE: number = 0.01; const NEAR_CLIP_PLANE: number = 0.01;
const FAR_CLIP_PLANE: number = 10000.0; const FAR_CLIP_PLANE: number = 200000.0;
const SCALE: glmatrix.vec3 = glmatrix.vec3.fromValues(1.0 / 200.0, 1.0 / 200.0, 1.0); const SCALE: glmatrix.vec3 = glmatrix.vec3.fromValues(1.0 / 200.0, 1.0 / 200.0, 1.0 / 200.0);
const TEXT_TRANSLATION: number[] = [-WIDTH * 0.5, 0.0, WIDTH * 0.5];
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = { const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
none: NoAAStrategy, none: NoAAStrategy,
@ -48,58 +51,65 @@ interface AntialiasingStrategyTable {
ssaa: typeof SSAAStrategy; ssaa: typeof SSAAStrategy;
} }
interface Panels { interface TextLine {
upper: string[][]; names: string[];
lower: string[][]; }
interface MonumentSide {
lines: TextLine[];
} }
class ThreeDController extends DemoAppController<ThreeDView> { class ThreeDController extends DemoAppController<ThreeDView> {
start() { start() {
super.start(); super.start();
this.textPromise = window.fetch(TEXT_DATA_URI) this.monumentPromise = window.fetch(TEXT_DATA_URI)
.then(response => response.json()) .then(response => response.json())
.then(textData => this.parseTextData(textData)); .then(textData => this.parseTextData(textData));
this.loadInitialFile(); this.loadInitialFile();
} }
private parseTextData(textData: any): string[][] { private parseTextData(textData: any): MonumentSide[] {
const panels = { const sides = [];
upper: [], for (let sideIndex = 0; sideIndex < 4; sideIndex++)
lower: [], sides[sideIndex] = { upper: { lines: [] }, lower: { lines: [] } };
};
for (const nameData of textData.monument) { for (const nameData of textData.monument) {
if (nameData.side !== '1') const side = parseInt(nameData.side) - 1;
const row = parseInt(nameData.row) - 1;
const number = parseInt(nameData.number) - 1;
if (sides[side] == null)
continue; continue;
const row = parseInt(nameData.row) - 1, number = parseInt(nameData.number) - 1; const lines: TextLine[] = sides[side][nameData.panel as ('upper' | 'lower')].lines;
const panel: string[][] = panels[nameData.panel as ('upper' | 'lower')]; if (lines[row] == null)
lines[row] = { names: [] };
if (panel[row] == null) lines[row].names[number] = nameData.name;
panel[row] = [];
panel[row][number] = nameData.name;
} }
return panels.upper.concat(panels.lower); return sides.map(side => ({ lines: side.upper.lines.concat(side.lower.lines) }));
} }
protected fileLoaded(): void { protected fileLoaded(): void {
const font = opentype.parse(this.fileData); const font = opentype.parse(this.fileData);
assert(font.isSupported(), "The font type is unsupported!"); assert(font.isSupported(), "The font type is unsupported!");
this.textPromise.then(text => this.layoutText(font, text)); this.monumentPromise.then(monument => this.layoutMonument(font, monument));
} }
private layoutText(font: opentype.Font, text: string[][]) { private layoutMonument(font: opentype.Font, monument: MonumentSide[]) {
const createGlyph = (glyph: opentype.Glyph) => new ThreeDGlyph(glyph); const createGlyph = (glyph: opentype.Glyph) => new ThreeDGlyph(glyph);
let textFrames = [];
for (const monumentSide of monument) {
let textRuns = []; let textRuns = [];
for (let lineNumber = 0; lineNumber < text.length; lineNumber++) { for (let lineNumber = 0; lineNumber < monumentSide.lines.length; lineNumber++) {
const line = text[lineNumber]; const line = monumentSide.lines[lineNumber];
const lineY = -lineNumber * font.lineHeight(); const lineY = -lineNumber * font.lineHeight();
const lineGlyphs = line.map(string => { const lineGlyphs = line.names.map(string => {
const glyphs = font.stringToGlyphs(string).map(createGlyph); const glyphs = font.stringToGlyphs(string).map(createGlyph);
return { glyphs: glyphs, width: _.sumBy(glyphs, glyph => glyph.advanceWidth) }; return { glyphs: glyphs, width: _.sumBy(glyphs, glyph => glyph.advanceWidth) };
}); });
@ -110,15 +120,16 @@ class ThreeDController extends DemoAppController<ThreeDView> {
let currentX = 0.0; let currentX = 0.0;
for (const glyphInfo of lineGlyphs) { for (const glyphInfo of lineGlyphs) {
textRuns.push(new TextRun(glyphInfo.glyphs, [currentX, lineY], font, createGlyph)); const textRunOrigin = [currentX, lineY];
textRuns.push(new TextRun(glyphInfo.glyphs, textRunOrigin, font, createGlyph));
currentX += glyphInfo.width + spacing; currentX += glyphInfo.width + spacing;
} }
} }
// TODO(pcwalton) textFrames.push(new TextFrame(textRuns));
const textFrame = new TextFrame(textRuns, glmatrix.vec3.create()); }
this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font); this.glyphStorage = new GlyphStorage(this.fileData, textFrames, createGlyph, font);
this.glyphStorage.layoutRuns(); this.glyphStorage.layoutRuns();
this.glyphStorage.partition().then((baseMeshes: PathfinderMeshData) => { this.glyphStorage.partition().then((baseMeshes: PathfinderMeshData) => {
@ -150,7 +161,7 @@ class ThreeDController extends DemoAppController<ThreeDView> {
private baseMeshes: PathfinderMeshData; private baseMeshes: PathfinderMeshData;
private expandedMeshes: ExpandedMeshData[]; private expandedMeshes: ExpandedMeshData[];
private textPromise: Promise<string[][]>; private monumentPromise: Promise<MonumentSide[]>;
} }
class ThreeDView extends PathfinderDemoView { class ThreeDView extends PathfinderDemoView {
@ -166,7 +177,15 @@ class ThreeDView extends PathfinderDemoView {
} }
uploadPathMetadata() { uploadPathMetadata() {
const textGlyphs = this.appController.glyphStorage.allGlyphs; this.pathColorsBufferTextures = [];
this.pathTransformBufferTextures = [];
const textFrameCount = this.appController.glyphStorage.textFrames.length;
for (let textFrameIndex = 0;
textFrameIndex < textFrameCount;
textFrameIndex++) {
const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
const textGlyphs = textFrame.allGlyphs;
const pathCount = textGlyphs.length; const pathCount = textGlyphs.length;
const pathColors = new Uint8Array(4 * (pathCount + 1)); const pathColors = new Uint8Array(4 * (pathCount + 1));
@ -184,8 +203,14 @@ class ThreeDView extends PathfinderDemoView {
pathTransforms.set([1, 1, glyphRect[0], glyphRect[1]], startOffset); pathTransforms.set([1, 1, glyphRect[0], glyphRect[1]], startOffset);
} }
this.pathColorsBufferTexture.upload(this.gl, pathColors); const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors');
this.pathTransformBufferTexture.upload(this.gl, pathTransforms); const pathTransformBufferTexture = new PathfinderBufferTexture(this.gl,
'uPathTransform');
pathColorsBufferTexture.upload(this.gl, pathColors);
pathTransformBufferTexture.upload(this.gl, pathTransforms);
this.pathColorsBufferTextures.push(pathColorsBufferTexture);
this.pathTransformBufferTextures.push(pathTransformBufferTexture);
}
} }
protected createAAStrategy(aaType: AntialiasingStrategyName, protected createAAStrategy(aaType: AntialiasingStrategyName,
@ -237,6 +262,13 @@ class ThreeDView extends PathfinderDemoView {
return transform; return transform;
} }
protected getModelviewTransform(objectIndex: number): glmatrix.mat4 {
const transform = glmatrix.mat4.create();
glmatrix.mat4.rotateY(transform, transform, Math.PI / 2.0 * objectIndex);
glmatrix.mat4.translate(transform, transform, TEXT_TRANSLATION);
return transform;
}
protected get directCurveProgramName(): keyof ShaderMap<void> { protected get directCurveProgramName(): keyof ShaderMap<void> {
return 'direct3DCurve'; return 'direct3DCurve';
} }

View File

@ -304,7 +304,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.setFramebufferSizeUniform(uniforms); view.setFramebufferSizeUniform(uniforms);
this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0); this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0);
this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1); this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1);
view.pathTransformBufferTexture.bind(view.gl, uniforms, 2); view.pathTransformBufferTextures[0].bind(view.gl, uniforms, 2);
view.gl.uniform1f(uniforms.uScaleX, this.supersampleScale[0]); view.gl.uniform1f(uniforms.uScaleX, this.supersampleScale[0]);
view.instancedArraysExt.drawElementsInstancedANGLE(view.gl.TRIANGLES, view.instancedArraysExt.drawElementsInstancedANGLE(view.gl.TRIANGLES,
6, 6,
@ -335,7 +335,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.setFramebufferSizeUniform(uniforms); view.setFramebufferSizeUniform(uniforms);
this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0); this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0);
this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1); this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1);
view.pathTransformBufferTexture.bind(view.gl, uniforms, 2); view.pathTransformBufferTextures[0].bind(view.gl, uniforms, 2);
view.gl.uniform1f(uniforms.uScaleX, this.supersampleScale[0]); view.gl.uniform1f(uniforms.uScaleX, this.supersampleScale[0]);
} }

View File

@ -45,7 +45,7 @@ class MeshDebuggerAppController extends AppController {
const createGlyph = (glyph: opentype.Glyph) => new MeshDebuggerGlyph(glyph); const createGlyph = (glyph: opentype.Glyph) => new MeshDebuggerGlyph(glyph);
const textRun = new TextRun<MeshDebuggerGlyph>(CHARACTER, [0, 0], font, createGlyph); const textRun = new TextRun<MeshDebuggerGlyph>(CHARACTER, [0, 0], font, createGlyph);
const textFrame = new TextFrame([textRun], glmatrix.vec3.create()); const textFrame = new TextFrame([textRun]);
this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font); this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font);
this.glyphStorage.partition().then(meshes => { this.glyphStorage.partition().then(meshes => {

View File

@ -21,6 +21,7 @@ import {ShaderMap, ShaderProgramSource} from './shader-loader';
import {panic, unwrapNull} from './utils'; import {panic, unwrapNull} from './utils';
import {PathfinderDemoView, Timings} from './view'; import {PathfinderDemoView, Timings} from './view';
import SSAAStrategy from "./ssaa-strategy"; import SSAAStrategy from "./ssaa-strategy";
import PathfinderBufferTexture from "./buffer-texture";
const parseColor = require('parse-color'); const parseColor = require('parse-color');
@ -208,8 +209,12 @@ class SVGDemoView extends PathfinderDemoView {
pathTransforms.set([1, 1, 0, 0], startOffset); pathTransforms.set([1, 1, 0, 0], startOffset);
} }
this.pathColorsBufferTexture.upload(this.gl, pathColors); const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors');
this.pathTransformBufferTexture.upload(this.gl, pathTransforms); const pathTransformBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathTransform');
pathColorsBufferTexture.upload(this.gl, pathColors);
pathTransformBufferTexture.upload(this.gl, pathTransforms);
this.pathColorsBufferTextures = [pathColorsBufferTexture];
this.pathTransformBufferTextures = [pathTransformBufferTexture];
} }
protected createAAStrategy(aaType: AntialiasingStrategyName, protected createAAStrategy(aaType: AntialiasingStrategyName,

View File

@ -248,7 +248,9 @@ class TextDemoView extends MonochromePathfinderView {
pathColors[(pathIndex + 1) * 4 + 3] = 0xff; // alpha pathColors[(pathIndex + 1) * 4 + 3] = 0xff; // alpha
} }
this.pathColorsBufferTexture.upload(this.gl, pathColors); const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors');
pathColorsBufferTexture.upload(this.gl, pathColors);
this.pathColorsBufferTextures = [pathColorsBufferTexture];
} }
/// Lays out glyphs on the canvas. /// Lays out glyphs on the canvas.
@ -322,7 +324,9 @@ class TextDemoView extends MonochromePathfinderView {
transforms[pathID * 4 + 3] = atlasOrigin[1]; transforms[pathID * 4 + 3] = atlasOrigin[1];
} }
this.pathTransformBufferTexture.upload(this.gl, transforms); const pathTransformBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathTransform');
pathTransformBufferTexture.upload(this.gl, transforms);
this.pathTransformBufferTextures = [pathTransformBufferTexture];
} }
private createAtlasFramebuffer() { private createAtlasFramebuffer() {

View File

@ -68,9 +68,8 @@ export class TextRun<Glyph extends PathfinderGlyph> {
} }
export class TextFrame<Glyph extends PathfinderGlyph> { export class TextFrame<Glyph extends PathfinderGlyph> {
constructor(runs: TextRun<Glyph>[], origin: glmatrix.vec3) { constructor(runs: TextRun<Glyph>[]) {
this.runs = runs; this.runs = runs;
this.origin = origin;
} }
expandMeshes(uniqueGlyphs: Glyph[], meshes: PathfinderMeshData): ExpandedMeshData { expandMeshes(uniqueGlyphs: Glyph[], meshes: PathfinderMeshData): ExpandedMeshData {
@ -253,7 +252,7 @@ export class SimpleTextLayout<Glyph extends PathfinderGlyph> {
const textRuns: TextRun<Glyph>[] = text.split("\n").map((line, lineNumber) => { const textRuns: TextRun<Glyph>[] = text.split("\n").map((line, lineNumber) => {
return new TextRun<Glyph>(line, [0.0, -lineHeight * lineNumber], font, createGlyph); return new TextRun<Glyph>(line, [0.0, -lineHeight * lineNumber], font, createGlyph);
}); });
this.textFrame = new TextFrame(textRuns, glmatrix.vec3.create()); this.textFrame = new TextFrame(textRuns);
this.glyphStorage = new GlyphStorage(fontData, [this.textFrame], createGlyph, font); this.glyphStorage = new GlyphStorage(fontData, [this.textFrame], createGlyph, font);
} }

View File

@ -104,8 +104,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
const shaderSource = this.compileShaders(commonShaderSource, shaderSources); const shaderSource = this.compileShaders(commonShaderSource, shaderSources);
this.shaderPrograms = this.linkShaders(shaderSource); this.shaderPrograms = this.linkShaders(shaderSource);
this.pathTransformBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathTransform'); this.pathTransformBufferTextures = [];
this.pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors'); this.pathColorsBufferTextures = [];
this.antialiasingStrategy = new NoAAStrategy(0, false); this.antialiasingStrategy = new NoAAStrategy(0, false);
this.antialiasingStrategy.init(this); this.antialiasingStrategy.init(this);
@ -266,15 +266,18 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.finishTiming(); this.finishTiming();
} }
private setTransformUniform(uniforms: UniformMap) { private setTransformUniform(uniforms: UniformMap, objectIndex: number) {
const transform = glmatrix.mat4.create(); const transform = glmatrix.mat4.create();
if (this.antialiasingStrategy != null) if (this.antialiasingStrategy != null)
glmatrix.mat4.mul(transform, this.antialiasingStrategy.transform, this.worldTransform); glmatrix.mat4.mul(transform, this.antialiasingStrategy.transform, this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
this.gl.uniformMatrix4fv(uniforms.uTransform, false, transform); this.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
} }
private renderDirect() { private renderDirect() {
for (const meshes of this.meshes) { for (let objectIndex = 0; objectIndex < this.meshes.length; objectIndex++) {
const meshes = this.meshes[objectIndex];
// Set up implicit cover state. // Set up implicit cover state.
this.gl.depthFunc(this.gl.GREATER); this.gl.depthFunc(this.gl.GREATER);
this.gl.depthMask(true); this.gl.depthMask(true);
@ -303,10 +306,14 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices);
// Draw direct interior parts. // Draw direct interior parts.
this.setTransformUniform(directInteriorProgram.uniforms); this.setTransformUniform(directInteriorProgram.uniforms, objectIndex);
this.setFramebufferSizeUniform(directInteriorProgram.uniforms); this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
this.pathColorsBufferTexture.bind(this.gl, directInteriorProgram.uniforms, 0); this.pathColorsBufferTextures[objectIndex].bind(this.gl,
this.pathTransformBufferTexture.bind(this.gl, directInteriorProgram.uniforms, 1); directInteriorProgram.uniforms,
0);
this.pathTransformBufferTextures[objectIndex].bind(this.gl,
directInteriorProgram.uniforms,
1);
let indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER, let indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
this.gl.BUFFER_SIZE) / UINT32_SIZE; this.gl.BUFFER_SIZE) / UINT32_SIZE;
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0); this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
@ -354,10 +361,14 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices);
// Draw direct curve parts. // Draw direct curve parts.
this.setTransformUniform(directCurveProgram.uniforms); this.setTransformUniform(directCurveProgram.uniforms, objectIndex);
this.setFramebufferSizeUniform(directCurveProgram.uniforms); this.setFramebufferSizeUniform(directCurveProgram.uniforms);
this.pathColorsBufferTexture.bind(this.gl, directCurveProgram.uniforms, 0); this.pathColorsBufferTextures[objectIndex].bind(this.gl,
this.pathTransformBufferTexture.bind(this.gl, directCurveProgram.uniforms, 1); directCurveProgram.uniforms,
0);
this.pathTransformBufferTextures[objectIndex].bind(this.gl,
directCurveProgram.uniforms,
1);
indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER, indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
this.gl.BUFFER_SIZE) / UINT32_SIZE; this.gl.BUFFER_SIZE) / UINT32_SIZE;
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0); this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
@ -413,6 +424,10 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]); this.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]);
} }
protected getModelviewTransform(pathIndex: number): glmatrix.mat4 {
return glmatrix.mat4.create();
}
protected abstract createAAStrategy(aaType: AntialiasingStrategyName, protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
aaLevel: number, aaLevel: number,
subpixelAA: boolean): subpixelAA: boolean):
@ -454,8 +469,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
meshes: PathfinderMeshBuffers[]; meshes: PathfinderMeshBuffers[];
meshData: PathfinderMeshData[]; meshData: PathfinderMeshData[];
pathTransformBufferTexture: PathfinderBufferTexture; pathTransformBufferTextures: PathfinderBufferTexture[];
protected pathColorsBufferTexture: PathfinderBufferTexture; protected pathColorsBufferTextures: PathfinderBufferTexture[];
private atlasRenderingTimerQuery: WebGLQuery; private atlasRenderingTimerQuery: WebGLQuery;
private compositingTimerQuery: WebGLQuery; private compositingTimerQuery: WebGLQuery;