127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
import regl from 'regl'
|
|
import {AffineTransform, Rect} from './math'
|
|
|
|
export interface ViewportRectangleRendererProps {
|
|
configSpaceToPhysicalViewSpace: AffineTransform
|
|
configSpaceViewportRect: Rect
|
|
}
|
|
|
|
export class ViewportRectangleRenderer {
|
|
private command: regl.Command<ViewportRectangleRendererProps>
|
|
constructor(regl: regl.Instance) {
|
|
this.command = regl<ViewportRectangleRendererProps>({
|
|
vert: `
|
|
attribute vec2 position;
|
|
|
|
void main() {
|
|
gl_Position = vec4(position, 0, 1);
|
|
}
|
|
`,
|
|
|
|
frag: `
|
|
precision mediump float;
|
|
|
|
uniform mat3 configSpaceToPhysicalViewSpace;
|
|
uniform vec2 physicalSize;
|
|
uniform vec2 physicalOrigin;
|
|
uniform vec2 configSpaceViewportOrigin;
|
|
uniform vec2 configSpaceViewportSize;
|
|
uniform float framebufferHeight;
|
|
|
|
void main() {
|
|
vec2 origin = (configSpaceToPhysicalViewSpace * vec3(configSpaceViewportOrigin, 1.0)).xy;
|
|
vec2 size = (configSpaceToPhysicalViewSpace * vec3(configSpaceViewportSize, 0.0)).xy;
|
|
|
|
vec2 halfSize = physicalSize / 2.0;
|
|
|
|
float borderWidth = 2.0;
|
|
|
|
origin = floor(origin * halfSize) / halfSize + borderWidth * vec2(1.0, 1.0);
|
|
size = floor(size * halfSize) / halfSize - 2.0 * borderWidth * vec2(1.0, 1.0);
|
|
|
|
vec2 coord = gl_FragCoord.xy;
|
|
coord.x = coord.x - physicalOrigin.x;
|
|
coord.y = framebufferHeight - coord.y - physicalOrigin.y;
|
|
vec2 clamped = clamp(coord, origin, origin + size);
|
|
vec2 gap = clamped - coord;
|
|
float maxdist = max(abs(gap.x), abs(gap.y));
|
|
|
|
// TOOD(jlfwong): Could probably optimize this to use mix somehow.
|
|
if (maxdist == 0.0) {
|
|
// Inside viewport rectangle
|
|
gl_FragColor = vec4(0, 0, 0, 0);
|
|
} else if (maxdist < borderWidth) {
|
|
// Inside viewport rectangle at border
|
|
gl_FragColor = vec4(0.7, 0.7, 0.7, 0.8);
|
|
} else {
|
|
// Outside viewport rectangle
|
|
gl_FragColor = vec4(0.7, 0.7, 0.7, 0.5);
|
|
}
|
|
}
|
|
`,
|
|
|
|
blend: {
|
|
enable: true,
|
|
func: {
|
|
srcRGB: 'src alpha',
|
|
srcAlpha: 'one',
|
|
dstRGB: 'one minus src alpha',
|
|
dstAlpha: 'one',
|
|
},
|
|
},
|
|
|
|
depth: {
|
|
enable: false,
|
|
},
|
|
|
|
attributes: {
|
|
// Cover full canvas with a rectangle
|
|
// with 2 triangles using a triangle
|
|
// strip.
|
|
//
|
|
// 0 +--+ 1
|
|
// | /|
|
|
// |/ |
|
|
// 2 +--+ 3
|
|
position: [[-1, 1], [1, 1], [-1, -1], [1, -1]],
|
|
},
|
|
|
|
uniforms: {
|
|
configSpaceToPhysicalViewSpace: (context, props) => {
|
|
return props.configSpaceToPhysicalViewSpace.flatten()
|
|
},
|
|
configSpaceViewportOrigin: (context, props) => {
|
|
return props.configSpaceViewportRect.origin.flatten()
|
|
},
|
|
configSpaceViewportSize: (context, props) => {
|
|
return props.configSpaceViewportRect.size.flatten()
|
|
},
|
|
physicalSize: (context, props) => {
|
|
return [context.viewportWidth, context.viewportHeight]
|
|
},
|
|
physicalOrigin: (context, props) => {
|
|
return [context.viewportX, context.viewportY]
|
|
},
|
|
framebufferHeight: (context, props) => {
|
|
return context.framebufferHeight
|
|
},
|
|
},
|
|
|
|
primitive: 'triangle strip',
|
|
|
|
count: 4,
|
|
})
|
|
}
|
|
|
|
render(props: ViewportRectangleRendererProps) {
|
|
this.command(props)
|
|
}
|
|
|
|
resetStats() {
|
|
return Object.assign(this.command.stats, {cpuTime: 0, gpuTime: 0, count: 0})
|
|
}
|
|
stats() {
|
|
return this.command.stats
|
|
}
|
|
}
|