Factor the SVG renderer out of the demo so it can be reused
This commit is contained in:
parent
15d8c98ff9
commit
d0d08816b9
|
@ -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<SVGDemoView> {
|
||||
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<void> {
|
||||
if (this.antialiasingStrategy instanceof XCAAStrategy)
|
||||
return 'xcaaMultiDirectCurve';
|
||||
return 'directCurve';
|
||||
}
|
||||
|
||||
protected directInteriorProgramName(): keyof ShaderMap<void> {
|
||||
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<number> = instances[pathIndex].color;
|
||||
pathColors.set(instances[pathIndex].color, startOffset);
|
||||
pathColors[startOffset + 3] = color[3] * 255;
|
||||
}
|
||||
|
||||
return pathColors;
|
||||
}
|
||||
|
||||
protected pathTransformsForObject(objectIndex: number): PathTransformBuffers<Float32Array> {
|
||||
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() {
|
||||
|
|
|
@ -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 <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.
|
||||
|
||||
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<void> {
|
||||
if (this.antialiasingStrategy instanceof XCAAStrategy)
|
||||
return 'xcaaMultiDirectCurve';
|
||||
return 'directCurve';
|
||||
}
|
||||
|
||||
protected directInteriorProgramName(): keyof ShaderMap<void> {
|
||||
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<number> = instances[pathIndex].color;
|
||||
pathColors.set(instances[pathIndex].color, startOffset);
|
||||
pathColors[startOffset + 3] = color[3] * 255;
|
||||
}
|
||||
|
||||
return pathColors;
|
||||
}
|
||||
|
||||
protected pathTransformsForObject(objectIndex: number): PathTransformBuffers<Float32Array> {
|
||||
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 {}
|
||||
}
|
Loading…
Reference in New Issue