123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- * @author David Sehnal <david.sehnal@gmail.com>
- */
- import { BehaviorSubject, Subscription } from 'rxjs';
- import { now } from '../mol-util/now';
- import { Vec3, Vec2 } from '../mol-math/linear-algebra';
- import InputObserver, { ModifiersKeys, ButtonsType } from '../mol-util/input/input-observer';
- import Renderer, { RendererStats, RendererParams } from '../mol-gl/renderer';
- import { GraphicsRenderObject } from '../mol-gl/render-object';
- import { TrackballControls, TrackballControlsParams } from './controls/trackball';
- import { Viewport } from './camera/util';
- import { createContext, WebGLContext, getGLContext } from '../mol-gl/webgl/context';
- import { Representation } from '../mol-repr/representation';
- import Scene from '../mol-gl/scene';
- import { PickingId } from '../mol-geo/geometry/picking';
- import { MarkerAction } from '../mol-util/marker-action';
- import { Loci, EmptyLoci, isEmptyLoci } from '../mol-model/loci';
- import { Camera } from './camera';
- import { ParamDefinition as PD } from '../mol-util/param-definition';
- import { DebugHelperParams } from './helper/bounding-sphere-helper';
- import { SetUtils } from '../mol-util/set';
- import { Canvas3dInteractionHelper } from './helper/interaction-events';
- import { PostprocessingParams } from './passes/postprocessing';
- import { MultiSampleHelper, MultiSampleParams, MultiSamplePass } from './passes/multi-sample';
- import { PickData } from './passes/pick';
- import { PickHelper } from './passes/pick';
- import { ImagePass, ImageProps } from './passes/image';
- import { Sphere3D } from '../mol-math/geometry';
- import { isDebugMode } from '../mol-util/debug';
- import { CameraHelperParams } from './helper/camera-helper';
- import { produce } from 'immer';
- import { HandleHelperParams } from './helper/handle-helper';
- import { StereoCamera, StereoCameraParams } from './camera/stereo';
- import { Helper } from './helper/helper';
- import { Passes } from './passes/passes';
- import { shallowEqual } from '../mol-util';
- export const Canvas3DParams = {
- camera: PD.Group({
- mode: PD.Select('perspective', PD.arrayToOptions(['perspective', 'orthographic'] as const), { label: 'Camera' }),
- helper: PD.Group(CameraHelperParams, { isFlat: true }),
- stereo: PD.MappedStatic('off', {
- on: PD.Group(StereoCameraParams),
- off: PD.Group({})
- }, { cycle: true, hideIf: p => p?.mode !== 'perspective' }),
- manualReset: PD.Boolean(false, { isHidden: true })
- }, { pivot: 'mode' }),
- cameraFog: PD.MappedStatic('on', {
- on: PD.Group({
- intensity: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
- }),
- off: PD.Group({})
- }, { cycle: true, description: 'Show fog in the distance' }),
- cameraClipping: PD.Group({
- radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
- far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
- }, { pivot: 'radius' }),
- viewport: PD.MappedStatic('canvas', {
- canvas: PD.Group({}),
- custom: PD.Group({
- x: PD.Numeric(0),
- y: PD.Numeric(0),
- width: PD.Numeric(128),
- height: PD.Numeric(128)
- })
- }),
- cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
- transparentBackground: PD.Boolean(false),
- multiSample: PD.Group(MultiSampleParams),
- postprocessing: PD.Group(PostprocessingParams),
- renderer: PD.Group(RendererParams),
- trackball: PD.Group(TrackballControlsParams),
- debug: PD.Group(DebugHelperParams),
- handle: PD.Group(HandleHelperParams),
- };
- export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams);
- export type Canvas3DProps = PD.Values<typeof Canvas3DParams>
- export type PartialCanvas3DProps = {
- [K in keyof Canvas3DProps]?: Canvas3DProps[K] extends { name: string, params: any } ? Canvas3DProps[K] : Partial<Canvas3DProps[K]>
- }
- export { Canvas3D };
- interface Canvas3D {
- readonly webgl: WebGLContext,
- add(repr: Representation.Any): void
- remove(repr: Representation.Any): void
- /**
- * This function must be called if animate() is not set up so that add/remove actions take place.
- */
- commit(isSynchronous?: boolean): void
- /**
- * Funcion for external "animation" control
- * Calls commit.
- */
- tick(t: now.Timestamp, options?: { isSynchronous?: boolean, manualDraw?: boolean }): void
- update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
- clear(): void
- syncVisibility(): void
- requestDraw(force?: boolean): void
- /** Reset the timers, used by "animate" */
- resetTime(t: number): void
- animate(): void
- pause(): void
- identify(x: number, y: number): PickData | undefined
- mark(loci: Representation.Loci, action: MarkerAction): void
- getLoci(pickingId: PickingId | undefined): Representation.Loci
- notifyDidDraw: boolean,
- readonly didDraw: BehaviorSubject<now.Timestamp>
- readonly reprCount: BehaviorSubject<number>
- readonly resized: BehaviorSubject<any>
- handleResize(): void
- /** Focuses camera on scene's bounding sphere, centered and zoomed. */
- requestCameraReset(options?: { durationMs?: number, snapshot?: Partial<Camera.Snapshot> }): void
- readonly camera: Camera
- readonly boundingSphere: Readonly<Sphere3D>
- setProps(props: PartialCanvas3DProps | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void), doNotRequestDraw?: boolean /* = false */): void
- getImagePass(props: Partial<ImageProps>): ImagePass
- /** Returns a copy of the current Canvas3D instance props */
- readonly props: Readonly<Canvas3DProps>
- readonly input: InputObserver
- readonly stats: RendererStats
- readonly interaction: Canvas3dInteractionHelper['events']
- dispose(): void
- }
- const requestAnimationFrame = typeof window !== 'undefined'
- ? window.requestAnimationFrame
- : (f: (time: number) => void) => setImmediate(() => f(Date.now())) as unknown as number;
- const cancelAnimationFrame = typeof window !== 'undefined'
- ? window.cancelAnimationFrame
- : (handle: number) => clearImmediate(handle as unknown as NodeJS.Immediate);
- namespace Canvas3D {
- export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
- 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 }
- export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, attribs: Partial<{ antialias: boolean, pixelScale: number, pickScale: number, enableWboit: boolean }> = {}) {
- const antialias = (attribs.antialias ?? true) && !attribs.enableWboit;
- const gl = getGLContext(canvas, {
- alpha: true,
- antialias,
- depth: true,
- preserveDrawingBuffer: true,
- premultipliedAlpha: true,
- });
- 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);
- // disable postprocessing anti-aliasing if canvas anti-aliasing is enabled
- if (antialias && !props.postprocessing?.antialiasing) {
- props.postprocessing = {
- ...DefaultCanvas3DParams.postprocessing,
- antialiasing: { name: 'off', params: {} }
- };
- }
- return create(webgl, input, passes, props, { pixelScale });
- }
- export function create(webgl: WebGLContext, input: InputObserver, passes: Passes, props: Partial<Canvas3DProps> = {}, attribs: Partial<{ pixelScale: number }>): Canvas3D {
- const p: Canvas3DProps = { ...DefaultCanvas3DParams, ...props };
- const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
- const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>();
- const reprCount = new BehaviorSubject(0);
- let startTime = now();
- const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
- const { gl, contextRestored } = webgl;
- let x = 0;
- let y = 0;
- let width = 128;
- let height = 128;
- updateViewport();
- const scene = Scene.create(webgl);
- const camera = new Camera({
- position: Vec3.create(0, 0, 100),
- mode: p.camera.mode,
- fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
- clipFar: p.cameraClipping.far
- }, { x, y, width, height }, { pixelScale: attribs.pixelScale });
- const stereoCamera = new StereoCamera(camera, p.camera.stereo.params);
- const controls = TrackballControls.create(input, camera, p.trackball);
- const renderer = Renderer.create(webgl, p.renderer);
- const helper = new Helper(webgl, scene, p);
- const pickHelper = new PickHelper(webgl, renderer, scene, helper, passes.pick, { x, y, width, height });
- const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera);
- const multiSampleHelper = new MultiSampleHelper(passes.multiSample);
- let drawPending = false;
- let cameraResetRequested = false;
- let nextCameraResetDuration: number | undefined = void 0;
- let nextCameraResetSnapshot: Partial<Camera.Snapshot> | undefined = void 0;
- let notifyDidDraw = true;
- function getLoci(pickingId: PickingId | undefined) {
- let loci: Loci = EmptyLoci;
- let repr: Representation.Any = Representation.Empty;
- if (pickingId) {
- loci = helper.handle.getLoci(pickingId);
- reprRenderObjects.forEach((_, _repr) => {
- const _loci = _repr.getLoci(pickingId);
- if (!isEmptyLoci(_loci)) {
- if (!isEmptyLoci(loci)) {
- console.warn('found another loci, this should not happen');
- }
- loci = _loci;
- repr = _repr;
- }
- });
- }
- return { loci, repr };
- }
- function mark(reprLoci: Representation.Loci, action: MarkerAction) {
- const { repr, loci } = reprLoci;
- let changed = false;
- if (repr) {
- changed = repr.mark(loci, action);
- } else {
- changed = helper.handle.mark(loci, action);
- reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
- }
- if (changed) {
- scene.update(void 0, true);
- helper.handle.scene.update(void 0, true);
- const prevPickDirty = pickHelper.dirty;
- draw(true);
- pickHelper.dirty = prevPickDirty; // marking does not change picking buffers
- }
- }
- function render(force: boolean) {
- if (webgl.isContextLost) return false;
- if (x > gl.drawingBufferWidth || x + width < 0 ||
- y > gl.drawingBufferHeight || y + height < 0
- ) return false;
- let didRender = false;
- controls.update(currentTime);
- const cameraChanged = camera.update();
- const multiSampleChanged = multiSampleHelper.update(force || cameraChanged, p.multiSample);
- if (force || cameraChanged || multiSampleChanged) {
- let cam: Camera | StereoCamera = camera;
- if (p.camera.stereo.name === 'on') {
- stereoCamera.update();
- cam = stereoCamera;
- }
- if (MultiSamplePass.isEnabled(p.multiSample)) {
- multiSampleHelper.render(renderer, cam, scene, helper, true, p.transparentBackground, p);
- } else {
- passes.draw.render(renderer, cam, scene, helper, true, p.transparentBackground, p.postprocessing);
- }
- pickHelper.dirty = true;
- didRender = true;
- }
- return didRender;
- }
- let forceNextDraw = false;
- let forceDrawAfterAllCommited = false;
- let currentTime = 0;
- function draw(force?: boolean) {
- if (render(!!force || forceNextDraw) && notifyDidDraw) {
- didDraw.next(now() - startTime as now.Timestamp);
- }
- forceNextDraw = false;
- drawPending = false;
- }
- function requestDraw(force?: boolean) {
- if (drawPending) return;
- drawPending = true;
- forceNextDraw = !!force;
- }
- let animationFrameHandle = 0;
- function tick(t: now.Timestamp, options?: { isSynchronous?: boolean, manualDraw?: boolean }) {
- currentTime = t;
- commit(options?.isSynchronous);
- camera.transition.tick(currentTime);
- if (options?.manualDraw) {
- return;
- }
- draw(false);
- if (!camera.transition.inTransition && !webgl.isContextLost) {
- interactionHelper.tick(currentTime);
- }
- }
- function _animate() {
- tick(now());
- animationFrameHandle = requestAnimationFrame(_animate);
- }
- function resetTime(t: now.Timestamp) {
- startTime = t;
- controls.start(t);
- }
- function animate() {
- controls.start(now());
- if (animationFrameHandle === 0) _animate();
- }
- function pause() {
- cancelAnimationFrame(animationFrameHandle);
- animationFrameHandle = 0;
- }
- function identify(x: number, y: number): PickData | undefined {
- const cam = p.camera.stereo.name === 'on' ? stereoCamera : camera;
- return webgl.isContextLost ? undefined : pickHelper.identify(x, y, cam);
- }
- function commit(isSynchronous: boolean = false) {
- const allCommited = commitScene(isSynchronous);
- // Only reset the camera after the full scene has been commited.
- if (allCommited) {
- resolveCameraReset();
- if (forceDrawAfterAllCommited) {
- if (helper.debug.isEnabled) helper.debug.update();
- draw(true);
- forceDrawAfterAllCommited = false;
- }
- }
- }
- function resolveCameraReset() {
- if (!cameraResetRequested) return;
- const { center, radius } = scene.boundingSphereVisible;
- if (radius > 0) {
- const duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration;
- const focus = camera.getFocus(center, radius);
- const snapshot = nextCameraResetSnapshot ? { ...focus, ...nextCameraResetSnapshot } : focus;
- camera.setState({ ...snapshot, radiusMax: scene.boundingSphere.radius }, duration);
- }
- nextCameraResetDuration = void 0;
- nextCameraResetSnapshot = void 0;
- cameraResetRequested = false;
- }
- const oldBoundingSphereVisible = Sphere3D();
- const cameraSphere = Sphere3D();
- function shouldResetCamera() {
- if (camera.state.radiusMax === 0) return true;
- if (camera.transition.inTransition || nextCameraResetSnapshot) return false;
- let cameraSphereOverlapsNone = true;
- Sphere3D.set(cameraSphere, camera.state.target, camera.state.radius);
- // check if any renderable has moved outside of the old bounding sphere
- // and if no renderable is overlapping with the camera sphere
- for (const r of scene.renderables) {
- if (!r.state.visible) continue;
- const b = r.values.boundingSphere.ref.value;
- if (!b.radius) continue;
- const cameraDist = Vec3.distance(cameraSphere.center, b.center);
- if ((cameraDist > cameraSphere.radius || cameraDist > b.radius || b.radius > camera.state.radiusMax) && !Sphere3D.includes(oldBoundingSphereVisible, b)) return true;
- if (Sphere3D.overlaps(cameraSphere, b)) cameraSphereOverlapsNone = false;
- }
- return cameraSphereOverlapsNone;
- }
- const sceneCommitTimeoutMs = 250;
- function commitScene(isSynchronous: boolean) {
- if (!scene.needsCommit) return true;
- // snapshot the current bounding sphere of visible objects
- Sphere3D.copy(oldBoundingSphereVisible, scene.boundingSphereVisible);
- if (!scene.commit(isSynchronous ? void 0 : sceneCommitTimeoutMs)) return false;
- if (helper.debug.isEnabled) helper.debug.update();
- if (!p.camera.manualReset && (reprCount.value === 0 || shouldResetCamera())) {
- cameraResetRequested = true;
- }
- if (oldBoundingSphereVisible.radius === 0) nextCameraResetDuration = 0;
- camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
- reprCount.next(reprRenderObjects.size);
- if (isDebugMode) consoleStats();
- return true;
- }
- function consoleStats() {
- console.table(scene.renderables.map(r => ({
- drawCount: r.values.drawCount.ref.value,
- instanceCount: r.values.instanceCount.ref.value,
- materialId: r.materialId,
- })));
- console.log(webgl.stats);
- }
- function add(repr: Representation.Any) {
- registerAutoUpdate(repr);
- const oldRO = reprRenderObjects.get(repr);
- const newRO = new Set<GraphicsRenderObject>();
- repr.renderObjects.forEach(o => newRO.add(o));
- if (oldRO) {
- if (!SetUtils.areEqual(newRO, oldRO)) {
- newRO.forEach(o => { if (!oldRO.has(o)) scene.add(o); });
- oldRO.forEach(o => { if (!newRO.has(o)) scene.remove(o); });
- }
- } else {
- repr.renderObjects.forEach(o => scene.add(o));
- }
- reprRenderObjects.set(repr, newRO);
- scene.update(repr.renderObjects, false);
- forceDrawAfterAllCommited = true;
- if (isDebugMode) consoleStats();
- }
- function remove(repr: Representation.Any) {
- unregisterAutoUpdate(repr);
- const renderObjects = reprRenderObjects.get(repr);
- if (renderObjects) {
- renderObjects.forEach(o => scene.remove(o));
- reprRenderObjects.delete(repr);
- forceDrawAfterAllCommited = true;
- if (isDebugMode) consoleStats();
- }
- }
- function registerAutoUpdate(repr: Representation.Any) {
- if (reprUpdatedSubscriptions.has(repr)) return;
- reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
- if (!repr.state.syncManually) add(repr);
- }));
- }
- function unregisterAutoUpdate(repr: Representation.Any) {
- const updatedSubscription = reprUpdatedSubscriptions.get(repr);
- if (updatedSubscription) {
- updatedSubscription.unsubscribe();
- reprUpdatedSubscriptions.delete(repr);
- }
- }
- function getProps(): Canvas3DProps {
- const radius = scene.boundingSphere.radius > 0
- ? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
- : 0;
- return {
- camera: {
- mode: camera.state.mode,
- helper: { ...helper.camera.props },
- stereo: { ...p.camera.stereo },
- manualReset: !!p.camera.manualReset
- },
- cameraFog: camera.state.fog > 0
- ? { name: 'on' as const, params: { intensity: camera.state.fog } }
- : { name: 'off' as const, params: {} },
- cameraClipping: { far: camera.state.clipFar, radius },
- cameraResetDurationMs: p.cameraResetDurationMs,
- transparentBackground: p.transparentBackground,
- viewport: p.viewport,
- postprocessing: { ...p.postprocessing },
- multiSample: { ...p.multiSample },
- renderer: { ...renderer.props },
- trackball: { ...controls.props },
- debug: { ...helper.debug.props },
- handle: { ...helper.handle.props },
- };
- }
- const contextRestoredSub = contextRestored.subscribe(() => {
- pickHelper.dirty = true;
- draw(true);
- });
- const resized = new BehaviorSubject<any>(0);
- return {
- webgl,
- add,
- remove,
- commit,
- update: (repr, keepSphere) => {
- if (repr) {
- if (!reprRenderObjects.has(repr)) return;
- scene.update(repr.renderObjects, !!keepSphere);
- } else {
- scene.update(void 0, !!keepSphere);
- }
- forceDrawAfterAllCommited = true;
- },
- clear: () => {
- reprUpdatedSubscriptions.forEach(v => v.unsubscribe());
- reprUpdatedSubscriptions.clear();
- reprRenderObjects.clear();
- scene.clear();
- helper.debug.clear();
- requestDraw(true);
- reprCount.next(reprRenderObjects.size);
- },
- syncVisibility: () => {
- if (camera.state.radiusMax === 0) {
- cameraResetRequested = true;
- nextCameraResetDuration = 0;
- }
- if (scene.syncVisibility()) {
- if (helper.debug.isEnabled) helper.debug.update();
- }
- requestDraw(true);
- },
- requestDraw,
- tick,
- animate,
- resetTime,
- pause,
- identify,
- mark,
- getLoci,
- handleResize: () => {
- passes.updateSize();
- updateViewport();
- syncViewport();
- requestDraw(true);
- resized.next(+new Date());
- },
- requestCameraReset: options => {
- nextCameraResetDuration = options?.durationMs;
- nextCameraResetSnapshot = options?.snapshot;
- cameraResetRequested = true;
- },
- camera,
- boundingSphere: scene.boundingSphere,
- get notifyDidDraw() { return notifyDidDraw; },
- set notifyDidDraw(v: boolean) { notifyDidDraw = v; },
- didDraw,
- reprCount,
- resized,
- setProps: (properties, doNotRequestDraw = false) => {
- const props: PartialCanvas3DProps = typeof properties === 'function'
- ? produce(getProps(), properties)
- : properties;
- const cameraState: Partial<Camera.Snapshot> = Object.create(null);
- if (props.camera && props.camera.mode !== undefined && props.camera.mode !== camera.state.mode) {
- cameraState.mode = props.camera.mode;
- }
- if (props.cameraFog !== undefined && props.cameraFog.params) {
- const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0;
- if (newFog !== camera.state.fog) cameraState.fog = newFog;
- }
- if (props.cameraClipping !== undefined) {
- if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
- cameraState.clipFar = props.cameraClipping.far;
- }
- if (props.cameraClipping.radius !== undefined) {
- const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius);
- if (radius > 0 && radius !== cameraState.radius) {
- // if radius = 0, NaNs happen
- cameraState.radius = Math.max(radius, 0.01);
- }
- }
- }
- if (Object.keys(cameraState).length > 0) camera.setState(cameraState);
- if (props.camera?.helper) helper.camera.setProps(props.camera.helper);
- if (props.camera?.manualReset !== undefined) p.camera.manualReset = props.camera.manualReset;
- if (props.camera?.stereo !== undefined) Object.assign(p.camera.stereo, props.camera.stereo);
- if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs;
- if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground;
- if (props.viewport !== undefined) {
- const doNotUpdate = p.viewport === props.viewport ||
- (p.viewport.name === props.viewport.name && shallowEqual(p.viewport.params, props.viewport.params));
- if (!doNotUpdate) {
- p.viewport = props.viewport;
- updateViewport();
- syncViewport();
- }
- }
- if (props.postprocessing) Object.assign(p.postprocessing, props.postprocessing);
- if (props.multiSample) Object.assign(p.multiSample, props.multiSample);
- if (props.renderer) renderer.setProps(props.renderer);
- if (props.trackball) controls.setProps(props.trackball);
- if (props.debug) helper.debug.setProps(props.debug);
- if (props.handle) helper.handle.setProps(props.handle);
- if (cameraState.mode === 'orthographic') {
- p.camera.stereo.name = 'off';
- }
- if (!doNotRequestDraw) {
- requestDraw(true);
- }
- },
- getImagePass: (props: Partial<ImageProps> = {}) => {
- return new ImagePass(webgl, renderer, scene, camera, helper, passes.draw.wboitEnabled, props);
- },
- get props() {
- return getProps();
- },
- get input() {
- return input;
- },
- get stats() {
- return renderer.stats;
- },
- get interaction() {
- return interactionHelper.events;
- },
- dispose: () => {
- contextRestoredSub.unsubscribe();
- scene.clear();
- helper.debug.clear();
- input.dispose();
- controls.dispose();
- renderer.dispose();
- interactionHelper.dispose();
- }
- };
- function updateViewport() {
- if (p.viewport.name === 'canvas') {
- x = 0;
- y = 0;
- width = gl.drawingBufferWidth;
- height = gl.drawingBufferHeight;
- } else {
- x = p.viewport.params.x * webgl.pixelRatio;
- y = p.viewport.params.y * webgl.pixelRatio;
- width = p.viewport.params.width * webgl.pixelRatio;
- height = p.viewport.params.height * webgl.pixelRatio;
- }
- }
- function syncViewport() {
- pickHelper.setViewport(x, y, width, height);
- renderer.setViewport(x, y, width, height);
- Viewport.set(camera.viewport, x, y, width, height);
- Viewport.set(controls.viewport, x, y, width, height);
- }
- }
- }
|