@needle-tools/engine 5.0.2 → 5.1.0-canary.87c4c44
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/CHANGELOG.md +24 -0
- package/README.md +6 -7
- package/SKILL.md +39 -21
- package/components.needle.json +1 -1
- package/dist/needle-engine.bundle-BlVD1I5a.min.js +1656 -0
- package/dist/{needle-engine.bundle-BoTyA-Le.js → needle-engine.bundle-CFOipCo_.js} +8519 -7920
- package/dist/needle-engine.bundle-DX2Y-SQF.umd.cjs +1656 -0
- package/dist/needle-engine.d.ts +628 -61
- package/dist/needle-engine.js +575 -565
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{vendor-vHLk8sXu.js → vendor-CAcsI0eU.js} +116 -115
- package/dist/{vendor-CntUvmJu.umd.cjs → vendor-CEM38hLE.umd.cjs} +2 -2
- package/dist/{vendor-DPbfJJ4d.min.js → vendor-HRlxIBga.min.js} +2 -2
- package/lib/engine/api.d.ts +2 -0
- package/lib/engine/api.js +2 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_addressables.js +5 -1
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_animation.d.ts +14 -7
- package/lib/engine/engine_animation.js +49 -9
- package/lib/engine/engine_animation.js.map +1 -1
- package/lib/engine/engine_components.js +33 -4
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_context.d.ts +7 -2
- package/lib/engine/engine_context.js +10 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_gameobject.d.ts +4 -0
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_init.js +2 -0
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.js +4 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_materialpropertyblock.js +1 -20
- package/lib/engine/engine_materialpropertyblock.js.map +1 -1
- package/lib/engine/engine_networking.d.ts +11 -8
- package/lib/engine/engine_networking.js +43 -26
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_networking_instantiate.d.ts +100 -5
- package/lib/engine/engine_networking_instantiate.js +150 -16
- package/lib/engine/engine_networking_instantiate.js.map +1 -1
- package/lib/engine/engine_networking_prefabs.d.ts +59 -0
- package/lib/engine/engine_networking_prefabs.js +67 -0
- package/lib/engine/engine_networking_prefabs.js.map +1 -0
- package/lib/engine/postprocessing/api.d.ts +2 -0
- package/lib/engine/postprocessing/api.js +2 -0
- package/lib/engine/postprocessing/api.js.map +1 -0
- package/lib/engine/postprocessing/index.d.ts +2 -0
- package/lib/engine/postprocessing/index.js +2 -0
- package/lib/engine/postprocessing/index.js.map +1 -0
- package/lib/engine/postprocessing/postprocessing.d.ts +83 -0
- package/lib/engine/postprocessing/postprocessing.js +280 -0
- package/lib/engine/postprocessing/postprocessing.js.map +1 -0
- package/lib/engine/postprocessing/types.d.ts +39 -0
- package/lib/engine/postprocessing/types.js +2 -0
- package/lib/engine/postprocessing/types.js.map +1 -0
- package/lib/engine/webcomponents/WebXRButtons.js +17 -3
- package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +3 -1
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +1 -0
- package/lib/engine/xr/NeedleXRSession.js +43 -10
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/init.d.ts +4 -0
- package/lib/engine/xr/init.js +49 -0
- package/lib/engine/xr/init.js.map +1 -0
- package/lib/engine-components/AnimationUtils.d.ts +4 -1
- package/lib/engine-components/AnimationUtils.js +7 -19
- package/lib/engine-components/AnimationUtils.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +135 -2
- package/lib/engine-components/AnimatorController.js +216 -13
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +4 -0
- package/lib/engine-components/OrbitControls.js +5 -1
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/SeeThrough.d.ts +0 -2
- package/lib/engine-components/SeeThrough.js +0 -89
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/SyncedRoom.d.ts +4 -0
- package/lib/engine-components/SyncedRoom.js +23 -8
- package/lib/engine-components/SyncedRoom.js.map +1 -1
- package/lib/engine-components/SyncedTransform.js +5 -5
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/Voip.d.ts +46 -0
- package/lib/engine-components/Voip.js +126 -2
- package/lib/engine-components/Voip.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -0
- package/lib/engine-components/api.js +1 -0
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.d.ts +5 -2
- package/lib/engine-components/postprocessing/Effects/Tonemapping.js +11 -18
- package/lib/engine-components/postprocessing/Effects/Tonemapping.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +3 -4
- package/lib/engine-components/postprocessing/PostProcessingEffect.js +6 -15
- package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +2 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/Volume.d.ts +18 -11
- package/lib/engine-components/postprocessing/Volume.js +61 -140
- package/lib/engine-components/postprocessing/Volume.js.map +1 -1
- package/lib/engine-components/postprocessing/index.d.ts +1 -0
- package/lib/engine-components/postprocessing/index.js +1 -0
- package/lib/engine-components/postprocessing/index.js.map +1 -1
- package/lib/engine-components/postprocessing/utils.d.ts +2 -0
- package/lib/engine-components/postprocessing/utils.js +2 -0
- package/lib/engine-components/postprocessing/utils.js.map +1 -1
- package/lib/engine-components/ui/Canvas.js +2 -2
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/lib/engine-components/ui/Graphic.d.ts +3 -3
- package/lib/engine-components/ui/Graphic.js +6 -2
- package/lib/engine-components/ui/Graphic.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +64 -11
- package/lib/engine-components/ui/Text.js +154 -45
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/engine-components/ui/index.d.ts +1 -0
- package/lib/engine-components/ui/index.js +1 -0
- package/lib/engine-components/ui/index.js.map +1 -1
- package/lib/engine-components-experimental/networking/PlayerSync.d.ts +25 -3
- package/lib/engine-components-experimental/networking/PlayerSync.js +60 -11
- package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
- package/package.json +6 -5
- package/plugins/vite/ai.d.ts +11 -10
- package/plugins/vite/ai.js +305 -31
- package/plugins/vite/dependencies.js +5 -0
- package/src/engine/api.ts +3 -0
- package/src/engine/engine_addressables.ts +4 -1
- package/src/engine/engine_animation.ts +47 -9
- package/src/engine/engine_components.ts +36 -7
- package/src/engine/engine_context.ts +11 -2
- package/src/engine/engine_gameobject.ts +5 -0
- package/src/engine/engine_init.ts +2 -0
- package/src/engine/engine_input.ts +2 -1
- package/src/engine/engine_materialpropertyblock.ts +1 -20
- package/src/engine/engine_networking.ts +46 -23
- package/src/engine/engine_networking_instantiate.ts +160 -18
- package/src/engine/engine_networking_prefabs.ts +80 -0
- package/src/engine/postprocessing/api.ts +2 -0
- package/src/engine/postprocessing/index.ts +2 -0
- package/src/engine/postprocessing/postprocessing.ts +322 -0
- package/src/engine/postprocessing/types.ts +43 -0
- package/src/engine/webcomponents/WebXRButtons.ts +21 -4
- package/src/engine/webcomponents/icons.ts +5 -3
- package/src/engine/xr/NeedleXRSession.ts +50 -15
- package/src/engine/xr/init.ts +56 -0
- package/src/engine-components/AnimationUtils.ts +7 -17
- package/src/engine-components/AnimatorController.ts +288 -18
- package/src/engine-components/OrbitControls.ts +5 -1
- package/src/engine-components/SeeThrough.ts +0 -116
- package/src/engine-components/SyncedRoom.ts +28 -9
- package/src/engine-components/SyncedTransform.ts +5 -5
- package/src/engine-components/Voip.ts +129 -2
- package/src/engine-components/api.ts +1 -0
- package/src/engine-components/postprocessing/Effects/Tonemapping.ts +16 -24
- package/src/engine-components/postprocessing/PostProcessingEffect.ts +9 -16
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +2 -1
- package/src/engine-components/postprocessing/Volume.ts +72 -163
- package/src/engine-components/postprocessing/index.ts +1 -0
- package/src/engine-components/postprocessing/utils.ts +2 -0
- package/src/engine-components/ui/Canvas.ts +2 -2
- package/src/engine-components/ui/Graphic.ts +7 -3
- package/src/engine-components/ui/Text.ts +170 -52
- package/src/engine-components/ui/index.ts +2 -1
- package/src/engine-components-experimental/networking/PlayerSync.ts +64 -11
- package/dist/needle-engine.bundle-B3ywqx5o.min.js +0 -1654
- package/dist/needle-engine.bundle-CzOPcOui.umd.cjs +0 -1654
|
@@ -2,9 +2,10 @@ import { AnimationAction, AnimationClip, AnimationMixer, KeyframeTrack, Object3D
|
|
|
2
2
|
|
|
3
3
|
import { isDevEnvironment } from "./debug/index.js";
|
|
4
4
|
import type { Context } from "./engine_context.js";
|
|
5
|
-
import {
|
|
5
|
+
import { IAnimationComponent, Model } from "./engine_types.js";
|
|
6
6
|
import { TypeStore } from "./engine_typestore.js";
|
|
7
7
|
|
|
8
|
+
// #region Registry
|
|
8
9
|
/**
|
|
9
10
|
* Registry for animation related data. Use {@link registerAnimationMixer} to register an animation mixer instance.
|
|
10
11
|
* Can be accessed from {@link Context.animations} and is used internally e.g. when exporting GLTF files.
|
|
@@ -52,10 +53,11 @@ export class AnimationsRegistry {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
|
|
56
|
+
// #region Animation Utils
|
|
55
57
|
/**
|
|
56
58
|
* Utility class for working with animations.
|
|
57
59
|
*/
|
|
58
|
-
export
|
|
60
|
+
export namespace AnimationUtils {
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
63
|
* Tests if the root object of an AnimationAction can be animated. Objects where matrixAutoUpdate or matrixWorldAutoUpdate is set to false may not animate correctly.
|
|
@@ -63,7 +65,7 @@ export class AnimationUtils {
|
|
|
63
65
|
* @param allowLog Whether to allow logging warnings. Default is false, which only allows logging in development environments.
|
|
64
66
|
* @returns True if the root object can be animated, false otherwise
|
|
65
67
|
*/
|
|
66
|
-
|
|
68
|
+
export function testIfRootCanAnimate(action: AnimationAction, allowLog?: boolean): boolean {
|
|
67
69
|
const root = action.getRoot();
|
|
68
70
|
|
|
69
71
|
if (root && (root.userData.static || root.matrixAutoUpdate === false || root.matrixWorldAutoUpdate === false)) {
|
|
@@ -79,13 +81,15 @@ export class AnimationUtils {
|
|
|
79
81
|
* @param mixer The animation mixer to get the actions from
|
|
80
82
|
* @returns The actions or null if the mixer is invalid
|
|
81
83
|
*/
|
|
82
|
-
|
|
84
|
+
|
|
85
|
+
export function tryGetActionsFromMixer(mixer: AnimationMixer): Array<AnimationAction> | null {
|
|
83
86
|
const actions = mixer["_actions"] as Array<AnimationAction>;
|
|
84
87
|
if (!actions) return null;
|
|
85
88
|
return actions;
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
|
|
92
|
+
export function tryGetAnimationClipsFromObjectHierarchy(obj: Object3D, target?: Array<AnimationClip>): Array<AnimationClip> {
|
|
89
93
|
if (!target) target = new Array<AnimationClip>();
|
|
90
94
|
|
|
91
95
|
if (!obj) {
|
|
@@ -96,18 +100,49 @@ export class AnimationUtils {
|
|
|
96
100
|
}
|
|
97
101
|
if (obj.children) {
|
|
98
102
|
for (const child of obj.children) {
|
|
99
|
-
|
|
103
|
+
tryGetAnimationClipsFromObjectHierarchy(child, target);
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
return target;
|
|
103
107
|
}
|
|
104
108
|
|
|
109
|
+
|
|
110
|
+
const $objectAnimationKey = Symbol("objectIsAnimatedData");
|
|
111
|
+
|
|
112
|
+
/** Internal method - This marks an object as being animated. Make sure to always call isAnimated=false if you stop animating the object
|
|
113
|
+
* @param obj The object to mark
|
|
114
|
+
* @param isAnimated Whether the object is animated or not
|
|
115
|
+
*/
|
|
116
|
+
export function setObjectAnimated(obj: Object3D, animatedBy: object, isAnimated: boolean) {
|
|
117
|
+
if (!obj) return;
|
|
118
|
+
if (obj[$objectAnimationKey] === undefined) {
|
|
119
|
+
if (!isAnimated) return;
|
|
120
|
+
obj[$objectAnimationKey] = new Set<object>();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const set = obj[$objectAnimationKey] as Set<object>;
|
|
124
|
+
if (isAnimated) {
|
|
125
|
+
set.add(animatedBy);
|
|
126
|
+
}
|
|
127
|
+
else if (set.has(animatedBy))
|
|
128
|
+
set.delete(animatedBy);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Get is the object is currently animated. Currently used by the Animator to check if a timeline animationtrack is actively animating an object */
|
|
132
|
+
export function getObjectAnimated(obj: Object3D): boolean {
|
|
133
|
+
if (!obj) return false;
|
|
134
|
+
const set = obj[$objectAnimationKey] as Set<object>;
|
|
135
|
+
return set !== undefined && set.size > 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// #region Autoplay
|
|
105
139
|
/**
|
|
106
140
|
* Assigns animations from a GLTF file to the objects in the scene.
|
|
107
141
|
* This method will look for objects in the scene that have animations and assign them to the correct objects.
|
|
108
142
|
* @param file The GLTF file to assign the animations from
|
|
109
143
|
*/
|
|
110
|
-
|
|
144
|
+
|
|
145
|
+
export function autoplayAnimations(file: Object3D | Pick<Model, "animations" | "scene">): Array<IAnimationComponent> | null {
|
|
111
146
|
if (!file || !file.animations) {
|
|
112
147
|
console.debug("No animations found in file");
|
|
113
148
|
return null;
|
|
@@ -171,11 +206,14 @@ export class AnimationUtils {
|
|
|
171
206
|
}
|
|
172
207
|
|
|
173
208
|
|
|
174
|
-
|
|
209
|
+
// #region Create Clips
|
|
210
|
+
|
|
211
|
+
export function emptyClip(): AnimationClip {
|
|
175
212
|
return new AnimationClip("empty", 0, []);
|
|
176
213
|
}
|
|
177
214
|
|
|
178
|
-
|
|
215
|
+
|
|
216
|
+
export function createScaleClip(options?: ScaleClipOptions): AnimationClip {
|
|
179
217
|
const duration = options?.duration ?? 0.3;
|
|
180
218
|
|
|
181
219
|
let baseScale: Vector3Like = { x: 1, y: 1, z: 1 };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Object3D, Scene } from "three";
|
|
2
|
+
import { v5 } from 'uuid';
|
|
2
3
|
|
|
3
4
|
import { ComponentLifecycleEvents } from "./engine_components_internal.js";
|
|
4
5
|
import { activeInHierarchyFieldName } from "./engine_constants.js";
|
|
@@ -6,8 +7,35 @@ import { removeScriptFromContext, updateActiveInHierarchyWithoutEventCall } from
|
|
|
6
7
|
import { InstantiateIdProvider } from "./engine_networking_instantiate.js";
|
|
7
8
|
import { Context, registerComponent } from "./engine_setup.js";
|
|
8
9
|
import type { ComponentInit, Constructor, ConstructorConcrete, IComponent, IGameObject } from "./engine_types.js";
|
|
10
|
+
import { $componentName } from "./engine_types.js";
|
|
9
11
|
import { getParam } from "./engine_utils.js";
|
|
10
12
|
import { apply } from "./js-extensions/index.js";
|
|
13
|
+
import { TypeStore } from "./engine_typestore.js";
|
|
14
|
+
|
|
15
|
+
const COMPONENT_GUID_NAMESPACE = 'eff8ba80-635d-11ec-90d6-0242ac120003';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generates a deterministic guid for a component based on the object's guid and the component type.
|
|
19
|
+
* If the object has a guid, the component guid is derived from `objectGuid:TypeName:index`
|
|
20
|
+
* using UUID v5 hashing. This ensures the same component type added to the same object on
|
|
21
|
+
* different clients gets the same guid — critical for networked components like SyncedTransform.
|
|
22
|
+
* Falls back to the shared counter-based IdProvider if the object has no guid.
|
|
23
|
+
*/
|
|
24
|
+
function generateDeterministicComponentGuid(obj: Object3D, component: IComponent): string {
|
|
25
|
+
const objectGuid = obj["guid"];
|
|
26
|
+
if (objectGuid) {
|
|
27
|
+
const typeName = component[$componentName] ?? component.constructor?.name ?? "Component";
|
|
28
|
+
// Count existing components of same type for uniqueness (e.g. multiple of the same component type)
|
|
29
|
+
const existingComponents = getComponents(obj, component.constructor as any);
|
|
30
|
+
let sameTypeCount = 0;
|
|
31
|
+
for (const c of existingComponents) {
|
|
32
|
+
if (c !== component) sameTypeCount++;
|
|
33
|
+
}
|
|
34
|
+
const key = `${objectGuid}:${typeName}:${sameTypeCount}`;
|
|
35
|
+
return v5(key, COMPONENT_GUID_NAMESPACE);
|
|
36
|
+
}
|
|
37
|
+
return getIdProvider().generateUUID();
|
|
38
|
+
}
|
|
11
39
|
|
|
12
40
|
|
|
13
41
|
const debug = getParam("debuggetcomponent");
|
|
@@ -57,9 +85,8 @@ export function addNewComponent<T extends IComponent>(obj: Object3D, componentIn
|
|
|
57
85
|
if (!obj.userData.components) obj.userData.components = [];
|
|
58
86
|
obj.userData.components.push(componentInstance);
|
|
59
87
|
componentInstance.gameObject = obj as IGameObject;
|
|
60
|
-
// TODO: currently add component does not ensure a new component instance has a guid
|
|
61
88
|
if (componentInstance.guid === undefined || componentInstance.guid === "invalid") {
|
|
62
|
-
componentInstance.guid =
|
|
89
|
+
componentInstance.guid = generateDeterministicComponentGuid(obj, componentInstance);
|
|
63
90
|
}
|
|
64
91
|
apply(obj);
|
|
65
92
|
// register the component - make sure to provide the component instance context (if assigned)
|
|
@@ -109,9 +136,9 @@ export function addComponent<T extends IComponent>(obj: Object3D, componentInsta
|
|
|
109
136
|
// componentInstance.__internalEnable();
|
|
110
137
|
// componentInstance.transform = obj;
|
|
111
138
|
if (componentInstance.guid === undefined || componentInstance.guid === "invalid") {
|
|
112
|
-
componentInstance.guid =
|
|
139
|
+
componentInstance.guid = generateDeterministicComponentGuid(obj, componentInstance);
|
|
113
140
|
}
|
|
114
|
-
if(init) componentInstance._internalInit(init);
|
|
141
|
+
if (init) componentInstance._internalInit(init);
|
|
115
142
|
// Register the component - make sure to provide the component instance context (if assigned)
|
|
116
143
|
registerComponent(componentInstance, componentInstance.context);
|
|
117
144
|
return componentInstance;
|
|
@@ -144,10 +171,12 @@ function onGetComponent<T>(obj: Object3D | null | undefined, componentType: Cons
|
|
|
144
171
|
}
|
|
145
172
|
if (!(obj?.userData?.components)) return null;
|
|
146
173
|
if (typeof componentType === "string") {
|
|
147
|
-
|
|
174
|
+
const type = TypeStore.get(componentType);
|
|
175
|
+
if (!didWarnAboutComponentAccess && !type) {
|
|
148
176
|
didWarnAboutComponentAccess = true;
|
|
149
177
|
console.warn(`Accessing components by name is not supported.\nPlease use the component type instead. This may keep working in local development but it will fail when bundling your application.\n\nYou can import other modules your main module to get access to types\nor if you use npmdefs you can make types available globally using globalThis:\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis`, componentType);
|
|
150
178
|
}
|
|
179
|
+
if (type) componentType = type as Constructor<T>;
|
|
151
180
|
}
|
|
152
181
|
|
|
153
182
|
if (debugEnabled())
|
|
@@ -222,7 +251,7 @@ export function getComponents<T extends IComponent>(obj: Object3D, componentType
|
|
|
222
251
|
* ```
|
|
223
252
|
*/
|
|
224
253
|
export function getComponentInChildren<T extends IComponent>(obj: Object3D, componentType: Constructor<T>, includeInactive: boolean = false): T | null {
|
|
225
|
-
if (includeInactive === false && obj[activeInHierarchyFieldName] === false) return null;
|
|
254
|
+
if (includeInactive === false && obj[activeInHierarchyFieldName] === false) return null;
|
|
226
255
|
const res = getComponent(obj, componentType) as IComponent | null;
|
|
227
256
|
if (includeInactive === false && (res?.enabled === false || res?.activeAndEnabled === false)) return null;
|
|
228
257
|
if (res) return res as T;
|
|
@@ -335,7 +364,7 @@ export function findObjectOfType<T extends IComponent>(type: Constructor<T>, con
|
|
|
335
364
|
if (!scene) return null;
|
|
336
365
|
|
|
337
366
|
const res = getComponentInChildren(scene, type, includeInactive);
|
|
338
|
-
if(res) return res;
|
|
367
|
+
if (res) return res;
|
|
339
368
|
return null;
|
|
340
369
|
}
|
|
341
370
|
|
|
@@ -43,6 +43,7 @@ import { patchTonemapping } from './engine_tonemapping.js';
|
|
|
43
43
|
import type { CoroutineData, ICamera, IComponent, IContext, ILight, LoadedModel, Model, SourceIdentifier, Vec2 } from "./engine_types.js";
|
|
44
44
|
import { deepClone, delay, DeviceUtilities, getParam } from './engine_utils.js';
|
|
45
45
|
import type { INeedleXRSessionEventReceiver, NeedleXRSession } from './engine_xr.js';
|
|
46
|
+
import { PostProcessing } from './postprocessing/index.js';
|
|
46
47
|
import { NeedleMenu } from './webcomponents/needle menu/needle-menu.js';
|
|
47
48
|
import type { NeedleEngineWebComponent } from './webcomponents/needle-engine.js';
|
|
48
49
|
|
|
@@ -387,9 +388,11 @@ export class Context implements IContext {
|
|
|
387
388
|
*/
|
|
388
389
|
renderer!: WebGLRenderer;
|
|
389
390
|
/**
|
|
390
|
-
* The effect composer
|
|
391
|
+
* The effect composer used for rendering postprocessing effects.
|
|
392
|
+
* @deprecated Use `context.postprocessing.composer` instead.
|
|
391
393
|
*/
|
|
392
|
-
composer: EffectComposer | ThreeEffectComposer | null
|
|
394
|
+
get composer(): EffectComposer | ThreeEffectComposer | null { return this.postprocessing.composer; }
|
|
395
|
+
set composer(value: EffectComposer | ThreeEffectComposer | null) { this.postprocessing.composer = value; }
|
|
393
396
|
|
|
394
397
|
// #region internal script lists
|
|
395
398
|
/**
|
|
@@ -504,6 +507,8 @@ export class Context implements IContext {
|
|
|
504
507
|
input: Input;
|
|
505
508
|
/** access physics related methods (e.g. raycasting). To access the phyiscs engine use `context.physics.engine` */
|
|
506
509
|
physics: Physics;
|
|
510
|
+
/** access postprocessing effects stack. Add/remove effects and configure adaptive performance settings */
|
|
511
|
+
postprocessing: PostProcessing;
|
|
507
512
|
/** access networking methods (use it to send or listen to messages or join a networking backend) */
|
|
508
513
|
connection: NetworkConnection;
|
|
509
514
|
/** @deprecated AssetDatabase is deprecated */
|
|
@@ -564,6 +569,7 @@ export class Context implements IContext {
|
|
|
564
569
|
this.time = new Time();
|
|
565
570
|
this.input = new Input(this);
|
|
566
571
|
this.physics = new Physics(this);
|
|
572
|
+
this.postprocessing = new PostProcessing(this);
|
|
567
573
|
this.connection = new NetworkConnection(this);
|
|
568
574
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
569
575
|
this.assets = new AssetDatabase();
|
|
@@ -1778,6 +1784,9 @@ export class Context implements IContext {
|
|
|
1778
1784
|
if (this.renderer.toneMapping !== NoToneMapping)
|
|
1779
1785
|
patchTonemapping(this);
|
|
1780
1786
|
|
|
1787
|
+
// Update postprocessing stack (applies dirty effects, adaptive multisampling/pixel ratio)
|
|
1788
|
+
this.postprocessing.update();
|
|
1789
|
+
|
|
1781
1790
|
if (this.composer && !this.isInXR) {
|
|
1782
1791
|
// if a camera is passed in we need to check if we need to update the composer's camera
|
|
1783
1792
|
if (camera && "setMainCamera" in this.composer) {
|
|
@@ -8,6 +8,7 @@ import { activeInHierarchyFieldName } from "./engine_constants.js";
|
|
|
8
8
|
import { editorGuidKeyName } from "./engine_constants.js";
|
|
9
9
|
import { $isUsingInstancing, InstancingUtil } from "./engine_instancing.js";
|
|
10
10
|
import { processNewScripts } from "./engine_mainloop_utils.js";
|
|
11
|
+
import type { syncInstantiate } from "./engine_networking_instantiate.js";
|
|
11
12
|
import { InstantiateIdProvider } from "./engine_networking_instantiate.js";
|
|
12
13
|
import { assign, ISerializable } from "./engine_serialization_core.js";
|
|
13
14
|
import { Context, registerComponent } from "./engine_setup.js";
|
|
@@ -19,7 +20,11 @@ import { apply } from "./js-extensions/index.js";
|
|
|
19
20
|
const debug = getParam("debuggetcomponent");
|
|
20
21
|
const debugInstantiate = getParam("debuginstantiate");
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Options for instantiating a GameObject, used in {@link instantiate} and {@link syncInstantiate}
|
|
25
|
+
*/
|
|
22
26
|
export type IInstantiateOptions = {
|
|
27
|
+
/** The ID provider for generating unique IDs / guids */
|
|
23
28
|
idProvider?: UIDProvider;
|
|
24
29
|
//** parent guid or object */
|
|
25
30
|
parent?: string | Object3D;
|
|
@@ -12,6 +12,7 @@ import { patchLayers } from "./js-extensions/Layers.js";
|
|
|
12
12
|
import { initObject3DExtensions } from "./js-extensions/Object3D.js";
|
|
13
13
|
import { initVectorExtensions } from "./js-extensions/Vector.js";
|
|
14
14
|
import { initWebComponents } from "./webcomponents/init.js";
|
|
15
|
+
import { initXR } from "./xr/init.js";
|
|
15
16
|
|
|
16
17
|
let initialized = false;
|
|
17
18
|
|
|
@@ -42,4 +43,5 @@ export function initEngine() {
|
|
|
42
43
|
initAnimationAutoplay();
|
|
43
44
|
initSkyboxAttributes();
|
|
44
45
|
initSceneSwitcherAttributes();
|
|
46
|
+
initXR();
|
|
45
47
|
}
|
|
@@ -1088,7 +1088,8 @@ export class Input implements IInput {
|
|
|
1088
1088
|
if (this.context.isInAR) return;
|
|
1089
1089
|
if (this.canReceiveInput(evt) === false) return;
|
|
1090
1090
|
if (evt.target instanceof HTMLElement) {
|
|
1091
|
-
evt.target.setPointerCapture(evt.pointerId);
|
|
1091
|
+
try { evt.target.setPointerCapture(evt.pointerId); }
|
|
1092
|
+
catch { /* may fail during pointer lock */ }
|
|
1092
1093
|
}
|
|
1093
1094
|
const id = this.getPointerId(evt);
|
|
1094
1095
|
if (debug) showBalloonMessage(`pointer down #${id}, identifier:${evt.pointerId}`);
|
|
@@ -560,25 +560,6 @@ const $savedTextureTransforms = Symbol("savedTextureTransforms");
|
|
|
560
560
|
*/
|
|
561
561
|
type ObjectRenderCallback = (this: Object3D, renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void;
|
|
562
562
|
|
|
563
|
-
/**
|
|
564
|
-
* Collect all materials from an object and its children
|
|
565
|
-
* @internal
|
|
566
|
-
*/
|
|
567
|
-
function collectMaterials(object: Object3D, materials: Set<Material>): void {
|
|
568
|
-
const obj = object as Object3D & { material?: Material | Material[] };
|
|
569
|
-
if (obj.material) {
|
|
570
|
-
if (Array.isArray(obj.material)) {
|
|
571
|
-
obj.material.forEach(mat => materials.add(mat));
|
|
572
|
-
} else {
|
|
573
|
-
materials.add(obj.material);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// For Groups, collect materials from children too
|
|
578
|
-
if (object.type === "Group") {
|
|
579
|
-
object.children.forEach(child => collectMaterials(child, materials));
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
563
|
|
|
583
564
|
/**
|
|
584
565
|
* Find property block by checking this object and parent (if parent is a Group).
|
|
@@ -626,7 +607,7 @@ const beforeRenderListTransparentChangedTransmission = new WeakMap<Object3D, num
|
|
|
626
607
|
* @internal
|
|
627
608
|
*/
|
|
628
609
|
const onBeforeRenderListPush = function (this: Object3D, _object: Object3D, _geometry: BufferGeometry, material: Material, _group: Group) {
|
|
629
|
-
const block =
|
|
610
|
+
const block = findPropertyBlockAndOwner(this)?.block;
|
|
630
611
|
if (!block) {
|
|
631
612
|
return;
|
|
632
613
|
}
|
|
@@ -248,6 +248,7 @@ export class OwnershipModel {
|
|
|
248
248
|
private _gainSubscription: (response: GainedOwnershipBroadcastResponse) => void;
|
|
249
249
|
private _lostSubscription: (guid: string) => void;
|
|
250
250
|
private _hasOwnerResponse: (response: OwnershipResponse) => void;
|
|
251
|
+
private _pendingOwnershipResolve: ((value: boolean) => void) | null = null;
|
|
251
252
|
|
|
252
253
|
constructor(connection: NetworkConnection, guid: string) {
|
|
253
254
|
this.connection = connection;
|
|
@@ -291,7 +292,6 @@ export class OwnershipModel {
|
|
|
291
292
|
}
|
|
292
293
|
|
|
293
294
|
private waitForHasOwnershipRequestResponse(res: OwnershipResponse) {
|
|
294
|
-
// console.log(res);
|
|
295
295
|
if (res.guid === this.guid) {
|
|
296
296
|
if (this._isWaitingForOwnershipResponseCallback) {
|
|
297
297
|
this.connection.stopListen(OwnershipEvent.ResponseHasOwner, this._isWaitingForOwnershipResponseCallback);
|
|
@@ -301,47 +301,58 @@ export class OwnershipModel {
|
|
|
301
301
|
if (!res.value) {
|
|
302
302
|
if (debugOwner)
|
|
303
303
|
console.log("request ownership", this.guid)
|
|
304
|
-
this.
|
|
304
|
+
this.connection.send(OwnershipEvent.RequestOwnership, { guid: this.guid });
|
|
305
305
|
}
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
|
|
310
|
+
|
|
310
311
|
/**
|
|
311
312
|
* Requests ownership and waits asynchronously until ownership is granted or timeout occurs.
|
|
312
313
|
* @returns Promise that resolves with this OwnershipModel when ownership is gained
|
|
313
314
|
* @throws Rejects with "Timeout" if ownership is not gained within ~1 second
|
|
314
315
|
* @example
|
|
315
316
|
* ```ts
|
|
316
|
-
*
|
|
317
|
-
*
|
|
317
|
+
* const owned = await ownership.requestOwnership();
|
|
318
|
+
* if (owned) {
|
|
318
319
|
* // Ownership granted, safe to modify object
|
|
319
|
-
* } catch(e) {
|
|
320
|
-
* console.warn("Could not gain ownership:", e);
|
|
321
320
|
* }
|
|
322
321
|
* ```
|
|
323
322
|
*/
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
323
|
+
/**
|
|
324
|
+
* Requests ownership of this object from the networking server.
|
|
325
|
+
* Returns a Promise that resolves with `true` when ownership is granted, or `false` on timeout/failure.
|
|
326
|
+
* If ownership is already held, resolves immediately with `true`.
|
|
327
|
+
* @returns Promise that resolves with `true` if ownership was gained, `false` otherwise
|
|
328
|
+
*/
|
|
329
|
+
public requestOwnership(): Promise<boolean> {
|
|
330
|
+
if (this._hasOwnership) return Promise.resolve(true);
|
|
331
|
+
|
|
332
|
+
// Cancel any previous pending request
|
|
333
|
+
this._pendingOwnershipResolve?.(false);
|
|
334
|
+
this._pendingOwnershipResolve = null;
|
|
335
|
+
|
|
336
|
+
if (debugOwner) console.log("Request ownership", this.guid);
|
|
337
|
+
this.connection.send(OwnershipEvent.RequestOwnership, { guid: this.guid });
|
|
338
|
+
|
|
339
|
+
return new Promise((resolve) => {
|
|
340
|
+
this._pendingOwnershipResolve = resolve;
|
|
341
|
+
|
|
342
|
+
// Timeout after ~2 seconds
|
|
343
|
+
setTimeout(() => {
|
|
344
|
+
if (this._pendingOwnershipResolve === resolve) {
|
|
345
|
+
this._pendingOwnershipResolve = null;
|
|
346
|
+
resolve(false);
|
|
347
|
+
}
|
|
348
|
+
}, 2000);
|
|
336
349
|
});
|
|
337
350
|
}
|
|
338
351
|
|
|
339
352
|
/**
|
|
340
|
-
*
|
|
341
|
-
* Ownership may not be granted immediately - check `hasOwnership` property or use `requestOwnershipAsync()`.
|
|
342
|
-
* @returns this OwnershipModel instance for method chaining
|
|
353
|
+
* @deprecated Use `requestOwnership()` instead for a more reliable way to gain ownership
|
|
343
354
|
*/
|
|
344
|
-
public
|
|
355
|
+
public async requestOwnershipAsync(): Promise<OwnershipModel> {
|
|
345
356
|
if (debugOwner) console.log("Request ownership", this.guid);
|
|
346
357
|
this.connection.send(OwnershipEvent.RequestOwnership, { guid: this.guid });
|
|
347
358
|
return this;
|
|
@@ -374,16 +385,23 @@ export class OwnershipModel {
|
|
|
374
385
|
this.connection.stopListen(OwnershipEvent.ResponseHasOwner, this._isWaitingForOwnershipResponseCallback);
|
|
375
386
|
this._isWaitingForOwnershipResponseCallback = null;
|
|
376
387
|
}
|
|
388
|
+
// Clean up pending ownership request
|
|
389
|
+
this._pendingOwnershipResolve?.(false);
|
|
390
|
+
this._pendingOwnershipResolve = null;
|
|
377
391
|
}
|
|
378
392
|
|
|
379
393
|
private onGainedOwnership(res: GainedOwnershipBroadcastResponse) {
|
|
380
394
|
if (res.guid === this.guid) {
|
|
381
395
|
this._isOwned = true;
|
|
382
|
-
// console.log(res.owner, connection.connectionId)
|
|
383
396
|
if (this.connection.connectionId === res.owner) {
|
|
384
397
|
if (debugOwner)
|
|
385
398
|
console.log("GAINED OWNERSHIP", this.guid)
|
|
386
399
|
this._hasOwnership = true;
|
|
400
|
+
// Resolve pending ownership request
|
|
401
|
+
if (this._pendingOwnershipResolve) {
|
|
402
|
+
this._pendingOwnershipResolve(true);
|
|
403
|
+
this._pendingOwnershipResolve = null;
|
|
404
|
+
}
|
|
387
405
|
}
|
|
388
406
|
else this._hasOwnership = false;
|
|
389
407
|
}
|
|
@@ -394,6 +412,11 @@ export class OwnershipModel {
|
|
|
394
412
|
console.log("LOST OWNERSHIP", this.guid)
|
|
395
413
|
this._hasOwnership = false;
|
|
396
414
|
this._isOwned = false;
|
|
415
|
+
// Resolve pending ownership request as failed
|
|
416
|
+
if (this._pendingOwnershipResolve) {
|
|
417
|
+
this._pendingOwnershipResolve(false);
|
|
418
|
+
this._pendingOwnershipResolve = null;
|
|
419
|
+
}
|
|
397
420
|
}
|
|
398
421
|
}
|
|
399
422
|
}
|