angular-three 2.0.0-beta.273 → 2.0.0-beta.275
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/index.mjs +5 -5
- package/esm2022/lib/canvas.mjs +2 -1
- package/esm2022/lib/dom/events.mjs +1 -1
- package/esm2022/lib/events.mjs +1 -1
- package/esm2022/lib/instance.mjs +1 -1
- package/esm2022/lib/loop.mjs +2 -2
- package/esm2022/lib/portal.mjs +1 -1
- package/esm2022/lib/renderer/index.mjs +1 -1
- package/esm2022/lib/renderer/utils.mjs +1 -1
- package/esm2022/lib/roots.mjs +2 -3
- package/esm2022/lib/store.mjs +1 -1
- package/esm2022/lib/three-types.mjs +1 -1
- package/esm2022/lib/types.mjs +1 -1
- package/esm2022/lib/utils/apply-props.mjs +1 -1
- package/esm2022/lib/utils/attach.mjs +1 -1
- package/esm2022/lib/utils/before-render.mjs +1 -1
- package/esm2022/lib/utils/is.mjs +1 -1
- package/esm2022/lib/utils/make.mjs +1 -1
- package/esm2022/lib/utils/object-events.mjs +1 -1
- package/esm2022/lib/utils/update.mjs +1 -1
- package/esm2022/testing/lib/test-bed.mjs +12 -6
- package/fesm2022/angular-three-testing.mjs +10 -4
- package/fesm2022/angular-three-testing.mjs.map +1 -1
- package/fesm2022/angular-three.mjs +528 -527
- package/fesm2022/angular-three.mjs.map +1 -1
- package/index.d.ts +4 -5
- package/lib/canvas.d.ts +199 -256
- package/lib/dom/events.d.ts +1 -2
- package/lib/events.d.ts +2 -78
- package/lib/html.d.ts +1 -1
- package/lib/instance.d.ts +1 -38
- package/lib/loop.d.ts +2 -2
- package/lib/portal.d.ts +3 -4
- package/lib/renderer/index.d.ts +1 -2
- package/lib/renderer/utils.d.ts +1 -1
- package/lib/roots.d.ts +1 -5
- package/lib/store.d.ts +2 -127
- package/lib/three-types.d.ts +1 -3
- package/lib/types.d.ts +290 -0
- package/lib/utils/apply-props.d.ts +1 -2
- package/lib/utils/attach.d.ts +1 -3
- package/lib/utils/before-render.d.ts +1 -1
- package/lib/utils/is.d.ts +1 -3
- package/lib/utils/make.d.ts +2 -5
- package/lib/utils/object-events.d.ts +1 -1
- package/lib/utils/update.d.ts +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { untracked, computed, signal, ElementRef, input, inject, ViewContainerRe
|
|
|
3
3
|
import { takeUntilDestroyed, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { injectAutoEffect } from 'ngxtension/auto-effect';
|
|
5
5
|
import { provideResizeOptions, NgxResize } from 'ngxtension/resize';
|
|
6
|
-
import { MathUtils, WebGLRenderer, OrthographicCamera, PerspectiveCamera, Vector3, Layers, Color, ColorManagement, Texture, RGBAFormat, UnsignedByteType, Raycaster, Scene, PCFSoftShadowMap, BasicShadowMap, PCFShadowMap, VSMShadowMap, NoToneMapping, ACESFilmicToneMapping
|
|
6
|
+
import { MathUtils, WebGLRenderer, OrthographicCamera, PerspectiveCamera, Vector3, Vector2, Clock, Layers, Color, ColorManagement, Texture, RGBAFormat, UnsignedByteType, Raycaster, Scene, PCFSoftShadowMap, BasicShadowMap, PCFShadowMap, VSMShadowMap, NoToneMapping, ACESFilmicToneMapping } from 'three';
|
|
7
7
|
import { DOCUMENT } from '@angular/common';
|
|
8
8
|
import { Subject, filter } from 'rxjs';
|
|
9
9
|
import { createInjectionToken } from 'ngxtension/create-injection-token';
|
|
@@ -814,526 +814,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.2", ngImpor
|
|
|
814
814
|
args: [{ selector: 'ng-template[args]', standalone: true }]
|
|
815
815
|
}], ctorParameters: () => [] });
|
|
816
816
|
|
|
817
|
-
|
|
818
|
-
function
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
817
|
+
const roots = new Map();
|
|
818
|
+
function createSubs(callback, subs) {
|
|
819
|
+
const sub = { callback };
|
|
820
|
+
subs.add(sub);
|
|
821
|
+
return () => void subs.delete(sub);
|
|
822
|
+
}
|
|
823
|
+
const globalEffects = new Set();
|
|
824
|
+
const globalAfterEffects = new Set();
|
|
825
|
+
const globalTailEffects = new Set();
|
|
826
|
+
/**
|
|
827
|
+
* Adds a global render callback which is called each frame.
|
|
828
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
|
|
829
|
+
*/
|
|
830
|
+
const addEffect = (callback) => createSubs(callback, globalEffects);
|
|
831
|
+
/**
|
|
832
|
+
* Adds a global after-render callback which is called each frame.
|
|
833
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
|
|
834
|
+
*/
|
|
835
|
+
const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
|
|
836
|
+
/**
|
|
837
|
+
* Adds a global callback which is called when rendering stops.
|
|
838
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
|
|
839
|
+
*/
|
|
840
|
+
const addTail = (callback) => createSubs(callback, globalTailEffects);
|
|
841
|
+
function run(effects, timestamp) {
|
|
842
|
+
if (!effects.size)
|
|
843
|
+
return;
|
|
844
|
+
for (const { callback } of effects.values()) {
|
|
845
|
+
callback(timestamp);
|
|
834
846
|
}
|
|
835
|
-
return changes;
|
|
836
847
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
848
|
+
function flushGlobalEffects(type, timestamp) {
|
|
849
|
+
switch (type) {
|
|
850
|
+
case 'before':
|
|
851
|
+
return run(globalEffects, timestamp);
|
|
852
|
+
case 'after':
|
|
853
|
+
return run(globalAfterEffects, timestamp);
|
|
854
|
+
case 'tail':
|
|
855
|
+
return run(globalTailEffects, timestamp);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
function render(timestamp, store, frame) {
|
|
859
|
+
const state = store.snapshot;
|
|
860
|
+
// Run local effects
|
|
861
|
+
let delta = state.clock.getDelta();
|
|
862
|
+
// In frameloop='never' mode, clock times are updated using the provided timestamp
|
|
863
|
+
if (state.frameloop === 'never' && typeof timestamp === 'number') {
|
|
864
|
+
delta = timestamp - state.clock.elapsedTime;
|
|
865
|
+
state.clock.oldTime = state.clock.elapsedTime;
|
|
866
|
+
state.clock.elapsedTime = timestamp;
|
|
867
|
+
}
|
|
868
|
+
// Call subscribers (beforeRender)
|
|
869
|
+
const subscribers = state.internal.subscribers;
|
|
870
|
+
for (let i = 0; i < subscribers.length; i++) {
|
|
871
|
+
const subscription = subscribers[i];
|
|
872
|
+
subscription.callback({ ...subscription.store.snapshot, delta, frame });
|
|
873
|
+
}
|
|
874
|
+
// Render content
|
|
875
|
+
if (!state.internal.priority && state.gl.render)
|
|
876
|
+
state.gl.render(state.scene, state.camera);
|
|
877
|
+
// Decrease frame count
|
|
878
|
+
state.internal.frames = Math.max(0, state.internal.frames - 1);
|
|
879
|
+
return state.frameloop === 'always' ? 1 : state.internal.frames;
|
|
880
|
+
}
|
|
881
|
+
function createLoop(roots) {
|
|
882
|
+
let running = false;
|
|
883
|
+
let repeat;
|
|
884
|
+
let frame;
|
|
885
|
+
let beforeRenderInProgress = false;
|
|
886
|
+
function loop(timestamp) {
|
|
887
|
+
frame = requestAnimationFrame(loop);
|
|
888
|
+
running = true;
|
|
889
|
+
repeat = 0;
|
|
890
|
+
// Run effects
|
|
891
|
+
flushGlobalEffects('before', timestamp);
|
|
892
|
+
// Render all roots
|
|
893
|
+
beforeRenderInProgress = true;
|
|
894
|
+
for (const root of roots.values()) {
|
|
895
|
+
const state = root.snapshot;
|
|
896
|
+
// If the frameloop is invalidated, do not run another frame
|
|
897
|
+
if (state.internal.active &&
|
|
898
|
+
(state.frameloop === 'always' || state.internal.frames > 0) &&
|
|
899
|
+
!state.gl.xr?.isPresenting) {
|
|
900
|
+
repeat += render(timestamp, root);
|
|
861
901
|
}
|
|
862
902
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
//
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (!isColor && targetProp['setScalar'])
|
|
887
|
-
targetProp['setScalar'](value);
|
|
888
|
-
// layers have no copy function, copy the mask
|
|
889
|
-
else if (targetProp instanceof Layers && value instanceof Layers)
|
|
890
|
-
targetProp.mask = value.mask;
|
|
891
|
-
// otherwise just set ...
|
|
892
|
-
else
|
|
893
|
-
targetProp['set'](value);
|
|
894
|
-
// auto-convert srgb
|
|
895
|
-
if (!ColorManagement && !rootState?.linear && isColor)
|
|
896
|
-
targetProp.convertSRGBToLinear();
|
|
897
|
-
}
|
|
898
|
-
} // else just overwrite the value
|
|
903
|
+
beforeRenderInProgress = false;
|
|
904
|
+
// Run after-effects
|
|
905
|
+
flushGlobalEffects('after', timestamp);
|
|
906
|
+
// Stop the loop if nothing invalidates it
|
|
907
|
+
if (repeat === 0) {
|
|
908
|
+
// Tail call effects, they are called when rendering stops
|
|
909
|
+
flushGlobalEffects('tail', timestamp);
|
|
910
|
+
// Flag end of operation
|
|
911
|
+
running = false;
|
|
912
|
+
return cancelAnimationFrame(frame);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function invalidate(store, frames = 1) {
|
|
916
|
+
const state = store?.snapshot;
|
|
917
|
+
if (!state)
|
|
918
|
+
return roots.forEach((root) => invalidate(root, frames));
|
|
919
|
+
if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
|
|
920
|
+
return;
|
|
921
|
+
if (frames > 1) {
|
|
922
|
+
// legacy support for people using frames parameters
|
|
923
|
+
// Increase frames, do not go higher than 60
|
|
924
|
+
state.internal.frames = Math.min(60, state.internal.frames + frames);
|
|
925
|
+
}
|
|
899
926
|
else {
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
if (is.colorSpaceExist(texture) && is.colorSpaceExist(rootState.gl))
|
|
908
|
-
texture.colorSpace = rootState.gl.outputColorSpace;
|
|
909
|
-
// @ts-expect-error - old version of threejs
|
|
910
|
-
else
|
|
911
|
-
texture.encoding = rootState.gl.outputEncoding;
|
|
912
|
-
}
|
|
927
|
+
if (beforeRenderInProgress) {
|
|
928
|
+
//called from within a beforeRender, it means the user wants an additional frame
|
|
929
|
+
state.internal.frames = 2;
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
//the user need a new frame, no need to increment further than 1
|
|
933
|
+
state.internal.frames = 1;
|
|
913
934
|
}
|
|
914
935
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
const parent = localState?.instanceStore?.get('parent');
|
|
921
|
-
if (parent && rootState.internal && instance['raycast'] && instanceHandlersCount !== localState?.eventCount) {
|
|
922
|
-
// Pre-emptively remove the instance from the interaction manager
|
|
923
|
-
const index = rootState.internal.interaction.indexOf(instance);
|
|
924
|
-
if (index > -1)
|
|
925
|
-
rootState.internal.interaction.splice(index, 1);
|
|
926
|
-
// Add the instance to the interaction manager only when it has handlers
|
|
927
|
-
if (localState?.eventCount)
|
|
928
|
-
rootState.internal.interaction.push(instance);
|
|
936
|
+
// If the render-loop isn't active, start it
|
|
937
|
+
if (!running) {
|
|
938
|
+
running = true;
|
|
939
|
+
requestAnimationFrame(loop);
|
|
940
|
+
}
|
|
929
941
|
}
|
|
930
|
-
|
|
931
|
-
|
|
942
|
+
function advance(timestamp, runGlobalEffects = true, store, frame) {
|
|
943
|
+
if (runGlobalEffects)
|
|
944
|
+
flushGlobalEffects('before', timestamp);
|
|
945
|
+
if (!store)
|
|
946
|
+
for (const root of roots.values())
|
|
947
|
+
render(timestamp, root);
|
|
948
|
+
else
|
|
949
|
+
render(timestamp, store, frame);
|
|
950
|
+
if (runGlobalEffects)
|
|
951
|
+
flushGlobalEffects('after', timestamp);
|
|
932
952
|
}
|
|
933
|
-
return
|
|
953
|
+
return { loop, invalidate, advance };
|
|
934
954
|
}
|
|
935
|
-
|
|
936
|
-
const shallowLoose = { objects: 'shallow', strict: false };
|
|
937
|
-
const roots = new Map();
|
|
938
|
-
function injectCanvasRootInitializer(injector) {
|
|
939
|
-
return assertInjector(injectCanvasRootInitializer, injector, () => {
|
|
940
|
-
const injectedStore = injectStore();
|
|
941
|
-
const loop = injectLoop();
|
|
942
|
-
return (canvas) => {
|
|
943
|
-
const exist = roots.has(canvas);
|
|
944
|
-
let store = roots.get(canvas);
|
|
945
|
-
if (store) {
|
|
946
|
-
console.warn('[NGT] Same canvas root is being created twice');
|
|
947
|
-
}
|
|
948
|
-
store ||= injectedStore;
|
|
949
|
-
if (!store) {
|
|
950
|
-
throw new Error('[NGT] No store initialized');
|
|
951
|
-
}
|
|
952
|
-
if (!exist) {
|
|
953
|
-
roots.set(canvas, store);
|
|
954
|
-
}
|
|
955
|
-
let isConfigured = false;
|
|
956
|
-
let lastCamera;
|
|
957
|
-
return {
|
|
958
|
-
isConfigured,
|
|
959
|
-
destroy: (timeout = 500) => {
|
|
960
|
-
const root = roots.get(canvas);
|
|
961
|
-
if (root) {
|
|
962
|
-
root.update((state) => ({ internal: { ...state.internal, active: false } }));
|
|
963
|
-
try {
|
|
964
|
-
const state = root.get();
|
|
965
|
-
state.events.disconnect?.();
|
|
966
|
-
state.gl?.renderLists?.dispose?.();
|
|
967
|
-
state.gl?.forceContextLoss?.();
|
|
968
|
-
if (state.gl?.xr)
|
|
969
|
-
state.xr.disconnect();
|
|
970
|
-
dispose(state);
|
|
971
|
-
roots.delete(canvas);
|
|
972
|
-
}
|
|
973
|
-
catch (e) {
|
|
974
|
-
console.error('[NGT] Unexpected error while destroying Canvas Root', e);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
|
-
configure: (inputs) => {
|
|
979
|
-
const { shadows = false, linear = false, flat = false, legacy = false, orthographic = false, frameloop = 'always', dpr = [1, 2], gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, lookAt, performance, } = inputs;
|
|
980
|
-
const state = store.snapshot;
|
|
981
|
-
const stateToUpdate = {};
|
|
982
|
-
// setup renderer
|
|
983
|
-
let gl = state.gl;
|
|
984
|
-
if (!state.gl)
|
|
985
|
-
stateToUpdate.gl = gl = makeRendererInstance(glOptions, canvas);
|
|
986
|
-
// setup raycaster
|
|
987
|
-
let raycaster = state.raycaster;
|
|
988
|
-
if (!raycaster)
|
|
989
|
-
stateToUpdate.raycaster = raycaster = new Raycaster();
|
|
990
|
-
// set raycaster options
|
|
991
|
-
const { params, ...options } = raycasterOptions || {};
|
|
992
|
-
if (!is.equ(options, raycaster, shallowLoose))
|
|
993
|
-
applyProps(raycaster, options);
|
|
994
|
-
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
995
|
-
applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
|
|
996
|
-
}
|
|
997
|
-
// Create default camera, don't overwrite any user-set state
|
|
998
|
-
if (!state.camera || (state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose))) {
|
|
999
|
-
lastCamera = cameraOptions;
|
|
1000
|
-
const isCamera = is.camera(cameraOptions);
|
|
1001
|
-
let camera = isCamera ? cameraOptions : makeCameraInstance(orthographic, state.size);
|
|
1002
|
-
if (!isCamera) {
|
|
1003
|
-
camera.position.z = 5;
|
|
1004
|
-
if (cameraOptions) {
|
|
1005
|
-
applyProps(camera, cameraOptions);
|
|
1006
|
-
if ('aspect' in cameraOptions ||
|
|
1007
|
-
'left' in cameraOptions ||
|
|
1008
|
-
'right' in cameraOptions ||
|
|
1009
|
-
'top' in cameraOptions ||
|
|
1010
|
-
'bottom' in cameraOptions) {
|
|
1011
|
-
Object.assign(camera, { manual: true });
|
|
1012
|
-
camera?.updateProjectionMatrix();
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
// always look at center or passed-in lookAt by default
|
|
1016
|
-
if (!state.camera && !cameraOptions?.rotation && !cameraOptions?.quaternion) {
|
|
1017
|
-
if (Array.isArray(lookAt))
|
|
1018
|
-
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
|
|
1019
|
-
else if (lookAt instanceof Vector3)
|
|
1020
|
-
camera.lookAt(lookAt);
|
|
1021
|
-
else
|
|
1022
|
-
camera.lookAt(0, 0, 0);
|
|
1023
|
-
}
|
|
1024
|
-
// update projection matrix after applyprops
|
|
1025
|
-
camera.updateProjectionMatrix?.();
|
|
1026
|
-
}
|
|
1027
|
-
if (!is.instance(camera))
|
|
1028
|
-
camera = prepare(camera, { store });
|
|
1029
|
-
stateToUpdate.camera = camera;
|
|
1030
|
-
// Configure raycaster
|
|
1031
|
-
// https://github.com/pmndrs/react-xr/issues/300
|
|
1032
|
-
raycaster.camera = camera;
|
|
1033
|
-
}
|
|
1034
|
-
// Set up scene (one time only!)
|
|
1035
|
-
if (!state.scene) {
|
|
1036
|
-
let scene;
|
|
1037
|
-
if (sceneOptions instanceof Scene) {
|
|
1038
|
-
scene = sceneOptions;
|
|
1039
|
-
}
|
|
1040
|
-
else {
|
|
1041
|
-
scene = new Scene();
|
|
1042
|
-
if (sceneOptions)
|
|
1043
|
-
applyProps(scene, sceneOptions);
|
|
1044
|
-
}
|
|
1045
|
-
applyProps(scene, {
|
|
1046
|
-
setAttribute: (name, value) => {
|
|
1047
|
-
if (canvas instanceof HTMLCanvasElement) {
|
|
1048
|
-
if (canvas.parentElement) {
|
|
1049
|
-
canvas.parentElement.setAttribute(name, value);
|
|
1050
|
-
}
|
|
1051
|
-
else {
|
|
1052
|
-
canvas.setAttribute(name, value);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
},
|
|
1056
|
-
});
|
|
1057
|
-
stateToUpdate.scene = prepare(scene, { store });
|
|
1058
|
-
}
|
|
1059
|
-
// Set up XR (one time only!)
|
|
1060
|
-
if (!state.xr) {
|
|
1061
|
-
// Handle frame behavior in WebXR
|
|
1062
|
-
const handleXRFrame = (timestamp, frame) => {
|
|
1063
|
-
const state = store.snapshot;
|
|
1064
|
-
if (state.frameloop === 'never')
|
|
1065
|
-
return;
|
|
1066
|
-
loop.advance(timestamp, true, store, frame);
|
|
1067
|
-
};
|
|
1068
|
-
// Toggle render switching on session
|
|
1069
|
-
const handleSessionChange = () => {
|
|
1070
|
-
const state = store.snapshot;
|
|
1071
|
-
state.gl.xr.enabled = state.gl.xr.isPresenting;
|
|
1072
|
-
state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
|
|
1073
|
-
if (!state.gl.xr.isPresenting)
|
|
1074
|
-
loop.invalidate(store);
|
|
1075
|
-
};
|
|
1076
|
-
// WebXR session manager
|
|
1077
|
-
const xr = {
|
|
1078
|
-
connect: () => {
|
|
1079
|
-
gl.xr.addEventListener('sessionstart', handleSessionChange);
|
|
1080
|
-
gl.xr.addEventListener('sessionend', handleSessionChange);
|
|
1081
|
-
},
|
|
1082
|
-
disconnect: () => {
|
|
1083
|
-
gl.xr.removeEventListener('sessionstart', handleSessionChange);
|
|
1084
|
-
gl.xr.removeEventListener('sessionend', handleSessionChange);
|
|
1085
|
-
},
|
|
1086
|
-
};
|
|
1087
|
-
// Subscribe to WebXR session events
|
|
1088
|
-
if (gl.xr && typeof gl.xr.addEventListener === 'function')
|
|
1089
|
-
xr.connect();
|
|
1090
|
-
stateToUpdate.xr = xr;
|
|
1091
|
-
}
|
|
1092
|
-
// Set shadowmap
|
|
1093
|
-
if (gl.shadowMap) {
|
|
1094
|
-
const oldEnabled = gl.shadowMap.enabled;
|
|
1095
|
-
const oldType = gl.shadowMap.type;
|
|
1096
|
-
gl.shadowMap.enabled = !!shadows;
|
|
1097
|
-
if (typeof shadows === 'boolean') {
|
|
1098
|
-
gl.shadowMap.type = PCFSoftShadowMap;
|
|
1099
|
-
}
|
|
1100
|
-
else if (typeof shadows === 'string') {
|
|
1101
|
-
const types = {
|
|
1102
|
-
basic: BasicShadowMap,
|
|
1103
|
-
percentage: PCFShadowMap,
|
|
1104
|
-
soft: PCFSoftShadowMap,
|
|
1105
|
-
variance: VSMShadowMap,
|
|
1106
|
-
};
|
|
1107
|
-
gl.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
|
|
1108
|
-
}
|
|
1109
|
-
else if (is.obj(shadows)) {
|
|
1110
|
-
Object.assign(gl.shadowMap, shadows);
|
|
1111
|
-
}
|
|
1112
|
-
if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
|
|
1113
|
-
checkNeedsUpdate(gl.shadowMap);
|
|
1114
|
-
}
|
|
1115
|
-
// Safely set color management if available.
|
|
1116
|
-
// Avoid accessing ColorManagement to play nice with older versions
|
|
1117
|
-
if (ColorManagement) {
|
|
1118
|
-
const colorManagement = ColorManagement;
|
|
1119
|
-
if ('enabled' in colorManagement)
|
|
1120
|
-
colorManagement['enabled'] = !legacy ?? false;
|
|
1121
|
-
else if ('legacyMode' in colorManagement)
|
|
1122
|
-
colorManagement['legacyMode'] = legacy ?? true;
|
|
1123
|
-
}
|
|
1124
|
-
if (!isConfigured) {
|
|
1125
|
-
// set color space and tonemapping preferences once
|
|
1126
|
-
const LinearEncoding = 3000;
|
|
1127
|
-
const sRGBEncoding = 3001;
|
|
1128
|
-
applyProps(gl, {
|
|
1129
|
-
outputEncoding: linear ? LinearEncoding : sRGBEncoding,
|
|
1130
|
-
toneMapping: flat ? NoToneMapping : ACESFilmicToneMapping,
|
|
1131
|
-
});
|
|
1132
|
-
}
|
|
1133
|
-
// Update color management state
|
|
1134
|
-
if (state.legacy !== legacy)
|
|
1135
|
-
stateToUpdate.legacy = legacy;
|
|
1136
|
-
if (state.linear !== linear)
|
|
1137
|
-
stateToUpdate.linear = linear;
|
|
1138
|
-
if (state.flat !== flat)
|
|
1139
|
-
stateToUpdate.flat = flat;
|
|
1140
|
-
// Set gl props
|
|
1141
|
-
gl.setClearAlpha(0);
|
|
1142
|
-
gl.setPixelRatio(makeDpr(state.viewport.dpr));
|
|
1143
|
-
gl.setSize(state.size.width, state.size.height);
|
|
1144
|
-
if (is.obj(glOptions) &&
|
|
1145
|
-
!(typeof glOptions === 'function') &&
|
|
1146
|
-
!is.renderer(glOptions) &&
|
|
1147
|
-
!is.equ(glOptions, gl, shallowLoose)) {
|
|
1148
|
-
applyProps(gl, glOptions);
|
|
1149
|
-
}
|
|
1150
|
-
// Store events internally
|
|
1151
|
-
if (events && !state.events.handlers)
|
|
1152
|
-
stateToUpdate.events = events(store);
|
|
1153
|
-
// Check performance
|
|
1154
|
-
if (performance && !is.equ(performance, state.performance, shallowLoose)) {
|
|
1155
|
-
stateToUpdate.performance = { ...state.performance, ...performance };
|
|
1156
|
-
}
|
|
1157
|
-
if (Object.keys(stateToUpdate).length) {
|
|
1158
|
-
store.update(stateToUpdate);
|
|
1159
|
-
}
|
|
1160
|
-
// Check size, allow it to take on container bounds initially
|
|
1161
|
-
const size = computeInitialSize(canvas, sizeOptions);
|
|
1162
|
-
if (!is.equ(size, state.size, shallowLoose)) {
|
|
1163
|
-
state.setSize(size.width, size.height, size.top, size.left);
|
|
1164
|
-
}
|
|
1165
|
-
// Check pixelratio
|
|
1166
|
-
if (dpr && state.viewport.dpr !== makeDpr(dpr))
|
|
1167
|
-
state.setDpr(dpr);
|
|
1168
|
-
// Check frameloop
|
|
1169
|
-
if (state.frameloop !== frameloop)
|
|
1170
|
-
state.setFrameloop(frameloop);
|
|
1171
|
-
isConfigured = true;
|
|
1172
|
-
},
|
|
1173
|
-
};
|
|
1174
|
-
};
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
function computeInitialSize(canvas, defaultSize) {
|
|
1178
|
-
if (defaultSize) {
|
|
1179
|
-
return defaultSize;
|
|
1180
|
-
}
|
|
1181
|
-
if (typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
|
|
1182
|
-
return canvas.parentElement.getBoundingClientRect();
|
|
1183
|
-
}
|
|
1184
|
-
if (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
|
|
1185
|
-
return { width: canvas.width, height: canvas.height, top: 0, left: 0 };
|
|
1186
|
-
}
|
|
1187
|
-
return { width: 0, height: 0, top: 0, left: 0 };
|
|
1188
|
-
}
|
|
1189
|
-
// Disposes an object and all its properties
|
|
1190
|
-
function dispose(obj) {
|
|
1191
|
-
if (obj.dispose && obj.type !== 'Scene')
|
|
1192
|
-
obj.dispose();
|
|
1193
|
-
for (const p in obj) {
|
|
1194
|
-
p.dispose?.();
|
|
1195
|
-
delete obj[p];
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
function createSubs(callback, subs) {
|
|
1200
|
-
const sub = { callback };
|
|
1201
|
-
subs.add(sub);
|
|
1202
|
-
return () => void subs.delete(sub);
|
|
1203
|
-
}
|
|
1204
|
-
const globalEffects = new Set();
|
|
1205
|
-
const globalAfterEffects = new Set();
|
|
1206
|
-
const globalTailEffects = new Set();
|
|
1207
|
-
/**
|
|
1208
|
-
* Adds a global render callback which is called each frame.
|
|
1209
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
|
|
1210
|
-
*/
|
|
1211
|
-
const addEffect = (callback) => createSubs(callback, globalEffects);
|
|
1212
|
-
/**
|
|
1213
|
-
* Adds a global after-render callback which is called each frame.
|
|
1214
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
|
|
1215
|
-
*/
|
|
1216
|
-
const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
|
|
1217
|
-
/**
|
|
1218
|
-
* Adds a global callback which is called when rendering stops.
|
|
1219
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
|
|
1220
|
-
*/
|
|
1221
|
-
const addTail = (callback) => createSubs(callback, globalTailEffects);
|
|
1222
|
-
function run(effects, timestamp) {
|
|
1223
|
-
if (!effects.size)
|
|
1224
|
-
return;
|
|
1225
|
-
for (const { callback } of effects.values()) {
|
|
1226
|
-
callback(timestamp);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
function flushGlobalEffects(type, timestamp) {
|
|
1230
|
-
switch (type) {
|
|
1231
|
-
case 'before':
|
|
1232
|
-
return run(globalEffects, timestamp);
|
|
1233
|
-
case 'after':
|
|
1234
|
-
return run(globalAfterEffects, timestamp);
|
|
1235
|
-
case 'tail':
|
|
1236
|
-
return run(globalTailEffects, timestamp);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
function render(timestamp, store, frame) {
|
|
1240
|
-
const state = store.snapshot;
|
|
1241
|
-
// Run local effects
|
|
1242
|
-
let delta = state.clock.getDelta();
|
|
1243
|
-
// In frameloop='never' mode, clock times are updated using the provided timestamp
|
|
1244
|
-
if (state.frameloop === 'never' && typeof timestamp === 'number') {
|
|
1245
|
-
delta = timestamp - state.clock.elapsedTime;
|
|
1246
|
-
state.clock.oldTime = state.clock.elapsedTime;
|
|
1247
|
-
state.clock.elapsedTime = timestamp;
|
|
1248
|
-
}
|
|
1249
|
-
// Call subscribers (beforeRender)
|
|
1250
|
-
const subscribers = state.internal.subscribers;
|
|
1251
|
-
for (let i = 0; i < subscribers.length; i++) {
|
|
1252
|
-
const subscription = subscribers[i];
|
|
1253
|
-
subscription.callback({ ...subscription.store.snapshot, delta, frame });
|
|
1254
|
-
}
|
|
1255
|
-
// Render content
|
|
1256
|
-
if (!state.internal.priority && state.gl.render)
|
|
1257
|
-
state.gl.render(state.scene, state.camera);
|
|
1258
|
-
// Decrease frame count
|
|
1259
|
-
state.internal.frames = Math.max(0, state.internal.frames - 1);
|
|
1260
|
-
return state.frameloop === 'always' ? 1 : state.internal.frames;
|
|
1261
|
-
}
|
|
1262
|
-
function createLoop(roots) {
|
|
1263
|
-
let running = false;
|
|
1264
|
-
let repeat;
|
|
1265
|
-
let frame;
|
|
1266
|
-
let beforeRenderInProgress = false;
|
|
1267
|
-
function loop(timestamp) {
|
|
1268
|
-
frame = requestAnimationFrame(loop);
|
|
1269
|
-
running = true;
|
|
1270
|
-
repeat = 0;
|
|
1271
|
-
// Run effects
|
|
1272
|
-
flushGlobalEffects('before', timestamp);
|
|
1273
|
-
// Render all roots
|
|
1274
|
-
beforeRenderInProgress = true;
|
|
1275
|
-
for (const root of roots.values()) {
|
|
1276
|
-
const state = root.snapshot;
|
|
1277
|
-
// If the frameloop is invalidated, do not run another frame
|
|
1278
|
-
if (state.internal.active &&
|
|
1279
|
-
(state.frameloop === 'always' || state.internal.frames > 0) &&
|
|
1280
|
-
!state.gl.xr?.isPresenting) {
|
|
1281
|
-
repeat += render(timestamp, root);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
beforeRenderInProgress = false;
|
|
1285
|
-
// Run after-effects
|
|
1286
|
-
flushGlobalEffects('after', timestamp);
|
|
1287
|
-
// Stop the loop if nothing invalidates it
|
|
1288
|
-
if (repeat === 0) {
|
|
1289
|
-
// Tail call effects, they are called when rendering stops
|
|
1290
|
-
flushGlobalEffects('tail', timestamp);
|
|
1291
|
-
// Flag end of operation
|
|
1292
|
-
running = false;
|
|
1293
|
-
return cancelAnimationFrame(frame);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
function invalidate(store, frames = 1) {
|
|
1297
|
-
const state = store?.snapshot;
|
|
1298
|
-
if (!state)
|
|
1299
|
-
return roots.forEach((root) => invalidate(root, frames));
|
|
1300
|
-
if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
|
|
1301
|
-
return;
|
|
1302
|
-
if (frames > 1) {
|
|
1303
|
-
// legacy support for people using frames parameters
|
|
1304
|
-
// Increase frames, do not go higher than 60
|
|
1305
|
-
state.internal.frames = Math.min(60, state.internal.frames + frames);
|
|
1306
|
-
}
|
|
1307
|
-
else {
|
|
1308
|
-
if (beforeRenderInProgress) {
|
|
1309
|
-
//called from within a beforeRender, it means the user wants an additional frame
|
|
1310
|
-
state.internal.frames = 2;
|
|
1311
|
-
}
|
|
1312
|
-
else {
|
|
1313
|
-
//the user need a new frame, no need to increment further than 1
|
|
1314
|
-
state.internal.frames = 1;
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
// If the render-loop isn't active, start it
|
|
1318
|
-
if (!running) {
|
|
1319
|
-
running = true;
|
|
1320
|
-
requestAnimationFrame(loop);
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
function advance(timestamp, runGlobalEffects = true, store, frame) {
|
|
1324
|
-
if (runGlobalEffects)
|
|
1325
|
-
flushGlobalEffects('before', timestamp);
|
|
1326
|
-
if (!store)
|
|
1327
|
-
for (const root of roots.values())
|
|
1328
|
-
render(timestamp, root);
|
|
1329
|
-
else
|
|
1330
|
-
render(timestamp, store, frame);
|
|
1331
|
-
if (runGlobalEffects)
|
|
1332
|
-
flushGlobalEffects('after', timestamp);
|
|
1333
|
-
}
|
|
1334
|
-
return { loop, invalidate, advance };
|
|
1335
|
-
}
|
|
1336
|
-
const [injectLoop] = createInjectionToken(() => createLoop(roots));
|
|
955
|
+
const [injectLoop] = createInjectionToken(() => createLoop(roots));
|
|
1337
956
|
|
|
1338
957
|
function storeFactory(previousStore) {
|
|
1339
958
|
const document = inject(DOCUMENT);
|
|
@@ -1509,6 +1128,125 @@ function injectStore(options) {
|
|
|
1509
1128
|
return inject(NGT_STORE, options);
|
|
1510
1129
|
}
|
|
1511
1130
|
|
|
1131
|
+
// This function prepares a set of changes to be applied to the instance
|
|
1132
|
+
function diffProps(instance, props) {
|
|
1133
|
+
const propsEntries = Object.entries(props);
|
|
1134
|
+
const changes = [];
|
|
1135
|
+
for (const [propKey, propValue] of propsEntries) {
|
|
1136
|
+
let key = propKey;
|
|
1137
|
+
if (is.colorSpaceExist(instance)) {
|
|
1138
|
+
if (propKey === 'encoding') {
|
|
1139
|
+
key = 'colorSpace';
|
|
1140
|
+
}
|
|
1141
|
+
else if (propKey === 'outputEncoding') {
|
|
1142
|
+
key = 'outputColorSpace';
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (is.equ(propValue, instance[key]))
|
|
1146
|
+
continue;
|
|
1147
|
+
changes.push([propKey, propValue]);
|
|
1148
|
+
}
|
|
1149
|
+
return changes;
|
|
1150
|
+
}
|
|
1151
|
+
// This function applies a set of changes to the instance
|
|
1152
|
+
function applyProps(instance, props) {
|
|
1153
|
+
// if props is empty
|
|
1154
|
+
if (!Object.keys(props).length)
|
|
1155
|
+
return instance;
|
|
1156
|
+
// filter equals, and reserved props
|
|
1157
|
+
const localState = getLocalState(instance);
|
|
1158
|
+
const rootState = localState?.store?.snapshot ?? {};
|
|
1159
|
+
const changes = diffProps(instance, props);
|
|
1160
|
+
for (let i = 0; i < changes.length; i++) {
|
|
1161
|
+
let [key, value] = changes[i];
|
|
1162
|
+
// Alias (output)encoding => (output)colorSpace (since r152)
|
|
1163
|
+
// https://github.com/pmndrs/react-three-fiber/pull/2829
|
|
1164
|
+
if (is.colorSpaceExist(instance)) {
|
|
1165
|
+
const sRGBEncoding = 3001;
|
|
1166
|
+
const SRGBColorSpace = 'srgb';
|
|
1167
|
+
const LinearSRGBColorSpace = 'srgb-linear';
|
|
1168
|
+
if (key === 'encoding') {
|
|
1169
|
+
key = 'colorSpace';
|
|
1170
|
+
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
1171
|
+
}
|
|
1172
|
+
else if (key === 'outputEncoding') {
|
|
1173
|
+
key = 'outputColorSpace';
|
|
1174
|
+
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
const currentInstance = instance;
|
|
1178
|
+
const targetProp = currentInstance[key];
|
|
1179
|
+
// special treatmen for objects with support for set/copy, and layers
|
|
1180
|
+
if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof Layers)) {
|
|
1181
|
+
const isColor = targetProp instanceof Color;
|
|
1182
|
+
// if value is an array
|
|
1183
|
+
if (Array.isArray(value)) {
|
|
1184
|
+
if (targetProp['fromArray'])
|
|
1185
|
+
targetProp['fromArray'](value);
|
|
1186
|
+
else
|
|
1187
|
+
targetProp['set'](...value);
|
|
1188
|
+
} // test again target.copy
|
|
1189
|
+
else if (targetProp['copy'] &&
|
|
1190
|
+
value &&
|
|
1191
|
+
value.constructor &&
|
|
1192
|
+
targetProp.constructor.name === value.constructor.name) {
|
|
1193
|
+
targetProp['copy'](value);
|
|
1194
|
+
if (!ColorManagement && !rootState.linear && isColor)
|
|
1195
|
+
targetProp['convertSRGBToLinear']();
|
|
1196
|
+
} // if nothing else fits, just set the single value, ignore undefined
|
|
1197
|
+
else if (value !== undefined) {
|
|
1198
|
+
const isColor = targetProp instanceof Color;
|
|
1199
|
+
// allow setting array scalars
|
|
1200
|
+
if (!isColor && targetProp['setScalar'])
|
|
1201
|
+
targetProp['setScalar'](value);
|
|
1202
|
+
// layers have no copy function, copy the mask
|
|
1203
|
+
else if (targetProp instanceof Layers && value instanceof Layers)
|
|
1204
|
+
targetProp.mask = value.mask;
|
|
1205
|
+
// otherwise just set ...
|
|
1206
|
+
else
|
|
1207
|
+
targetProp['set'](value);
|
|
1208
|
+
// auto-convert srgb
|
|
1209
|
+
if (!ColorManagement && !rootState?.linear && isColor)
|
|
1210
|
+
targetProp.convertSRGBToLinear();
|
|
1211
|
+
}
|
|
1212
|
+
} // else just overwrite the value
|
|
1213
|
+
else {
|
|
1214
|
+
currentInstance[key] = value;
|
|
1215
|
+
// auto-convert srgb textures
|
|
1216
|
+
if (currentInstance[key] instanceof Texture &&
|
|
1217
|
+
currentInstance[key].format === RGBAFormat &&
|
|
1218
|
+
currentInstance[key].type === UnsignedByteType) {
|
|
1219
|
+
const texture = currentInstance[key];
|
|
1220
|
+
if (rootState?.gl) {
|
|
1221
|
+
if (is.colorSpaceExist(texture) && is.colorSpaceExist(rootState.gl))
|
|
1222
|
+
texture.colorSpace = rootState.gl.outputColorSpace;
|
|
1223
|
+
// @ts-expect-error - old version of threejs
|
|
1224
|
+
else
|
|
1225
|
+
texture.encoding = rootState.gl.outputEncoding;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
checkUpdate(currentInstance[key]);
|
|
1230
|
+
checkUpdate(targetProp);
|
|
1231
|
+
invalidateInstance(instance);
|
|
1232
|
+
}
|
|
1233
|
+
const instanceHandlersCount = localState?.eventCount;
|
|
1234
|
+
const parent = localState?.instanceStore?.get('parent');
|
|
1235
|
+
if (parent && rootState.internal && instance['raycast'] && instanceHandlersCount !== localState?.eventCount) {
|
|
1236
|
+
// Pre-emptively remove the instance from the interaction manager
|
|
1237
|
+
const index = rootState.internal.interaction.indexOf(instance);
|
|
1238
|
+
if (index > -1)
|
|
1239
|
+
rootState.internal.interaction.splice(index, 1);
|
|
1240
|
+
// Add the instance to the interaction manager only when it has handlers
|
|
1241
|
+
if (localState?.eventCount)
|
|
1242
|
+
rootState.internal.interaction.push(instance);
|
|
1243
|
+
}
|
|
1244
|
+
if (parent && localState?.onUpdate && changes.length) {
|
|
1245
|
+
localState.onUpdate(instance);
|
|
1246
|
+
}
|
|
1247
|
+
return instance;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1512
1250
|
const catalogue = {};
|
|
1513
1251
|
function extend(objects) {
|
|
1514
1252
|
Object.assign(catalogue, objects);
|
|
@@ -2294,24 +2032,286 @@ class NgtRenderer {
|
|
|
2294
2032
|
store = instance;
|
|
2295
2033
|
break;
|
|
2296
2034
|
}
|
|
2297
|
-
i--;
|
|
2298
|
-
}
|
|
2299
|
-
destroyed.forEach((index) => {
|
|
2300
|
-
this.portalCommentsNodes.splice(index, 1);
|
|
2301
|
-
});
|
|
2302
|
-
return store || this.rootStore;
|
|
2035
|
+
i--;
|
|
2036
|
+
}
|
|
2037
|
+
destroyed.forEach((index) => {
|
|
2038
|
+
this.portalCommentsNodes.splice(index, 1);
|
|
2039
|
+
});
|
|
2040
|
+
return store || this.rootStore;
|
|
2041
|
+
}
|
|
2042
|
+
get data() {
|
|
2043
|
+
return this.delegate.data;
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
function provideNgtRenderer(store) {
|
|
2047
|
+
const providers = [
|
|
2048
|
+
NgtRendererFactory,
|
|
2049
|
+
{ provide: RendererFactory2, useExisting: NgtRendererFactory },
|
|
2050
|
+
provideStore(() => store),
|
|
2051
|
+
];
|
|
2052
|
+
return makeEnvironmentProviders(providers);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
const shallowLoose = { objects: 'shallow', strict: false };
|
|
2056
|
+
function injectCanvasRootInitializer(injector) {
|
|
2057
|
+
return assertInjector(injectCanvasRootInitializer, injector, () => {
|
|
2058
|
+
const injectedStore = injectStore();
|
|
2059
|
+
const loop = injectLoop();
|
|
2060
|
+
return (canvas) => {
|
|
2061
|
+
const exist = roots.has(canvas);
|
|
2062
|
+
let store = roots.get(canvas);
|
|
2063
|
+
if (store) {
|
|
2064
|
+
console.warn('[NGT] Same canvas root is being created twice');
|
|
2065
|
+
}
|
|
2066
|
+
store ||= injectedStore;
|
|
2067
|
+
if (!store) {
|
|
2068
|
+
throw new Error('[NGT] No store initialized');
|
|
2069
|
+
}
|
|
2070
|
+
if (!exist) {
|
|
2071
|
+
roots.set(canvas, store);
|
|
2072
|
+
}
|
|
2073
|
+
let isConfigured = false;
|
|
2074
|
+
let lastCamera;
|
|
2075
|
+
return {
|
|
2076
|
+
isConfigured,
|
|
2077
|
+
destroy: (timeout = 500) => {
|
|
2078
|
+
const root = roots.get(canvas);
|
|
2079
|
+
if (root) {
|
|
2080
|
+
root.update((state) => ({ internal: { ...state.internal, active: false } }));
|
|
2081
|
+
try {
|
|
2082
|
+
const state = root.get();
|
|
2083
|
+
state.events.disconnect?.();
|
|
2084
|
+
state.gl?.renderLists?.dispose?.();
|
|
2085
|
+
state.gl?.forceContextLoss?.();
|
|
2086
|
+
if (state.gl?.xr)
|
|
2087
|
+
state.xr.disconnect();
|
|
2088
|
+
dispose(state);
|
|
2089
|
+
roots.delete(canvas);
|
|
2090
|
+
}
|
|
2091
|
+
catch (e) {
|
|
2092
|
+
console.error('[NGT] Unexpected error while destroying Canvas Root', e);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
},
|
|
2096
|
+
configure: (inputs) => {
|
|
2097
|
+
const { shadows = false, linear = false, flat = false, legacy = false, orthographic = false, frameloop = 'always', dpr = [1, 2], gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, lookAt, performance, } = inputs;
|
|
2098
|
+
const state = store.snapshot;
|
|
2099
|
+
const stateToUpdate = {};
|
|
2100
|
+
// setup renderer
|
|
2101
|
+
let gl = state.gl;
|
|
2102
|
+
if (!state.gl)
|
|
2103
|
+
stateToUpdate.gl = gl = makeRendererInstance(glOptions, canvas);
|
|
2104
|
+
// setup raycaster
|
|
2105
|
+
let raycaster = state.raycaster;
|
|
2106
|
+
if (!raycaster)
|
|
2107
|
+
stateToUpdate.raycaster = raycaster = new Raycaster();
|
|
2108
|
+
// set raycaster options
|
|
2109
|
+
const { params, ...options } = raycasterOptions || {};
|
|
2110
|
+
if (!is.equ(options, raycaster, shallowLoose))
|
|
2111
|
+
applyProps(raycaster, options);
|
|
2112
|
+
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
2113
|
+
applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
|
|
2114
|
+
}
|
|
2115
|
+
// Create default camera, don't overwrite any user-set state
|
|
2116
|
+
if (!state.camera || (state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose))) {
|
|
2117
|
+
lastCamera = cameraOptions;
|
|
2118
|
+
const isCamera = is.camera(cameraOptions);
|
|
2119
|
+
let camera = isCamera ? cameraOptions : makeCameraInstance(orthographic, state.size);
|
|
2120
|
+
if (!isCamera) {
|
|
2121
|
+
camera.position.z = 5;
|
|
2122
|
+
if (cameraOptions) {
|
|
2123
|
+
applyProps(camera, cameraOptions);
|
|
2124
|
+
if ('aspect' in cameraOptions ||
|
|
2125
|
+
'left' in cameraOptions ||
|
|
2126
|
+
'right' in cameraOptions ||
|
|
2127
|
+
'top' in cameraOptions ||
|
|
2128
|
+
'bottom' in cameraOptions) {
|
|
2129
|
+
Object.assign(camera, { manual: true });
|
|
2130
|
+
camera?.updateProjectionMatrix();
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
// always look at center or passed-in lookAt by default
|
|
2134
|
+
if (!state.camera && !cameraOptions?.rotation && !cameraOptions?.quaternion) {
|
|
2135
|
+
if (Array.isArray(lookAt))
|
|
2136
|
+
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
|
|
2137
|
+
else if (lookAt instanceof Vector3)
|
|
2138
|
+
camera.lookAt(lookAt);
|
|
2139
|
+
else
|
|
2140
|
+
camera.lookAt(0, 0, 0);
|
|
2141
|
+
}
|
|
2142
|
+
// update projection matrix after applyprops
|
|
2143
|
+
camera.updateProjectionMatrix?.();
|
|
2144
|
+
}
|
|
2145
|
+
if (!is.instance(camera))
|
|
2146
|
+
camera = prepare(camera, { store });
|
|
2147
|
+
stateToUpdate.camera = camera;
|
|
2148
|
+
// Configure raycaster
|
|
2149
|
+
// https://github.com/pmndrs/react-xr/issues/300
|
|
2150
|
+
raycaster.camera = camera;
|
|
2151
|
+
}
|
|
2152
|
+
// Set up scene (one time only!)
|
|
2153
|
+
if (!state.scene) {
|
|
2154
|
+
let scene;
|
|
2155
|
+
if (sceneOptions instanceof Scene) {
|
|
2156
|
+
scene = sceneOptions;
|
|
2157
|
+
}
|
|
2158
|
+
else {
|
|
2159
|
+
scene = new Scene();
|
|
2160
|
+
if (sceneOptions)
|
|
2161
|
+
applyProps(scene, sceneOptions);
|
|
2162
|
+
}
|
|
2163
|
+
applyProps(scene, {
|
|
2164
|
+
setAttribute: (name, value) => {
|
|
2165
|
+
if (canvas instanceof HTMLCanvasElement) {
|
|
2166
|
+
if (canvas.parentElement) {
|
|
2167
|
+
canvas.parentElement.setAttribute(name, value);
|
|
2168
|
+
}
|
|
2169
|
+
else {
|
|
2170
|
+
canvas.setAttribute(name, value);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
},
|
|
2174
|
+
});
|
|
2175
|
+
stateToUpdate.scene = prepare(scene, { store });
|
|
2176
|
+
}
|
|
2177
|
+
// Set up XR (one time only!)
|
|
2178
|
+
if (!state.xr) {
|
|
2179
|
+
// Handle frame behavior in WebXR
|
|
2180
|
+
const handleXRFrame = (timestamp, frame) => {
|
|
2181
|
+
const state = store.snapshot;
|
|
2182
|
+
if (state.frameloop === 'never')
|
|
2183
|
+
return;
|
|
2184
|
+
loop.advance(timestamp, true, store, frame);
|
|
2185
|
+
};
|
|
2186
|
+
// Toggle render switching on session
|
|
2187
|
+
const handleSessionChange = () => {
|
|
2188
|
+
const state = store.snapshot;
|
|
2189
|
+
state.gl.xr.enabled = state.gl.xr.isPresenting;
|
|
2190
|
+
state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
|
|
2191
|
+
if (!state.gl.xr.isPresenting)
|
|
2192
|
+
loop.invalidate(store);
|
|
2193
|
+
};
|
|
2194
|
+
// WebXR session manager
|
|
2195
|
+
const xr = {
|
|
2196
|
+
connect: () => {
|
|
2197
|
+
gl.xr.addEventListener('sessionstart', handleSessionChange);
|
|
2198
|
+
gl.xr.addEventListener('sessionend', handleSessionChange);
|
|
2199
|
+
},
|
|
2200
|
+
disconnect: () => {
|
|
2201
|
+
gl.xr.removeEventListener('sessionstart', handleSessionChange);
|
|
2202
|
+
gl.xr.removeEventListener('sessionend', handleSessionChange);
|
|
2203
|
+
},
|
|
2204
|
+
};
|
|
2205
|
+
// Subscribe to WebXR session events
|
|
2206
|
+
if (gl.xr && typeof gl.xr.addEventListener === 'function')
|
|
2207
|
+
xr.connect();
|
|
2208
|
+
stateToUpdate.xr = xr;
|
|
2209
|
+
}
|
|
2210
|
+
// Set shadowmap
|
|
2211
|
+
if (gl.shadowMap) {
|
|
2212
|
+
const oldEnabled = gl.shadowMap.enabled;
|
|
2213
|
+
const oldType = gl.shadowMap.type;
|
|
2214
|
+
gl.shadowMap.enabled = !!shadows;
|
|
2215
|
+
if (typeof shadows === 'boolean') {
|
|
2216
|
+
gl.shadowMap.type = PCFSoftShadowMap;
|
|
2217
|
+
}
|
|
2218
|
+
else if (typeof shadows === 'string') {
|
|
2219
|
+
const types = {
|
|
2220
|
+
basic: BasicShadowMap,
|
|
2221
|
+
percentage: PCFShadowMap,
|
|
2222
|
+
soft: PCFSoftShadowMap,
|
|
2223
|
+
variance: VSMShadowMap,
|
|
2224
|
+
};
|
|
2225
|
+
gl.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
|
|
2226
|
+
}
|
|
2227
|
+
else if (is.obj(shadows)) {
|
|
2228
|
+
Object.assign(gl.shadowMap, shadows);
|
|
2229
|
+
}
|
|
2230
|
+
if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
|
|
2231
|
+
checkNeedsUpdate(gl.shadowMap);
|
|
2232
|
+
}
|
|
2233
|
+
// Safely set color management if available.
|
|
2234
|
+
// Avoid accessing ColorManagement to play nice with older versions
|
|
2235
|
+
if (ColorManagement) {
|
|
2236
|
+
const colorManagement = ColorManagement;
|
|
2237
|
+
if ('enabled' in colorManagement)
|
|
2238
|
+
colorManagement['enabled'] = !legacy ?? false;
|
|
2239
|
+
else if ('legacyMode' in colorManagement)
|
|
2240
|
+
colorManagement['legacyMode'] = legacy ?? true;
|
|
2241
|
+
}
|
|
2242
|
+
if (!isConfigured) {
|
|
2243
|
+
// set color space and tonemapping preferences once
|
|
2244
|
+
const LinearEncoding = 3000;
|
|
2245
|
+
const sRGBEncoding = 3001;
|
|
2246
|
+
applyProps(gl, {
|
|
2247
|
+
outputEncoding: linear ? LinearEncoding : sRGBEncoding,
|
|
2248
|
+
toneMapping: flat ? NoToneMapping : ACESFilmicToneMapping,
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
// Update color management state
|
|
2252
|
+
if (state.legacy !== legacy)
|
|
2253
|
+
stateToUpdate.legacy = legacy;
|
|
2254
|
+
if (state.linear !== linear)
|
|
2255
|
+
stateToUpdate.linear = linear;
|
|
2256
|
+
if (state.flat !== flat)
|
|
2257
|
+
stateToUpdate.flat = flat;
|
|
2258
|
+
// Set gl props
|
|
2259
|
+
gl.setClearAlpha(0);
|
|
2260
|
+
gl.setPixelRatio(makeDpr(state.viewport.dpr));
|
|
2261
|
+
gl.setSize(state.size.width, state.size.height);
|
|
2262
|
+
if (is.obj(glOptions) &&
|
|
2263
|
+
!(typeof glOptions === 'function') &&
|
|
2264
|
+
!is.renderer(glOptions) &&
|
|
2265
|
+
!is.equ(glOptions, gl, shallowLoose)) {
|
|
2266
|
+
applyProps(gl, glOptions);
|
|
2267
|
+
}
|
|
2268
|
+
// Store events internally
|
|
2269
|
+
if (events && !state.events.handlers)
|
|
2270
|
+
stateToUpdate.events = events(store);
|
|
2271
|
+
// Check performance
|
|
2272
|
+
if (performance && !is.equ(performance, state.performance, shallowLoose)) {
|
|
2273
|
+
stateToUpdate.performance = { ...state.performance, ...performance };
|
|
2274
|
+
}
|
|
2275
|
+
if (Object.keys(stateToUpdate).length) {
|
|
2276
|
+
store.update(stateToUpdate);
|
|
2277
|
+
}
|
|
2278
|
+
// Check size, allow it to take on container bounds initially
|
|
2279
|
+
const size = computeInitialSize(canvas, sizeOptions);
|
|
2280
|
+
if (!is.equ(size, state.size, shallowLoose)) {
|
|
2281
|
+
state.setSize(size.width, size.height, size.top, size.left);
|
|
2282
|
+
}
|
|
2283
|
+
// Check pixelratio
|
|
2284
|
+
if (dpr && state.viewport.dpr !== makeDpr(dpr))
|
|
2285
|
+
state.setDpr(dpr);
|
|
2286
|
+
// Check frameloop
|
|
2287
|
+
if (state.frameloop !== frameloop)
|
|
2288
|
+
state.setFrameloop(frameloop);
|
|
2289
|
+
isConfigured = true;
|
|
2290
|
+
},
|
|
2291
|
+
};
|
|
2292
|
+
};
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
function computeInitialSize(canvas, defaultSize) {
|
|
2296
|
+
if (defaultSize) {
|
|
2297
|
+
return defaultSize;
|
|
2303
2298
|
}
|
|
2304
|
-
|
|
2305
|
-
return
|
|
2299
|
+
if (typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
|
|
2300
|
+
return canvas.parentElement.getBoundingClientRect();
|
|
2301
|
+
}
|
|
2302
|
+
if (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
|
|
2303
|
+
return { width: canvas.width, height: canvas.height, top: 0, left: 0 };
|
|
2306
2304
|
}
|
|
2305
|
+
return { width: 0, height: 0, top: 0, left: 0 };
|
|
2307
2306
|
}
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2307
|
+
// Disposes an object and all its properties
|
|
2308
|
+
function dispose(obj) {
|
|
2309
|
+
if (obj.dispose && obj.type !== 'Scene')
|
|
2310
|
+
obj.dispose();
|
|
2311
|
+
for (const p in obj) {
|
|
2312
|
+
p.dispose?.();
|
|
2313
|
+
delete obj[p];
|
|
2314
|
+
}
|
|
2315
2315
|
}
|
|
2316
2316
|
|
|
2317
2317
|
var _a$1;
|
|
@@ -2514,6 +2514,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.2", ngImpor
|
|
|
2514
2514
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2515
2515
|
}]
|
|
2516
2516
|
}], ctorParameters: () => [] });
|
|
2517
|
+
const injectRoot = injectCanvasRootInitializer;
|
|
2517
2518
|
|
|
2518
2519
|
var _a;
|
|
2519
2520
|
const NGT_HTML_DOM_ELEMENT = new InjectionToken('NGT_HTML_DOM_ELEMENT');
|
|
@@ -2971,5 +2972,5 @@ function vector3(inputOrOptions, keyOrKeepUndefined, keepUndefined) {
|
|
|
2971
2972
|
* Generated bundle index. Do not edit.
|
|
2972
2973
|
*/
|
|
2973
2974
|
|
|
2974
|
-
export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtHTML, NgtPortal, NgtPortalContent, NgtRenderer, NgtRendererFactory, NgtRoutedScene, ROUTED_SCENE, addAfterEffect, addEffect, addTail, applyProps, checkNeedsUpdate, checkUpdate, createAttachFunction, extend, getLocalState, injectBeforeRender, injectCanvasRootInitializer, injectLoader, injectObjectEvents, injectStore, invalidateInstance, is, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, omit, pick, prepare, provideHTMLDomElement, provideNgtRenderer, provideStore, resolveRef, signalStore, updateCamera, vector2, vector3 };
|
|
2975
|
+
export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtHTML, NgtPortal, NgtPortalBeforeRender, NgtPortalContent, NgtRenderer, NgtRendererFactory, NgtRoutedScene, ROUTED_SCENE, addAfterEffect, addEffect, addTail, applyProps, attach, checkNeedsUpdate, checkUpdate, createAttachFunction, detach, dispose, extend, flushGlobalEffects, getLocalState, injectBeforeRender, injectCanvasRootInitializer, injectLoader, injectLoop, injectObjectEvents, injectRoot, injectStore, invalidateInstance, is, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, omit, pick, prepare, privateKeys, provideHTMLDomElement, provideNgtRenderer, provideStore, resolveRef, roots, signalStore, updateCamera, vector2, vector3 };
|
|
2975
2976
|
//# sourceMappingURL=angular-three.mjs.map
|