2017-10-16 22:29:13 -04:00
|
|
|
// pathfinder/client/src/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 {StemDarkeningMode, SubpixelAAType} from './aa-strategy';
|
|
|
|
import PathfinderBufferTexture from "./buffer-texture";
|
|
|
|
import {UniformMap} from './gl-utils';
|
|
|
|
import {PathfinderMeshBuffers, PathfinderMeshData} from "./meshes";
|
|
|
|
import {ShaderMap} from './shader-loader';
|
2017-10-17 18:30:33 -04:00
|
|
|
import {FLOAT32_SIZE, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull} from './utils';
|
2017-10-16 22:29:13 -04:00
|
|
|
import {RenderContext, Timings} from "./view";
|
2017-10-30 16:34:55 -04:00
|
|
|
import {MCAAMulticolorStrategy} from './xcaa-strategy';
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
const MAX_PATHS: number = 65535;
|
|
|
|
|
|
|
|
const TIME_INTERVAL_DELAY: number = 32;
|
|
|
|
|
|
|
|
const B_LOOP_BLINN_DATA_SIZE: number = 4;
|
|
|
|
const B_LOOP_BLINN_DATA_TEX_COORD_OFFSET: number = 0;
|
|
|
|
const B_LOOP_BLINN_DATA_SIGN_OFFSET: number = 2;
|
|
|
|
|
|
|
|
export abstract class Renderer {
|
|
|
|
readonly renderContext: RenderContext;
|
|
|
|
|
|
|
|
readonly pathTransformBufferTextures: PathfinderBufferTexture[];
|
|
|
|
|
|
|
|
meshes: PathfinderMeshBuffers[];
|
|
|
|
meshData: PathfinderMeshData[];
|
|
|
|
|
|
|
|
get emboldenAmount(): glmatrix.vec2 {
|
|
|
|
return glmatrix.vec2.create();
|
|
|
|
}
|
|
|
|
|
|
|
|
get bgColor(): glmatrix.vec4 | null {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
get fgColor(): glmatrix.vec4 | null {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract get destFramebuffer(): WebGLFramebuffer | null;
|
|
|
|
abstract get destAllocatedSize(): glmatrix.vec2;
|
|
|
|
abstract get destUsedSize(): glmatrix.vec2;
|
|
|
|
|
|
|
|
protected antialiasingStrategy: AntialiasingStrategy | null;
|
|
|
|
protected lastTimings: Timings;
|
|
|
|
protected pathColorsBufferTextures: PathfinderBufferTexture[];
|
|
|
|
|
|
|
|
protected get pathIDsAreInstanced(): boolean {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-26 23:15:41 -04:00
|
|
|
protected get backgroundColor(): glmatrix.vec4 {
|
|
|
|
return glmatrix.vec4.create();
|
|
|
|
}
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
protected abstract get depthFunction(): GLenum;
|
|
|
|
protected abstract get usedSizeFactor(): glmatrix.vec2;
|
|
|
|
protected abstract get worldTransform(): glmatrix.mat4;
|
|
|
|
|
2017-10-18 22:16:56 -04:00
|
|
|
private implicitCoverInteriorVAO: WebGLVertexArrayObjectOES | null;
|
|
|
|
private implicitCoverCurveVAO: WebGLVertexArrayObjectOES | null;
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
private instancedPathIDVBO: WebGLBuffer | null;
|
|
|
|
private timerQueryPollInterval: number | null;
|
|
|
|
|
|
|
|
constructor(renderContext: RenderContext) {
|
|
|
|
this.renderContext = renderContext;
|
|
|
|
|
|
|
|
this.lastTimings = { rendering: 0, compositing: 0 };
|
|
|
|
|
|
|
|
this.pathTransformBufferTextures = [];
|
|
|
|
this.pathColorsBufferTextures = [];
|
|
|
|
|
|
|
|
if (this.pathIDsAreInstanced)
|
|
|
|
this.initInstancedPathIDVBO();
|
|
|
|
|
|
|
|
this.antialiasingStrategy = new NoAAStrategy(0, 'none');
|
|
|
|
this.antialiasingStrategy.init(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
attachMeshes(meshes: PathfinderMeshData[]): void {
|
|
|
|
const renderContext = this.renderContext;
|
|
|
|
this.meshData = meshes;
|
|
|
|
this.meshes = meshes.map(meshes => new PathfinderMeshBuffers(renderContext.gl, meshes));
|
|
|
|
unwrapNull(this.antialiasingStrategy).attachMeshes(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract pathBoundingRects(objectIndex: number): Float32Array;
|
|
|
|
abstract setHintsUniform(uniforms: UniformMap): void;
|
|
|
|
|
|
|
|
redraw(): void {
|
|
|
|
const renderContext = this.renderContext;
|
|
|
|
|
|
|
|
if (this.meshes == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Start timing rendering.
|
|
|
|
if (this.timerQueryPollInterval == null) {
|
|
|
|
renderContext.timerQueryExt.beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT,
|
|
|
|
renderContext.atlasRenderingTimerQuery);
|
|
|
|
}
|
|
|
|
|
2017-10-30 16:34:55 -04:00
|
|
|
// Draw "scenery" (used in the 3D view).
|
|
|
|
this.drawSceneryIfNecessary();
|
|
|
|
|
|
|
|
// Antialias.
|
2017-10-16 22:29:13 -04:00
|
|
|
const antialiasingStrategy = unwrapNull(this.antialiasingStrategy);
|
2017-10-30 16:34:55 -04:00
|
|
|
antialiasingStrategy.antialias(this);
|
|
|
|
|
|
|
|
// Prepare for direct rendering.
|
|
|
|
antialiasingStrategy.prepareForDirectRendering(this);
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
// Clear.
|
|
|
|
this.clearForDirectRendering();
|
|
|
|
|
|
|
|
// Perform direct rendering (Loop-Blinn).
|
2017-10-21 01:04:53 -04:00
|
|
|
if (antialiasingStrategy.directRenderingMode !== 'none')
|
2017-10-31 15:41:38 -04:00
|
|
|
this.renderDirect();
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
// End the timer, and start a new one.
|
|
|
|
if (this.timerQueryPollInterval == null) {
|
|
|
|
renderContext.timerQueryExt.endQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT);
|
|
|
|
renderContext.timerQueryExt.beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT,
|
|
|
|
renderContext.compositingTimerQuery);
|
|
|
|
}
|
|
|
|
|
|
|
|
antialiasingStrategy.resolve(this);
|
|
|
|
|
|
|
|
// Draw the glyphs with the resolved atlas to the default framebuffer.
|
|
|
|
this.compositeIfNecessary();
|
|
|
|
|
|
|
|
// Finish timing.
|
|
|
|
this.finishTiming();
|
|
|
|
}
|
|
|
|
|
|
|
|
setAntialiasingOptions(aaType: AntialiasingStrategyName,
|
|
|
|
aaLevel: number,
|
|
|
|
subpixelAA: SubpixelAAType,
|
|
|
|
stemDarkening: StemDarkeningMode) {
|
|
|
|
this.antialiasingStrategy = this.createAAStrategy(aaType,
|
|
|
|
aaLevel,
|
|
|
|
subpixelAA,
|
|
|
|
stemDarkening);
|
|
|
|
|
|
|
|
this.antialiasingStrategy.init(this);
|
|
|
|
if (this.meshData != null)
|
|
|
|
this.antialiasingStrategy.attachMeshes(this);
|
|
|
|
|
|
|
|
this.renderContext.setDirty();
|
|
|
|
}
|
|
|
|
|
|
|
|
canvasResized() {
|
|
|
|
if (this.antialiasingStrategy != null)
|
|
|
|
this.antialiasingStrategy.init(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
setFramebufferSizeUniform(uniforms: UniformMap) {
|
2017-10-30 16:34:55 -04:00
|
|
|
const gl = this.renderContext.gl;
|
|
|
|
gl.uniform2i(uniforms.uFramebufferSize,
|
|
|
|
this.destAllocatedSize[0],
|
|
|
|
this.destAllocatedSize[1]);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
setTransformAndTexScaleUniformsForDest(uniforms: UniformMap): void {
|
|
|
|
const renderContext = this.renderContext;
|
|
|
|
const usedSize = this.usedSizeFactor;
|
|
|
|
|
|
|
|
const transform = glmatrix.mat4.create();
|
|
|
|
glmatrix.mat4.fromTranslation(transform, [-1.0, -1.0, 0.0]);
|
|
|
|
glmatrix.mat4.scale(transform, transform, [2.0 * usedSize[0], 2.0 * usedSize[1], 1.0]);
|
|
|
|
renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
|
|
|
|
|
|
|
|
renderContext.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
setTransformSTAndTexScaleUniformsForDest(uniforms: UniformMap): void {
|
|
|
|
const renderContext = this.renderContext;
|
2017-10-30 16:34:55 -04:00
|
|
|
const gl = renderContext.gl;
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
const usedSize = this.usedSizeFactor;
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.uniform4f(uniforms.uTransformST, 2.0 * usedSize[0], 2.0 * usedSize[1], -1.0, -1.0);
|
|
|
|
gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
setTransformSTUniform(uniforms: UniformMap, objectIndex: number) {
|
|
|
|
// FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an ST matrix is ugly and fragile.
|
|
|
|
// Refactor.
|
|
|
|
const renderContext = this.renderContext;
|
2017-10-30 16:34:55 -04:00
|
|
|
const gl = renderContext.gl;
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
const transform = glmatrix.mat4.clone(this.worldTransform);
|
|
|
|
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
|
|
|
|
|
|
|
|
const translation = glmatrix.vec4.clone([transform[12], transform[13], 0.0, 1.0]);
|
|
|
|
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.uniform4f(uniforms.uTransformST,
|
|
|
|
transform[0],
|
|
|
|
transform[5],
|
|
|
|
transform[12],
|
|
|
|
transform[13]);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uploadPathColors(objectCount: number) {
|
|
|
|
const renderContext = this.renderContext;
|
|
|
|
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
|
|
|
|
const pathColors = this.pathColorsForObject(objectIndex);
|
2017-10-18 22:47:44 -04:00
|
|
|
|
|
|
|
let pathColorsBufferTexture;
|
|
|
|
if (objectIndex >= this.pathColorsBufferTextures.length) {
|
|
|
|
pathColorsBufferTexture = new PathfinderBufferTexture(renderContext.gl,
|
|
|
|
'uPathColors');
|
|
|
|
this.pathColorsBufferTextures[objectIndex] = pathColorsBufferTexture;
|
|
|
|
} else {
|
|
|
|
pathColorsBufferTexture = this.pathColorsBufferTextures[objectIndex];
|
|
|
|
}
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
pathColorsBufferTexture.upload(renderContext.gl, pathColors);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uploadPathTransforms(objectCount: number) {
|
|
|
|
const renderContext = this.renderContext;
|
|
|
|
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
|
|
|
|
const pathTransforms = this.pathTransformsForObject(objectIndex);
|
2017-10-18 22:47:44 -04:00
|
|
|
|
|
|
|
let pathTransformBufferTexture;
|
|
|
|
if (objectIndex >= this.pathTransformBufferTextures.length) {
|
|
|
|
pathTransformBufferTexture = new PathfinderBufferTexture(renderContext.gl,
|
|
|
|
'uPathTransform');
|
|
|
|
this.pathTransformBufferTextures[objectIndex] = pathTransformBufferTexture;
|
|
|
|
} else {
|
|
|
|
pathTransformBufferTexture = this.pathTransformBufferTextures[objectIndex];
|
|
|
|
}
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
pathTransformBufferTexture.upload(renderContext.gl, pathTransforms);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-20 19:29:05 -04:00
|
|
|
setPathColorsUniform(objectIndex: number, uniforms: UniformMap, textureUnit: number): void {
|
2017-10-30 16:34:55 -04:00
|
|
|
const gl = this.renderContext.gl;
|
|
|
|
this.pathColorsBufferTextures[objectIndex].bind(gl, uniforms, textureUnit);
|
2017-10-20 19:29:05 -04:00
|
|
|
}
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
|
|
|
|
aaLevel: number,
|
|
|
|
subpixelAA: SubpixelAAType,
|
|
|
|
stemDarkening: StemDarkeningMode):
|
|
|
|
AntialiasingStrategy;
|
|
|
|
protected abstract compositeIfNecessary(): void;
|
|
|
|
protected abstract pathColorsForObject(objectIndex: number): Uint8Array;
|
|
|
|
protected abstract pathTransformsForObject(objectIndex: number): Float32Array;
|
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
protected abstract directCurveProgramName(): keyof ShaderMap<void>;
|
|
|
|
protected abstract directInteriorProgramName(): keyof ShaderMap<void>;
|
2017-10-30 16:34:55 -04:00
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
protected drawSceneryIfNecessary(): void {}
|
|
|
|
|
|
|
|
protected clearForDirectRendering(): void {
|
2017-10-21 01:04:53 -04:00
|
|
|
const renderingMode = unwrapNull(this.antialiasingStrategy).directRenderingMode;
|
2017-10-16 22:29:13 -04:00
|
|
|
const renderContext = this.renderContext;
|
2017-10-20 19:29:05 -04:00
|
|
|
const gl = renderContext.gl;
|
|
|
|
|
2017-10-30 16:34:55 -04:00
|
|
|
const clearColor = this.backgroundColor;
|
|
|
|
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
|
|
|
|
|
2017-10-21 01:04:53 -04:00
|
|
|
switch (renderingMode) {
|
|
|
|
case 'color':
|
|
|
|
gl.clearDepth(0.0);
|
|
|
|
gl.depthMask(true);
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
|
break;
|
2017-10-31 15:41:38 -04:00
|
|
|
case 'color-depth':
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
2017-10-21 01:04:53 -04:00
|
|
|
break;
|
|
|
|
case 'none':
|
|
|
|
// Nothing to do.
|
2017-10-30 16:34:55 -04:00
|
|
|
break;
|
2017-10-21 01:04:53 -04:00
|
|
|
}
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected getModelviewTransform(pathIndex: number): glmatrix.mat4 {
|
|
|
|
return glmatrix.mat4.create();
|
|
|
|
}
|
|
|
|
|
2017-10-17 18:30:33 -04:00
|
|
|
/// If non-instanced, returns instance 0. An empty range skips rendering the object entirely.
|
|
|
|
protected instanceRangeForObject(objectIndex: number): Range {
|
|
|
|
return new Range(0, 1);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Called whenever new GPU timing statistics are available.
|
2017-10-26 23:15:41 -04:00
|
|
|
protected newTimingsReceived(): void {}
|
2017-10-16 22:29:13 -04:00
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
private renderDirect(): void {
|
2017-10-16 22:29:13 -04:00
|
|
|
const renderContext = this.renderContext;
|
2017-10-21 01:04:53 -04:00
|
|
|
const gl = renderContext.gl;
|
|
|
|
|
2017-10-30 16:34:55 -04:00
|
|
|
const antialiasingStrategy = unwrapNull(this.antialiasingStrategy);
|
|
|
|
const renderingMode = antialiasingStrategy.directRenderingMode;
|
|
|
|
|
2017-10-16 22:29:13 -04:00
|
|
|
for (let objectIndex = 0; objectIndex < this.meshes.length; objectIndex++) {
|
2017-10-17 18:30:33 -04:00
|
|
|
const instanceRange = this.instanceRangeForObject(objectIndex);
|
|
|
|
if (instanceRange.isEmpty)
|
2017-10-16 22:29:13 -04:00
|
|
|
continue;
|
|
|
|
|
2017-10-25 22:38:41 -04:00
|
|
|
const meshData = this.meshData[objectIndex];
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
// Set up implicit cover state.
|
2017-10-21 01:04:53 -04:00
|
|
|
gl.depthFunc(this.depthFunction);
|
|
|
|
gl.depthMask(true);
|
|
|
|
gl.enable(gl.DEPTH_TEST);
|
|
|
|
gl.disable(gl.BLEND);
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
// Set up the implicit cover interior VAO.
|
2017-10-31 15:41:38 -04:00
|
|
|
const directInteriorProgramName = this.directInteriorProgramName();
|
2017-10-30 16:34:55 -04:00
|
|
|
const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName];
|
2017-10-18 22:16:56 -04:00
|
|
|
if (this.implicitCoverInteriorVAO == null) {
|
|
|
|
this.implicitCoverInteriorVAO = renderContext.vertexArrayObjectExt
|
|
|
|
.createVertexArrayOES();
|
|
|
|
}
|
|
|
|
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverInteriorVAO);
|
2017-10-31 15:41:38 -04:00
|
|
|
this.initImplicitCoverInteriorVAO(objectIndex, instanceRange);
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
// Draw direct interior parts.
|
|
|
|
this.setTransformUniform(directInteriorProgram.uniforms, objectIndex);
|
|
|
|
this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
|
|
|
|
this.setHintsUniform(directInteriorProgram.uniforms);
|
2017-10-30 16:34:55 -04:00
|
|
|
this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0);
|
2017-10-21 01:04:53 -04:00
|
|
|
this.pathTransformBufferTextures[objectIndex]
|
|
|
|
.bind(gl, directInteriorProgram.uniforms, 1);
|
2017-10-31 15:41:38 -04:00
|
|
|
if (renderingMode === 'color-depth') {
|
2017-10-30 16:34:55 -04:00
|
|
|
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
|
|
|
|
strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 2);
|
|
|
|
}
|
2017-10-25 22:38:41 -04:00
|
|
|
let indexCount = meshData.coverInteriorIndices.byteLength / UINT32_SIZE;
|
2017-10-17 18:30:33 -04:00
|
|
|
if (!this.pathIDsAreInstanced) {
|
2017-10-21 01:04:53 -04:00
|
|
|
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
|
2017-10-16 22:29:13 -04:00
|
|
|
} else {
|
2017-10-21 01:04:53 -04:00
|
|
|
renderContext.instancedArraysExt.drawElementsInstancedANGLE(gl.TRIANGLES,
|
|
|
|
indexCount,
|
|
|
|
gl.UNSIGNED_INT,
|
|
|
|
0,
|
|
|
|
instanceRange.length);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set up direct curve state.
|
2017-10-31 15:41:38 -04:00
|
|
|
if (renderingMode === 'color-depth') {
|
|
|
|
gl.depthMask(true);
|
|
|
|
gl.disable(gl.BLEND);
|
|
|
|
} else {
|
|
|
|
gl.depthMask(false);
|
|
|
|
gl.enable(gl.BLEND);
|
|
|
|
gl.blendEquation(gl.FUNC_ADD);
|
|
|
|
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
|
|
|
|
}
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
// Set up the direct curve VAO.
|
|
|
|
//
|
|
|
|
// TODO(pcwalton): Cache these.
|
2017-10-31 15:41:38 -04:00
|
|
|
const directCurveProgramName = this.directCurveProgramName();
|
2017-10-30 16:34:55 -04:00
|
|
|
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
|
2017-10-18 22:16:56 -04:00
|
|
|
if (this.implicitCoverCurveVAO == null) {
|
|
|
|
this.implicitCoverCurveVAO = renderContext.vertexArrayObjectExt
|
|
|
|
.createVertexArrayOES();
|
|
|
|
}
|
|
|
|
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverCurveVAO);
|
2017-10-31 15:41:38 -04:00
|
|
|
this.initImplicitCoverCurveVAO(objectIndex, instanceRange);
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
// Draw direct curve parts.
|
|
|
|
this.setTransformUniform(directCurveProgram.uniforms, objectIndex);
|
|
|
|
this.setFramebufferSizeUniform(directCurveProgram.uniforms);
|
2017-10-20 19:29:05 -04:00
|
|
|
this.setHintsUniform(directCurveProgram.uniforms);
|
2017-10-30 16:34:55 -04:00
|
|
|
this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0);
|
2017-10-21 01:04:53 -04:00
|
|
|
this.pathTransformBufferTextures[objectIndex].bind(gl, directCurveProgram.uniforms, 1);
|
2017-10-31 15:41:38 -04:00
|
|
|
if (renderingMode === 'color-depth') {
|
2017-10-30 16:34:55 -04:00
|
|
|
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
|
|
|
|
strategy.bindEdgeDepthTexture(gl, directCurveProgram.uniforms, 2);
|
|
|
|
}
|
2017-10-25 22:38:41 -04:00
|
|
|
indexCount = meshData.coverCurveIndices.byteLength / UINT32_SIZE;
|
2017-10-17 18:30:33 -04:00
|
|
|
if (!this.pathIDsAreInstanced) {
|
2017-10-21 01:04:53 -04:00
|
|
|
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
|
2017-10-16 22:29:13 -04:00
|
|
|
} else {
|
2017-10-21 01:04:53 -04:00
|
|
|
renderContext.instancedArraysExt.drawElementsInstancedANGLE(gl.TRIANGLES,
|
|
|
|
indexCount,
|
|
|
|
gl.UNSIGNED_INT,
|
|
|
|
0,
|
|
|
|
instanceRange.length);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private finishTiming() {
|
|
|
|
const renderContext = this.renderContext;
|
|
|
|
|
|
|
|
if (this.timerQueryPollInterval != null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
renderContext.timerQueryExt.endQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT);
|
|
|
|
|
|
|
|
this.timerQueryPollInterval = window.setInterval(() => {
|
|
|
|
for (const queryName of ['atlasRenderingTimerQuery', 'compositingTimerQuery'] as
|
|
|
|
Array<'atlasRenderingTimerQuery' | 'compositingTimerQuery'>) {
|
|
|
|
if (renderContext.timerQueryExt
|
|
|
|
.getQueryObjectEXT(renderContext[queryName],
|
|
|
|
renderContext.timerQueryExt
|
|
|
|
.QUERY_RESULT_AVAILABLE_EXT) ===
|
|
|
|
0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const atlasRenderingTime =
|
|
|
|
renderContext.timerQueryExt
|
|
|
|
.getQueryObjectEXT(renderContext.atlasRenderingTimerQuery,
|
|
|
|
renderContext.timerQueryExt.QUERY_RESULT_EXT);
|
|
|
|
const compositingTime =
|
|
|
|
renderContext.timerQueryExt
|
|
|
|
.getQueryObjectEXT(renderContext.compositingTimerQuery,
|
|
|
|
renderContext.timerQueryExt.QUERY_RESULT_EXT);
|
|
|
|
this.lastTimings = {
|
|
|
|
compositing: compositingTime / 1000000.0,
|
|
|
|
rendering: atlasRenderingTime / 1000000.0,
|
|
|
|
};
|
|
|
|
|
|
|
|
this.newTimingsReceived();
|
|
|
|
|
|
|
|
window.clearInterval(this.timerQueryPollInterval!);
|
|
|
|
this.timerQueryPollInterval = null;
|
|
|
|
}, TIME_INTERVAL_DELAY);
|
|
|
|
}
|
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
private initImplicitCoverCurveVAO(objectIndex: number, instanceRange: Range): void {
|
2017-10-16 22:29:13 -04:00
|
|
|
const renderContext = this.renderContext;
|
2017-10-30 16:34:55 -04:00
|
|
|
const gl = renderContext.gl;
|
2017-10-16 22:29:13 -04:00
|
|
|
const meshes = this.meshes[objectIndex];
|
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
const directCurveProgramName = this.directCurveProgramName();
|
2017-10-30 16:34:55 -04:00
|
|
|
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
|
|
|
|
gl.useProgram(directCurveProgram.program);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions);
|
|
|
|
gl.vertexAttribPointer(directCurveProgram.attributes.aPosition, 2, gl.FLOAT, false, 0, 0);
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
if (this.pathIDsAreInstanced)
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
|
2017-10-16 22:29:13 -04:00
|
|
|
else
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
|
|
|
|
gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
|
|
|
|
1,
|
|
|
|
gl.UNSIGNED_SHORT,
|
|
|
|
false,
|
|
|
|
0,
|
|
|
|
instanceRange.start * UINT16_SIZE);
|
2017-10-16 22:29:13 -04:00
|
|
|
if (this.pathIDsAreInstanced) {
|
|
|
|
renderContext.instancedArraysExt
|
2017-10-17 18:30:33 -04:00
|
|
|
.vertexAttribDivisorANGLE(directCurveProgram.attributes.aPathID, 1);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData);
|
|
|
|
gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord,
|
|
|
|
2,
|
|
|
|
gl.UNSIGNED_BYTE,
|
|
|
|
false,
|
|
|
|
B_LOOP_BLINN_DATA_SIZE,
|
|
|
|
B_LOOP_BLINN_DATA_TEX_COORD_OFFSET);
|
|
|
|
gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
|
|
|
|
1,
|
|
|
|
gl.BYTE,
|
|
|
|
false,
|
|
|
|
B_LOOP_BLINN_DATA_SIZE,
|
|
|
|
B_LOOP_BLINN_DATA_SIGN_OFFSET);
|
|
|
|
gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
|
|
|
|
gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
|
|
|
|
gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
|
|
|
|
gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
|
|
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
private initImplicitCoverInteriorVAO(objectIndex: number, instanceRange: Range): void {
|
2017-10-16 22:29:13 -04:00
|
|
|
const renderContext = this.renderContext;
|
2017-10-30 16:34:55 -04:00
|
|
|
const gl = renderContext.gl;
|
2017-10-16 22:29:13 -04:00
|
|
|
const meshes = this.meshes[objectIndex];
|
|
|
|
|
2017-10-31 15:41:38 -04:00
|
|
|
const directInteriorProgramName = this.directInteriorProgramName();
|
2017-10-30 16:34:55 -04:00
|
|
|
const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName];
|
|
|
|
gl.useProgram(directInteriorProgram.program);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions);
|
|
|
|
gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition,
|
|
|
|
2,
|
|
|
|
gl.FLOAT,
|
|
|
|
false,
|
|
|
|
0,
|
|
|
|
0);
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
if (this.pathIDsAreInstanced)
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
|
2017-10-16 22:29:13 -04:00
|
|
|
else
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
|
|
|
|
gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID,
|
|
|
|
1,
|
|
|
|
gl.UNSIGNED_SHORT,
|
|
|
|
false,
|
|
|
|
0,
|
|
|
|
instanceRange.start * UINT16_SIZE);
|
2017-10-16 22:29:13 -04:00
|
|
|
if (this.pathIDsAreInstanced) {
|
|
|
|
renderContext.instancedArraysExt
|
|
|
|
.vertexAttribDivisorANGLE(directInteriorProgram.attributes.aPathID, 1);
|
|
|
|
}
|
|
|
|
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
|
|
|
|
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
|
|
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private initInstancedPathIDVBO(): void {
|
|
|
|
const renderContext = this.renderContext;
|
2017-10-30 16:34:55 -04:00
|
|
|
const gl = renderContext.gl;
|
2017-10-16 22:29:13 -04:00
|
|
|
|
|
|
|
const pathIDs = new Uint16Array(MAX_PATHS);
|
|
|
|
for (let pathIndex = 0; pathIndex < MAX_PATHS; pathIndex++)
|
|
|
|
pathIDs[pathIndex] = pathIndex + 1;
|
|
|
|
|
|
|
|
this.instancedPathIDVBO = renderContext.gl.createBuffer();
|
2017-10-30 16:34:55 -04:00
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
|
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, pathIDs, gl.STATIC_DRAW);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
2017-10-16 22:29:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private setTransformUniform(uniforms: UniformMap, objectIndex: number) {
|
|
|
|
const transform = glmatrix.mat4.clone(this.worldTransform);
|
|
|
|
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
|
|
|
|
this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
|
|
|
|
}
|
|
|
|
}
|