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 SSAAStrategy from "./ssaa-strategy";
import * as _ from "lodash";
import PathfinderBufferTexture from "./buffer-texture";
const WIDTH: number = 150000;
@ -34,9 +35,11 @@ const PIXELS_PER_UNIT: number = 1.0;
const FOV: number = 45.0;
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 = {
none: NoAAStrategy,
@ -48,77 +51,85 @@ interface AntialiasingStrategyTable {
ssaa: typeof SSAAStrategy;
}
interface Panels {
upper: string[][];
lower: string[][];
interface TextLine {
names: string[];
}
interface MonumentSide {
lines: TextLine[];
}
class ThreeDController extends DemoAppController<ThreeDView> {
start() {
super.start();
this.textPromise = window.fetch(TEXT_DATA_URI)
.then(response => response.json())
.then(textData => this.parseTextData(textData));
this.monumentPromise = window.fetch(TEXT_DATA_URI)
.then(response => response.json())
.then(textData => this.parseTextData(textData));
this.loadInitialFile();
}
private parseTextData(textData: any): string[][] {
const panels = {
upper: [],
lower: [],
};
private parseTextData(textData: any): MonumentSide[] {
const sides = [];
for (let sideIndex = 0; sideIndex < 4; sideIndex++)
sides[sideIndex] = { upper: { lines: [] }, lower: { lines: [] } };
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;
const row = parseInt(nameData.row) - 1, number = parseInt(nameData.number) - 1;
const panel: string[][] = panels[nameData.panel as ('upper' | 'lower')];
const lines: TextLine[] = sides[side][nameData.panel as ('upper' | 'lower')].lines;
if (lines[row] == null)
lines[row] = { names: [] };
if (panel[row] == null)
panel[row] = [];
panel[row][number] = nameData.name;
lines[row].names[number] = nameData.name;
}
return panels.upper.concat(panels.lower);
return sides.map(side => ({ lines: side.upper.lines.concat(side.lower.lines) }));
}
protected fileLoaded(): void {
const font = opentype.parse(this.fileData);
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);
let textRuns = [];
for (let lineNumber = 0; lineNumber < text.length; lineNumber++) {
const line = text[lineNumber];
let textFrames = [];
for (const monumentSide of monument) {
let textRuns = [];
for (let lineNumber = 0; lineNumber < monumentSide.lines.length; lineNumber++) {
const line = monumentSide.lines[lineNumber];
const lineY = -lineNumber * font.lineHeight();
const lineGlyphs = line.map(string => {
const glyphs = font.stringToGlyphs(string).map(createGlyph);
return { glyphs: glyphs, width: _.sumBy(glyphs, glyph => glyph.advanceWidth) };
});
const lineY = -lineNumber * font.lineHeight();
const lineGlyphs = line.names.map(string => {
const glyphs = font.stringToGlyphs(string).map(createGlyph);
return { glyphs: glyphs, width: _.sumBy(glyphs, glyph => glyph.advanceWidth) };
});
const usedSpace = _.sumBy(lineGlyphs, 'width');
const emptySpace = Math.max(WIDTH - usedSpace, 0.0);
const spacing = emptySpace / Math.max(lineGlyphs.length - 1, 1);
const usedSpace = _.sumBy(lineGlyphs, 'width');
const emptySpace = Math.max(WIDTH - usedSpace, 0.0);
const spacing = emptySpace / Math.max(lineGlyphs.length - 1, 1);
let currentX = 0.0;
for (const glyphInfo of lineGlyphs) {
textRuns.push(new TextRun(glyphInfo.glyphs, [currentX, lineY], font, createGlyph));
currentX += glyphInfo.width + spacing;
let currentX = 0.0;
for (const glyphInfo of lineGlyphs) {
const textRunOrigin = [currentX, lineY];
textRuns.push(new TextRun(glyphInfo.glyphs, textRunOrigin, font, createGlyph));
currentX += glyphInfo.width + spacing;
}
}
textFrames.push(new TextFrame(textRuns));
}
// TODO(pcwalton)
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.partition().then((baseMeshes: PathfinderMeshData) => {
@ -150,7 +161,7 @@ class ThreeDController extends DemoAppController<ThreeDView> {
private baseMeshes: PathfinderMeshData;
private expandedMeshes: ExpandedMeshData[];
private textPromise: Promise<string[][]>;
private monumentPromise: Promise<MonumentSide[]>;
}
class ThreeDView extends PathfinderDemoView {
@ -166,26 +177,40 @@ class ThreeDView extends PathfinderDemoView {
}
uploadPathMetadata() {
const textGlyphs = this.appController.glyphStorage.allGlyphs;
const pathCount = textGlyphs.length;
this.pathColorsBufferTextures = [];
this.pathTransformBufferTextures = [];
const pathColors = new Uint8Array(4 * (pathCount + 1));
const pathTransforms = new Float32Array(4 * (pathCount + 1));
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;
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
const startOffset = (pathIndex + 1) * 4;
const pathColors = new Uint8Array(4 * (pathCount + 1));
const pathTransforms = new Float32Array(4 * (pathCount + 1));
for (let channel = 0; channel < 3; channel++)
pathColors[startOffset + channel] = 0x00; // RGB
pathColors[startOffset + 3] = 0xff; // alpha
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
const startOffset = (pathIndex + 1) * 4;
const textGlyph = textGlyphs[pathIndex];
const glyphRect = textGlyph.pixelRect(PIXELS_PER_UNIT);
pathTransforms.set([1, 1, glyphRect[0], glyphRect[1]], startOffset);
for (let channel = 0; channel < 3; channel++)
pathColors[startOffset + channel] = 0x00; // RGB
pathColors[startOffset + 3] = 0xff; // alpha
const textGlyph = textGlyphs[pathIndex];
const glyphRect = textGlyph.pixelRect(PIXELS_PER_UNIT);
pathTransforms.set([1, 1, glyphRect[0], glyphRect[1]], startOffset);
}
const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors');
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);
}
this.pathColorsBufferTexture.upload(this.gl, pathColors);
this.pathTransformBufferTexture.upload(this.gl, pathTransforms);
}
protected createAAStrategy(aaType: AntialiasingStrategyName,
@ -237,6 +262,13 @@ class ThreeDView extends PathfinderDemoView {
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> {
return 'direct3DCurve';
}

View File

@ -304,7 +304,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.setFramebufferSizeUniform(uniforms);
this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0);
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.instancedArraysExt.drawElementsInstancedANGLE(view.gl.TRIANGLES,
6,
@ -335,7 +335,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.setFramebufferSizeUniform(uniforms);
this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0);
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 File

@ -45,7 +45,7 @@ class MeshDebuggerAppController extends AppController {
const createGlyph = (glyph: opentype.Glyph) => new MeshDebuggerGlyph(glyph);
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.partition().then(meshes => {

View File

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

View File

@ -248,7 +248,9 @@ class TextDemoView extends MonochromePathfinderView {
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.
@ -322,7 +324,9 @@ class TextDemoView extends MonochromePathfinderView {
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() {

View File

@ -68,9 +68,8 @@ export class TextRun<Glyph extends PathfinderGlyph> {
}
export class TextFrame<Glyph extends PathfinderGlyph> {
constructor(runs: TextRun<Glyph>[], origin: glmatrix.vec3) {
constructor(runs: TextRun<Glyph>[]) {
this.runs = runs;
this.origin = origin;
}
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) => {
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);
}

View File

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