Avoid triggering reflow on canvasSize querying
This commit is contained in:
parent
582a0cea08
commit
3fb6cc0411
9 changed files with 116 additions and 76 deletions
10
package.json
10
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "sdf-2d",
|
"name": "sdf-2d",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"description": "Graphics framework for efficiently rendering 2D signed distance fields.",
|
"description": "Graphics framework for efficiently rendering 2D signed distance fields.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"webgl",
|
"webgl",
|
||||||
|
|
@ -27,6 +27,11 @@
|
||||||
"url": "git://github.com/schmelczerandras/sdf-2d.git"
|
"url": "git://github.com/schmelczerandras/sdf-2d.git"
|
||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/main.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "rm -rf lib/* && webpack --mode development -w",
|
"start": "rm -rf lib/* && webpack --mode development -w",
|
||||||
"build": "rm -rf lib/* docs/* && typedoc && webpack --mode production"
|
"build": "rm -rf lib/* docs/* && typedoc && webpack --mode production"
|
||||||
|
|
@ -40,7 +45,8 @@
|
||||||
"src/main.ts"
|
"src/main.ts"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gl-matrix": "^3.3.0"
|
"gl-matrix": "^3.3.0",
|
||||||
|
"resize-observer-polyfill": "^1.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^3.10.1",
|
"@typescript-eslint/eslint-plugin": "^3.10.1",
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
|
import { ReadonlyVec2 } from 'gl-matrix';
|
||||||
import { UniversalRenderingContext } from '../universal-rendering-context';
|
import { UniversalRenderingContext } from '../universal-rendering-context';
|
||||||
import { FrameBuffer } from './frame-buffer';
|
import { FrameBuffer } from './frame-buffer';
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export class DefaultFrameBuffer extends FrameBuffer {
|
export class DefaultFrameBuffer extends FrameBuffer {
|
||||||
constructor(gl: UniversalRenderingContext) {
|
constructor(gl: UniversalRenderingContext, canvasSize: ReadonlyVec2) {
|
||||||
super(gl);
|
super(gl);
|
||||||
this.frameBuffer = null;
|
this.frameBuffer = null;
|
||||||
this.setSize();
|
this.setSize(canvasSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSize(): boolean {
|
public setSize(canvasSize: ReadonlyVec2): boolean {
|
||||||
const hasChanged = super.setSize();
|
const hasChanged = super.setSize(canvasSize);
|
||||||
|
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
this.gl.canvas.width = this.size.x;
|
this.gl.canvas.width = this.size.x;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { ReadonlyVec2, vec2 } from 'gl-matrix';
|
||||||
import { Texture } from '../texture/texture';
|
import { Texture } from '../texture/texture';
|
||||||
import { UniversalRenderingContext } from '../universal-rendering-context';
|
import { UniversalRenderingContext } from '../universal-rendering-context';
|
||||||
|
|
||||||
|
|
@ -26,15 +26,12 @@ export abstract class FrameBuffer {
|
||||||
this.gl.deleteFramebuffer(this.frameBuffer);
|
this.gl.deleteFramebuffer(this.frameBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSize(): boolean {
|
public setSize(canvasSize: ReadonlyVec2): boolean {
|
||||||
const realToCssPixels =
|
const realToCssPixels =
|
||||||
(this.enableHighDpiRendering ? devicePixelRatio : 1) * this.renderScale;
|
(this.enableHighDpiRendering ? devicePixelRatio : 1) * this.renderScale;
|
||||||
|
|
||||||
const canvasWidth = (this.gl.canvas as HTMLCanvasElement).clientWidth;
|
const displayWidth = Math.floor(canvasSize.x * realToCssPixels);
|
||||||
const canvasHeight = (this.gl.canvas as HTMLCanvasElement).clientHeight;
|
const displayHeight = Math.floor(canvasSize.y * realToCssPixels);
|
||||||
|
|
||||||
const displayWidth = Math.floor(canvasWidth * realToCssPixels);
|
|
||||||
const displayHeight = Math.floor(canvasHeight * realToCssPixels);
|
|
||||||
|
|
||||||
const oldSize = vec2.clone(this.getSize());
|
const oldSize = vec2.clone(this.getSize());
|
||||||
this.size = vec2.fromValues(displayWidth, displayHeight);
|
this.size = vec2.fromValues(displayWidth, displayHeight);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ReadonlyVec2 } from 'gl-matrix';
|
||||||
import { ColorTexture } from '../texture/color-texture';
|
import { ColorTexture } from '../texture/color-texture';
|
||||||
import { DistanceTexture } from '../texture/distance-texture';
|
import { DistanceTexture } from '../texture/distance-texture';
|
||||||
import { Texture } from '../texture/texture';
|
import { Texture } from '../texture/texture';
|
||||||
|
|
@ -9,7 +10,7 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
||||||
private distanceTexture?: DistanceTexture;
|
private distanceTexture?: DistanceTexture;
|
||||||
private colorTexture: ColorTexture;
|
private colorTexture: ColorTexture;
|
||||||
|
|
||||||
constructor(gl: UniversalRenderingContext) {
|
constructor(gl: UniversalRenderingContext, canvasSize: ReadonlyVec2) {
|
||||||
super(gl);
|
super(gl);
|
||||||
|
|
||||||
this.colorTexture = new ColorTexture(gl);
|
this.colorTexture = new ColorTexture(gl);
|
||||||
|
|
@ -21,7 +22,7 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
||||||
this.frameBuffer = this.gl.createFramebuffer()!;
|
this.frameBuffer = this.gl.createFramebuffer()!;
|
||||||
this.configureFrameBuffer();
|
this.configureFrameBuffer();
|
||||||
|
|
||||||
this.setSize();
|
this.setSize(canvasSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
|
|
@ -38,8 +39,8 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
||||||
: [this.colorTexture];
|
: [this.colorTexture];
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSize(): boolean {
|
public setSize(canvasSize: ReadonlyVec2): boolean {
|
||||||
const hasChanged = super.setSize();
|
const hasChanged = super.setSize(canvasSize);
|
||||||
|
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
this.colorTexture.setSize(this.size);
|
this.colorTexture.setSize(this.size);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { ReadonlyVec2, vec2 } from 'gl-matrix';
|
||||||
import { Drawable } from '../../../drawables/drawable';
|
import { Drawable } from '../../../drawables/drawable';
|
||||||
import { DrawableDescriptor } from '../../../drawables/drawable-descriptor';
|
import { DrawableDescriptor } from '../../../drawables/drawable-descriptor';
|
||||||
import { RuntimeSettings } from '../settings/runtime-settings';
|
import { RuntimeSettings } from '../settings/runtime-settings';
|
||||||
|
|
@ -14,8 +14,8 @@ export class ContextAwareRenderer implements Renderer {
|
||||||
private readyPromise!: Promise<void>;
|
private readyPromise!: Promise<void>;
|
||||||
private runtimeOverrides: Partial<RuntimeSettings> = {};
|
private runtimeOverrides: Partial<RuntimeSettings> = {};
|
||||||
private ignoreWebGL2?: boolean;
|
private ignoreWebGL2?: boolean;
|
||||||
private previousViewAreaTopLeft?: vec2;
|
private previousViewAreaTopLeft?: ReadonlyVec2;
|
||||||
private previousViewAreaSize?: vec2;
|
private previousViewAreaSize?: ReadonlyVec2;
|
||||||
|
|
||||||
private contextRestoredHandler = this.handleContextRestored.bind(this);
|
private contextRestoredHandler = this.handleContextRestored.bind(this);
|
||||||
private contextLostHandler = this.handleContextLost.bind(this);
|
private contextLostHandler = this.handleContextLost.bind(this);
|
||||||
|
|
@ -102,11 +102,11 @@ export class ContextAwareRenderer implements Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get canvasSize(): vec2 {
|
public get canvasSize(): ReadonlyVec2 {
|
||||||
return this.handle(() => this.renderer.canvasSize, vec2.create());
|
return this.handle(() => this.renderer.canvasSize, vec2.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
public get viewAreaSize(): vec2 {
|
public get viewAreaSize(): ReadonlyVec2 {
|
||||||
return this.handle(() => this.renderer.viewAreaSize, vec2.create());
|
return this.handle(() => this.renderer.viewAreaSize, vec2.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ export class ContextAwareRenderer implements Renderer {
|
||||||
return this.handle(() => this.renderer.insights, null);
|
return this.handle(() => this.renderer.insights, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setViewArea(topLeft: vec2, size: vec2): void {
|
public setViewArea(topLeft: ReadonlyVec2, size: ReadonlyVec2): void {
|
||||||
this.previousViewAreaTopLeft = topLeft;
|
this.previousViewAreaTopLeft = topLeft;
|
||||||
this.previousViewAreaSize = size;
|
this.previousViewAreaSize = size;
|
||||||
return this.handle(() => this.renderer.setViewArea(topLeft, size), undefined);
|
return this.handle(() => this.renderer.setViewArea(topLeft, size), undefined);
|
||||||
|
|
@ -133,14 +133,14 @@ export class ContextAwareRenderer implements Renderer {
|
||||||
return this.handle(() => this.renderer.addDrawable(drawable), undefined);
|
return this.handle(() => this.renderer.addDrawable(drawable), undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public displayToWorldCoordinates(displayCoordinates: vec2): vec2 {
|
public displayToWorldCoordinates(displayCoordinates: ReadonlyVec2): vec2 {
|
||||||
return this.handle(
|
return this.handle(
|
||||||
() => this.renderer.displayToWorldCoordinates(displayCoordinates),
|
() => this.renderer.displayToWorldCoordinates(displayCoordinates),
|
||||||
vec2.create()
|
vec2.create()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public worldToDisplayCoordinates(worldCoordinates: vec2): vec2 {
|
public worldToDisplayCoordinates(worldCoordinates: ReadonlyVec2): vec2 {
|
||||||
return this.handle(
|
return this.handle(
|
||||||
() => this.renderer.worldToDisplayCoordinates(worldCoordinates),
|
() => this.renderer.worldToDisplayCoordinates(worldCoordinates),
|
||||||
vec2.create()
|
vec2.create()
|
||||||
|
|
@ -159,9 +159,7 @@ export class ContextAwareRenderer implements Renderer {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.canvas.removeEventListener('webglcontextlost', this.contextLostHandler, false);
|
this.canvas.removeEventListener('webglcontextlost', this.contextLostHandler, false);
|
||||||
|
|
||||||
this.isRendererReady = false;
|
this.isRendererReady = false;
|
||||||
|
|
||||||
this.handle(() => this.renderer.destroy(), undefined);
|
this.handle(() => this.renderer.destroy(), undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { ReadonlyVec2, vec2 } from 'gl-matrix';
|
||||||
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
import { Drawable } from '../../../drawables/drawable';
|
import { Drawable } from '../../../drawables/drawable';
|
||||||
import { DrawableDescriptor } from '../../../drawables/drawable-descriptor';
|
import { DrawableDescriptor } from '../../../drawables/drawable-descriptor';
|
||||||
import { LightDrawable } from '../../../drawables/lights/light-drawable';
|
import { LightDrawable } from '../../../drawables/lights/light-drawable';
|
||||||
|
|
@ -38,15 +39,16 @@ import { RendererInfo } from './renderer-info';
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export class RendererImplementation implements Renderer {
|
export class RendererImplementation implements Renderer {
|
||||||
private readonly gl: UniversalRenderingContext;
|
private readonly gl: UniversalRenderingContext;
|
||||||
|
private readonly uniformsProvider: UniformsProvider;
|
||||||
|
private readonly distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
||||||
|
private readonly distancePass: DistanceRenderPass;
|
||||||
|
private readonly lightingFrameBuffer: DefaultFrameBuffer;
|
||||||
|
private readonly lightsPass: LightsRenderPass;
|
||||||
private stopwatch?: WebGlStopwatch;
|
private stopwatch?: WebGlStopwatch;
|
||||||
private uniformsProvider: UniformsProvider;
|
|
||||||
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
|
||||||
private distancePass: DistanceRenderPass;
|
|
||||||
private lightingFrameBuffer: DefaultFrameBuffer;
|
|
||||||
private lightsPass: LightsRenderPass;
|
|
||||||
private palette!: PaletteTexture;
|
|
||||||
|
|
||||||
private textures: Array<Texture> = [];
|
private textures: Array<Texture> = [];
|
||||||
|
private palette!: PaletteTexture;
|
||||||
|
private _canvasSize = vec2.create();
|
||||||
|
private canvasResizeObserver!: ResizeObserver;
|
||||||
|
|
||||||
private applyRuntimeSettings: {
|
private applyRuntimeSettings: {
|
||||||
[key in keyof RuntimeSettings]: (value: any) => void;
|
[key in keyof RuntimeSettings]: (value: any) => void;
|
||||||
|
|
@ -78,8 +80,8 @@ export class RendererImplementation implements Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private canvas: HTMLCanvasElement,
|
private readonly canvas: HTMLCanvasElement,
|
||||||
private descriptors: Array<DrawableDescriptor>,
|
private readonly descriptors: Array<DrawableDescriptor>,
|
||||||
ignoreWebGL2?: boolean
|
ignoreWebGL2?: boolean
|
||||||
) {
|
) {
|
||||||
this.gl = getUniversalRenderingContext(
|
this.gl = getUniversalRenderingContext(
|
||||||
|
|
@ -89,17 +91,19 @@ export class RendererImplementation implements Renderer {
|
||||||
|
|
||||||
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
|
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
|
this.applyCanvasResizeObserver();
|
||||||
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl);
|
|
||||||
|
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl, this.canvasSize);
|
||||||
|
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl, this.canvasSize);
|
||||||
|
|
||||||
this.distancePass = new DistanceRenderPass(this.gl, this.distanceFieldFrameBuffer);
|
this.distancePass = new DistanceRenderPass(this.gl, this.distanceFieldFrameBuffer);
|
||||||
this.lightsPass = new LightsRenderPass(this.gl, this.lightingFrameBuffer);
|
this.lightsPass = new LightsRenderPass(this.gl, this.lightingFrameBuffer);
|
||||||
|
|
||||||
this.uniformsProvider = new UniformsProvider(this.gl);
|
this.uniformsProvider = new UniformsProvider(this);
|
||||||
|
|
||||||
this.setViewArea(
|
this.setViewArea(
|
||||||
vec2.fromValues(0, canvas.clientHeight),
|
vec2.fromValues(0, this.canvasSize.y),
|
||||||
vec2.fromValues(canvas.clientWidth, canvas.clientHeight)
|
vec2.fromValues(this.canvasSize.x, this.canvasSize.y)
|
||||||
);
|
);
|
||||||
|
|
||||||
const hardwareInfo = getHardwareInfo(this.gl);
|
const hardwareInfo = getHardwareInfo(this.gl);
|
||||||
|
|
@ -107,6 +111,18 @@ export class RendererImplementation implements Renderer {
|
||||||
this.gl.insights.vendor = hardwareInfo?.vendor;
|
this.gl.insights.vendor = hardwareInfo?.vendor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private applyCanvasResizeObserver() {
|
||||||
|
this.canvasResizeObserver = new ResizeObserver((e) => {
|
||||||
|
const entry = e[0];
|
||||||
|
this._canvasSize = vec2.fromValues(
|
||||||
|
entry.contentRect.width,
|
||||||
|
entry.contentRect.height
|
||||||
|
);
|
||||||
|
this.uniformsProvider.calculateScreenToWorldTransformations();
|
||||||
|
});
|
||||||
|
this.canvasResizeObserver.observe(this.canvas);
|
||||||
|
}
|
||||||
|
|
||||||
public async initialize(settingsOverrides: Partial<StartupSettings>): Promise<void> {
|
public async initialize(settingsOverrides: Partial<StartupSettings>): Promise<void> {
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultStartupSettings,
|
...defaultStartupSettings,
|
||||||
|
|
@ -208,8 +224,8 @@ export class RendererImplementation implements Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer.setSize();
|
this.distanceFieldFrameBuffer.setSize(this.canvasSize);
|
||||||
this.lightingFrameBuffer.setSize();
|
this.lightingFrameBuffer.setSize(this.canvasSize);
|
||||||
|
|
||||||
const common = {
|
const common = {
|
||||||
// texture units
|
// texture units
|
||||||
|
|
@ -238,28 +254,28 @@ export class RendererImplementation implements Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public displayToWorldCoordinates(displayCoordinates: vec2): vec2 {
|
public displayToWorldCoordinates(displayCoordinates: ReadonlyVec2): vec2 {
|
||||||
return this.uniformsProvider.screenToWorldPosition(displayCoordinates);
|
return this.uniformsProvider.screenToWorldPosition(displayCoordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
public worldToDisplayCoordinates(worldCoordinates: vec2): vec2 {
|
public worldToDisplayCoordinates(worldCoordinates: ReadonlyVec2): vec2 {
|
||||||
return this.uniformsProvider.worldToDisplayCoordinates(worldCoordinates);
|
return this.uniformsProvider.worldToDisplayCoordinates(worldCoordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setViewArea(topLeft: vec2, size: vec2) {
|
public setViewArea(topLeft: ReadonlyVec2, size: ReadonlyVec2) {
|
||||||
this.uniformsProvider.setViewArea(topLeft, size);
|
this.uniformsProvider.setViewArea(topLeft, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get viewAreaSize(): vec2 {
|
public get viewAreaSize(): ReadonlyVec2 {
|
||||||
return this.uniformsProvider.getViewArea();
|
return this.uniformsProvider.getViewArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get canvasSize(): vec2 {
|
public get canvasSize(): ReadonlyVec2 {
|
||||||
const { width, height } = this.canvas.getBoundingClientRect();
|
return this._canvasSize;
|
||||||
return vec2.fromValues(width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
|
this.canvasResizeObserver.disconnect();
|
||||||
this.distancePass.destroy();
|
this.distancePass.destroy();
|
||||||
this.lightsPass.destroy();
|
this.lightsPass.destroy();
|
||||||
this.palette.destroy();
|
this.palette.destroy();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { ReadonlyVec2, vec2 } from 'gl-matrix';
|
||||||
import { Drawable } from '../../../drawables/drawable';
|
import { Drawable } from '../../../drawables/drawable';
|
||||||
import { RuntimeSettings } from '../settings/runtime-settings';
|
import { RuntimeSettings } from '../settings/runtime-settings';
|
||||||
import { RendererInfo } from './renderer-info';
|
import { RendererInfo } from './renderer-info';
|
||||||
|
|
@ -10,16 +10,18 @@ import { RendererInfo } from './renderer-info';
|
||||||
*/
|
*/
|
||||||
export interface Renderer {
|
export interface Renderer {
|
||||||
/**
|
/**
|
||||||
* Get the actual resolution of the canvas.
|
* Get the actual resolution of the canvas without triggering a reflow.
|
||||||
|
*
|
||||||
|
* A ResizeObserver is utilised fot achieving this.
|
||||||
*/
|
*/
|
||||||
readonly canvasSize: vec2;
|
readonly canvasSize: ReadonlyVec2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the viewArea size set by the last `setViewArea`.
|
* Get the viewArea size set by the last `setViewArea`.
|
||||||
*
|
*
|
||||||
* By default, `canvasSize` is used for the view area size.
|
* By default, `canvasSize` is used for the view area size.
|
||||||
*/
|
*/
|
||||||
readonly viewAreaSize: vec2;
|
readonly viewAreaSize: ReadonlyVec2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the camera transformation.
|
* Set the camera transformation.
|
||||||
|
|
@ -28,7 +30,7 @@ export interface Renderer {
|
||||||
* @param size need not be equal to the canvas size, though their aspect ratio
|
* @param size need not be equal to the canvas size, though their aspect ratio
|
||||||
* should be the same to avoid stretching.
|
* should be the same to avoid stretching.
|
||||||
*/
|
*/
|
||||||
setViewArea(topLeft: vec2, size: vec2): void;
|
setViewArea(topLeft: ReadonlyVec2, size: ReadonlyVec2): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The inverse of `worldToDisplayCoordinates`, returns the world coordinates
|
* The inverse of `worldToDisplayCoordinates`, returns the world coordinates
|
||||||
|
|
@ -41,7 +43,7 @@ export interface Renderer {
|
||||||
* @param displayCoordinates The origin is in the display's top left corner.
|
* @param displayCoordinates The origin is in the display's top left corner.
|
||||||
* Just as in mouse events' clientX and clientY.
|
* Just as in mouse events' clientX and clientY.
|
||||||
*/
|
*/
|
||||||
displayToWorldCoordinates(displayCoordinates: vec2): vec2;
|
displayToWorldCoordinates(displayCoordinates: ReadonlyVec2): vec2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The inverse of `displayToWorldCoordinates`, returns the screen space position
|
* The inverse of `displayToWorldCoordinates`, returns the screen space position
|
||||||
|
|
@ -52,7 +54,7 @@ export interface Renderer {
|
||||||
*
|
*
|
||||||
* @param worldCoordinates Coordinates used when drawing objects.
|
* @param worldCoordinates Coordinates used when drawing objects.
|
||||||
*/
|
*/
|
||||||
worldToDisplayCoordinates(worldCoordinates: vec2): vec2;
|
worldToDisplayCoordinates(worldCoordinates: ReadonlyVec2): vec2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patch the current runtime settings with new values.
|
* Patch the current runtime settings with new values.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { mat2d, vec2, vec3 } from 'gl-matrix';
|
import { mat2d, ReadonlyVec2, vec2, vec3 } from 'gl-matrix';
|
||||||
import { UniversalRenderingContext } from '../graphics-library/universal-rendering-context';
|
import { Renderer } from './renderer/renderer';
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export class UniformsProvider {
|
export class UniformsProvider {
|
||||||
|
|
@ -9,11 +9,13 @@ export class UniformsProvider {
|
||||||
private scaleWorldLengthToNDC = 1;
|
private scaleWorldLengthToNDC = 1;
|
||||||
private transformWorldToNDC = mat2d.create();
|
private transformWorldToNDC = mat2d.create();
|
||||||
private viewAreaBottomLeft = vec2.create();
|
private viewAreaBottomLeft = vec2.create();
|
||||||
private worldAreaInView = vec2.create();
|
private worldAreaInView: ReadonlyVec2 = vec2.create();
|
||||||
private squareToAspectRatio = vec2.create();
|
private squareToAspectRatio = vec2.create();
|
||||||
private uvToWorld = mat2d.create();
|
private uvToWorld = mat2d.create();
|
||||||
|
private screenToWorldTransformation = mat2d.create();
|
||||||
|
private worldToScreenTransformation = mat2d.create();
|
||||||
|
|
||||||
public constructor(private readonly gl: UniversalRenderingContext) {}
|
public constructor(private readonly renderer: Renderer) {}
|
||||||
|
|
||||||
public getUniforms(uniforms: any): any {
|
public getUniforms(uniforms: any): any {
|
||||||
return {
|
return {
|
||||||
|
|
@ -29,11 +31,11 @@ export class UniformsProvider {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getViewArea(): vec2 {
|
public getViewArea(): ReadonlyVec2 {
|
||||||
return this.worldAreaInView;
|
return this.worldAreaInView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setViewArea(topLeft: vec2, size: vec2) {
|
public setViewArea(topLeft: ReadonlyVec2, size: ReadonlyVec2) {
|
||||||
this.worldAreaInView = size;
|
this.worldAreaInView = size;
|
||||||
|
|
||||||
// world coordinates
|
// world coordinates
|
||||||
|
|
@ -68,31 +70,43 @@ export class UniformsProvider {
|
||||||
|
|
||||||
this.uvToWorld = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
|
this.uvToWorld = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
|
||||||
mat2d.scale(this.uvToWorld, this.uvToWorld, this.worldAreaInView);
|
mat2d.scale(this.uvToWorld, this.uvToWorld, this.worldAreaInView);
|
||||||
|
|
||||||
|
this.calculateScreenToWorldTransformations();
|
||||||
}
|
}
|
||||||
|
|
||||||
public screenToWorldPosition(screenPosition: vec2): vec2 {
|
public screenToWorldPosition(screenPosition: ReadonlyVec2): vec2 {
|
||||||
const transform = this.calculateScreenToWorldTransformation();
|
return vec2.transformMat2d(
|
||||||
return vec2.transformMat2d(vec2.create(), screenPosition, transform);
|
vec2.create(),
|
||||||
|
screenPosition,
|
||||||
|
this.screenToWorldTransformation
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public worldToDisplayCoordinates(worldCoordinates: vec2): vec2 {
|
public worldToDisplayCoordinates(worldCoordinates: ReadonlyVec2): vec2 {
|
||||||
const transform = this.calculateScreenToWorldTransformation();
|
return vec2.transformMat2d(
|
||||||
mat2d.invert(transform, transform);
|
vec2.create(),
|
||||||
return vec2.transformMat2d(vec2.create(), worldCoordinates, transform);
|
worldCoordinates,
|
||||||
|
this.worldToScreenTransformation
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateScreenToWorldTransformation(): mat2d {
|
public calculateScreenToWorldTransformations() {
|
||||||
const { width, height } = (this.gl
|
const width = this.renderer.canvasSize.x;
|
||||||
.canvas as HTMLCanvasElement).getBoundingClientRect();
|
const height = this.renderer.canvasSize.y;
|
||||||
const resolution = vec2.fromValues(width, height);
|
const resolution = vec2.fromValues(width, height);
|
||||||
|
|
||||||
const transform = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
|
const transform = mat2d.fromTranslation(
|
||||||
|
this.screenToWorldTransformation,
|
||||||
|
this.viewAreaBottomLeft
|
||||||
|
);
|
||||||
mat2d.scale(
|
mat2d.scale(
|
||||||
transform,
|
transform,
|
||||||
transform,
|
transform,
|
||||||
vec2.divide(vec2.create(), this.worldAreaInView, resolution)
|
vec2.divide(vec2.create(), this.worldAreaInView, resolution)
|
||||||
);
|
);
|
||||||
mat2d.translate(transform, transform, vec2.fromValues(0.5, height - 0.5));
|
mat2d.translate(transform, transform, vec2.fromValues(0.5, height - 0.5));
|
||||||
return mat2d.scale(transform, transform, vec2.fromValues(1, -1));
|
mat2d.scale(transform, transform, vec2.fromValues(1, -1));
|
||||||
|
|
||||||
|
mat2d.invert(this.worldToScreenTransformation, this.screenToWorldTransformation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,11 @@ declare global {
|
||||||
y: T;
|
y: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ReadonlyArray<T> {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
}
|
||||||
|
|
||||||
interface Float32Array {
|
interface Float32Array {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue