@needle-tools/engine 2.58.3-pre → 2.59.0-pre
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 +16 -0
- package/dist/needle-engine.js +8820 -8736
- package/dist/needle-engine.umd.cjs +195 -195
- package/lib/engine/debug/debug_overlay.js +11 -0
- package/lib/engine/debug/debug_overlay.js.map +1 -1
- package/lib/engine/engine_addressables.js +8 -5
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_element.js +2 -1
- package/lib/engine/engine_element.js.map +1 -1
- package/lib/engine/engine_gameobject.d.ts +3 -1
- package/lib/engine/engine_gameobject.js +31 -2
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_hot_reload.d.ts +2 -0
- package/lib/engine/engine_hot_reload.js +15 -2
- package/lib/engine/engine_hot_reload.js.map +1 -1
- package/lib/engine/engine_input.d.ts +2 -0
- package/lib/engine/engine_input.js +16 -0
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +1 -1
- package/lib/engine/engine_utils.js +5 -2
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_gameobject_data.js +6 -4
- package/lib/engine/extensions/NEEDLE_gameobject_data.js.map +1 -1
- package/lib/engine-components/Animator.js +10 -6
- package/lib/engine-components/Animator.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +1 -1
- package/lib/engine-components/AnimatorController.js +19 -12
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/Component.d.ts +1 -0
- package/lib/engine-components/Component.js +4 -1
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +7 -2
- package/lib/engine-components/OrbitControls.js +34 -15
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/ParticleSystem.d.ts +1 -0
- package/lib/engine-components/ParticleSystem.js +15 -2
- package/lib/engine-components/ParticleSystem.js.map +1 -1
- package/lib/engine-components/WebXR.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js +5 -2
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +1 -0
- package/lib/engine-components/timeline/TimelineTracks.js +14 -3
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/engine-components/ui/EventSystem.d.ts +10 -5
- package/lib/engine-components/ui/EventSystem.js +45 -41
- package/lib/engine-components/ui/EventSystem.js.map +1 -1
- package/lib/engine-components/ui/Raycaster.js +2 -2
- package/lib/engine-components/ui/Raycaster.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/engine/debug/debug_overlay.ts +17 -5
- package/src/engine/engine_addressables.ts +8 -5
- package/src/engine/engine_element.ts +2 -1
- package/src/engine/engine_gameobject.ts +34 -3
- package/src/engine/engine_hot_reload.ts +16 -5
- package/src/engine/engine_input.ts +11 -0
- package/src/engine/engine_utils.ts +4 -3
- package/src/engine/extensions/NEEDLE_gameobject_data.ts +11 -6
- package/src/engine-components/Animator.ts +12 -9
- package/src/engine-components/AnimatorController.ts +20 -13
- package/src/engine-components/Component.ts +5 -1
- package/src/engine-components/OrbitControls.ts +44 -19
- package/src/engine-components/ParticleSystem.ts +18 -3
- package/src/engine-components/WebXR.ts +1 -0
- package/src/engine-components/timeline/PlayableDirector.ts +4 -2
- package/src/engine-components/timeline/TimelineTracks.ts +15 -4
- package/src/engine-components/ui/EventSystem.ts +48 -42
- package/src/engine-components/ui/Raycaster.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.59.0-pre",
|
|
4
4
|
"description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
|
|
5
5
|
"main": "dist/needle-engine.umd.cjs",
|
|
6
6
|
"module": "dist/needle-engine.js",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"peerjs": "1.3.2",
|
|
48
48
|
"simplex-noise": "^4.0.1",
|
|
49
49
|
"stats.js": "^0.17.0",
|
|
50
|
-
"three": "npm:@needle-tools/three@^0.146.
|
|
50
|
+
"three": "npm:@needle-tools/three@^0.146.4",
|
|
51
51
|
"three-mesh-ui": "^6.4.5",
|
|
52
52
|
"three.quarks": "^0.7.3",
|
|
53
53
|
"uuid": "^9.0.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@babel/runtime": "^7.16.0",
|
|
58
58
|
"@luncheon/esbuild-plugin-gzip": "^0.1.0",
|
|
59
|
-
"@needle-tools/gltf-transform-extensions": "^0.10.
|
|
59
|
+
"@needle-tools/gltf-transform-extensions": "^0.10.8-pre",
|
|
60
60
|
"@needle-tools/needle-component-compiler": "1.9.3",
|
|
61
61
|
"@needle-tools/helper": "^0.2.1-pre",
|
|
62
62
|
"@types/three": "0.146.0",
|
|
@@ -30,6 +30,7 @@ export function makeErrorsVisibleForDevelopment() {
|
|
|
30
30
|
const error = console.error;
|
|
31
31
|
console.error = (...args: any[]) => {
|
|
32
32
|
error.apply(console, args);
|
|
33
|
+
onParseError(args);
|
|
33
34
|
addLog(LogType.Error, args, null, null);
|
|
34
35
|
onReceivedError();
|
|
35
36
|
};
|
|
@@ -48,20 +49,31 @@ export function makeErrorsVisibleForDevelopment() {
|
|
|
48
49
|
|
|
49
50
|
let errorCount = 0;
|
|
50
51
|
|
|
51
|
-
function onReceivedError(){
|
|
52
|
+
function onReceivedError() {
|
|
52
53
|
errorCount += 1;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
function onParseError(args: Array<any>) {
|
|
57
|
+
if (Array.isArray(args)) {
|
|
58
|
+
for (let i = 0; i < args.length; i++) {
|
|
59
|
+
const arg = args[i];
|
|
60
|
+
if (typeof arg === "string" && arg.startsWith("THREE.PropertyBinding: Trying to update node for track:")) {
|
|
61
|
+
args[i] = "Some animated objects couldn't be found: see console for details";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
55
67
|
export function addLog(type: LogType, message: string | any[], _file?: string | null, _line?: number | null) {
|
|
56
|
-
if(hide) return;
|
|
68
|
+
if (hide) return;
|
|
57
69
|
const context = ContextRegistry.Current;
|
|
58
70
|
const domElement = context?.domElement ?? document.querySelector("needle-engine");
|
|
59
71
|
if (!domElement) return;
|
|
60
72
|
if (Array.isArray(message)) {
|
|
61
73
|
let newMessage = "";
|
|
62
|
-
for(let i = 0; i < message.length; i++) {
|
|
63
|
-
if(typeof message[i] === "object") continue;
|
|
64
|
-
if(i > 0) newMessage += " ";
|
|
74
|
+
for (let i = 0; i < message.length; i++) {
|
|
75
|
+
if (typeof message[i] === "object") continue;
|
|
76
|
+
if (i > 0) newMessage += " ";
|
|
65
77
|
newMessage += message[i];
|
|
66
78
|
}
|
|
67
79
|
message = newMessage;
|
|
@@ -8,7 +8,7 @@ import { registerPrefabProvider, syncInstantiate } from "./engine_networking_ins
|
|
|
8
8
|
import { download, hash } from "./engine_web_api";
|
|
9
9
|
import { getLoader } from "./engine_gltf";
|
|
10
10
|
import { SourceIdentifier } from "./engine_types";
|
|
11
|
-
import { destroy, instantiate, InstantiateOptions } from "./engine_gameobject";
|
|
11
|
+
import { destroy, instantiate, InstantiateOptions, isDestroyed } from "./engine_gameobject";
|
|
12
12
|
import { IGameObject } from "./engine_types";
|
|
13
13
|
|
|
14
14
|
const debug = getParam("debugaddressables");
|
|
@@ -111,7 +111,7 @@ export class AssetReference {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
private get mustLoad() {
|
|
114
|
-
return !this.asset || this.asset.__destroyed === true;
|
|
114
|
+
return !this.asset || this.asset.__destroyed === true || isDestroyed(this.asset) === true;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
isLoaded() { return this._rawBinary || this.asset !== undefined }
|
|
@@ -121,10 +121,11 @@ export class AssetReference {
|
|
|
121
121
|
if (debug) console.log("Unload", this.asset);
|
|
122
122
|
// TODO: we need a way to remove objects from the context (including components) without actually "destroying" them
|
|
123
123
|
if (this.asset.scene)
|
|
124
|
-
destroy(this.asset.scene);
|
|
125
|
-
else destroy(this.asset);
|
|
124
|
+
destroy(this.asset.scene, true);
|
|
125
|
+
else destroy(this.asset, true);
|
|
126
126
|
}
|
|
127
127
|
this.asset = null;
|
|
128
|
+
this._rawBinary = undefined;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
async preload(): Promise<ArrayBuffer | null> {
|
|
@@ -142,7 +143,9 @@ export class AssetReference {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
async loadAssetAsync(prog?: ProgressCallback | null) {
|
|
145
|
-
if (
|
|
146
|
+
if (debug)
|
|
147
|
+
console.log("loadAssetAsync", this.uri);
|
|
148
|
+
if (!this.mustLoad) return this.asset;
|
|
146
149
|
if (prog)
|
|
147
150
|
this._progressListeners.push(prog);
|
|
148
151
|
if (this._loading !== undefined) {
|
|
@@ -404,7 +404,8 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
|
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
window.customElements.
|
|
407
|
+
if (!window.customElements.get(htmlTagName))
|
|
408
|
+
window.customElements.define(htmlTagName, EngineElement);
|
|
408
409
|
|
|
409
410
|
|
|
410
411
|
|
|
@@ -51,8 +51,8 @@ export class InstantiateOptions {
|
|
|
51
51
|
// main.processStart(Context.Current, go);
|
|
52
52
|
// }
|
|
53
53
|
|
|
54
|
-
const $isActive = Symbol("isActive");
|
|
55
54
|
|
|
55
|
+
const $isActive = Symbol("isActive");
|
|
56
56
|
export function isActiveSelf(go: Object3D): boolean {
|
|
57
57
|
const visible = go.visible || isUsingInstancing(go);
|
|
58
58
|
if (go[$isActive] === undefined) {
|
|
@@ -86,7 +86,19 @@ export function findByGuid(guid: string, hierarchy: THREE.Object3D): GameObject
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
const $isDestroyed = Symbol("isDestroyed");
|
|
90
|
+
export function isDestroyed(go: Object3D): boolean {
|
|
91
|
+
return go[$isDestroyed];
|
|
92
|
+
}
|
|
93
|
+
export function setDestroyed(go: Object3D, value: boolean) {
|
|
94
|
+
go[$isDestroyed] = value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function destroy(instance: Object3D | Component, recursive: boolean = true, dispose: boolean = false) {
|
|
98
|
+
internalDestroy(instance, recursive, dispose, true);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function internalDestroy(instance: Object3D | Component, recursive: boolean = true, dispose: boolean = false, isRoot: boolean = true) {
|
|
90
102
|
const comp = instance as Component;
|
|
91
103
|
if (comp.isComponent) {
|
|
92
104
|
comp.__internalDisable();
|
|
@@ -94,12 +106,17 @@ export function destroy(instance: Object3D | Component, recursive: boolean = tru
|
|
|
94
106
|
return;
|
|
95
107
|
}
|
|
96
108
|
|
|
109
|
+
|
|
97
110
|
const obj = instance as GameObject;
|
|
111
|
+
if (dispose) disposeObject(obj);
|
|
112
|
+
setDestroyed(obj, true);
|
|
113
|
+
|
|
114
|
+
|
|
98
115
|
if (debug) console.log(obj);
|
|
99
116
|
|
|
100
117
|
if (recursive && obj.children) {
|
|
101
118
|
for (const ch of obj.children) {
|
|
102
|
-
|
|
119
|
+
internalDestroy(ch, recursive, false, false);
|
|
103
120
|
}
|
|
104
121
|
}
|
|
105
122
|
|
|
@@ -125,6 +142,20 @@ export function destroy(instance: Object3D | Component, recursive: boolean = tru
|
|
|
125
142
|
obj.removeFromParent();
|
|
126
143
|
}
|
|
127
144
|
|
|
145
|
+
function disposeObject(obj: Object3D) {
|
|
146
|
+
if (!obj) return;
|
|
147
|
+
const mesh = obj as THREE.Mesh;
|
|
148
|
+
if (mesh.geometry) {
|
|
149
|
+
mesh.geometry.dispose();
|
|
150
|
+
}
|
|
151
|
+
if (mesh.material) {
|
|
152
|
+
if (Array.isArray(mesh.material)) {
|
|
153
|
+
mesh.material.forEach(m => m.dispose());
|
|
154
|
+
} else {
|
|
155
|
+
mesh.material.dispose();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
128
159
|
|
|
129
160
|
export function foreachComponent(instance: THREE.Object3D, cb: (comp: Component) => any, recursive: boolean = true): any {
|
|
130
161
|
if (!instance) return;
|
|
@@ -3,6 +3,7 @@ import { TypeStore } from "./engine_typestore";
|
|
|
3
3
|
import { addScriptToArrays, removeScriptFromContext } from "./engine_mainloop_utils"
|
|
4
4
|
import { showBalloonWarning } from "./debug/debug";
|
|
5
5
|
import { getParam } from "./engine_utils";
|
|
6
|
+
import { addLog, LogType } from "./debug/debug_overlay";
|
|
6
7
|
|
|
7
8
|
const debug = getParam("debughotreload");
|
|
8
9
|
|
|
@@ -27,6 +28,11 @@ let isApplyingChanges = false;
|
|
|
27
28
|
|
|
28
29
|
const instances: Map<string, object[]> = new Map();
|
|
29
30
|
|
|
31
|
+
/** true during hot reload, can be used to modify behaviour in onEnable and onDisable */
|
|
32
|
+
export function isHotReloading() {
|
|
33
|
+
return isApplyingChanges;
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
export function register(instance: object) {
|
|
31
37
|
if (isApplyingChanges) return;
|
|
32
38
|
const type = instance.constructor;
|
|
@@ -77,9 +83,10 @@ function reloadPageOnHotReloadError() {
|
|
|
77
83
|
|
|
78
84
|
export function applyChanges(newModule): boolean {
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
if (debug)
|
|
87
|
+
console.log("Hot Reload - apply changes");
|
|
81
88
|
|
|
82
|
-
|
|
89
|
+
reloadPageOnHotReloadError();
|
|
83
90
|
|
|
84
91
|
// console.dir(newModule);
|
|
85
92
|
|
|
@@ -88,14 +95,17 @@ export function applyChanges(newModule): boolean {
|
|
|
88
95
|
isApplyingChanges = true;
|
|
89
96
|
|
|
90
97
|
const typeToUpdate = TypeStore.get(key);
|
|
91
|
-
if (!typeToUpdate)
|
|
92
|
-
{
|
|
98
|
+
if (!typeToUpdate) {
|
|
93
99
|
continue;
|
|
94
100
|
}
|
|
95
101
|
const newType = newModule[key];
|
|
96
102
|
const instancesOfType = instances.get(newType.name);
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
let hotReloadMessage = "[Needle Engine] Updating type: " + key;
|
|
105
|
+
let typesCount = instancesOfType?.length ?? -1;
|
|
106
|
+
if (typesCount > 0) hotReloadMessage += " x" + typesCount;
|
|
107
|
+
else hotReloadMessage += " - no instances";
|
|
108
|
+
console.log(hotReloadMessage);
|
|
99
109
|
|
|
100
110
|
// Update prototype (methods and properties)
|
|
101
111
|
const previousMethods = Object.getOwnPropertyNames(typeToUpdate.prototype);
|
|
@@ -177,6 +187,7 @@ export function applyChanges(newModule): boolean {
|
|
|
177
187
|
}
|
|
178
188
|
finally {
|
|
179
189
|
isApplyingChanges = false;
|
|
190
|
+
addLog(LogType.Log, "Script changes applied (HMR)")
|
|
180
191
|
}
|
|
181
192
|
}
|
|
182
193
|
|
|
@@ -80,6 +80,7 @@ export class Input extends EventTarget {
|
|
|
80
80
|
this.context.domElement.style.cursor = "default";
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/** how many pointers are currently pressed */
|
|
83
84
|
getPointerPressedCount(): number {
|
|
84
85
|
let count = 0;
|
|
85
86
|
for (let i = 0; i < this._pointerPressed.length; i++) {
|
|
@@ -340,6 +341,13 @@ export class Input extends EventTarget {
|
|
|
340
341
|
this._mouseWheelDeltaY[i] = 0;
|
|
341
342
|
}
|
|
342
343
|
|
|
344
|
+
private canReceiveInput(evt : Event) {
|
|
345
|
+
// If the user has HTML objects ontop of the canvas
|
|
346
|
+
if(evt.target instanceof HTMLCanvasElement) return true;
|
|
347
|
+
const css = window.getComputedStyle(evt.target as HTMLElement);
|
|
348
|
+
if(css.pointerEvents === "all") return false;
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
343
351
|
|
|
344
352
|
private keysPressed: { [key: number]: { pressed: boolean, frame: number, startFrame: number, key: string } } = {};
|
|
345
353
|
|
|
@@ -372,6 +380,7 @@ export class Input extends EventTarget {
|
|
|
372
380
|
}
|
|
373
381
|
|
|
374
382
|
private onMouseWheel(evt: WheelEvent) {
|
|
383
|
+
if(this.canReceiveInput(evt) === false) return;
|
|
375
384
|
if (this._mouseWheelDeltaY.length <= 0) this._mouseWheelDeltaY.push(0);
|
|
376
385
|
if (this._mouseWheelChanged.length <= 0) this._mouseWheelChanged.push(false);
|
|
377
386
|
this._mouseWheelChanged[0] = true;
|
|
@@ -381,6 +390,7 @@ export class Input extends EventTarget {
|
|
|
381
390
|
|
|
382
391
|
private onTouchStart(evt: TouchEvent) {
|
|
383
392
|
if (evt.changedTouches.length <= 0) return;
|
|
393
|
+
if(this.canReceiveInput(evt) === false) return;
|
|
384
394
|
for (let i = 0; i < evt.changedTouches.length; i++) {
|
|
385
395
|
const touch = evt.changedTouches[i];
|
|
386
396
|
const id = this.getPointerIndex(touch.identifier)
|
|
@@ -415,6 +425,7 @@ export class Input extends EventTarget {
|
|
|
415
425
|
|
|
416
426
|
private onMouseDown(evt: MouseEvent) {
|
|
417
427
|
if (evt.defaultPrevented) return;
|
|
428
|
+
if(this.canReceiveInput(evt) === false) return;
|
|
418
429
|
let i = evt.button;
|
|
419
430
|
this.onDown({ button: i, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, source: evt });
|
|
420
431
|
}
|
|
@@ -80,12 +80,13 @@ export function setParam(paramName: string, paramValue: string): void {
|
|
|
80
80
|
document.location.search = urlParams.toString();
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
export function setParamWithoutReload(paramName: string, paramValue: string, appendHistory = true): void {
|
|
83
|
+
export function setParamWithoutReload(paramName: string, paramValue: string | null, appendHistory = true): void {
|
|
84
84
|
const urlParams = getUrlParams();
|
|
85
85
|
if (urlParams.has(paramName)) {
|
|
86
|
-
urlParams.
|
|
86
|
+
if(paramValue === null) urlParams.delete(paramName);
|
|
87
|
+
else urlParams.set(paramName, paramValue);
|
|
87
88
|
}
|
|
88
|
-
else
|
|
89
|
+
else if(paramValue !== null)
|
|
89
90
|
urlParams.append(paramName, paramValue);
|
|
90
91
|
if (appendHistory) pushState(paramName, urlParams);
|
|
91
92
|
else setState(paramName, urlParams);
|
|
@@ -46,8 +46,7 @@ export class NEEDLE_gameobject_data implements GLTFLoaderPlugin {
|
|
|
46
46
|
const node = this.parser.json.nodes[index];
|
|
47
47
|
if (node && node.extensions) {
|
|
48
48
|
const ext = node.extensions[EXTENSION_NAME];
|
|
49
|
-
if (ext)
|
|
50
|
-
{
|
|
49
|
+
if (ext) {
|
|
51
50
|
const p = this.findAndApplyExtensionData(index, ext);
|
|
52
51
|
promises.push(p);
|
|
53
52
|
}
|
|
@@ -65,13 +64,19 @@ export class NEEDLE_gameobject_data implements GLTFLoaderPlugin {
|
|
|
65
64
|
|
|
66
65
|
|
|
67
66
|
private applyExtensionData(node: Object3D, ext: GameObjectData) {
|
|
67
|
+
if (ext.layers === undefined) ext.layers = 0;
|
|
68
68
|
node.userData.layer = ext.layers;
|
|
69
69
|
node.layers.disableAll();
|
|
70
70
|
node.layers.set(ext.layers);
|
|
71
|
-
|
|
72
|
-
node.userData.
|
|
73
|
-
|
|
74
|
-
node.
|
|
71
|
+
|
|
72
|
+
node.userData.tag = ext.tag ?? "none";
|
|
73
|
+
|
|
74
|
+
node.userData.hideFlags = ext.hideFlags ?? 0;
|
|
75
|
+
|
|
76
|
+
node.userData.static = ext.static ?? false;
|
|
77
|
+
|
|
78
|
+
node.visible = ext.activeSelf ?? true;
|
|
79
|
+
|
|
75
80
|
node["guid"] = ext.guid;
|
|
76
81
|
// console.log(node.name, ext.activeSelf, node);
|
|
77
82
|
}
|
|
@@ -4,7 +4,7 @@ import { LoopOnce, AnimationActionLoopStyles, AnimationAction } from "three";
|
|
|
4
4
|
import { getParam, deepClone } from "../engine/engine_utils";
|
|
5
5
|
import { AnimatorControllerModel } from "../engine/extensions/NEEDLE_animator_controller_model";
|
|
6
6
|
import { AnimatorController } from "./AnimatorController";
|
|
7
|
-
import {
|
|
7
|
+
import { serializable } from "../engine/engine_serialization_decorator";
|
|
8
8
|
import { Mathf } from "../engine/engine_math";
|
|
9
9
|
|
|
10
10
|
const debug = getParam("debuganimator");
|
|
@@ -39,7 +39,7 @@ export class Animator extends Behaviour {
|
|
|
39
39
|
}
|
|
40
40
|
if (val) {
|
|
41
41
|
if (!(val instanceof AnimatorController)) {
|
|
42
|
-
if(debug) console.log("Assign animator controller", val, this);
|
|
42
|
+
if (debug) console.log("Assign animator controller", val, this);
|
|
43
43
|
this._animatorController = new AnimatorController(val);
|
|
44
44
|
}
|
|
45
45
|
else this._animatorController = val;
|
|
@@ -50,7 +50,7 @@ export class Animator extends Behaviour {
|
|
|
50
50
|
return this._animatorController;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
Play(name: string | number, layer: number = -1, normalizedTime: number = Number.NEGATIVE_INFINITY, transitionDurationInSec:number = 0) {
|
|
53
|
+
Play(name: string | number, layer: number = -1, normalizedTime: number = Number.NEGATIVE_INFINITY, transitionDurationInSec: number = 0) {
|
|
54
54
|
this.runtimeAnimatorController?.Play(name, layer, normalizedTime, transitionDurationInSec);
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -125,12 +125,15 @@ export class Animator extends Behaviour {
|
|
|
125
125
|
if (!this.gameObject) return;
|
|
126
126
|
if (this.runtimeAnimatorController) {
|
|
127
127
|
const clone = this.runtimeAnimatorController.clone();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
if (clone) {
|
|
129
|
+
console.assert(this.runtimeAnimatorController !== clone);
|
|
130
|
+
this.runtimeAnimatorController = clone;
|
|
131
|
+
console.assert(this.runtimeAnimatorController === clone);
|
|
132
|
+
this.runtimeAnimatorController.bind(this);
|
|
133
|
+
this.runtimeAnimatorController.SetSpeed(this.speed);
|
|
134
|
+
this.runtimeAnimatorController.normalizedStartOffset = this.normalizedStartOffset;
|
|
135
|
+
}
|
|
136
|
+
else console.warn("Could not clone animator controller", this.runtimeAnimatorController);
|
|
134
137
|
}
|
|
135
138
|
}
|
|
136
139
|
|
|
@@ -39,44 +39,44 @@ export class AnimatorController {
|
|
|
39
39
|
|
|
40
40
|
SetBool(name: string | number, value: boolean) {
|
|
41
41
|
const key = typeof name === "string" ? "name" : "hash";
|
|
42
|
-
return this.model?.parameters
|
|
42
|
+
return this.model?.parameters?.filter(p => p[key] === name).forEach(p => p.value = value);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
GetBool(name: string | number): boolean {
|
|
46
46
|
const key = typeof name === "string" ? "name" : "hash";
|
|
47
|
-
return this.model?.parameters
|
|
47
|
+
return this.model?.parameters?.find(p => p[key] === name)?.value as boolean ?? false;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
SetFloat(name: string | number, val: number) {
|
|
51
51
|
const key = typeof name === "string" ? "name" : "hash";
|
|
52
|
-
return this.model?.parameters
|
|
52
|
+
return this.model?.parameters?.filter(p => p[key] === name).forEach(p => p.value = val);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
GetFloat(name: string | number): number {
|
|
56
56
|
const key = typeof name === "string" ? "name" : "hash";
|
|
57
|
-
return this.model?.parameters
|
|
57
|
+
return this.model?.parameters?.find(p => p[key] === name)?.value as number ?? 0;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
SetInteger(name: string | number, val: number) {
|
|
61
61
|
const key = typeof name === "string" ? "name" : "hash";
|
|
62
|
-
return this.model?.parameters
|
|
62
|
+
return this.model?.parameters?.filter(p => p[key] === name).forEach(p => p.value = val);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
GetInteger(name: string | number): number {
|
|
66
66
|
const key = typeof name === "string" ? "name" : "hash";
|
|
67
|
-
return this.model?.parameters
|
|
67
|
+
return this.model?.parameters?.find(p => p[key] === name)?.value as number ?? 0;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
SetTrigger(name: string | number) {
|
|
71
71
|
if (debug)
|
|
72
72
|
console.log("SET TRIGGER", name);
|
|
73
73
|
const key = typeof name === "string" ? "name" : "hash";
|
|
74
|
-
return this.model?.parameters
|
|
74
|
+
return this.model?.parameters?.filter(p => p[key] === name).forEach(p => p.value = true);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
ResetTrigger(name: string | number) {
|
|
78
78
|
const key = typeof name === "string" ? "name" : "hash";
|
|
79
|
-
return this.model?.parameters
|
|
79
|
+
return this.model?.parameters?.filter(p => p[key] === name).forEach(p => p.value = false);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
IsInTransition(): boolean {
|
|
@@ -89,9 +89,11 @@ export class AnimatorController {
|
|
|
89
89
|
|
|
90
90
|
FindState(name: string | undefined | null): State | null {
|
|
91
91
|
if (!name) return null;
|
|
92
|
-
|
|
93
|
-
for (const
|
|
94
|
-
|
|
92
|
+
if (Array.isArray(this.model.layers)) {
|
|
93
|
+
for (const layer of this.model.layers) {
|
|
94
|
+
for (const state of layer.stateMachine.states) {
|
|
95
|
+
if (state.name === name) return state;
|
|
96
|
+
}
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
return null;
|
|
@@ -117,6 +119,10 @@ export class AnimatorController {
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
clone() {
|
|
122
|
+
if (typeof this.model === "string") {
|
|
123
|
+
console.warn("AnimatorController has not been resolved, can not create model from string", this.model);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
120
126
|
// clone runtime controller but dont clone clip or action
|
|
121
127
|
const clonedModel = deepClone(this.model, (_owner, _key, _value) => {
|
|
122
128
|
if (_value === null || _value === undefined) return true;
|
|
@@ -435,6 +441,7 @@ export class AnimatorController {
|
|
|
435
441
|
}
|
|
436
442
|
|
|
437
443
|
private createActions(_animator: Animator) {
|
|
444
|
+
// console.trace(this.model, _animator);
|
|
438
445
|
for (const layer of this.model.layers) {
|
|
439
446
|
const sm = layer.stateMachine;
|
|
440
447
|
for (let index = 0; index < sm.states.length; index++) {
|
|
@@ -444,9 +451,9 @@ export class AnimatorController {
|
|
|
444
451
|
if (!state.transitions) {
|
|
445
452
|
state.transitions = [];
|
|
446
453
|
}
|
|
447
|
-
for(const t of state.transitions) {
|
|
454
|
+
for (const t of state.transitions) {
|
|
448
455
|
// can happen if conditions are empty in blender - the exporter seems to skip empty arrays
|
|
449
|
-
if(!t.conditions) t.conditions = [];
|
|
456
|
+
if (!t.conditions) t.conditions = [];
|
|
450
457
|
}
|
|
451
458
|
|
|
452
459
|
// ensure we have a motion even if none was exported
|
|
@@ -8,7 +8,7 @@ import { Object3D } from "three";
|
|
|
8
8
|
import { syncDestroy, syncInstantiate } from "../engine/engine_networking_instantiate";
|
|
9
9
|
import { ConstructorConcrete, SourceIdentifier, IComponent, IGameObject, Constructor, GuidsMap, UIDProvider, Collision, ICollider } from "../engine/engine_types";
|
|
10
10
|
import { addNewComponent, destroyComponentInstance, findObjectOfType, findObjectsOfType, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, moveComponentInstance, removeComponent } from "../engine/engine_components";
|
|
11
|
-
import { findByGuid, destroy, InstantiateOptions, instantiate, HideFlags, foreachComponent, markAsInstancedRendered, isActiveInHierarchy, isActiveSelf, isUsingInstancing, setActive } from "../engine/engine_gameobject";
|
|
11
|
+
import { findByGuid, destroy, InstantiateOptions, instantiate, HideFlags, foreachComponent, markAsInstancedRendered, isActiveInHierarchy, isActiveSelf, isUsingInstancing, setActive, isDestroyed } from "../engine/engine_gameobject";
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
// export interface ISerializationCallbackReceiver {
|
|
@@ -23,6 +23,10 @@ abstract class GameObject extends THREE.Object3D implements THREE.Object3D, IGam
|
|
|
23
23
|
|
|
24
24
|
guid: string | undefined;
|
|
25
25
|
|
|
26
|
+
public static isDestroyed(go: THREE.Object3D): boolean {
|
|
27
|
+
return isDestroyed(go);
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
public static setActive(go: THREE.Object3D, active: boolean, processStart: boolean = true, setVisible: boolean = true) {
|
|
27
31
|
if (!go) return;
|
|
28
32
|
setActive(go, active, setVisible);
|
|
@@ -8,6 +8,8 @@ import { getParam, isMobileDevice } from "../engine/engine_utils";
|
|
|
8
8
|
|
|
9
9
|
import { Box3, Object3D, PerspectiveCamera, Vector2, Vector3 } from "three";
|
|
10
10
|
import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
|
11
|
+
import { EventSystem, EventSystemEvents } from "./ui/EventSystem";
|
|
12
|
+
import { transformWithEsbuild } from "vite";
|
|
11
13
|
|
|
12
14
|
const freeCam = getParam("freecam");
|
|
13
15
|
|
|
@@ -60,11 +62,40 @@ export class OrbitControls extends Behaviour {
|
|
|
60
62
|
private _enableTime: number = 0; // use to disable double click when double clicking on UI
|
|
61
63
|
private _startedListeningToKeyEvents: boolean = false;
|
|
62
64
|
|
|
65
|
+
private _eventSystem?: EventSystem;
|
|
66
|
+
private _afterHandleInputFn?: any;
|
|
67
|
+
|
|
68
|
+
targetElement: HTMLElement | null = null;
|
|
69
|
+
|
|
63
70
|
awake(): void {
|
|
64
71
|
this._lookTargetPosition = new Vector3();
|
|
65
72
|
this._startedListeningToKeyEvents = false;
|
|
66
73
|
}
|
|
67
74
|
|
|
75
|
+
start() {
|
|
76
|
+
if (this._controls) {
|
|
77
|
+
const camGo = GameObject.getComponent(this.gameObject, Camera);
|
|
78
|
+
if (camGo && !this.setFromTargetPosition()) {
|
|
79
|
+
if (this.debugLog)
|
|
80
|
+
console.log("NO TARGET");
|
|
81
|
+
const forward = new Vector3(0, 0, -1).applyMatrix4(camGo.cam.matrixWorld);
|
|
82
|
+
this.setTarget(forward, true);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.startCoroutine(this.startRaycastDelayed());
|
|
86
|
+
|
|
87
|
+
this._eventSystem = EventSystem.get(this.context) ?? undefined;
|
|
88
|
+
if (this._eventSystem) {
|
|
89
|
+
this._afterHandleInputFn = this.afterHandleInput.bind(this);
|
|
90
|
+
this._eventSystem.addEventListener(EventSystemEvents.AfterHandleInput, this._afterHandleInputFn!);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onDestroy() {
|
|
95
|
+
this._controls?.dispose();
|
|
96
|
+
this._eventSystem?.removeEventListener(EventSystemEvents.AfterHandleInput, this._afterHandleInputFn!);
|
|
97
|
+
}
|
|
98
|
+
|
|
68
99
|
onEnable() {
|
|
69
100
|
this._enableTime = this.context.time.time;
|
|
70
101
|
const camGo = GameObject.getComponent(this.gameObject, Camera);
|
|
@@ -73,7 +104,10 @@ export class OrbitControls extends Behaviour {
|
|
|
73
104
|
console.assert(cam !== null && cam !== undefined, "Missing camera", this);
|
|
74
105
|
if (cam)
|
|
75
106
|
this._cameraObject = cam;
|
|
76
|
-
|
|
107
|
+
// Using the parent if possible to make it possible to disable input on the canvas
|
|
108
|
+
// for having HTML content behind it and still receive input
|
|
109
|
+
const element = this.targetElement ?? this.context.domElement;
|
|
110
|
+
this._controls = new ThreeOrbitControls(cam!, element);
|
|
77
111
|
if (defaultKeys === undefined) defaultKeys = { ...this._controls.keys };
|
|
78
112
|
}
|
|
79
113
|
|
|
@@ -115,6 +149,7 @@ export class OrbitControls extends Behaviour {
|
|
|
115
149
|
this._controls.listenToKeyEvents(window.document.body);
|
|
116
150
|
}
|
|
117
151
|
}
|
|
152
|
+
|
|
118
153
|
}
|
|
119
154
|
|
|
120
155
|
onDisable() {
|
|
@@ -125,21 +160,11 @@ export class OrbitControls extends Behaviour {
|
|
|
125
160
|
}
|
|
126
161
|
}
|
|
127
162
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
start() {
|
|
133
|
-
if (this._controls) {
|
|
134
|
-
const camGo = GameObject.getComponent(this.gameObject, Camera);
|
|
135
|
-
if (camGo && !this.setFromTargetPosition()) {
|
|
136
|
-
if (this.debugLog)
|
|
137
|
-
console.log("NO TARGET");
|
|
138
|
-
const forward = new Vector3(0, 0, -1).applyMatrix4(camGo.cam.matrixWorld);
|
|
139
|
-
this.setTarget(forward, true);
|
|
140
|
-
}
|
|
163
|
+
private _shouldDisable : boolean = false;
|
|
164
|
+
private afterHandleInput() {
|
|
165
|
+
if (this._controls && this._eventSystem) {
|
|
166
|
+
this._shouldDisable = this._eventSystem.hasActiveUI;
|
|
141
167
|
}
|
|
142
|
-
this.startCoroutine(this.startRaycastDelayed());
|
|
143
168
|
}
|
|
144
169
|
|
|
145
170
|
// we need to wait one frame (when starting the scene for the very first time)
|
|
@@ -221,7 +246,7 @@ export class OrbitControls extends Behaviour {
|
|
|
221
246
|
if (this._controls && !this.context.isInXR) {
|
|
222
247
|
if (this.debugLog)
|
|
223
248
|
this._controls.domElement = this.context.renderer.domElement;
|
|
224
|
-
this._controls.enabled =
|
|
249
|
+
this._controls.enabled = !this._shouldDisable;
|
|
225
250
|
this._controls.update();
|
|
226
251
|
}
|
|
227
252
|
}
|
|
@@ -309,11 +334,11 @@ export class OrbitControls extends Behaviour {
|
|
|
309
334
|
for (const object of objects)
|
|
310
335
|
box.expandByObject(object);
|
|
311
336
|
|
|
312
|
-
box.getSize(
|
|
313
|
-
box.getCenter(
|
|
337
|
+
box.getSize(size);
|
|
338
|
+
box.getCenter(center);
|
|
314
339
|
|
|
315
340
|
const maxSize = Math.max(size.x, size.y, size.z);
|
|
316
|
-
const fitHeightDistance = maxSize / (2 * Math.atan(
|
|
341
|
+
const fitHeightDistance = maxSize / (2 * Math.atan(Math.PI * camera.fov / 360));
|
|
317
342
|
const fitWidthDistance = fitHeightDistance / camera.aspect;
|
|
318
343
|
const distance = fitOffset * Math.max(fitHeightDistance, fitWidthDistance);
|
|
319
344
|
|