From d0d08816b97515cb8bf5c3c1d71de23815336f5c Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 5 Dec 2017 11:23:13 -0800 Subject: [PATCH] Factor the SVG renderer out of the demo so it can be reused --- demo/client/src/svg-demo.ts | 189 ++---------------------------- demo/client/src/svg-renderer.ts | 201 ++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 181 deletions(-) create mode 100644 demo/client/src/svg-renderer.ts diff --git a/demo/client/src/svg-demo.ts b/demo/client/src/svg-demo.ts index d0edaa4d..01d6ffec 100644 --- a/demo/client/src/svg-demo.ts +++ b/demo/client/src/svg-demo.ts @@ -1,4 +1,4 @@ -// pathfinder/client/src/svg-demo.ts +// pathfinder/demo/client/src/svg-demo.ts // // Copyright © 2017 The Pathfinder Project Developers. // @@ -11,21 +11,13 @@ import * as glmatrix from 'gl-matrix'; import * as _ from 'lodash'; -import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy"; -import {SubpixelAAType} from "./aa-strategy"; import {DemoAppController} from './app-controller'; -import PathfinderBufferTexture from "./buffer-texture"; import {OrthographicCamera} from "./camera"; -import {UniformMap} from './gl-utils'; import {PathfinderMeshData} from "./meshes"; -import {CompositingOperation, RenderTaskType} from './render-task'; -import {PathTransformBuffers, Renderer} from './renderer'; import {ShaderMap, ShaderProgramSource} from './shader-loader'; -import SSAAStrategy from "./ssaa-strategy"; import {BUILTIN_SVG_URI, SVGLoader} from './svg-loader'; -import {panic, Range, unwrapNull} from './utils'; -import {DemoView, Timings} from './view'; -import {ECAAMulticolorStrategy, XCAAStrategy} from "./xcaa-strategy"; +import {SVGRenderer} from './svg-renderer'; +import {DemoView} from './view'; const parseColor = require('parse-color'); @@ -33,18 +25,6 @@ const SVG_NS: string = "http://www.w3.org/2000/svg"; const DEFAULT_FILE: string = 'tiger'; -const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = { - none: NoAAStrategy, - ssaa: SSAAStrategy, - xcaa: ECAAMulticolorStrategy, -}; - -interface AntialiasingStrategyTable { - none: typeof NoAAStrategy; - ssaa: typeof SSAAStrategy; - xcaa: typeof XCAAStrategy; -} - class SVGDemoController extends DemoAppController { loader: SVGLoader; @@ -112,173 +92,20 @@ class SVGDemoView extends DemoView { } } -class SVGDemoRenderer extends Renderer { +class SVGDemoRenderer extends SVGRenderer { renderContext: SVGDemoView; - camera: OrthographicCamera; - - get usesSTTransform(): boolean { - return this.camera.usesSTTransform; + protected get loader(): SVGLoader { + return this.renderContext.appController.loader; } - get destAllocatedSize(): glmatrix.vec2 { - const canvas = this.renderContext.canvas; - return glmatrix.vec2.clone([canvas.width, canvas.height]); - } - - get destFramebuffer(): WebGLFramebuffer | null { - return null; - } - - get destUsedSize(): glmatrix.vec2 { - return this.destAllocatedSize; - } - - get usesIntermediateRenderTargets(): boolean { - return true; - } - - get backgroundColor(): glmatrix.vec4 { - return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]); - } - - protected get objectCount(): number { - const loader = this.renderContext.appController.loader; - return loader.renderTasks.length; - } - - 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(); - this.camera.onRotate = () => this.renderContext.setDirty(); - } - - setHintsUniform(uniforms: UniformMap): void { - this.renderContext.gl.uniform4f(uniforms.uHints, 0, 0, 0, 0); - } - - pathBoundingRects(objectIndex: number): Float32Array { - const loader = this.renderContext.appController.loader; - const boundingRectsBuffer = new Float32Array((loader.pathBounds.length + 1) * 4); - for (let pathIndex = 0; pathIndex < loader.pathBounds.length; pathIndex++) - boundingRectsBuffer.set(loader.pathBounds[pathIndex], (pathIndex + 1) * 4); - return boundingRectsBuffer; - } - - 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(); - } - - renderTaskTypeForObject(objectIndex: number): RenderTaskType { - const loader = this.renderContext.appController.loader; - return loader.renderTasks[objectIndex].type; - } - - compositingOperationForObject(objectIndex: number): CompositingOperation | null { - const loader = this.renderContext.appController.loader; - return loader.renderTasks[objectIndex].compositingOperation; - } - - meshIndexForObject(objectIndex: number): number { - return 0; - } - - pathRangeForObject(objectIndex: number): Range { - const loader = this.renderContext.appController.loader; - return loader.renderTasks[objectIndex].instanceIndices; - } - - protected get usedSizeFactor(): glmatrix.vec2 { - return glmatrix.vec2.clone([1.0, 1.0]); - } - - protected get worldTransform(): glmatrix.mat4 { - const canvas = this.renderContext.canvas; - - const transform = glmatrix.mat4.create(); - - glmatrix.mat4.translate(transform, transform, [-1.0, -1.0, 0.0]); - glmatrix.mat4.scale(transform, transform, [2.0 / canvas.width, 2.0 / canvas.height, 1.0]); - - const centerPoint = glmatrix.vec3.clone([canvas.width * 0.5, canvas.height * 0.5, 0.0]); - glmatrix.mat4.translate(transform, transform, centerPoint); - glmatrix.mat4.rotateZ(transform, transform, this.camera.rotationAngle); - glmatrix.vec3.negate(centerPoint, centerPoint); - glmatrix.mat4.translate(transform, transform, centerPoint); - - const translation = this.camera.translation; - 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; - } - - protected clearColorForObject(objectIndex: number): glmatrix.vec4 | null { - return glmatrix.vec4.create(); - } - - protected directCurveProgramName(): keyof ShaderMap { - if (this.antialiasingStrategy instanceof XCAAStrategy) - return 'xcaaMultiDirectCurve'; - return 'directCurve'; - } - - protected directInteriorProgramName(): keyof ShaderMap { - if (this.antialiasingStrategy instanceof XCAAStrategy) - return 'xcaaMultiDirectInterior'; - return 'directInterior'; + protected get canvas(): HTMLCanvasElement { + return this.renderContext.canvas; } protected newTimingsReceived(): void { this.renderContext.appController.newTimingsReceived(this.lastTimings); } - - protected pathColorsForObject(objectIndex: number): Uint8Array { - const instances = this.renderContext.appController.loader.pathInstances; - const pathColors = new Uint8Array(4 * (instances.length + 1)); - - for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) { - const startOffset = (pathIndex + 1) * 4; - - // Set color. - const color: ArrayLike = instances[pathIndex].color; - pathColors.set(instances[pathIndex].color, startOffset); - pathColors[startOffset + 3] = color[3] * 255; - } - - return pathColors; - } - - protected pathTransformsForObject(objectIndex: number): PathTransformBuffers { - const instances = this.renderContext.appController.loader.pathInstances; - const pathTransforms = this.createPathTransformBuffers(instances.length); - - for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) { - // TODO(pcwalton): Set transform. - const startOffset = (pathIndex + 1) * 4; - pathTransforms.st.set([1, 1, 0, 0], startOffset); - } - - return pathTransforms; - } - - protected createAAStrategy(aaType: AntialiasingStrategyName, - aaLevel: number, - subpixelAA: SubpixelAAType): - AntialiasingStrategy { - return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA); - } - - protected compositeIfNecessary(): void {} } function main() { diff --git a/demo/client/src/svg-renderer.ts b/demo/client/src/svg-renderer.ts new file mode 100644 index 00000000..b7964b4a --- /dev/null +++ b/demo/client/src/svg-renderer.ts @@ -0,0 +1,201 @@ +// pathfinder/demo/client/src/svg-renderer.ts +// +// Copyright © 2017 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +import * as glmatrix from 'gl-matrix'; + +import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from './aa-strategy'; +import {SubpixelAAType} from './aa-strategy'; +import {OrthographicCamera} from "./camera"; +import {UniformMap} from './gl-utils'; +import {PathfinderMeshData} from './meshes'; +import {CompositingOperation, RenderTaskType} from './render-task'; +import {PathTransformBuffers, Renderer} from "./renderer"; +import {ShaderMap} from './shader-loader'; +import SSAAStrategy from './ssaa-strategy'; +import {SVGLoader} from './svg-loader'; +import {Range} from './utils'; +import {RenderContext} from './view'; +import {ECAAMulticolorStrategy, XCAAStrategy} from './xcaa-strategy'; + +interface AntialiasingStrategyTable { + none: typeof NoAAStrategy; + ssaa: typeof SSAAStrategy; + xcaa: typeof XCAAStrategy; +} + +const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = { + none: NoAAStrategy, + ssaa: SSAAStrategy, + xcaa: ECAAMulticolorStrategy, +}; + +export abstract class SVGRenderer extends Renderer { + renderContext: RenderContext; + + camera: OrthographicCamera; + + get usesSTTransform(): boolean { + return this.camera.usesSTTransform; + } + + get destAllocatedSize(): glmatrix.vec2 { + const canvas = this.canvas; + return glmatrix.vec2.clone([canvas.width, canvas.height]); + } + + get destFramebuffer(): WebGLFramebuffer | null { + return null; + } + + get destUsedSize(): glmatrix.vec2 { + return this.destAllocatedSize; + } + + get usesIntermediateRenderTargets(): boolean { + return true; + } + + get backgroundColor(): glmatrix.vec4 { + return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]); + } + + protected get objectCount(): number { + return this.loader.renderTasks.length; + } + + protected abstract get loader(): SVGLoader; + protected abstract get canvas(): HTMLCanvasElement; + + constructor(renderContext: RenderContext) { + super(renderContext); + + this.camera = new OrthographicCamera(this.canvas, { scaleBounds: true }); + this.camera.onPan = () => this.renderContext.setDirty(); + this.camera.onZoom = () => this.renderContext.setDirty(); + this.camera.onRotate = () => this.renderContext.setDirty(); + } + + setHintsUniform(uniforms: UniformMap): void { + this.renderContext.gl.uniform4f(uniforms.uHints, 0, 0, 0, 0); + } + + pathBoundingRects(objectIndex: number): Float32Array { + const loader = this.loader; + const boundingRectsBuffer = new Float32Array((loader.pathBounds.length + 1) * 4); + for (let pathIndex = 0; pathIndex < loader.pathBounds.length; pathIndex++) + boundingRectsBuffer.set(loader.pathBounds[pathIndex], (pathIndex + 1) * 4); + return boundingRectsBuffer; + } + + 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(); + } + + renderTaskTypeForObject(objectIndex: number): RenderTaskType { + return this.loader.renderTasks[objectIndex].type; + } + + compositingOperationForObject(objectIndex: number): CompositingOperation | null { + return this.loader.renderTasks[objectIndex].compositingOperation; + } + + meshIndexForObject(objectIndex: number): number { + return 0; + } + + pathRangeForObject(objectIndex: number): Range { + return this.loader.renderTasks[objectIndex].instanceIndices; + } + + protected get usedSizeFactor(): glmatrix.vec2 { + return glmatrix.vec2.clone([1.0, 1.0]); + } + + protected get worldTransform(): glmatrix.mat4 { + const canvas = this.canvas; + + const transform = glmatrix.mat4.create(); + + glmatrix.mat4.translate(transform, transform, [-1.0, -1.0, 0.0]); + glmatrix.mat4.scale(transform, transform, [2.0 / canvas.width, 2.0 / canvas.height, 1.0]); + + const centerPoint = glmatrix.vec3.clone([canvas.width * 0.5, canvas.height * 0.5, 0.0]); + glmatrix.mat4.translate(transform, transform, centerPoint); + glmatrix.mat4.rotateZ(transform, transform, this.camera.rotationAngle); + glmatrix.vec3.negate(centerPoint, centerPoint); + glmatrix.mat4.translate(transform, transform, centerPoint); + + const translation = this.camera.translation; + 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; + } + + protected clearColorForObject(objectIndex: number): glmatrix.vec4 | null { + return glmatrix.vec4.create(); + } + + protected directCurveProgramName(): keyof ShaderMap { + if (this.antialiasingStrategy instanceof XCAAStrategy) + return 'xcaaMultiDirectCurve'; + return 'directCurve'; + } + + protected directInteriorProgramName(): keyof ShaderMap { + if (this.antialiasingStrategy instanceof XCAAStrategy) + return 'xcaaMultiDirectInterior'; + return 'directInterior'; + } + + protected pathColorsForObject(objectIndex: number): Uint8Array { + const instances = this.loader.pathInstances; + const pathColors = new Uint8Array(4 * (instances.length + 1)); + + for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) { + const startOffset = (pathIndex + 1) * 4; + + // Set color. + const color: ArrayLike = instances[pathIndex].color; + pathColors.set(instances[pathIndex].color, startOffset); + pathColors[startOffset + 3] = color[3] * 255; + } + + return pathColors; + } + + protected pathTransformsForObject(objectIndex: number): PathTransformBuffers { + const instances = this.loader.pathInstances; + const pathTransforms = this.createPathTransformBuffers(instances.length); + + for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) { + // TODO(pcwalton): Set transform. + const startOffset = (pathIndex + 1) * 4; + pathTransforms.st.set([1, 1, 0, 0], startOffset); + } + + return pathTransforms; + } + + protected createAAStrategy(aaType: AntialiasingStrategyName, + aaLevel: number, + subpixelAA: SubpixelAAType): + AntialiasingStrategy { + return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA); + } + + protected compositeIfNecessary(): void {} +}