2017-08-29 22:46:18 -04:00
|
|
|
// pathfinder/client/src/3d-demo.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.
|
2017-08-31 19:11:09 -04:00
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
import * as glmatrix from 'gl-matrix';
|
2017-09-07 01:11:32 -04:00
|
|
|
import * as opentype from "opentype.js";
|
2017-08-31 20:08:22 -04:00
|
|
|
|
|
|
|
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
|
2017-09-01 21:11:44 -04:00
|
|
|
import {DemoAppController} from "./app-controller";
|
2017-09-02 16:41:08 -04:00
|
|
|
import {PerspectiveCamera} from "./camera";
|
2017-08-31 19:11:09 -04:00
|
|
|
import {mat4, vec2} from "gl-matrix";
|
2017-08-31 20:08:22 -04:00
|
|
|
import {PathfinderMeshData} from "./meshes";
|
2017-08-31 19:11:09 -04:00
|
|
|
import {ShaderMap, ShaderProgramSource} from "./shader-loader";
|
2017-09-07 19:13:55 -04:00
|
|
|
import {BUILTIN_FONT_URI, ExpandedMeshData, GlyphStorage, PathfinderGlyph} from "./text";
|
2017-09-09 16:12:51 -04:00
|
|
|
import {Hint, SimpleTextLayout, TextFrame, TextRun} from "./text";
|
2017-09-07 01:11:32 -04:00
|
|
|
import {PathfinderError, assert, panic, unwrapNull} from "./utils";
|
2017-09-02 01:29:05 -04:00
|
|
|
import {PathfinderDemoView, Timings} from "./view";
|
2017-08-31 20:08:22 -04:00
|
|
|
import SSAAStrategy from "./ssaa-strategy";
|
2017-09-07 01:11:32 -04:00
|
|
|
import * as _ from "lodash";
|
2017-09-07 22:01:55 -04:00
|
|
|
import PathfinderBufferTexture from "./buffer-texture";
|
2017-08-31 20:08:22 -04:00
|
|
|
|
2017-09-12 22:43:43 -04:00
|
|
|
const TEXT_AVAILABLE_WIDTH: number = 150000;
|
2017-09-14 18:16:06 -04:00
|
|
|
const TEXT_PADDING: number = 2000;
|
|
|
|
|
|
|
|
const TEXT_SCALE: glmatrix.vec3 = glmatrix.vec3.fromValues(1.0 / 200.0, 1.0 / 200.0, 1.0 / 200.0);
|
2017-09-07 01:11:32 -04:00
|
|
|
|
2017-09-07 01:50:07 -04:00
|
|
|
const TEXT_DATA_URI: string = "/data/mozmonument.json";
|
2017-08-31 20:08:22 -04:00
|
|
|
|
|
|
|
const FONT: string = 'open-sans';
|
|
|
|
|
2017-08-31 22:19:26 -04:00
|
|
|
const PIXELS_PER_UNIT: number = 1.0;
|
|
|
|
|
2017-09-02 16:41:08 -04:00
|
|
|
const FOV: number = 45.0;
|
2017-09-09 03:04:35 -04:00
|
|
|
const NEAR_CLIP_PLANE: number = 0.1;
|
2017-09-14 18:16:06 -04:00
|
|
|
const FAR_CLIP_PLANE: number = 10000.0;
|
2017-09-07 22:01:55 -04:00
|
|
|
|
2017-09-12 22:43:43 -04:00
|
|
|
const TEXT_TRANSLATION: number[] = [
|
2017-09-14 18:16:06 -04:00
|
|
|
-TEXT_AVAILABLE_WIDTH * 0.5,
|
2017-09-12 22:43:43 -04:00
|
|
|
0.0,
|
2017-09-14 18:16:06 -04:00
|
|
|
TEXT_AVAILABLE_WIDTH * 0.5 + TEXT_PADDING,
|
2017-09-12 22:43:43 -04:00
|
|
|
];
|
2017-09-09 03:04:35 -04:00
|
|
|
|
|
|
|
const MONUMENT_TRANSLATION: glmatrix.vec3 = glmatrix.vec3.fromValues(0.0, -690.0, 0.0);
|
|
|
|
const MONUMENT_SCALE: glmatrix.vec3 =
|
2017-09-14 18:16:06 -04:00
|
|
|
glmatrix.vec3.fromValues((TEXT_AVAILABLE_WIDTH * 0.5 + TEXT_PADDING) * TEXT_SCALE[0],
|
2017-09-09 03:04:35 -04:00
|
|
|
700.0,
|
2017-09-14 18:16:06 -04:00
|
|
|
(TEXT_AVAILABLE_WIDTH * 0.5 + TEXT_PADDING) * TEXT_SCALE[2]);
|
2017-09-09 03:04:35 -04:00
|
|
|
|
|
|
|
const TEXT_COLOR: Uint8Array = new Uint8Array([0xf2, 0xf8, 0xf8, 0xff]);
|
|
|
|
const MONUMENT_COLOR: number[] = [0x70 / 0xff, 0x80 / 0xff, 0x80 / 0xff];
|
|
|
|
|
|
|
|
const CUBE_VERTEX_POSITIONS: Float32Array = new Float32Array([
|
|
|
|
-1.0, -1.0, -1.0, // 0
|
|
|
|
1.0, -1.0, -1.0, // 1
|
|
|
|
-1.0, -1.0, 1.0, // 2
|
|
|
|
1.0, -1.0, 1.0, // 3
|
|
|
|
-1.0, 1.0, -1.0, // 4
|
|
|
|
1.0, 1.0, -1.0, // 5
|
|
|
|
-1.0, 1.0, 1.0, // 6
|
|
|
|
1.0, 1.0, 1.0, // 7
|
|
|
|
]);
|
|
|
|
|
|
|
|
const CUBE_INDICES: Uint16Array = new Uint16Array([
|
|
|
|
0, 1, 2, 2, 1, 3, // bottom
|
|
|
|
0, 5, 1, 0, 4, 5, // front
|
|
|
|
2, 4, 0, 2, 6, 4, // left
|
|
|
|
3, 5, 1, 3, 7, 5, // right
|
|
|
|
2, 7, 3, 2, 6, 7, // back
|
|
|
|
4, 5, 6, 6, 5, 7, // top
|
|
|
|
]);
|
2017-09-02 16:41:08 -04:00
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
|
|
|
|
none: NoAAStrategy,
|
|
|
|
ssaa: SSAAStrategy,
|
|
|
|
};
|
|
|
|
|
|
|
|
interface AntialiasingStrategyTable {
|
|
|
|
none: typeof NoAAStrategy;
|
|
|
|
ssaa: typeof SSAAStrategy;
|
|
|
|
}
|
2017-08-31 19:11:09 -04:00
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
interface TextLine {
|
|
|
|
names: string[];
|
|
|
|
}
|
|
|
|
|
|
|
|
interface MonumentSide {
|
|
|
|
lines: TextLine[];
|
2017-09-07 01:50:07 -04:00
|
|
|
}
|
|
|
|
|
2017-09-01 21:11:44 -04:00
|
|
|
class ThreeDController extends DemoAppController<ThreeDView> {
|
2017-08-31 22:19:26 -04:00
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
this.monumentPromise = window.fetch(TEXT_DATA_URI)
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(textData => this.parseTextData(textData));
|
2017-09-07 01:50:07 -04:00
|
|
|
|
2017-08-31 22:19:26 -04:00
|
|
|
this.loadInitialFile();
|
|
|
|
}
|
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
private parseTextData(textData: any): MonumentSide[] {
|
|
|
|
const sides = [];
|
|
|
|
for (let sideIndex = 0; sideIndex < 4; sideIndex++)
|
|
|
|
sides[sideIndex] = { upper: { lines: [] }, lower: { lines: [] } };
|
2017-09-07 01:50:07 -04:00
|
|
|
|
|
|
|
for (const nameData of textData.monument) {
|
2017-09-07 22:01:55 -04:00
|
|
|
const side = parseInt(nameData.side) - 1;
|
|
|
|
const row = parseInt(nameData.row) - 1;
|
|
|
|
const number = parseInt(nameData.number) - 1;
|
|
|
|
|
|
|
|
if (sides[side] == null)
|
2017-09-07 01:50:07 -04:00
|
|
|
continue;
|
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
const lines: TextLine[] = sides[side][nameData.panel as ('upper' | 'lower')].lines;
|
|
|
|
if (lines[row] == null)
|
|
|
|
lines[row] = { names: [] };
|
2017-09-07 01:50:07 -04:00
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
lines[row].names[number] = nameData.name;
|
2017-09-07 01:50:07 -04:00
|
|
|
}
|
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
return sides.map(side => ({ lines: side.upper.lines.concat(side.lower.lines) }));
|
2017-09-07 01:50:07 -04:00
|
|
|
}
|
|
|
|
|
2017-08-31 19:11:09 -04:00
|
|
|
protected fileLoaded(): void {
|
2017-09-07 01:11:32 -04:00
|
|
|
const font = opentype.parse(this.fileData);
|
|
|
|
assert(font.isSupported(), "The font type is unsupported!");
|
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
this.monumentPromise.then(monument => this.layoutMonument(font, monument));
|
2017-09-07 01:50:07 -04:00
|
|
|
}
|
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
private layoutMonument(font: opentype.Font, monument: MonumentSide[]) {
|
2017-09-07 01:11:32 -04:00
|
|
|
const createGlyph = (glyph: opentype.Glyph) => new ThreeDGlyph(glyph);
|
2017-09-07 22:01:55 -04:00
|
|
|
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.names.map(string => {
|
|
|
|
const glyphs = font.stringToGlyphs(string).map(createGlyph);
|
|
|
|
return { glyphs: glyphs, width: _.sumBy(glyphs, glyph => glyph.advanceWidth) };
|
|
|
|
});
|
|
|
|
|
|
|
|
const usedSpace = _.sumBy(lineGlyphs, 'width');
|
2017-09-12 22:43:43 -04:00
|
|
|
const emptySpace = Math.max(TEXT_AVAILABLE_WIDTH - usedSpace, 0.0);
|
2017-09-07 22:01:55 -04:00
|
|
|
const spacing = emptySpace / Math.max(lineGlyphs.length - 1, 1);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2017-09-07 01:11:32 -04:00
|
|
|
}
|
|
|
|
|
2017-09-11 19:07:11 -04:00
|
|
|
textFrames.push(new TextFrame(textRuns, font));
|
2017-09-07 22:01:55 -04:00
|
|
|
}
|
2017-09-07 19:13:55 -04:00
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
this.glyphStorage = new GlyphStorage(this.fileData, textFrames, createGlyph, font);
|
2017-09-07 01:11:32 -04:00
|
|
|
this.glyphStorage.layoutRuns();
|
|
|
|
|
|
|
|
this.glyphStorage.partition().then((baseMeshes: PathfinderMeshData) => {
|
2017-09-06 17:11:58 -04:00
|
|
|
this.baseMeshes = baseMeshes;
|
2017-09-07 19:13:55 -04:00
|
|
|
this.expandedMeshes = this.glyphStorage.expandMeshes(baseMeshes);
|
2017-08-31 20:08:22 -04:00
|
|
|
this.view.then(view => {
|
2017-09-12 15:40:14 -04:00
|
|
|
view.uploadPathColors(this.expandedMeshes.length);
|
|
|
|
view.uploadPathTransforms(this.expandedMeshes.length);
|
2017-09-07 19:13:55 -04:00
|
|
|
view.attachMeshes(this.expandedMeshes.map(meshes => meshes.meshes));
|
2017-08-31 20:08:22 -04:00
|
|
|
});
|
|
|
|
});
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2017-09-01 21:11:44 -04:00
|
|
|
protected createView(): ThreeDView {
|
|
|
|
return new ThreeDView(this,
|
|
|
|
unwrapNull(this.commonShaderSource),
|
|
|
|
unwrapNull(this.shaderSources));
|
2017-08-31 20:08:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected get builtinFileURI(): string {
|
|
|
|
return BUILTIN_FONT_URI;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get defaultFile(): string {
|
|
|
|
return FONT;
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2017-09-07 01:11:32 -04:00
|
|
|
glyphStorage: GlyphStorage<ThreeDGlyph>;
|
2017-09-06 17:11:58 -04:00
|
|
|
|
|
|
|
private baseMeshes: PathfinderMeshData;
|
2017-09-07 19:13:55 -04:00
|
|
|
private expandedMeshes: ExpandedMeshData[];
|
2017-09-07 01:50:07 -04:00
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
private monumentPromise: Promise<MonumentSide[]>;
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2017-09-02 01:29:05 -04:00
|
|
|
class ThreeDView extends PathfinderDemoView {
|
2017-08-31 20:08:22 -04:00
|
|
|
constructor(appController: ThreeDController,
|
|
|
|
commonShaderSource: string,
|
|
|
|
shaderSources: ShaderMap<ShaderProgramSource>) {
|
2017-09-02 01:29:05 -04:00
|
|
|
super(commonShaderSource, shaderSources);
|
2017-08-31 20:08:22 -04:00
|
|
|
|
|
|
|
this.appController = appController;
|
2017-09-02 15:14:10 -04:00
|
|
|
|
2017-09-02 16:41:08 -04:00
|
|
|
this.camera = new PerspectiveCamera(this.canvas);
|
|
|
|
this.camera.onChange = () => this.setDirty();
|
2017-09-09 03:04:35 -04:00
|
|
|
|
|
|
|
this.cubeVertexPositionBuffer = unwrapNull(this.gl.createBuffer());
|
|
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexPositionBuffer);
|
|
|
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, CUBE_VERTEX_POSITIONS, this.gl.STATIC_DRAW);
|
|
|
|
|
|
|
|
this.cubeIndexBuffer = unwrapNull(this.gl.createBuffer());
|
|
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.cubeIndexBuffer);
|
|
|
|
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, CUBE_INDICES, this.gl.STATIC_DRAW);
|
2017-08-31 20:08:22 -04:00
|
|
|
}
|
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
protected pathColorsForObject(textFrameIndex: number): Uint8Array {
|
|
|
|
const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
|
|
|
|
const textGlyphs = textFrame.allGlyphs;
|
|
|
|
const pathCount = textGlyphs.length;
|
|
|
|
|
|
|
|
const pathColors = new Uint8Array(4 * (pathCount + 1));
|
|
|
|
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++)
|
|
|
|
pathColors.set(TEXT_COLOR, (pathIndex + 1) * 4);
|
2017-09-07 22:01:55 -04:00
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
return pathColors;
|
|
|
|
}
|
2017-09-07 22:01:55 -04:00
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
protected pathTransformsForObject(textFrameIndex: number): Float32Array {
|
|
|
|
const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
|
|
|
|
const textGlyphs = textFrame.allGlyphs;
|
|
|
|
const pathCount = textGlyphs.length;
|
|
|
|
|
|
|
|
const hint = new Hint(this.appController.glyphStorage.font, PIXELS_PER_UNIT, false);
|
2017-09-07 22:01:55 -04:00
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
const pathTransforms = new Float32Array(4 * (pathCount + 1));
|
2017-09-07 22:01:55 -04:00
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
|
|
|
|
const textGlyph = textGlyphs[pathIndex];
|
|
|
|
const glyphOrigin = textGlyph.calculatePixelOrigin(hint, PIXELS_PER_UNIT);
|
|
|
|
pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]], (pathIndex + 1) * 4);
|
2017-08-31 20:08:22 -04:00
|
|
|
}
|
2017-09-12 15:40:14 -04:00
|
|
|
|
|
|
|
return pathTransforms;
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2017-09-06 19:32:11 -04:00
|
|
|
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
|
|
|
aaLevel: number,
|
|
|
|
subpixelAA: boolean):
|
2017-08-31 19:11:09 -04:00
|
|
|
AntialiasingStrategy {
|
2017-09-07 01:11:32 -04:00
|
|
|
if (aaType !== 'ecaa')
|
2017-09-06 19:32:11 -04:00
|
|
|
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
2017-08-31 20:08:22 -04:00
|
|
|
throw new PathfinderError("Unsupported antialiasing type!");
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2017-09-09 03:04:35 -04:00
|
|
|
protected drawSceneryIfNecessary(): void {
|
|
|
|
// Set up the cube VBO.
|
|
|
|
const shaderProgram = this.shaderPrograms.demo3DMonument;
|
|
|
|
this.gl.useProgram(shaderProgram.program);
|
|
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexPositionBuffer);
|
|
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.cubeIndexBuffer);
|
|
|
|
this.gl.vertexAttribPointer(shaderProgram.attributes.aPosition,
|
|
|
|
3,
|
|
|
|
this.gl.FLOAT,
|
|
|
|
false,
|
|
|
|
0,
|
|
|
|
0);
|
|
|
|
this.gl.enableVertexAttribArray(shaderProgram.attributes.aPosition);
|
|
|
|
|
|
|
|
// Set uniforms for the monument.
|
|
|
|
const transform = this.calculateWorldTransform(MONUMENT_TRANSLATION, MONUMENT_SCALE);
|
|
|
|
this.gl.uniformMatrix4fv(shaderProgram.uniforms.uTransform, false, transform);
|
|
|
|
this.gl.uniform4f(shaderProgram.uniforms.uColor,
|
|
|
|
MONUMENT_COLOR[0],
|
|
|
|
MONUMENT_COLOR[1],
|
|
|
|
MONUMENT_COLOR[2],
|
|
|
|
1.0);
|
|
|
|
|
|
|
|
// Set state for the monument.
|
|
|
|
this.gl.enable(this.gl.DEPTH_TEST);
|
|
|
|
this.gl.depthMask(true);
|
|
|
|
this.gl.disable(this.gl.SCISSOR_TEST);
|
|
|
|
|
|
|
|
// Draw the monument!
|
|
|
|
this.gl.drawElements(this.gl.TRIANGLES, CUBE_INDICES.length, this.gl.UNSIGNED_SHORT, 0);
|
2017-09-14 18:16:06 -04:00
|
|
|
|
|
|
|
// Clear to avoid Z-fighting.
|
|
|
|
this.gl.clearDepth(1.0);
|
|
|
|
this.gl.clear(this.gl.DEPTH_BUFFER_BIT);
|
2017-09-09 03:04:35 -04:00
|
|
|
}
|
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
protected compositeIfNecessary(): void {}
|
2017-08-31 19:11:09 -04:00
|
|
|
|
2017-09-13 14:56:40 -04:00
|
|
|
protected newTimingsReceived() {
|
|
|
|
this.appController.newTimingsReceived(_.pick(this.lastTimings, ['rendering']));
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
get destAllocatedSize(): glmatrix.vec2 {
|
|
|
|
return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height);
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
destFramebuffer: WebGLFramebuffer | null = null;
|
2017-08-31 20:08:22 -04:00
|
|
|
|
|
|
|
get destUsedSize(): glmatrix.vec2 {
|
|
|
|
return this.destAllocatedSize;
|
|
|
|
}
|
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
protected usedSizeFactor: glmatrix.vec2 = glmatrix.vec2.clone([1.0, 1.0]);
|
2017-08-31 20:08:22 -04:00
|
|
|
|
2017-09-09 03:04:35 -04:00
|
|
|
private calculateWorldTransform(modelviewTranslation: glmatrix.vec3,
|
|
|
|
modelviewScale: glmatrix.vec3):
|
|
|
|
glmatrix.mat4 {
|
2017-09-05 22:47:19 -04:00
|
|
|
const projection = glmatrix.mat4.create();
|
|
|
|
glmatrix.mat4.perspective(projection,
|
2017-09-02 16:41:08 -04:00
|
|
|
FOV / 180.0 * Math.PI,
|
|
|
|
this.canvas.width / this.canvas.height,
|
|
|
|
NEAR_CLIP_PLANE,
|
|
|
|
FAR_CLIP_PLANE);
|
2017-09-05 22:47:19 -04:00
|
|
|
|
|
|
|
const modelview = glmatrix.mat4.create();
|
|
|
|
glmatrix.mat4.mul(modelview, modelview, this.camera.rotationMatrix);
|
|
|
|
glmatrix.mat4.translate(modelview, modelview, this.camera.translation);
|
2017-09-09 03:04:35 -04:00
|
|
|
glmatrix.mat4.translate(modelview, modelview, modelviewTranslation);
|
|
|
|
glmatrix.mat4.scale(modelview, modelview, modelviewScale);
|
2017-09-05 22:47:19 -04:00
|
|
|
|
|
|
|
const transform = glmatrix.mat4.create();
|
|
|
|
glmatrix.mat4.mul(transform, projection, modelview);
|
2017-08-31 20:08:22 -04:00
|
|
|
return transform;
|
|
|
|
}
|
|
|
|
|
2017-09-09 03:04:35 -04:00
|
|
|
protected clearForResolve(): void {
|
|
|
|
this.gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
|
|
|
this.gl.clearDepth(1.0);
|
|
|
|
this.gl.depthMask(true);
|
|
|
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get worldTransform() {
|
2017-09-14 18:16:06 -04:00
|
|
|
return this.calculateWorldTransform(glmatrix.vec3.create(), TEXT_SCALE);
|
2017-09-09 03:04:35 -04:00
|
|
|
}
|
|
|
|
|
2017-09-07 22:01:55 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-09-14 18:16:06 -04:00
|
|
|
// Cheap but effective backface culling.
|
|
|
|
protected shouldRenderObject(objectIndex: number): boolean {
|
|
|
|
const translation = this.camera.translation;
|
|
|
|
const extent = TEXT_TRANSLATION[2] * TEXT_SCALE[2];
|
|
|
|
switch (objectIndex) {
|
|
|
|
case 0: return translation[2] < -extent;
|
|
|
|
case 1: return translation[0] < -extent;
|
|
|
|
case 2: return translation[2] > extent;
|
|
|
|
default: return translation[0] > extent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
protected directCurveProgramName: keyof ShaderMap<void> = 'direct3DCurve';
|
|
|
|
protected directInteriorProgramName: keyof ShaderMap<void> = 'direct3DInterior';
|
2017-09-05 22:47:19 -04:00
|
|
|
|
2017-09-09 03:04:35 -04:00
|
|
|
protected depthFunction: number = this.gl.LESS;
|
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
private _scale: number;
|
|
|
|
|
|
|
|
private appController: ThreeDController;
|
2017-09-02 15:14:10 -04:00
|
|
|
|
2017-09-09 03:04:35 -04:00
|
|
|
private cubeVertexPositionBuffer: WebGLBuffer;
|
|
|
|
private cubeIndexBuffer: WebGLBuffer;
|
|
|
|
|
2017-09-02 16:41:08 -04:00
|
|
|
camera: PerspectiveCamera;
|
2017-08-31 20:08:22 -04:00
|
|
|
}
|
|
|
|
|
2017-08-31 22:19:26 -04:00
|
|
|
class ThreeDGlyph extends PathfinderGlyph {
|
2017-08-31 20:08:22 -04:00
|
|
|
constructor(glyph: opentype.Glyph) {
|
|
|
|
super(glyph);
|
|
|
|
}
|
2017-08-31 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function main() {
|
|
|
|
const controller = new ThreeDController;
|
|
|
|
window.addEventListener('load', () => controller.start(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
main();
|