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';
|
|
|
|
|
|
|
|
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-08-31 20:08:22 -04:00
|
|
|
import {BUILTIN_FONT_URI, TextLayout, PathfinderGlyph} from "./text";
|
2017-09-01 21:11:44 -04:00
|
|
|
import {PathfinderError, 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";
|
|
|
|
|
|
|
|
const TEXT: string = "Lorem ipsum dolor sit amet";
|
|
|
|
|
|
|
|
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;
|
|
|
|
const NEAR_CLIP_PLANE: number = 0.01;
|
2017-09-05 22:47:19 -04:00
|
|
|
const FAR_CLIP_PLANE: number = 10000.0;
|
|
|
|
|
|
|
|
const SCALE: glmatrix.vec3 = glmatrix.vec3.fromValues(1.0 / 50.0, 1.0 / 50.0, 1.0);
|
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-01 21:11:44 -04:00
|
|
|
class ThreeDController extends DemoAppController<ThreeDView> {
|
2017-08-31 22:19:26 -04:00
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
this.loadInitialFile();
|
|
|
|
}
|
|
|
|
|
2017-08-31 19:11:09 -04:00
|
|
|
protected fileLoaded(): void {
|
2017-08-31 22:19:26 -04:00
|
|
|
this.layout = new TextLayout(this.fileData, TEXT, glyph => new ThreeDGlyph(glyph));
|
|
|
|
this.layout.layoutText();
|
2017-09-06 17:11:58 -04:00
|
|
|
this.layout.glyphStorage.partition().then((baseMeshes: PathfinderMeshData) => {
|
|
|
|
this.baseMeshes = baseMeshes;
|
|
|
|
this.expandedMeshes = this.layout.glyphStorage.expandMeshes(baseMeshes).meshes;
|
2017-08-31 20:08:22 -04:00
|
|
|
this.view.then(view => {
|
2017-09-02 01:29:05 -04:00
|
|
|
view.uploadPathMetadata(this.layout.glyphStorage.textGlyphs.length);
|
2017-09-06 17:11:58 -04:00
|
|
|
view.attachMeshes(this.expandedMeshes);
|
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-08-31 22:19:26 -04:00
|
|
|
layout: TextLayout<ThreeDGlyph>;
|
2017-09-06 17:11:58 -04:00
|
|
|
|
|
|
|
private baseMeshes: PathfinderMeshData;
|
|
|
|
private expandedMeshes: PathfinderMeshData;
|
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-08-31 20:08:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uploadPathMetadata(pathCount: number) {
|
2017-09-02 01:29:05 -04:00
|
|
|
const textGlyphs = this.appController.layout.glyphStorage.textGlyphs;
|
2017-08-31 22:19:26 -04:00
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
const pathColors = new Uint8Array(4 * (pathCount + 1));
|
2017-08-31 22:19:26 -04:00
|
|
|
const pathTransforms = new Float32Array(4 * (pathCount + 1));
|
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
|
2017-08-31 22:19:26 -04:00
|
|
|
const startOffset = (pathIndex + 1) * 4;
|
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
for (let channel = 0; channel < 3; channel++)
|
2017-08-31 22:19:26 -04:00
|
|
|
pathColors[startOffset + channel] = 0x00; // RGB
|
|
|
|
pathColors[startOffset + 3] = 0xff; // alpha
|
|
|
|
|
|
|
|
const textGlyph = textGlyphs[pathIndex];
|
2017-09-03 19:35:10 -04:00
|
|
|
const glyphRect = textGlyph.pixelRect(PIXELS_PER_UNIT);
|
2017-08-31 22:19:26 -04:00
|
|
|
pathTransforms.set([1, 1, glyphRect[0], glyphRect[1]], startOffset);
|
2017-08-31 20:08:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
this.pathColorsBufferTexture.upload(this.gl, pathColors);
|
2017-08-31 22:19:26 -04:00
|
|
|
this.pathTransformBufferTexture.upload(this.gl, 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-08-31 20:08:22 -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-08-31 20:08:22 -04:00
|
|
|
protected compositeIfNecessary(): void {}
|
2017-08-31 19:11:09 -04:00
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
protected updateTimings(timings: Timings) {
|
|
|
|
// TODO(pcwalton)
|
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-08-31 20:08:22 -04:00
|
|
|
get destFramebuffer(): WebGLFramebuffer | null {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
get destUsedSize(): glmatrix.vec2 {
|
|
|
|
return this.destAllocatedSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get usedSizeFactor(): glmatrix.vec2 {
|
|
|
|
return glmatrix.vec2.fromValues(1.0, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get worldTransform() {
|
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);
|
|
|
|
glmatrix.mat4.scale(modelview, modelview, SCALE);
|
|
|
|
|
|
|
|
const transform = glmatrix.mat4.create();
|
|
|
|
glmatrix.mat4.mul(transform, projection, modelview);
|
2017-08-31 20:08:22 -04:00
|
|
|
return transform;
|
|
|
|
}
|
|
|
|
|
2017-09-05 22:47:19 -04:00
|
|
|
protected get directCurveProgramName(): keyof ShaderMap<void> {
|
|
|
|
return 'direct3DCurve';
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get directInteriorProgramName(): keyof ShaderMap<void> {
|
|
|
|
return 'direct3DInterior';
|
|
|
|
}
|
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
private _scale: number;
|
|
|
|
|
|
|
|
private appController: ThreeDController;
|
2017-09-02 15:14:10 -04:00
|
|
|
|
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();
|