|
@@ -85,6 +85,105 @@ export type PartialCanvas3DProps = {
|
|
|
[K in keyof Canvas3DProps]?: Canvas3DProps[K] extends { name: string, params: any } ? Canvas3DProps[K] : Partial<Canvas3DProps[K]>
|
|
|
}
|
|
|
|
|
|
+export { Canvas3DContext };
|
|
|
+
|
|
|
+/** Can be used to create multiple Canvas3D objects */
|
|
|
+interface Canvas3DContext {
|
|
|
+ readonly webgl: WebGLContext
|
|
|
+ readonly input: InputObserver
|
|
|
+ readonly passes: Passes
|
|
|
+ readonly attribs: Readonly<Canvas3DContext.Attribs>
|
|
|
+ readonly contextLost: BehaviorSubject<now.Timestamp>
|
|
|
+ readonly contextRestored: BehaviorSubject<now.Timestamp>
|
|
|
+ dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => void
|
|
|
+}
|
|
|
+
|
|
|
+namespace Canvas3DContext {
|
|
|
+ const DefaultAttribs = {
|
|
|
+ /** true by default to avoid issues with Safari (Jan 2021) */
|
|
|
+ antialias: true,
|
|
|
+ /** true to support multiple Canvas3D objects with a single context */
|
|
|
+ preserveDrawingBuffer: true,
|
|
|
+ pixelScale: 1,
|
|
|
+ pickScale: 0.25,
|
|
|
+ enableWboit: true
|
|
|
+ };
|
|
|
+ export type Attribs = typeof DefaultAttribs
|
|
|
+
|
|
|
+ export function fromCanvas(canvas: HTMLCanvasElement, attribs: Partial<Attribs> = {}): Canvas3DContext {
|
|
|
+ const a = { ...DefaultAttribs, ...attribs };
|
|
|
+ const { antialias, preserveDrawingBuffer, pixelScale } = a;
|
|
|
+ const gl = getGLContext(canvas, {
|
|
|
+ antialias,
|
|
|
+ preserveDrawingBuffer,
|
|
|
+ alpha: true, // the renderer requires an alpha channel
|
|
|
+ depth: true, // the renderer requires a depth buffer
|
|
|
+ premultipliedAlpha: true, // the renderer outputs PMA
|
|
|
+ });
|
|
|
+ if (gl === null) throw new Error('Could not create a WebGL rendering context');
|
|
|
+
|
|
|
+ const input = InputObserver.fromElement(canvas, { pixelScale });
|
|
|
+ const webgl = createContext(gl, { pixelScale });
|
|
|
+ const passes = new Passes(webgl, attribs);
|
|
|
+
|
|
|
+ if (isDebugMode) {
|
|
|
+ const loseContextExt = gl.getExtension('WEBGL_lose_context');
|
|
|
+ if (loseContextExt) {
|
|
|
+ /** Hold down shift+ctrl+alt and press any mouse button to trigger lose context */
|
|
|
+ canvas.addEventListener('mousedown', e => {
|
|
|
+ if (webgl.isContextLost) return;
|
|
|
+ if (!e.shiftKey || !e.ctrlKey || !e.altKey) return;
|
|
|
+
|
|
|
+ if (isDebugMode) console.log('lose context');
|
|
|
+ loseContextExt.loseContext();
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ if (!webgl.isContextLost) return;
|
|
|
+ if (isDebugMode) console.log('restore context');
|
|
|
+ loseContextExt.restoreContext();
|
|
|
+ }, 1000);
|
|
|
+ }, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // https://www.khronos.org/webgl/wiki/HandlingContextLost
|
|
|
+
|
|
|
+ const contextLost = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
|
|
|
+
|
|
|
+ const handleWebglContextLost = (e: Event) => {
|
|
|
+ webgl.setContextLost();
|
|
|
+ e.preventDefault();
|
|
|
+ if (isDebugMode) console.log('context lost');
|
|
|
+ contextLost.next(now());
|
|
|
+ };
|
|
|
+
|
|
|
+ const handlewWebglContextRestored = () => {
|
|
|
+ if (!webgl.isContextLost) return;
|
|
|
+ webgl.handleContextRestored();
|
|
|
+ if (isDebugMode) console.log('context restored');
|
|
|
+ };
|
|
|
+
|
|
|
+ canvas.addEventListener('webglcontextlost', handleWebglContextLost, false);
|
|
|
+ canvas.addEventListener('webglcontextrestored', handlewWebglContextRestored, false);
|
|
|
+
|
|
|
+ return {
|
|
|
+ webgl,
|
|
|
+ input,
|
|
|
+ passes,
|
|
|
+ attribs: a,
|
|
|
+ contextLost,
|
|
|
+ contextRestored: webgl.contextRestored,
|
|
|
+ dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => {
|
|
|
+ input.dispose();
|
|
|
+
|
|
|
+ canvas.removeEventListener('webglcontextlost', handleWebglContextLost, false);
|
|
|
+ canvas.removeEventListener('webglcontextrestored', handlewWebglContextRestored, false);
|
|
|
+ webgl.destroy(options);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
export { Canvas3D };
|
|
|
|
|
|
interface Canvas3D {
|
|
@@ -135,7 +234,7 @@ interface Canvas3D {
|
|
|
readonly stats: RendererStats
|
|
|
readonly interaction: Canvas3dInteractionHelper['events']
|
|
|
|
|
|
- dispose(options?: { doNotForceWebGLContextLoss?: boolean }): void
|
|
|
+ dispose(): void
|
|
|
}
|
|
|
|
|
|
const requestAnimationFrame = typeof window !== 'undefined'
|
|
@@ -150,69 +249,7 @@ namespace Canvas3D {
|
|
|
export interface DragEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, pageStart: Vec2, pageEnd: Vec2 }
|
|
|
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
|
|
|
|
|
|
-
|
|
|
- type Attribs = {
|
|
|
- /** true by default to avoid issues with Safari (Jan 2021) */
|
|
|
- antialias: boolean,
|
|
|
- /** true to support multiple viewports with a single context */
|
|
|
- preserveDrawingBuffer: boolean,
|
|
|
- pixelScale: number,
|
|
|
- pickScale: number,
|
|
|
- enableWboit: boolean
|
|
|
- }
|
|
|
-
|
|
|
- export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, attribs: Partial<Attribs> = {}) {
|
|
|
- const gl = getGLContext(canvas, {
|
|
|
- antialias: attribs.antialias ?? true,
|
|
|
- preserveDrawingBuffer: attribs.preserveDrawingBuffer ?? true,
|
|
|
- alpha: true, // the renderer requires an alpha channel
|
|
|
- depth: true, // the renderer requires a depth buffer
|
|
|
- premultipliedAlpha: true, // the renderer outputs PMA
|
|
|
- });
|
|
|
- if (gl === null) throw new Error('Could not create a WebGL rendering context');
|
|
|
-
|
|
|
- const { pixelScale } = attribs;
|
|
|
- const input = InputObserver.fromElement(canvas, { pixelScale });
|
|
|
- const webgl = createContext(gl, { pixelScale });
|
|
|
- const passes = new Passes(webgl, attribs);
|
|
|
-
|
|
|
- if (isDebugMode) {
|
|
|
- const loseContextExt = gl.getExtension('WEBGL_lose_context');
|
|
|
- if (loseContextExt) {
|
|
|
- canvas.addEventListener('mousedown', e => {
|
|
|
- if (webgl.isContextLost) return;
|
|
|
- if (!e.shiftKey || !e.ctrlKey || !e.altKey) return;
|
|
|
-
|
|
|
- if (isDebugMode) console.log('lose context');
|
|
|
- loseContextExt.loseContext();
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- if (!webgl.isContextLost) return;
|
|
|
- if (isDebugMode) console.log('restore context');
|
|
|
- loseContextExt.restoreContext();
|
|
|
- }, 1000);
|
|
|
- }, false);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // https://www.khronos.org/webgl/wiki/HandlingContextLost
|
|
|
-
|
|
|
- canvas.addEventListener('webglcontextlost', e => {
|
|
|
- webgl.setContextLost();
|
|
|
- e.preventDefault();
|
|
|
- if (isDebugMode) console.log('context lost');
|
|
|
- }, false);
|
|
|
-
|
|
|
- canvas.addEventListener('webglcontextrestored', () => {
|
|
|
- if (!webgl.isContextLost) return;
|
|
|
- webgl.handleContextRestored();
|
|
|
- if (isDebugMode) console.log('context restored');
|
|
|
- }, false);
|
|
|
-
|
|
|
- return create(webgl, input, passes, props, { pixelScale });
|
|
|
- }
|
|
|
-
|
|
|
- export function create(webgl: WebGLContext, input: InputObserver, passes: Passes, props: Partial<Canvas3DProps> = {}, attribs: Partial<{ pixelScale: number }>): Canvas3D {
|
|
|
+ export function create({ webgl, input, passes, attribs }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
|
|
|
const p: Canvas3DProps = { ...DefaultCanvas3DParams, ...props };
|
|
|
|
|
|
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
|
|
@@ -705,17 +742,14 @@ namespace Canvas3D {
|
|
|
get interaction() {
|
|
|
return interactionHelper.events;
|
|
|
},
|
|
|
- dispose: (options?: { doNotForceWebGLContextLoss?: boolean }) => {
|
|
|
+ dispose: () => {
|
|
|
contextRestoredSub.unsubscribe();
|
|
|
|
|
|
scene.clear();
|
|
|
helper.debug.clear();
|
|
|
- input.dispose();
|
|
|
controls.dispose();
|
|
|
renderer.dispose();
|
|
|
interactionHelper.dispose();
|
|
|
-
|
|
|
- if (!options?.doNotForceWebGLContextLoss) gl.getExtension('WEBGL_lose_context')?.loseContext();
|
|
|
}
|
|
|
};
|
|
|
|