Load new stuff in the demo
This commit is contained in:
parent
6a2b488291
commit
a92eedef93
|
@ -15,11 +15,12 @@ precision highp float;
|
||||||
uniform sampler2D uStencilTexture;
|
uniform sampler2D uStencilTexture;
|
||||||
|
|
||||||
in vec2 vTexCoord;
|
in vec2 vTexCoord;
|
||||||
|
in float vBackdrop;
|
||||||
in vec4 vColor;
|
in vec4 vColor;
|
||||||
|
|
||||||
out vec4 oFragColor;
|
out vec4 oFragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float coverage = abs(texture(uStencilTexture, vTexCoord).r);
|
float coverage = abs(texture(uStencilTexture, vTexCoord).r + vBackdrop);
|
||||||
oFragColor = vec4(vColor.rgb, vColor.a * coverage);
|
oFragColor = vec4(vColor.rgb, vColor.a * coverage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,12 @@ uniform vec2 uStencilTextureSize;
|
||||||
|
|
||||||
in vec2 aTessCoord;
|
in vec2 aTessCoord;
|
||||||
in vec2 aTileOrigin;
|
in vec2 aTileOrigin;
|
||||||
|
in float aBackdrop;
|
||||||
in uint aTileIndex;
|
in uint aTileIndex;
|
||||||
in vec4 aColor;
|
in vec4 aColor;
|
||||||
|
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
|
out float vBackdrop;
|
||||||
out vec4 vColor;
|
out vec4 vColor;
|
||||||
|
|
||||||
vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
||||||
|
@ -34,6 +36,7 @@ void main() {
|
||||||
vec2 position = aTileOrigin + uTileSize * aTessCoord;
|
vec2 position = aTileOrigin + uTileSize * aTessCoord;
|
||||||
vec2 texCoord = computeTileOffset(aTileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
|
vec2 texCoord = computeTileOffset(aTileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
|
||||||
vTexCoord = texCoord / uStencilTextureSize;
|
vTexCoord = texCoord / uStencilTextureSize;
|
||||||
|
vBackdrop = aBackdrop;
|
||||||
vColor = aColor;
|
vColor = aColor;
|
||||||
gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);
|
gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,13 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<title>Pathfinder Demo</title>
|
<title>Pathfinder Demo</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" type="text/css" media="screen" href="pathfinder.css" />
|
<link rel="stylesheet" type="text/css" media="screen" href="pathfinder.scss" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<canvas id=canvas width=640 height=480></canvas>
|
<canvas id=canvas width=640 height=480></canvas>
|
||||||
|
<label class="btn btn-primary m-2" id="open-wrapper">
|
||||||
|
Open…<input type="file" id="open" hidden>
|
||||||
|
</label>
|
||||||
<script src="pathfinder.ts"></script>
|
<script src="pathfinder.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,10 +2,12 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"glslify-bundle": "^5.1.1",
|
"glslify-bundle": "^5.1.1",
|
||||||
"glslify-deps": "^1.3.1",
|
"glslify-deps": "^1.3.1",
|
||||||
|
"sass": "^1.15.2",
|
||||||
"typescript": "^3.1.6"
|
"typescript": "^3.1.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/webgl2": "0.0.4",
|
"@types/webgl2": "0.0.4",
|
||||||
|
"bootstrap": "^4.1.3",
|
||||||
"parse-color": "^1.0.0",
|
"parse-color": "^1.0.0",
|
||||||
"svg-path-outline": "^1.0.1",
|
"svg-path-outline": "^1.0.1",
|
||||||
"svgpath": "^2.2.1"
|
"svgpath": "^2.2.1"
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
html, body {
|
|
||||||
background: #e0e0e0;
|
|
||||||
margin: 0;
|
|
||||||
/*overflow: hidden;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
@import "./node_modules/bootstrap/scss/bootstrap.scss";
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#open-wrapper {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
|
@ -14,17 +14,12 @@ import OPAQUE_VERTEX_SHADER_SOURCE from "./opaque.vs.glsl";
|
||||||
import OPAQUE_FRAGMENT_SHADER_SOURCE from "./opaque.fs.glsl";
|
import OPAQUE_FRAGMENT_SHADER_SOURCE from "./opaque.fs.glsl";
|
||||||
import STENCIL_VERTEX_SHADER_SOURCE from "./stencil.vs.glsl";
|
import STENCIL_VERTEX_SHADER_SOURCE from "./stencil.vs.glsl";
|
||||||
import STENCIL_FRAGMENT_SHADER_SOURCE from "./stencil.fs.glsl";
|
import STENCIL_FRAGMENT_SHADER_SOURCE from "./stencil.fs.glsl";
|
||||||
import SVG from "../resources/svg/Ghostscript_Tiger.svg";
|
|
||||||
import AREA_LUT from "../resources/textures/area-lut.png";
|
import AREA_LUT from "../resources/textures/area-lut.png";
|
||||||
import {Matrix2D, Point2D, Rect, Size2D, Vector3D, approxEq, cross, lerp} from "./geometry";
|
import {Matrix2D, Size2D} from "./geometry";
|
||||||
import {flattenPath, Outline, makePathMonotonic} from "./path-utils";
|
import {SVGPath, TILE_SIZE} from "./tiling";
|
||||||
import {SVGPath, TILE_SIZE, TileDebugger, Tiler, testIntervals, TileStrip} from "./tiling";
|
|
||||||
import {staticCast, unwrapNull} from "./util";
|
import {staticCast, unwrapNull} from "./util";
|
||||||
|
|
||||||
const SVGPath: (path: string) => SVGPath = require('svgpath');
|
const SVGPath: (path: string) => SVGPath = require('svgpath');
|
||||||
const parseColor: (color: string) => any = require('parse-color');
|
|
||||||
|
|
||||||
const SVG_NS: string = "http://www.w3.org/2000/svg";
|
|
||||||
|
|
||||||
const STENCIL_FRAMEBUFFER_SIZE: Size2D = {
|
const STENCIL_FRAMEBUFFER_SIZE: Size2D = {
|
||||||
width: TILE_SIZE.width * 128,
|
width: TILE_SIZE.width * 128,
|
||||||
|
@ -38,7 +33,8 @@ const QUAD_VERTEX_POSITIONS: Uint8Array = new Uint8Array([
|
||||||
0, 1,
|
0, 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const GLOBAL_TRANSFORM: Matrix2D = new Matrix2D(3.0, 0.0, 0.0, 3.0, 800.0, 550.0);
|
const FILL_INSTANCE_SIZE: number = 20;
|
||||||
|
const TILE_INSTANCE_SIZE: number = 16;
|
||||||
|
|
||||||
interface Color {
|
interface Color {
|
||||||
r: number;
|
r: number;
|
||||||
|
@ -51,7 +47,7 @@ type Edge = 'left' | 'top' | 'right' | 'bottom';
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
private canvas: HTMLCanvasElement;
|
private canvas: HTMLCanvasElement;
|
||||||
private svg: XMLDocument;
|
private openButton: HTMLInputElement;
|
||||||
private areaLUT: HTMLImageElement;
|
private areaLUT: HTMLImageElement;
|
||||||
|
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
|
@ -59,33 +55,37 @@ class App {
|
||||||
private areaLUTTexture: WebGLTexture;
|
private areaLUTTexture: WebGLTexture;
|
||||||
private stencilTexture: WebGLTexture;
|
private stencilTexture: WebGLTexture;
|
||||||
private stencilFramebuffer: WebGLFramebuffer;
|
private stencilFramebuffer: WebGLFramebuffer;
|
||||||
private stencilProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT',
|
private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT',
|
||||||
'TessCoord' | 'From' | 'Ctrl' | 'To' | 'TileIndex'>;
|
'TessCoord' | 'From' | 'To' | 'TileIndex'>;
|
||||||
private opaqueProgram: Program<'FramebufferSize' | 'TileSize',
|
private solidTileProgram: Program<'FramebufferSize' | 'TileSize',
|
||||||
'TessCoord' | 'TileOrigin' | 'Color'>;
|
'TessCoord' | 'TileOrigin' | 'Color'>;
|
||||||
private coverProgram:
|
private maskTileProgram:
|
||||||
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize',
|
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize',
|
||||||
'TessCoord' | 'TileOrigin' | 'TileIndex' | 'Color'>;
|
'TessCoord' | 'TileOrigin' | 'TileIndex' | 'Backdrop' | 'Color'>;
|
||||||
private quadVertexBuffer: WebGLBuffer;
|
private quadVertexBuffer: WebGLBuffer;
|
||||||
private stencilVertexPositionsBuffer: WebGLBuffer;
|
private fillVertexBuffer: WebGLBuffer;
|
||||||
private stencilVertexTileIndicesBuffer: WebGLBuffer;
|
private fillVertexArray: WebGLVertexArrayObject;
|
||||||
private stencilVertexArray: WebGLVertexArrayObject;
|
private tileVertexBuffer: WebGLBuffer;
|
||||||
private opaqueVertexBuffer: WebGLBuffer;
|
private instanceIDVertexBuffer: WebGLBuffer;
|
||||||
private opaqueVertexArray: WebGLVertexArrayObject;
|
private solidIndexBuffer: WebGLBuffer;
|
||||||
private coverVertexBuffer: WebGLBuffer;
|
private solidVertexArray: WebGLVertexArrayObject;
|
||||||
private coverVertexArray: WebGLVertexArrayObject;
|
private maskIndexBuffer: WebGLBuffer;
|
||||||
|
private maskVertexArray: WebGLVertexArrayObject;
|
||||||
|
|
||||||
private scene: Scene | null;
|
private fillPrimitiveCount: number;
|
||||||
private primitiveCount: number;
|
private totalTileCount: number;
|
||||||
private tileCount: number;
|
private solidTileCount: number;
|
||||||
private opaqueTileCount: number;
|
private maskTileCount: number;
|
||||||
|
|
||||||
constructor(svg: XMLDocument, areaLUT: HTMLImageElement) {
|
constructor(areaLUT: HTMLImageElement) {
|
||||||
const canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement);
|
const canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement);
|
||||||
|
const openButton = staticCast(document.getElementById('open'), HTMLInputElement);
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.svg = svg;
|
this.openButton = openButton;
|
||||||
this.areaLUT = areaLUT;
|
this.areaLUT = areaLUT;
|
||||||
|
|
||||||
|
this.openButton.addEventListener('change', event => this.loadFile(), false);
|
||||||
|
|
||||||
const devicePixelRatio = window.devicePixelRatio;
|
const devicePixelRatio = window.devicePixelRatio;
|
||||||
canvas.width = window.innerWidth * devicePixelRatio;
|
canvas.width = window.innerWidth * devicePixelRatio;
|
||||||
canvas.height = window.innerHeight * devicePixelRatio;
|
canvas.height = window.innerHeight * devicePixelRatio;
|
||||||
|
@ -131,7 +131,7 @@ class App {
|
||||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
|
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
|
||||||
throw new Error("Stencil framebuffer incomplete!");
|
throw new Error("Stencil framebuffer incomplete!");
|
||||||
|
|
||||||
const coverProgram = new Program(gl,
|
const maskTileProgram = new Program(gl,
|
||||||
COVER_VERTEX_SHADER_SOURCE,
|
COVER_VERTEX_SHADER_SOURCE,
|
||||||
COVER_FRAGMENT_SHADER_SOURCE,
|
COVER_FRAGMENT_SHADER_SOURCE,
|
||||||
[
|
[
|
||||||
|
@ -140,123 +140,162 @@ class App {
|
||||||
'StencilTexture',
|
'StencilTexture',
|
||||||
'StencilTextureSize'
|
'StencilTextureSize'
|
||||||
],
|
],
|
||||||
['TessCoord', 'TileOrigin', 'TileIndex', 'Color']);
|
[
|
||||||
this.coverProgram = coverProgram;
|
'TessCoord',
|
||||||
|
'TileOrigin',
|
||||||
|
'TileIndex',
|
||||||
|
'Backdrop',
|
||||||
|
'Color',
|
||||||
|
]);
|
||||||
|
this.maskTileProgram = maskTileProgram;
|
||||||
|
|
||||||
const opaqueProgram = new Program(gl,
|
const solidTileProgram = new Program(gl,
|
||||||
OPAQUE_VERTEX_SHADER_SOURCE,
|
OPAQUE_VERTEX_SHADER_SOURCE,
|
||||||
OPAQUE_FRAGMENT_SHADER_SOURCE,
|
OPAQUE_FRAGMENT_SHADER_SOURCE,
|
||||||
['FramebufferSize', 'TileSize'],
|
['FramebufferSize', 'TileSize'],
|
||||||
['TessCoord', 'TileOrigin', 'Color']);
|
['TessCoord', 'TileOrigin', 'Color']);
|
||||||
this.opaqueProgram = opaqueProgram;
|
this.solidTileProgram = solidTileProgram;
|
||||||
|
|
||||||
const stencilProgram = new Program(gl,
|
const fillProgram = new Program(gl,
|
||||||
STENCIL_VERTEX_SHADER_SOURCE,
|
STENCIL_VERTEX_SHADER_SOURCE,
|
||||||
STENCIL_FRAGMENT_SHADER_SOURCE,
|
STENCIL_FRAGMENT_SHADER_SOURCE,
|
||||||
['FramebufferSize', 'TileSize', 'AreaLUT'],
|
['FramebufferSize', 'TileSize', 'AreaLUT'],
|
||||||
['TessCoord', 'From', 'Ctrl', 'To', 'TileIndex']);
|
['TessCoord', 'From', 'To', 'TileIndex']);
|
||||||
this.stencilProgram = stencilProgram;
|
this.fillProgram = fillProgram;
|
||||||
|
|
||||||
// Initialize quad VBO.
|
// Initialize quad VBO.
|
||||||
this.quadVertexBuffer = unwrapNull(gl.createBuffer());
|
this.quadVertexBuffer = unwrapNull(gl.createBuffer());
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTEX_POSITIONS, gl.STATIC_DRAW);
|
gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTEX_POSITIONS, gl.STATIC_DRAW);
|
||||||
|
|
||||||
// Initialize stencil VBOs.
|
// Initialize fill VBOs.
|
||||||
this.stencilVertexPositionsBuffer = unwrapNull(gl.createBuffer());
|
this.fillVertexBuffer = unwrapNull(gl.createBuffer());
|
||||||
this.stencilVertexTileIndicesBuffer = unwrapNull(gl.createBuffer());
|
|
||||||
|
|
||||||
// Initialize stencil VAO.
|
// Initialize fill VAO.
|
||||||
this.stencilVertexArray = unwrapNull(gl.createVertexArray());
|
this.fillVertexArray = unwrapNull(gl.createVertexArray());
|
||||||
gl.bindVertexArray(this.stencilVertexArray);
|
gl.bindVertexArray(this.fillVertexArray);
|
||||||
gl.useProgram(this.stencilProgram.program);
|
gl.useProgram(this.fillProgram.program);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
||||||
gl.vertexAttribPointer(stencilProgram.attributes.TessCoord,
|
gl.vertexAttribPointer(fillProgram.attributes.TessCoord,
|
||||||
2,
|
2,
|
||||||
gl.UNSIGNED_BYTE,
|
gl.UNSIGNED_BYTE,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.stencilVertexPositionsBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.fillVertexBuffer);
|
||||||
gl.vertexAttribPointer(stencilProgram.attributes.From, 2, gl.FLOAT, false, 24, 0);
|
gl.vertexAttribPointer(fillProgram.attributes.From,
|
||||||
gl.vertexAttribDivisor(stencilProgram.attributes.From, 1);
|
2,
|
||||||
gl.vertexAttribPointer(stencilProgram.attributes.Ctrl, 2, gl.FLOAT, false, 24, 8);
|
gl.FLOAT,
|
||||||
gl.vertexAttribDivisor(stencilProgram.attributes.Ctrl, 1);
|
false,
|
||||||
gl.vertexAttribPointer(stencilProgram.attributes.To, 2, gl.FLOAT, false, 24, 16);
|
FILL_INSTANCE_SIZE,
|
||||||
gl.vertexAttribDivisor(stencilProgram.attributes.To, 1);
|
0);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.stencilVertexTileIndicesBuffer);
|
gl.vertexAttribDivisor(fillProgram.attributes.From, 1);
|
||||||
gl.vertexAttribIPointer(stencilProgram.attributes.TileIndex,
|
gl.vertexAttribPointer(fillProgram.attributes.To,
|
||||||
|
2,
|
||||||
|
gl.FLOAT,
|
||||||
|
false,
|
||||||
|
FILL_INSTANCE_SIZE,
|
||||||
|
8);
|
||||||
|
gl.vertexAttribDivisor(fillProgram.attributes.To, 1);
|
||||||
|
gl.vertexAttribIPointer(fillProgram.attributes.TileIndex,
|
||||||
1,
|
1,
|
||||||
gl.UNSIGNED_SHORT,
|
gl.UNSIGNED_INT,
|
||||||
0,
|
FILL_INSTANCE_SIZE,
|
||||||
0);
|
16);
|
||||||
gl.vertexAttribDivisor(stencilProgram.attributes.TileIndex, 1);
|
gl.vertexAttribDivisor(fillProgram.attributes.TileIndex, 1);
|
||||||
gl.enableVertexAttribArray(stencilProgram.attributes.TessCoord);
|
gl.enableVertexAttribArray(fillProgram.attributes.TessCoord);
|
||||||
gl.enableVertexAttribArray(stencilProgram.attributes.From);
|
gl.enableVertexAttribArray(fillProgram.attributes.From);
|
||||||
gl.enableVertexAttribArray(stencilProgram.attributes.Ctrl);
|
gl.enableVertexAttribArray(fillProgram.attributes.To);
|
||||||
gl.enableVertexAttribArray(stencilProgram.attributes.To);
|
gl.enableVertexAttribArray(fillProgram.attributes.TileIndex);
|
||||||
gl.enableVertexAttribArray(stencilProgram.attributes.TileIndex);
|
|
||||||
|
|
||||||
// Initialize opaque VBO.
|
// Initialize tile VBOs and IBOs.
|
||||||
this.opaqueVertexBuffer = unwrapNull(gl.createBuffer());
|
this.tileVertexBuffer = unwrapNull(gl.createBuffer());
|
||||||
|
this.instanceIDVertexBuffer = unwrapNull(gl.createBuffer());
|
||||||
|
this.solidIndexBuffer = unwrapNull(gl.createBuffer());
|
||||||
|
this.maskIndexBuffer = unwrapNull(gl.createBuffer());
|
||||||
|
|
||||||
// Initialize opaque VAO.
|
// Initialize solid tile VAO.
|
||||||
this.opaqueVertexArray = unwrapNull(gl.createVertexArray());
|
this.solidVertexArray = unwrapNull(gl.createVertexArray());
|
||||||
gl.bindVertexArray(this.opaqueVertexArray);
|
gl.bindVertexArray(this.solidVertexArray);
|
||||||
gl.useProgram(this.opaqueProgram.program);
|
gl.useProgram(this.solidTileProgram.program);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
||||||
gl.vertexAttribPointer(opaqueProgram.attributes.TessCoord,
|
gl.vertexAttribPointer(solidTileProgram.attributes.TessCoord,
|
||||||
2,
|
2,
|
||||||
gl.UNSIGNED_BYTE,
|
gl.UNSIGNED_BYTE,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.opaqueVertexBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.tileVertexBuffer);
|
||||||
gl.vertexAttribPointer(opaqueProgram.attributes.TileOrigin, 2, gl.SHORT, false, 10, 0);
|
gl.vertexAttribPointer(solidTileProgram.attributes.TileOrigin,
|
||||||
gl.vertexAttribDivisor(opaqueProgram.attributes.TileOrigin, 1);
|
2,
|
||||||
gl.vertexAttribPointer(opaqueProgram.attributes.Color, 4, gl.UNSIGNED_BYTE, true, 10, 6);
|
gl.FLOAT,
|
||||||
gl.vertexAttribDivisor(opaqueProgram.attributes.Color, 1);
|
false,
|
||||||
gl.enableVertexAttribArray(opaqueProgram.attributes.TessCoord);
|
TILE_INSTANCE_SIZE,
|
||||||
gl.enableVertexAttribArray(opaqueProgram.attributes.TileOrigin);
|
0);
|
||||||
gl.enableVertexAttribArray(opaqueProgram.attributes.Color);
|
gl.vertexAttribDivisor(solidTileProgram.attributes.TileOrigin, 1);
|
||||||
|
gl.vertexAttribPointer(solidTileProgram.attributes.Color,
|
||||||
|
4,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
true,
|
||||||
|
TILE_INSTANCE_SIZE,
|
||||||
|
12);
|
||||||
|
gl.vertexAttribDivisor(solidTileProgram.attributes.Color, 1);
|
||||||
|
gl.enableVertexAttribArray(solidTileProgram.attributes.TessCoord);
|
||||||
|
gl.enableVertexAttribArray(solidTileProgram.attributes.TileOrigin);
|
||||||
|
gl.enableVertexAttribArray(solidTileProgram.attributes.Color);
|
||||||
|
|
||||||
// Initialize cover VBO.
|
// Initialize mask tile VAO.
|
||||||
this.coverVertexBuffer = unwrapNull(gl.createBuffer());
|
this.maskVertexArray = unwrapNull(gl.createVertexArray());
|
||||||
|
gl.bindVertexArray(this.maskVertexArray);
|
||||||
// Initialize cover VAO.
|
gl.useProgram(this.maskTileProgram.program);
|
||||||
this.coverVertexArray = unwrapNull(gl.createVertexArray());
|
|
||||||
gl.bindVertexArray(this.coverVertexArray);
|
|
||||||
gl.useProgram(this.coverProgram.program);
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
||||||
gl.vertexAttribPointer(coverProgram.attributes.TessCoord,
|
gl.vertexAttribPointer(maskTileProgram.attributes.TessCoord,
|
||||||
2,
|
2,
|
||||||
gl.UNSIGNED_BYTE,
|
gl.UNSIGNED_BYTE,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.coverVertexBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.tileVertexBuffer);
|
||||||
gl.vertexAttribPointer(coverProgram.attributes.TileOrigin, 2, gl.SHORT, false, 10, 0);
|
gl.vertexAttribPointer(maskTileProgram.attributes.TileOrigin,
|
||||||
gl.vertexAttribDivisor(coverProgram.attributes.TileOrigin, 1);
|
2,
|
||||||
gl.vertexAttribIPointer(coverProgram.attributes.TileIndex, 1, gl.UNSIGNED_SHORT, 10, 4);
|
gl.FLOAT,
|
||||||
gl.vertexAttribDivisor(coverProgram.attributes.TileIndex, 1);
|
false,
|
||||||
gl.vertexAttribPointer(coverProgram.attributes.Color, 4, gl.UNSIGNED_BYTE, true, 10, 6);
|
TILE_INSTANCE_SIZE,
|
||||||
gl.vertexAttribDivisor(coverProgram.attributes.Color, 1);
|
0);
|
||||||
gl.enableVertexAttribArray(coverProgram.attributes.TessCoord);
|
gl.vertexAttribDivisor(maskTileProgram.attributes.TileOrigin, 1);
|
||||||
gl.enableVertexAttribArray(coverProgram.attributes.TileOrigin);
|
gl.vertexAttribPointer(maskTileProgram.attributes.Backdrop,
|
||||||
gl.enableVertexAttribArray(coverProgram.attributes.TileIndex);
|
1,
|
||||||
gl.enableVertexAttribArray(coverProgram.attributes.Color);
|
gl.FLOAT,
|
||||||
|
false,
|
||||||
|
TILE_INSTANCE_SIZE,
|
||||||
|
8);
|
||||||
|
gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1);
|
||||||
|
gl.vertexAttribPointer(maskTileProgram.attributes.Color,
|
||||||
|
4,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
true,
|
||||||
|
TILE_INSTANCE_SIZE,
|
||||||
|
12);
|
||||||
|
gl.vertexAttribDivisor(maskTileProgram.attributes.Color, 1);
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.tileVertexBuffer);
|
||||||
|
gl.vertexAttribIPointer(maskTileProgram.attributes.TileIndex, 1, gl.UNSIGNED_SHORT, 10, 4);
|
||||||
|
gl.vertexAttribDivisor(maskTileProgram.attributes.TileIndex, 1);
|
||||||
|
gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord);
|
||||||
|
gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin);
|
||||||
|
gl.enableVertexAttribArray(maskTileProgram.attributes.TileIndex);
|
||||||
|
gl.enableVertexAttribArray(maskTileProgram.attributes.Color);
|
||||||
|
|
||||||
// Set up event handlers.
|
// Set up event handlers.
|
||||||
this.canvas.addEventListener('click', event => this.onClick(event), false);
|
this.canvas.addEventListener('click', event => this.onClick(event), false);
|
||||||
|
|
||||||
this.scene = null;
|
this.fillPrimitiveCount = 0;
|
||||||
this.primitiveCount = 0;
|
this.totalTileCount = 0;
|
||||||
this.tileCount = 0;
|
this.solidTileCount = 0;
|
||||||
this.opaqueTileCount = 0;
|
this.maskTileCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
redraw(): void {
|
redraw(): void {
|
||||||
const gl = this.gl, canvas = this.canvas, scene = unwrapNull(this.scene);
|
const gl = this.gl, canvas = this.canvas;
|
||||||
|
|
||||||
// Start timer.
|
// Start timer.
|
||||||
let timerQuery = null;
|
let timerQuery = null;
|
||||||
|
@ -265,28 +304,75 @@ class App {
|
||||||
gl.beginQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT, timerQuery);
|
gl.beginQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT, timerQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stencil.
|
// Fill.
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.stencilFramebuffer);
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.stencilFramebuffer);
|
||||||
gl.viewport(0, 0, STENCIL_FRAMEBUFFER_SIZE.width, STENCIL_FRAMEBUFFER_SIZE.height);
|
gl.viewport(0, 0, STENCIL_FRAMEBUFFER_SIZE.width, STENCIL_FRAMEBUFFER_SIZE.height);
|
||||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
gl.bindVertexArray(this.stencilVertexArray);
|
gl.bindVertexArray(this.fillVertexArray);
|
||||||
gl.useProgram(this.stencilProgram.program);
|
gl.useProgram(this.fillProgram.program);
|
||||||
gl.uniform2f(this.stencilProgram.uniforms.FramebufferSize,
|
gl.uniform2f(this.fillProgram.uniforms.FramebufferSize,
|
||||||
STENCIL_FRAMEBUFFER_SIZE.width,
|
STENCIL_FRAMEBUFFER_SIZE.width,
|
||||||
STENCIL_FRAMEBUFFER_SIZE.height);
|
STENCIL_FRAMEBUFFER_SIZE.height);
|
||||||
gl.uniform2f(this.stencilProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
|
gl.uniform2f(this.fillProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, this.areaLUTTexture);
|
gl.bindTexture(gl.TEXTURE_2D, this.areaLUTTexture);
|
||||||
gl.uniform1i(this.stencilProgram.uniforms.AreaLUT, 0);
|
gl.uniform1i(this.fillProgram.uniforms.AreaLUT, 0);
|
||||||
gl.blendEquation(gl.FUNC_ADD);
|
gl.blendEquation(gl.FUNC_ADD);
|
||||||
gl.blendFunc(gl.ONE, gl.ONE);
|
gl.blendFunc(gl.ONE, gl.ONE);
|
||||||
gl.enable(gl.BLEND);
|
gl.enable(gl.BLEND);
|
||||||
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, unwrapNull(this.primitiveCount));
|
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, unwrapNull(this.fillPrimitiveCount));
|
||||||
gl.disable(gl.BLEND);
|
gl.disable(gl.BLEND);
|
||||||
|
|
||||||
// Read back stencil and dump it.
|
// Read back stencil and dump it.
|
||||||
|
//this.dumpStencil();
|
||||||
|
|
||||||
|
// Draw solid tiles.
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||||
|
const framebufferSize = {width: canvas.width, height: canvas.height};
|
||||||
|
gl.viewport(0, 0, framebufferSize.width, framebufferSize.height);
|
||||||
|
gl.clearColor(0.85, 0.85, 0.85, 1.0);
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
gl.bindVertexArray(this.solidVertexArray);
|
||||||
|
gl.useProgram(this.solidTileProgram.program);
|
||||||
|
gl.uniform2f(this.solidTileProgram.uniforms.FramebufferSize,
|
||||||
|
framebufferSize.width,
|
||||||
|
framebufferSize.height);
|
||||||
|
gl.uniform2f(this.solidTileProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.solidTileCount);
|
||||||
|
|
||||||
|
// Draw masked tiles.
|
||||||
|
gl.bindVertexArray(this.maskVertexArray);
|
||||||
|
gl.useProgram(this.maskTileProgram.program);
|
||||||
|
gl.uniform2f(this.maskTileProgram.uniforms.FramebufferSize,
|
||||||
|
framebufferSize.width,
|
||||||
|
framebufferSize.height);
|
||||||
|
gl.uniform2f(this.maskTileProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture);
|
||||||
|
gl.uniform1i(this.maskTileProgram.uniforms.StencilTexture, 0);
|
||||||
|
gl.uniform2f(this.maskTileProgram.uniforms.StencilTextureSize,
|
||||||
|
STENCIL_FRAMEBUFFER_SIZE.width,
|
||||||
|
STENCIL_FRAMEBUFFER_SIZE.height);
|
||||||
|
gl.blendEquation(gl.FUNC_ADD);
|
||||||
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.maskTileCount);
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
|
||||||
|
// End timer.
|
||||||
|
if (timerQuery != null) {
|
||||||
|
gl.endQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT);
|
||||||
|
waitForQuery(gl, this.disjointTimerQueryExt, timerQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private dumpStencil(): void {
|
||||||
|
const gl = this.gl;
|
||||||
|
|
||||||
const totalStencilFramebufferSize = STENCIL_FRAMEBUFFER_SIZE.width *
|
const totalStencilFramebufferSize = STENCIL_FRAMEBUFFER_SIZE.width *
|
||||||
STENCIL_FRAMEBUFFER_SIZE.height * 4;
|
STENCIL_FRAMEBUFFER_SIZE.height * 4;
|
||||||
const stencilData = new Float32Array(totalStencilFramebufferSize);
|
const stencilData = new Float32Array(totalStencilFramebufferSize);
|
||||||
|
@ -312,145 +398,71 @@ class App {
|
||||||
stencilDumpCanvasContext.putImageData(stencilDumpImageData, 0, 0);
|
stencilDumpCanvasContext.putImageData(stencilDumpImageData, 0, 0);
|
||||||
document.body.appendChild(stencilDumpCanvas);
|
document.body.appendChild(stencilDumpCanvas);
|
||||||
//console.log(stencilData);
|
//console.log(stencilData);
|
||||||
|
|
||||||
// Draw opaque tiles.
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
||||||
const framebufferSize = {width: canvas.width, height: canvas.height};
|
|
||||||
gl.viewport(0, 0, framebufferSize.width, framebufferSize.height);
|
|
||||||
gl.clearColor(0.85, 0.85, 0.85, 1.0);
|
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
gl.bindVertexArray(this.opaqueVertexArray);
|
|
||||||
gl.useProgram(this.opaqueProgram.program);
|
|
||||||
gl.uniform2f(this.opaqueProgram.uniforms.FramebufferSize,
|
|
||||||
framebufferSize.width,
|
|
||||||
framebufferSize.height);
|
|
||||||
gl.uniform2f(this.opaqueProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
|
|
||||||
gl.disable(gl.BLEND);
|
|
||||||
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.opaqueTileCount);
|
|
||||||
|
|
||||||
// Cover.
|
|
||||||
gl.bindVertexArray(this.coverVertexArray);
|
|
||||||
gl.useProgram(this.coverProgram.program);
|
|
||||||
gl.uniform2f(this.coverProgram.uniforms.FramebufferSize,
|
|
||||||
framebufferSize.width,
|
|
||||||
framebufferSize.height);
|
|
||||||
gl.uniform2f(this.coverProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
|
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture);
|
|
||||||
gl.uniform1i(this.coverProgram.uniforms.StencilTexture, 0);
|
|
||||||
gl.uniform2f(this.coverProgram.uniforms.StencilTextureSize,
|
|
||||||
STENCIL_FRAMEBUFFER_SIZE.width,
|
|
||||||
STENCIL_FRAMEBUFFER_SIZE.height);
|
|
||||||
gl.blendEquation(gl.FUNC_ADD);
|
|
||||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
||||||
gl.enable(gl.BLEND);
|
|
||||||
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.tileCount);
|
|
||||||
gl.disable(gl.BLEND);
|
|
||||||
|
|
||||||
// End timer.
|
|
||||||
if (timerQuery != null) {
|
|
||||||
gl.endQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT);
|
|
||||||
waitForQuery(gl, this.disjointTimerQueryExt, timerQuery);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildScene(): void {
|
private loadFile(): void {
|
||||||
this.scene = new Scene(this.svg);
|
console.log("loadFile");
|
||||||
}
|
|
||||||
|
|
||||||
prepare(): void {
|
|
||||||
const gl = this.gl, scene = unwrapNull(this.scene);
|
|
||||||
|
|
||||||
// Construct opaque tile VBOs.
|
|
||||||
this.opaqueTileCount = 0;
|
|
||||||
const opaqueVertexData: number[] = [];
|
|
||||||
const opaqueTiles: number[][] = [];
|
|
||||||
for (let pathIndex = scene.pathTileStrips.length - 1; pathIndex >= 0; pathIndex--) {
|
|
||||||
const pathTileStrips = scene.pathTileStrips[pathIndex];
|
|
||||||
for (const tileStrip of pathTileStrips) {
|
|
||||||
for (const tile of tileStrip.tiles) {
|
|
||||||
// TODO(pcwalton)
|
// TODO(pcwalton)
|
||||||
const color = scene.pathColors[pathIndex];
|
const file = unwrapNull(unwrapNull(this.openButton.files)[0]);
|
||||||
if (!tile.isFilled())
|
const reader = new FileReader;
|
||||||
continue;
|
reader.addEventListener('loadend', () => {
|
||||||
|
const gl = this.gl;
|
||||||
if (opaqueTiles[tile.tileLeft] == null)
|
const arrayBuffer = staticCast(reader.result, ArrayBuffer);
|
||||||
opaqueTiles[tile.tileLeft] = [];
|
const root = new RIFFChunk(new DataView(arrayBuffer));
|
||||||
if (opaqueTiles[tile.tileLeft][tileStrip.tileTop] != null)
|
for (const subchunk of root.subchunks()) {
|
||||||
continue;
|
let bindPoint, buffer;
|
||||||
opaqueTiles[tile.tileLeft][tileStrip.tileTop] = pathIndex;
|
let countFieldName: 'fillPrimitiveCount' | 'totalTileCount' | 'solidTileCount' |
|
||||||
|
'maskTileCount';
|
||||||
opaqueVertexData.push(Math.floor(tile.tileLeft),
|
let instanceSize;
|
||||||
Math.floor(tileStrip.tileTop),
|
const id = subchunk.stringID();
|
||||||
0,
|
switch (id) {
|
||||||
color.r | (color.g << 8),
|
case 'fill':
|
||||||
color.b | (color.a << 8));
|
bindPoint = gl.ARRAY_BUFFER;
|
||||||
this.opaqueTileCount++;
|
buffer = this.fillVertexBuffer;
|
||||||
}
|
countFieldName = 'fillPrimitiveCount';
|
||||||
}
|
instanceSize = FILL_INSTANCE_SIZE;
|
||||||
|
break;
|
||||||
|
case 'tile':
|
||||||
|
bindPoint = gl.ARRAY_BUFFER;
|
||||||
|
buffer = this.tileVertexBuffer;
|
||||||
|
countFieldName = 'totalTileCount';
|
||||||
|
instanceSize = TILE_INSTANCE_SIZE;
|
||||||
|
break;
|
||||||
|
case 'soli':
|
||||||
|
bindPoint = gl.ELEMENT_ARRAY_BUFFER;
|
||||||
|
buffer = this.solidIndexBuffer;
|
||||||
|
countFieldName = 'solidTileCount';
|
||||||
|
instanceSize = 4;
|
||||||
|
break;
|
||||||
|
case 'mask':
|
||||||
|
bindPoint = gl.ELEMENT_ARRAY_BUFFER;
|
||||||
|
buffer = this.maskIndexBuffer;
|
||||||
|
countFieldName = 'maskTileCount';
|
||||||
|
instanceSize = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unexpected subchunk ID: " + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct stencil and cover VBOs.
|
gl.bindBuffer(bindPoint, buffer);
|
||||||
this.tileCount = 0;
|
gl.bufferData(bindPoint, subchunk.contents(), gl.DYNAMIC_DRAW);
|
||||||
let primitiveCount = 0;
|
this[countFieldName] = subchunk.length() / instanceSize;
|
||||||
const stencilVertexPositions: number[] = [], stencilVertexTileIndices: number[] = [];
|
|
||||||
const coverVertexData: number[] = [];
|
|
||||||
for (let pathIndex = 0; pathIndex < scene.pathTileStrips.length; pathIndex++) {
|
|
||||||
const pathTileStrips = scene.pathTileStrips[pathIndex];
|
|
||||||
for (const tileStrip of pathTileStrips) {
|
|
||||||
for (const tile of tileStrip.tiles) {
|
|
||||||
const color = scene.pathColors[pathIndex];
|
|
||||||
if (tile.isFilled())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (opaqueTiles[tile.tileLeft] != null &&
|
|
||||||
opaqueTiles[tile.tileLeft][tileStrip.tileTop] != null &&
|
|
||||||
pathIndex <= opaqueTiles[tile.tileLeft][tileStrip.tileTop]) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const edge of tile.edges) {
|
this.regenerateInstanceIDBuffer();
|
||||||
let ctrl;
|
this.redraw();
|
||||||
if (edge.ctrl == null)
|
}, false);
|
||||||
ctrl = edge.from.lerp(edge.to, 0.5);
|
reader.readAsArrayBuffer(file);
|
||||||
else
|
|
||||||
ctrl = edge.ctrl;
|
|
||||||
stencilVertexPositions.push(edge.from.x, edge.from.y,
|
|
||||||
ctrl.x, ctrl.y,
|
|
||||||
edge.to.x, edge.to.y);
|
|
||||||
stencilVertexTileIndices.push(this.tileCount);
|
|
||||||
primitiveCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coverVertexData.push(Math.floor(tile.tileLeft),
|
private regenerateInstanceIDBuffer(): void {
|
||||||
Math.floor(tileStrip.tileTop),
|
const instanceIDs = new Uint32Array(this.totalTileCount);
|
||||||
this.tileCount,
|
for (let instanceID = 0; instanceID < this.totalTileCount; instanceID++)
|
||||||
color.r | (color.g << 8),
|
instanceIDs[instanceID] = instanceID;
|
||||||
color.b | (color.a << 8));
|
|
||||||
|
|
||||||
this.tileCount++;
|
const gl = this.gl;
|
||||||
}
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceIDVertexBuffer);
|
||||||
}
|
gl.bufferData(gl.ARRAY_BUFFER, instanceIDs, gl.DYNAMIC_DRAW);
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the stencil VBOs.
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.stencilVertexPositionsBuffer);
|
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(stencilVertexPositions), gl.STATIC_DRAW);
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.stencilVertexTileIndicesBuffer);
|
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Uint16Array(stencilVertexTileIndices), gl.STATIC_DRAW);
|
|
||||||
|
|
||||||
// Populate the opaque VBO.
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.opaqueVertexBuffer);
|
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Int16Array(opaqueVertexData), gl.DYNAMIC_DRAW);
|
|
||||||
|
|
||||||
// Populate the cover VBO.
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.coverVertexBuffer);
|
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Int16Array(coverVertexData), gl.DYNAMIC_DRAW);
|
|
||||||
//console.log(coverVertexData);
|
|
||||||
|
|
||||||
this.primitiveCount = primitiveCount;
|
|
||||||
console.log(primitiveCount + " primitives");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onClick(event: MouseEvent): void {
|
private onClick(event: MouseEvent): void {
|
||||||
|
@ -458,148 +470,6 @@ class App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Scene {
|
|
||||||
pathTileStrips: TileStrip[][];
|
|
||||||
pathColors: Color[];
|
|
||||||
|
|
||||||
constructor(svg: XMLDocument) {
|
|
||||||
const svgElement = unwrapNull(svg.documentElement).cloneNode(true);
|
|
||||||
document.body.appendChild(svgElement);
|
|
||||||
|
|
||||||
const pathElements = Array.from(document.getElementsByTagName('path'));
|
|
||||||
const pathColors: any[] = [];
|
|
||||||
|
|
||||||
this.pathTileStrips = [];
|
|
||||||
|
|
||||||
//const tileDebugger = new TileDebugger(document);
|
|
||||||
|
|
||||||
let fillCount = 0, strokeCount = 0;
|
|
||||||
const paths: SVGPath[] = [];
|
|
||||||
for (let pathElementIndex = 0;
|
|
||||||
pathElementIndex < pathElements.length;
|
|
||||||
pathElementIndex++) {
|
|
||||||
const pathElement = pathElements[pathElementIndex];
|
|
||||||
const pathString = unwrapNull(pathElement.getAttribute('d'));
|
|
||||||
|
|
||||||
const style = window.getComputedStyle(pathElement);
|
|
||||||
if (style.fill != null && style.fill !== 'none') {
|
|
||||||
fillCount++;
|
|
||||||
this.addPath(paths, pathColors, style.fill, pathString, null);
|
|
||||||
}
|
|
||||||
if (style.stroke != null && style.stroke !== 'none') {
|
|
||||||
strokeCount++;
|
|
||||||
const strokeWidth =
|
|
||||||
style.strokeWidth == null ? 1.0 : parseFloat(style.strokeWidth);
|
|
||||||
this.addPath(paths, pathColors, style.stroke, pathString, strokeWidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log("", fillCount, "fills,", strokeCount, "strokes");
|
|
||||||
|
|
||||||
const startTime = window.performance.now();
|
|
||||||
|
|
||||||
for (const path of paths) {
|
|
||||||
const tiler = new Tiler(path);
|
|
||||||
tiler.tile();
|
|
||||||
//tileDebugger.addTiler(tiler, paint, "" + realPathIndex);
|
|
||||||
//console.log("path", pathElementIndex, "tiles", tiler.getStrips());
|
|
||||||
|
|
||||||
const pathTileStrips = tiler.getTileStrips();
|
|
||||||
this.pathTileStrips.push(pathTileStrips);
|
|
||||||
}
|
|
||||||
|
|
||||||
const endTime = window.performance.now();
|
|
||||||
console.log("elapsed time for tiling: " + (endTime - startTime) + "ms");
|
|
||||||
|
|
||||||
/*
|
|
||||||
for (const tile of tiles) {
|
|
||||||
const newSVG = staticCast(document.createElementNS(SVG_NS, 'svg'), SVGElement);
|
|
||||||
newSVG.setAttribute('class', "tile");
|
|
||||||
newSVG.style.left = (GLOBAL_OFFSET.x + tile.origin.x) + "px";
|
|
||||||
newSVG.style.top = (GLOBAL_OFFSET.y + tile.origin.y) + "px";
|
|
||||||
newSVG.style.width = TILE_SIZE + "px";
|
|
||||||
newSVG.style.height = TILE_SIZE + "px";
|
|
||||||
|
|
||||||
const newPath = document.createElementNS(SVG_NS, 'path');
|
|
||||||
newPath.setAttribute('d',
|
|
||||||
tile.path
|
|
||||||
.translate(-tile.origin.x, -tile.origin.y)
|
|
||||||
.toString());
|
|
||||||
|
|
||||||
const color = pathColors[tile.pathIndex];
|
|
||||||
newPath.setAttribute('fill',
|
|
||||||
"rgba(" + color.r + "," + color.g + "," + color.b + "," +
|
|
||||||
(color.a / 255.0));
|
|
||||||
|
|
||||||
newSVG.appendChild(newPath);
|
|
||||||
document.body.appendChild(newSVG);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
document.body.removeChild(svgElement);
|
|
||||||
|
|
||||||
const svgContainer = document.createElement('div');
|
|
||||||
svgContainer.style.position = 'relative';
|
|
||||||
svgContainer.style.width = "2000px";
|
|
||||||
svgContainer.style.height = "2000px";
|
|
||||||
//svgContainer.appendChild(tileDebugger.svg);
|
|
||||||
document.body.appendChild(svgContainer);
|
|
||||||
|
|
||||||
this.pathColors = pathColors;
|
|
||||||
}
|
|
||||||
|
|
||||||
private addPath(paths: SVGPath[],
|
|
||||||
pathColors: any[],
|
|
||||||
paint: string,
|
|
||||||
pathString: string,
|
|
||||||
strokeWidth: number | null):
|
|
||||||
void {
|
|
||||||
const color = parseColor(paint).rgba;
|
|
||||||
pathColors.push({
|
|
||||||
r: color[0],
|
|
||||||
g: color[1],
|
|
||||||
b: color[2],
|
|
||||||
a: Math.round(color[3] * 255.),
|
|
||||||
});
|
|
||||||
|
|
||||||
let path: SVGPath = SVGPath(pathString);
|
|
||||||
path = path.matrix([
|
|
||||||
GLOBAL_TRANSFORM.a, GLOBAL_TRANSFORM.b,
|
|
||||||
GLOBAL_TRANSFORM.c, GLOBAL_TRANSFORM.d,
|
|
||||||
GLOBAL_TRANSFORM.tx, GLOBAL_TRANSFORM.ty,
|
|
||||||
]);
|
|
||||||
|
|
||||||
path = flattenPath(path);
|
|
||||||
|
|
||||||
if (strokeWidth != null) {
|
|
||||||
const outline = new Outline(path);
|
|
||||||
outline.calculateNormals();
|
|
||||||
outline.stroke(strokeWidth * GLOBAL_TRANSFORM.a);
|
|
||||||
const strokedPathString = outline.toSVGPathString();
|
|
||||||
|
|
||||||
/*
|
|
||||||
const newSVG = staticCast(document.createElementNS(SVG_NS, 'svg'), SVGElement);
|
|
||||||
newSVG.style.position = 'absolute';
|
|
||||||
newSVG.style.left = "0";
|
|
||||||
newSVG.style.top = "0";
|
|
||||||
newSVG.style.width = "2000px";
|
|
||||||
newSVG.style.height = "2000px";
|
|
||||||
|
|
||||||
const newPath = document.createElementNS(SVG_NS, 'path');
|
|
||||||
newPath.setAttribute('d', strokedPathString);
|
|
||||||
newPath.setAttribute('fill',
|
|
||||||
"rgba(" + color[0] + "," + color[1] + "," + color[2] + "," +
|
|
||||||
color[3] + ")");
|
|
||||||
newSVG.appendChild(newPath);
|
|
||||||
document.body.appendChild(newSVG);
|
|
||||||
*/
|
|
||||||
|
|
||||||
path = SVGPath(strokedPathString);
|
|
||||||
path = makePathMonotonic(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
paths.push(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Program<U extends string, A extends string> {
|
class Program<U extends string, A extends string> {
|
||||||
program: WebGLProgram;
|
program: WebGLProgram;
|
||||||
uniforms: {[key in U]: WebGLUniformLocation | null};
|
uniforms: {[key in U]: WebGLUniformLocation | null};
|
||||||
|
@ -652,6 +522,43 @@ class Program<U extends string, A extends string> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RIFFChunk {
|
||||||
|
private data: DataView;
|
||||||
|
|
||||||
|
constructor(data: DataView) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
stringID(): string {
|
||||||
|
return String.fromCharCode(this.data.getUint8(0),
|
||||||
|
this.data.getUint8(1),
|
||||||
|
this.data.getUint8(2),
|
||||||
|
this.data.getUint8(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
length(): number {
|
||||||
|
return this.data.getUint32(4, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
contents(): DataView {
|
||||||
|
return new DataView(this.data.buffer, this.data.byteOffset + 8, this.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
subchunks(): RIFFChunk[] {
|
||||||
|
const subchunks = [];
|
||||||
|
const contents = this.contents(), length = this.length();
|
||||||
|
let offset = 4;
|
||||||
|
while (offset < length) {
|
||||||
|
const subchunk = new RIFFChunk(new DataView(contents.buffer,
|
||||||
|
contents.byteOffset + offset,
|
||||||
|
length - offset));
|
||||||
|
subchunks.push(subchunk);
|
||||||
|
offset += subchunk.length() + 8;
|
||||||
|
}
|
||||||
|
return subchunks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function waitForQuery(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, query: WebGLQuery):
|
function waitForQuery(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, query: WebGLQuery):
|
||||||
void {
|
void {
|
||||||
const queryResultAvailable = disjointTimerQueryExt.QUERY_RESULT_AVAILABLE_EXT;
|
const queryResultAvailable = disjointTimerQueryExt.QUERY_RESULT_AVAILABLE_EXT;
|
||||||
|
@ -664,27 +571,16 @@ function waitForQuery(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, qu
|
||||||
console.log(elapsed + "ms elapsed");
|
console.log(elapsed + "ms elapsed");
|
||||||
}
|
}
|
||||||
|
|
||||||
function main(): void {
|
function loadAreaLUT(): Promise<HTMLImageElement> {
|
||||||
window.fetch(SVG).then(svg => {
|
return new Promise((resolve, reject) => {;
|
||||||
svg.text().then(svgText => {
|
|
||||||
//testIntervals();
|
|
||||||
|
|
||||||
const svg = staticCast((new DOMParser).parseFromString(svgText, 'image/svg+xml'),
|
|
||||||
XMLDocument);
|
|
||||||
const image = new Image;
|
const image = new Image;
|
||||||
image.src = AREA_LUT;
|
image.src = AREA_LUT;
|
||||||
image.addEventListener('load', event => {
|
image.addEventListener('load', event => resolve(image), false);
|
||||||
try {
|
});
|
||||||
const app = new App(svg, image);
|
|
||||||
app.buildScene();
|
|
||||||
app.prepare();
|
|
||||||
app.redraw();
|
|
||||||
} catch (e) {
|
|
||||||
console.error("error", e, e.stack);
|
|
||||||
}
|
}
|
||||||
}, false);
|
|
||||||
});
|
function main(): void {
|
||||||
});
|
loadAreaLUT().then(image => new App(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => main(), false);
|
document.addEventListener('DOMContentLoaded', () => main(), false);
|
||||||
|
|
|
@ -17,7 +17,6 @@ uniform vec2 uTileSize;
|
||||||
|
|
||||||
in vec2 aTessCoord;
|
in vec2 aTessCoord;
|
||||||
in vec2 aFrom;
|
in vec2 aFrom;
|
||||||
in vec2 aCtrl;
|
|
||||||
in vec2 aTo;
|
in vec2 aTo;
|
||||||
in uint aTileIndex;
|
in uint aTileIndex;
|
||||||
|
|
||||||
|
@ -34,23 +33,24 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
||||||
void main() {
|
void main() {
|
||||||
vec2 tileOrigin = computeTileOffset(aTileIndex, uFramebufferSize.x);
|
vec2 tileOrigin = computeTileOffset(aTileIndex, uFramebufferSize.x);
|
||||||
|
|
||||||
vec2 from = aFrom, ctrl = aCtrl, to = aTo;
|
vec2 from = aFrom, to = aTo;
|
||||||
|
|
||||||
vec2 position;
|
vec2 position;
|
||||||
bool zeroArea = !(abs(from.x - to.x) > 0.1) || !(abs(uTileSize.y - min(from.y, to.y)) > 0.1);
|
bool zeroArea = !(abs(from.x - to.x) > 0.1) || !(abs(uTileSize.y - min(from.y, to.y)) > 0.1);
|
||||||
if (aTessCoord.x < 0.5)
|
if (aTessCoord.x < 0.5)
|
||||||
position.x = floor(min(min(from.x, to.x), ctrl.x));
|
position.x = floor(min(from.x, to.x));
|
||||||
else
|
else
|
||||||
position.x = ceil(max(max(from.x, to.x), ctrl.x));
|
position.x = ceil(max(from.x, to.x));
|
||||||
if (aTessCoord.y < 0.5)
|
if (aTessCoord.y < 0.5)
|
||||||
position.y = floor(min(min(from.y, to.y), ctrl.y));
|
position.y = floor(min(from.y, to.y));
|
||||||
else
|
else
|
||||||
position.y = uTileSize.y;
|
position.y = uTileSize.y;
|
||||||
|
|
||||||
vFrom = from - position;
|
vFrom = from - position;
|
||||||
vCtrl = ctrl - position;
|
|
||||||
vTo = to - position;
|
vTo = to - position;
|
||||||
|
|
||||||
|
vCtrl = mix(vFrom, vTo, 0.5);
|
||||||
|
|
||||||
if (zeroArea)
|
if (zeroArea)
|
||||||
gl_Position = vec4(0.0);
|
gl_Position = vec4(0.0);
|
||||||
else
|
else
|
||||||
|
|
|
@ -1049,6 +1049,7 @@ impl BuiltScene {
|
||||||
for tile_primitive in &self.tiles {
|
for tile_primitive in &self.tiles {
|
||||||
let color = tile_primitive.color;
|
let color = tile_primitive.color;
|
||||||
write_point(writer, &tile_primitive.position)?;
|
write_point(writer, &tile_primitive.position)?;
|
||||||
|
writer.write_f32::<LittleEndian>(tile_primitive.backdrop)?;
|
||||||
writer.write_all(&[color.r, color.g, color.b, color.a]).unwrap();
|
writer.write_all(&[color.r, color.g, color.b, color.a]).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue