2017-08-29 21:57:43 -04:00
|
|
|
// pathfinder/client/src/svg-demo.ts
|
2017-08-25 23:20:45 -04:00
|
|
|
//
|
|
|
|
// 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-27 15:43:17 -04:00
|
|
|
|
2017-08-28 19:47:27 -04:00
|
|
|
import * as glmatrix from 'gl-matrix';
|
2017-08-29 17:15:23 -04:00
|
|
|
import * as _ from 'lodash';
|
2017-08-28 19:47:27 -04:00
|
|
|
|
2017-10-02 19:31:54 -04:00
|
|
|
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
|
|
|
|
import {SubpixelAAType} from "./aa-strategy";
|
2017-09-28 17:34:48 -04:00
|
|
|
import {DemoAppController} from './app-controller';
|
|
|
|
import PathfinderBufferTexture from "./buffer-texture";
|
2017-09-02 15:14:10 -04:00
|
|
|
import {OrthographicCamera} from "./camera";
|
2017-10-06 19:11:53 -04:00
|
|
|
import {UniformMap} from './gl-utils';
|
2017-08-28 20:18:44 -04:00
|
|
|
import {PathfinderMeshData} from "./meshes";
|
2017-10-16 22:29:13 -04:00
|
|
|
import {Renderer} from './renderer';
|
2017-08-27 15:43:17 -04:00
|
|
|
import {ShaderMap, ShaderProgramSource} from './shader-loader';
|
2017-09-28 17:34:48 -04:00
|
|
|
import SSAAStrategy from "./ssaa-strategy";
|
|
|
|
import {BUILTIN_SVG_URI, SVGLoader} from './svg-loader';
|
2017-09-01 21:11:44 -04:00
|
|
|
import {panic, unwrapNull} from './utils';
|
2017-10-03 18:24:56 -04:00
|
|
|
import {DemoView, Timings} from './view';
|
2017-10-09 17:14:24 -04:00
|
|
|
import {MCAAMulticolorStrategy, XCAAStrategy} from "./xcaa-strategy";
|
2017-08-27 15:43:17 -04:00
|
|
|
|
2017-08-29 01:11:15 -04:00
|
|
|
const parseColor = require('parse-color');
|
|
|
|
|
2017-08-28 19:47:27 -04:00
|
|
|
const SVG_NS: string = "http://www.w3.org/2000/svg";
|
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
const DEFAULT_FILE: string = 'tiger';
|
|
|
|
|
2017-08-28 20:18:44 -04:00
|
|
|
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
|
|
|
|
none: NoAAStrategy,
|
|
|
|
ssaa: SSAAStrategy,
|
2017-10-09 17:14:24 -04:00
|
|
|
xcaa: MCAAMulticolorStrategy,
|
2017-08-28 20:18:44 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
interface AntialiasingStrategyTable {
|
|
|
|
none: typeof NoAAStrategy;
|
|
|
|
ssaa: typeof SSAAStrategy;
|
2017-10-09 17:14:24 -04:00
|
|
|
xcaa: typeof XCAAStrategy;
|
2017-08-28 20:18:44 -04:00
|
|
|
}
|
|
|
|
|
2017-09-01 21:11:44 -04:00
|
|
|
class SVGDemoController extends DemoAppController<SVGDemoView> {
|
2017-09-28 17:34:48 -04:00
|
|
|
loader: SVGLoader;
|
|
|
|
|
|
|
|
protected readonly builtinFileURI: string = BUILTIN_SVG_URI;
|
|
|
|
|
|
|
|
private meshes: PathfinderMeshData;
|
|
|
|
|
2017-08-28 19:47:27 -04:00
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2017-09-19 21:04:42 -04:00
|
|
|
this.loader = new SVGLoader;
|
2017-08-30 22:48:18 -04:00
|
|
|
|
2017-09-19 23:19:53 -04:00
|
|
|
this.loadInitialFile(this.builtinFileURI);
|
2017-08-28 19:47:27 -04:00
|
|
|
}
|
|
|
|
|
2017-09-23 16:09:45 -04:00
|
|
|
protected fileLoaded(fileData: ArrayBuffer) {
|
|
|
|
this.loader.loadFile(fileData);
|
2017-09-19 23:19:53 -04:00
|
|
|
this.loader.partition().then(meshes => {
|
2017-09-19 21:04:42 -04:00
|
|
|
this.meshes = meshes;
|
|
|
|
this.meshesReceived();
|
2017-09-28 17:34:48 -04:00
|
|
|
});
|
2017-08-28 19:47:27 -04:00
|
|
|
}
|
|
|
|
|
2017-09-01 21:11:44 -04:00
|
|
|
protected createView() {
|
|
|
|
return new SVGDemoView(this,
|
|
|
|
unwrapNull(this.commonShaderSource),
|
|
|
|
unwrapNull(this.shaderSources));
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
2017-08-28 19:47:27 -04:00
|
|
|
|
2017-08-31 20:08:22 -04:00
|
|
|
protected get defaultFile(): string {
|
|
|
|
return DEFAULT_FILE;
|
|
|
|
}
|
|
|
|
|
2017-09-19 21:04:42 -04:00
|
|
|
private meshesReceived(): void {
|
2017-08-28 20:18:44 -04:00
|
|
|
this.view.then(view => {
|
2017-09-07 19:13:55 -04:00
|
|
|
view.attachMeshes([this.meshes]);
|
2017-10-16 22:29:13 -04:00
|
|
|
view.initCameraBounds(this.loader.bounds);
|
2017-09-28 17:34:48 -04:00
|
|
|
});
|
2017-08-28 20:18:44 -04:00
|
|
|
}
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
|
|
|
|
2017-10-03 18:24:56 -04:00
|
|
|
class SVGDemoView extends DemoView {
|
2017-10-16 22:29:13 -04:00
|
|
|
renderer: SVGDemoRenderer;
|
|
|
|
appController: SVGDemoController;
|
2017-09-28 17:34:48 -04:00
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
get camera(): OrthographicCamera {
|
|
|
|
return this.renderer.camera;
|
|
|
|
}
|
2017-09-28 17:34:48 -04:00
|
|
|
|
2017-08-27 15:43:17 -04:00
|
|
|
constructor(appController: SVGDemoController,
|
|
|
|
commonShaderSource: string,
|
|
|
|
shaderSources: ShaderMap<ShaderProgramSource>) {
|
2017-09-02 01:29:05 -04:00
|
|
|
super(commonShaderSource, shaderSources);
|
2017-08-27 15:43:17 -04:00
|
|
|
|
2017-08-28 19:47:27 -04:00
|
|
|
this.appController = appController;
|
2017-10-16 22:29:13 -04:00
|
|
|
this.renderer = new SVGDemoRenderer(this);
|
|
|
|
|
|
|
|
this.resizeToFit(true);
|
|
|
|
}
|
2017-09-02 15:14:10 -04:00
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
initCameraBounds(bounds: glmatrix.vec4): void {
|
|
|
|
this.renderer.initCameraBounds(bounds);
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
class SVGDemoRenderer extends Renderer {
|
|
|
|
renderContext: SVGDemoView;
|
|
|
|
|
|
|
|
camera: OrthographicCamera;
|
2017-08-27 15:43:17 -04:00
|
|
|
|
2017-08-28 19:47:27 -04:00
|
|
|
get destAllocatedSize(): glmatrix.vec2 {
|
2017-10-30 16:34:55 -04:00
|
|
|
const canvas = this.renderContext.canvas;
|
|
|
|
return glmatrix.vec2.clone([canvas.width, canvas.height]);
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
|
|
|
|
2017-08-29 01:11:15 -04:00
|
|
|
get destFramebuffer(): WebGLFramebuffer | null {
|
|
|
|
return null;
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
|
|
|
|
2017-08-28 19:47:27 -04:00
|
|
|
get destUsedSize(): glmatrix.vec2 {
|
|
|
|
return this.destAllocatedSize;
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
|
|
|
|
2017-10-26 23:15:41 -04:00
|
|
|
protected get backgroundColor(): glmatrix.vec4 {
|
|
|
|
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
|
|
|
|
}
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
constructor(renderContext: SVGDemoView) {
|
|
|
|
super(renderContext);
|
|
|
|
|
|
|
|
this.camera = new OrthographicCamera(renderContext.canvas, { scaleBounds: true });
|
|
|
|
this.camera.onPan = () => this.renderContext.setDirty();
|
|
|
|
this.camera.onZoom = () => this.renderContext.setDirty();
|
|
|
|
}
|
|
|
|
|
2017-10-05 22:14:52 -04:00
|
|
|
setHintsUniform(uniforms: UniformMap): void {
|
2017-10-16 22:29:13 -04:00
|
|
|
this.renderContext.gl.uniform4f(uniforms.uHints, 0, 0, 0, 0);
|
2017-10-05 22:14:52 -04:00
|
|
|
}
|
|
|
|
|
2017-10-09 17:14:24 -04:00
|
|
|
pathBoundingRects(objectIndex: number): Float32Array {
|
2017-10-20 19:29:05 -04:00
|
|
|
// TODO
|
|
|
|
return new Float32Array(0);
|
2017-10-09 17:14:24 -04:00
|
|
|
}
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
attachMeshes(meshes: PathfinderMeshData[]): void {
|
|
|
|
super.attachMeshes(meshes);
|
|
|
|
this.uploadPathColors(1);
|
|
|
|
this.uploadPathTransforms(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
initCameraBounds(bounds: glmatrix.vec4): void {
|
|
|
|
this.camera.bounds = bounds;
|
|
|
|
this.camera.zoomToFit();
|
|
|
|
}
|
|
|
|
|
2017-10-21 01:04:53 -04:00
|
|
|
protected get depthFunction(): GLenum {
|
2017-10-16 22:29:13 -04:00
|
|
|
return this.renderContext.gl.GREATER;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get usedSizeFactor(): glmatrix.vec2 {
|
|
|
|
return glmatrix.vec2.clone([1.0, 1.0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get worldTransform(): glmatrix.mat4 {
|
|
|
|
const transform = glmatrix.mat4.create();
|
|
|
|
const translation = this.camera.translation;
|
|
|
|
glmatrix.mat4.translate(transform, transform, [-1.0, -1.0, 0.0]);
|
|
|
|
glmatrix.mat4.scale(transform, transform, [
|
|
|
|
2.0 / this.renderContext.canvas.width,
|
|
|
|
2.0 / this.renderContext.canvas.height,
|
|
|
|
1.0,
|
|
|
|
]);
|
|
|
|
glmatrix.mat4.translate(transform, transform, [translation[0], translation[1], 0]);
|
|
|
|
glmatrix.mat4.scale(transform, transform, [this.camera.scale, this.camera.scale, 1.0]);
|
|
|
|
return transform;
|
|
|
|
}
|
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
protected directCurveProgramName(): keyof ShaderMap<void> {
|
|
|
|
if (this.antialiasingStrategy instanceof XCAAStrategy)
|
|
|
|
return 'xcaaMultiDirectCurve';
|
2017-10-16 22:29:13 -04:00
|
|
|
return 'directCurve';
|
|
|
|
}
|
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
protected directInteriorProgramName(): keyof ShaderMap<void> {
|
|
|
|
if (this.antialiasingStrategy instanceof XCAAStrategy)
|
|
|
|
return 'xcaaMultiDirectInterior';
|
2017-10-16 22:29:13 -04:00
|
|
|
return 'directInterior';
|
|
|
|
}
|
|
|
|
|
|
|
|
protected newTimingsReceived(): void {
|
2017-10-31 15:35:24 -04:00
|
|
|
this.renderContext.appController.newTimingsReceived(this.lastTimings);
|
2017-10-09 17:14:24 -04:00
|
|
|
}
|
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
protected pathColorsForObject(objectIndex: number): Uint8Array {
|
2017-10-16 22:29:13 -04:00
|
|
|
const instances = this.renderContext.appController.loader.pathInstances;
|
2017-09-08 19:49:15 -04:00
|
|
|
const pathColors = new Uint8Array(4 * (instances.length + 1));
|
2017-09-12 15:40:14 -04:00
|
|
|
|
2017-09-08 19:49:15 -04:00
|
|
|
for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) {
|
2017-08-29 15:29:16 -04:00
|
|
|
const startOffset = (pathIndex + 1) * 4;
|
|
|
|
|
|
|
|
// Set color.
|
2017-10-31 17:50:15 -04:00
|
|
|
const color: ArrayLike<number> = instances[pathIndex].color;
|
|
|
|
pathColors.set(instances[pathIndex].color, startOffset);
|
2017-09-08 19:49:15 -04:00
|
|
|
pathColors[startOffset + 3] = color[3] * 255;
|
2017-09-12 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return pathColors;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected pathTransformsForObject(objectIndex: number): Float32Array {
|
2017-10-16 22:29:13 -04:00
|
|
|
const instances = this.renderContext.appController.loader.pathInstances;
|
2017-09-12 15:40:14 -04:00
|
|
|
const pathTransforms = new Float32Array(4 * (instances.length + 1));
|
2017-08-29 15:29:16 -04:00
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) {
|
2017-08-29 15:29:16 -04:00
|
|
|
// TODO(pcwalton): Set transform.
|
2017-09-12 15:40:14 -04:00
|
|
|
const startOffset = (pathIndex + 1) * 4;
|
2017-08-29 15:29:16 -04:00
|
|
|
pathTransforms.set([1, 1, 0, 0], startOffset);
|
2017-08-29 01:11:15 -04:00
|
|
|
}
|
2017-08-27 15:43:17 -04:00
|
|
|
|
2017-09-12 15:40:14 -04:00
|
|
|
return pathTransforms;
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
|
|
|
|
2017-09-06 19:32:11 -04:00
|
|
|
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
|
|
|
aaLevel: number,
|
2017-09-30 01:12:09 -04:00
|
|
|
subpixelAA: SubpixelAAType):
|
2017-08-28 20:18:44 -04:00
|
|
|
AntialiasingStrategy {
|
2017-09-06 19:32:11 -04:00
|
|
|
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
|
2017-08-28 20:18:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected compositeIfNecessary(): void {}
|
2017-08-27 15:43:17 -04:00
|
|
|
}
|
2017-08-28 19:47:27 -04:00
|
|
|
|
|
|
|
function main() {
|
|
|
|
const controller = new SVGDemoController;
|
|
|
|
window.addEventListener('load', () => controller.start(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
main();
|