@needle-tools/engine 2.58.0-pre → 2.58.1-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 +4 -0
- package/dist/needle-engine.d.ts +10 -2
- package/dist/needle-engine.js +240 -240
- package/dist/needle-engine.js.map +3 -3
- package/dist/needle-engine.min.js +25 -25
- package/dist/needle-engine.min.js.map +3 -3
- package/dist/needle-engine.tsbuildinfo +1 -1
- package/lib/engine/engine_mainloop_utils.d.ts +3 -0
- package/lib/engine/engine_mainloop_utils.js +61 -0
- package/lib/engine/engine_mainloop_utils.js.map +1 -1
- package/lib/engine/engine_scenetools.js +3 -0
- package/lib/engine/engine_scenetools.js.map +1 -1
- package/lib/engine/engine_setup.js +1 -0
- package/lib/engine/engine_setup.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +7 -2
- package/lib/engine-components/Renderer.js +62 -2
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/engine/engine_mainloop_utils.ts +61 -1
- package/src/engine/engine_scenetools.ts +3 -0
- package/src/engine/engine_setup.ts +1 -0
- package/src/engine-components/Renderer.ts +59 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "2.58.
|
|
3
|
+
"version": "2.58.1-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.js",
|
|
6
6
|
"module": "src/needle-engine.ts",
|
|
@@ -2,7 +2,7 @@ import { Context } from './engine_setup';
|
|
|
2
2
|
import * as utils from "./engine_generic_utils";
|
|
3
3
|
import * as constants from "./engine_constants";
|
|
4
4
|
import { getParam } from './engine_utils';
|
|
5
|
-
import { Object3D } from 'three';
|
|
5
|
+
import { CubeCamera, Object3D, WebGLCubeRenderTarget } from 'three';
|
|
6
6
|
import { IComponent } from './engine_types';
|
|
7
7
|
import { isActiveSelf, setActive } from './engine_gameobject';
|
|
8
8
|
|
|
@@ -83,6 +83,8 @@ export function processNewScripts(context: Context) {
|
|
|
83
83
|
updateActiveInHierarchyWithoutEventCall(script.gameObject);
|
|
84
84
|
if (script.activeAndEnabled)
|
|
85
85
|
utils.safeInvoke(script.__internalAwake.bind(script));
|
|
86
|
+
|
|
87
|
+
// registerPrewarmObject(script.gameObject, context);
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
catch (err) {
|
|
@@ -320,4 +322,62 @@ function perComponent(go: THREE.Object3D, evt: (comp: IComponent) => void) {
|
|
|
320
322
|
evt(comp);
|
|
321
323
|
}
|
|
322
324
|
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
const prewarmList: Map<Context, Object3D[]> = new Map();
|
|
329
|
+
const $prewarmedFlag = Symbol("prewarmFlag");
|
|
330
|
+
const $waitingForPrewarm = Symbol("waitingForPrewarm");
|
|
331
|
+
const debugPrewarm = getParam("debugprewarm");
|
|
332
|
+
|
|
333
|
+
export function registerPrewarmObject(obj: Object3D, context: Context) {
|
|
334
|
+
if (!obj) return;
|
|
335
|
+
// allow objects to be marked as prewarmed in which case we dont need to register them again
|
|
336
|
+
if (obj[$prewarmedFlag] === true) return;
|
|
337
|
+
if (obj[$waitingForPrewarm] === true) return;
|
|
338
|
+
if (!prewarmList.has(context)) {
|
|
339
|
+
prewarmList.set(context, []);
|
|
340
|
+
}
|
|
341
|
+
obj[$waitingForPrewarm] = true;
|
|
342
|
+
const list = prewarmList.get(context);
|
|
343
|
+
list!.push(obj);
|
|
344
|
+
if(debugPrewarm) console.debug("register prewarm", obj.name);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
let prewarmTarget: WebGLCubeRenderTarget | null = null;
|
|
348
|
+
let prewarmCamera: CubeCamera | null = null;
|
|
349
|
+
|
|
350
|
+
// called by the engine to remove scroll or animation hiccup when objects are rendered/compiled for the first time
|
|
351
|
+
export function runPrewarm(context: Context) {
|
|
352
|
+
if (!context) return;
|
|
353
|
+
const list = prewarmList.get(context);
|
|
354
|
+
if (!list?.length) return;
|
|
355
|
+
|
|
356
|
+
const cam = context.mainCamera;
|
|
357
|
+
if (cam) {
|
|
358
|
+
if(debugPrewarm) console.log("prewarm", list.length, "objects", [...list]);
|
|
359
|
+
const renderer = context.renderer;
|
|
360
|
+
const scene = context.scene;
|
|
361
|
+
renderer.compile(scene, cam!)
|
|
362
|
+
prewarmTarget ??= new WebGLCubeRenderTarget(64)
|
|
363
|
+
prewarmCamera ??= new CubeCamera(0.001, 9999999, prewarmTarget);
|
|
364
|
+
prewarmCamera.update(renderer, scene);
|
|
365
|
+
for (const obj of list) {
|
|
366
|
+
obj[$prewarmedFlag] = true;
|
|
367
|
+
obj[$waitingForPrewarm] = false;
|
|
368
|
+
}
|
|
369
|
+
list.length = 0;
|
|
370
|
+
if(debugPrewarm) console.log("prewarm done");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function clearPrewarmList(context: Context) {
|
|
375
|
+
const list = prewarmList.get(context);
|
|
376
|
+
if (list) {
|
|
377
|
+
for (const obj of list) {
|
|
378
|
+
obj[$waitingForPrewarm] = false;
|
|
379
|
+
}
|
|
380
|
+
list.length = 0;
|
|
381
|
+
}
|
|
382
|
+
prewarmList.delete(context);
|
|
323
383
|
}
|
|
@@ -12,6 +12,7 @@ import { createBuiltinComponents, writeBuiltinComponentData } from "./engine_glt
|
|
|
12
12
|
import { SerializationContext } from "./engine_serialization_core";
|
|
13
13
|
import { NEEDLE_components } from "./extensions/NEEDLE_components";
|
|
14
14
|
import { addNewComponent, getComponentInChildren } from "./engine_components";
|
|
15
|
+
import { registerPrewarmObject } from "./engine_mainloop_utils";
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
export class NeedleGltfLoader implements INeedleGltfLoader {
|
|
@@ -116,6 +117,7 @@ export function parseSync(context: Context, data, path: string, seed: number | U
|
|
|
116
117
|
invokeEvents(GltfLoadEventType.AfterLoaded, new GltfLoadEvent(context, path, loader, data));
|
|
117
118
|
await handleLoadedGltf(context, path, data, seed, componentsExtension);
|
|
118
119
|
invokeEvents(GltfLoadEventType.FinishedSetup, new GltfLoadEvent(context, path, loader, data));
|
|
120
|
+
registerPrewarmObject(data.scene, context);
|
|
119
121
|
resolve(data);
|
|
120
122
|
|
|
121
123
|
}, err => {
|
|
@@ -146,6 +148,7 @@ export function loadSync(context: Context, url: string, seed: number | UIDProvid
|
|
|
146
148
|
invokeEvents(GltfLoadEventType.AfterLoaded, new GltfLoadEvent(context, url, loader, data));
|
|
147
149
|
await handleLoadedGltf(context, url, data, seed, componentsExtension);
|
|
148
150
|
invokeEvents(GltfLoadEventType.FinishedSetup, new GltfLoadEvent(context, url, loader, data));
|
|
151
|
+
registerPrewarmObject(data.scene, context);
|
|
149
152
|
resolve(data);
|
|
150
153
|
|
|
151
154
|
}, evt => {
|
|
@@ -12,6 +12,8 @@ import { NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing";
|
|
|
12
12
|
import { IRenderer, ISharedMaterials } from "../engine/engine_types";
|
|
13
13
|
import { debug, ReflectionProbe } from "./ReflectionProbe";
|
|
14
14
|
import { setCustomVisibility } from "../engine/js-extensions/Layers";
|
|
15
|
+
import { isLocalNetwork } from "../engine/engine_networking_utils";
|
|
16
|
+
import { showBalloonWarning } from "../engine/debug/debug";
|
|
15
17
|
|
|
16
18
|
// for staying compatible with old code
|
|
17
19
|
export { InstancingUtil } from "../engine/engine_instancing";
|
|
@@ -53,12 +55,14 @@ class SharedMaterialArray implements ISharedMaterials {
|
|
|
53
55
|
private _renderer: Renderer;
|
|
54
56
|
private _targets: THREE.Object3D[] = [];
|
|
55
57
|
|
|
58
|
+
private _indexMapMaxIndex?: number;
|
|
59
|
+
private _indexMap?: Map<number, number>;
|
|
56
60
|
|
|
57
61
|
is(renderer: Renderer) {
|
|
58
62
|
return this._renderer === renderer;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
constructor(renderer: Renderer) {
|
|
65
|
+
constructor(renderer: Renderer, originalMaterials: Material[]) {
|
|
62
66
|
this._renderer = renderer;
|
|
63
67
|
const setMaterial = this.setMaterial.bind(this);
|
|
64
68
|
const getMaterial = this.getMaterial.bind(this);
|
|
@@ -76,6 +80,38 @@ class SharedMaterialArray implements ISharedMaterials {
|
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
// this is useful to have an index map when e.g. materials are trying to be assigned by index
|
|
84
|
+
let hasMissingMaterials = false;
|
|
85
|
+
let indexMap: Map<number, number> | undefined = undefined;
|
|
86
|
+
let maxIndex: number = 0;
|
|
87
|
+
for (let i = 0; i < this._targets.length; i++) {
|
|
88
|
+
const target = this._targets[i] as Mesh;
|
|
89
|
+
if (!target) continue;
|
|
90
|
+
const mat = target.material as Material;
|
|
91
|
+
if (!mat) continue;
|
|
92
|
+
for (let k = 0; k < originalMaterials.length; k++) {
|
|
93
|
+
const orig = originalMaterials[k];
|
|
94
|
+
if (!orig) {
|
|
95
|
+
hasMissingMaterials = true;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (mat.name === orig.name) {
|
|
99
|
+
if (indexMap === undefined) indexMap = new Map();
|
|
100
|
+
indexMap.set(k, i);
|
|
101
|
+
maxIndex = Math.max(maxIndex, k);
|
|
102
|
+
// console.log(`Material ${mat.name} at ${k} was found at index ${i} in renderer ${renderer.name}.`)
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (hasMissingMaterials) {
|
|
108
|
+
this._indexMapMaxIndex = maxIndex;
|
|
109
|
+
this._indexMap = indexMap;
|
|
110
|
+
const warningMessage = `Renderer ${renderer.name} was initialized with missing materials - this may lead to unexpected behaviour when trying to access sharedMaterials by index.`;
|
|
111
|
+
console.warn(warningMessage);
|
|
112
|
+
if(isLocalNetwork()) showBalloonWarning("Found renderer with missing materials: please check the console for details.");
|
|
113
|
+
}
|
|
114
|
+
|
|
79
115
|
// this lets us override the javascript indexer, only works in ES6 tho
|
|
80
116
|
// but like that we can use sharedMaterials[index] and it will be assigned to the object directly
|
|
81
117
|
return new Proxy(this, {
|
|
@@ -98,10 +134,22 @@ class SharedMaterialArray implements ISharedMaterials {
|
|
|
98
134
|
}
|
|
99
135
|
|
|
100
136
|
get length(): number {
|
|
137
|
+
if (this._indexMapMaxIndex !== undefined) return this._indexMapMaxIndex + 1;
|
|
101
138
|
return this._targets.length;
|
|
102
139
|
}
|
|
103
140
|
|
|
141
|
+
private resolveIndex(index: number): number {
|
|
142
|
+
const map = this._indexMap;
|
|
143
|
+
// if we have a index map it means that some materials were missing
|
|
144
|
+
if (map) {
|
|
145
|
+
if (map.has(index)) return map.get(index) as number;
|
|
146
|
+
// return -1;
|
|
147
|
+
}
|
|
148
|
+
return index;
|
|
149
|
+
}
|
|
150
|
+
|
|
104
151
|
private setMaterial(mat: Material, index: number) {
|
|
152
|
+
index = this.resolveIndex(index);
|
|
105
153
|
if (index < 0 || index >= this._targets.length) return;
|
|
106
154
|
const target = this._targets[index];
|
|
107
155
|
if (!target || target["material"] === undefined) return;
|
|
@@ -109,6 +157,7 @@ class SharedMaterialArray implements ISharedMaterials {
|
|
|
109
157
|
}
|
|
110
158
|
|
|
111
159
|
private getMaterial(index: number) {
|
|
160
|
+
index = this.resolveIndex(index);
|
|
112
161
|
if (index < 0) return null;
|
|
113
162
|
const obj = this._targets;
|
|
114
163
|
if (index >= obj.length) return null;
|
|
@@ -193,10 +242,18 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
193
242
|
}
|
|
194
243
|
|
|
195
244
|
private _sharedMaterials!: SharedMaterialArray;
|
|
245
|
+
private _originalMaterials: Material[] = [];
|
|
246
|
+
|
|
247
|
+
// this is just available during deserialization
|
|
248
|
+
private set sharedMaterials(_val: Array<Material | null>) {
|
|
249
|
+
// TODO: elements in the array might be missing at the moment which leads to problems if an index is serialized
|
|
250
|
+
this._originalMaterials = _val as Material[];
|
|
251
|
+
}
|
|
196
252
|
|
|
253
|
+
//@ts-ignore
|
|
197
254
|
get sharedMaterials(): SharedMaterialArray {
|
|
198
255
|
if (!this._sharedMaterials || !this._sharedMaterials.is(this))
|
|
199
|
-
this._sharedMaterials = new SharedMaterialArray(this);
|
|
256
|
+
this._sharedMaterials = new SharedMaterialArray(this, this._originalMaterials);
|
|
200
257
|
return this._sharedMaterials!;
|
|
201
258
|
}
|
|
202
259
|
|