@@ -1,5 +1,5 @@
|
|
1
1
|
import { TypeStore } from "./../engine_typestore"
|
2
|
-
|
2
|
+
|
3
3
|
// Import types
|
4
4
|
import { __Ignore } from "../../engine-components/codegen/components";
|
5
5
|
import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder";
|
@@ -217,7 +217,7 @@
|
|
217
217
|
import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering";
|
218
218
|
import { XRRig } from "../../engine-components/webxr/WebXRRig";
|
219
219
|
import { XRState } from "../../engine-components/XRFlag";
|
220
|
-
|
220
|
+
|
221
221
|
// Register types
|
222
222
|
TypeStore.add("__Ignore", __Ignore);
|
223
223
|
TypeStore.add("ActionBuilder", ActionBuilder);
|
@@ -239,7 +239,11 @@
|
|
239
239
|
const useNormalRenderer = true;// this.context.isInXR || !composer;
|
240
240
|
const renderer = useNormalRenderer ? this.context.renderer : composer;
|
241
241
|
if (renderer) {
|
242
|
-
|
242
|
+
// TODO: we should do this in onBeforeRender for the main camera only
|
243
|
+
const mainCam = this.context.mainCameraComponent;
|
244
|
+
this.applyClearFlags();
|
245
|
+
this._targetTexture.render(this.context.scene, this._cam, renderer);
|
246
|
+
mainCam?.applyClearFlags();
|
243
247
|
}
|
244
248
|
}
|
245
249
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { InternalUsageTrackerPlugin } from "./extensions/usage_tracker";
|
2
|
-
import { Bone, BufferAttribute, BufferGeometry, InterleavedBuffer, InterleavedBufferAttribute, Material, Mesh, Object3D, Scene, Skeleton, SkinnedMesh, Source, Texture, Uniform, WebGLRenderer } from "three";
|
2
|
+
import { Bone, BufferAttribute, BufferGeometry, InterleavedBuffer, InterleavedBufferAttribute, Material, Mesh, NeverCompare, Object3D, Scene, Skeleton, SkinnedMesh, Source, Texture, Uniform, WebGLRenderer } from "three";
|
3
3
|
import { addPatch } from "./engine_patcher";
|
4
4
|
import { getParam } from "./engine_utils";
|
5
5
|
|
@@ -20,19 +20,22 @@
|
|
20
20
|
}
|
21
21
|
}
|
22
22
|
|
23
|
-
const trackUsageParam = getParam("
|
23
|
+
const trackUsageParam = getParam("trackresources");
|
24
24
|
|
25
25
|
function autoDispose() {
|
26
26
|
return trackUsageParam === "dispose";
|
27
27
|
}
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
let allowUsageTracking = true;// trackUsageParam === true || trackUsageParam === 1;
|
30
|
+
if (trackUsageParam === 0) allowUsageTracking = false;
|
31
31
|
|
32
|
-
|
32
|
+
/**
|
33
|
+
* Disable usage tracking
|
34
|
+
*/
|
35
|
+
export function setResourceTrackingEnabled(enabled: boolean) {
|
33
36
|
allowUsageTracking = enabled;
|
34
37
|
}
|
35
|
-
export function
|
38
|
+
export function isResourceTrackingEnabled() {
|
36
39
|
return allowUsageTracking;
|
37
40
|
}
|
38
41
|
|
@@ -52,7 +55,7 @@
|
|
52
55
|
export function disposeObjectResources(obj: object | null | undefined) {
|
53
56
|
if (!obj) return;
|
54
57
|
if (obj[$disposable] === false) {
|
55
|
-
if(debug) console.warn("Object is marked as not disposable", obj);
|
58
|
+
if (debug) console.warn("Object is marked as not disposable", obj);
|
56
59
|
return;
|
57
60
|
}
|
58
61
|
|
@@ -183,7 +186,7 @@
|
|
183
186
|
* @param set Set to add users to, a new one will be created if none is provided
|
184
187
|
* @returns a set of users
|
185
188
|
*/
|
186
|
-
export function
|
189
|
+
export function findResourceUsers(object: object, recursive: boolean, predicate: UserFilter | null | undefined = null, set?: Set<object>): Set<object> {
|
187
190
|
if (!set) {
|
188
191
|
set = usersBuffer;
|
189
192
|
set.clear();
|
@@ -198,29 +201,29 @@
|
|
198
201
|
if (predicate?.call(null, user) === false) continue;
|
199
202
|
set.add(user);
|
200
203
|
if (recursive)
|
201
|
-
|
204
|
+
findResourceUsers(user, true, predicate, set);
|
202
205
|
}
|
203
206
|
}
|
204
207
|
return set;
|
205
208
|
}
|
206
209
|
|
207
|
-
export function
|
210
|
+
export function getResourceUserCount(object: object): number | undefined {
|
208
211
|
return object[$objectUsersCountKey];
|
209
212
|
}
|
210
213
|
|
211
214
|
|
212
215
|
|
213
|
-
const debug = getParam("
|
216
|
+
const debug = getParam("debugresourceusers") || getParam("debugmemory");
|
214
217
|
|
215
218
|
// Should we check if the type has the
|
216
|
-
const $objectUsersKey = Symbol("needle-users");
|
217
|
-
const $objectUsersCountKey = Symbol("needle-users-count");
|
219
|
+
const $objectUsersKey = Symbol("needle-resource-users");
|
220
|
+
const $objectUsersCountKey = Symbol("needle-resource-users-count");
|
218
221
|
|
219
222
|
function trackValueChange(prototype, fieldName) {
|
220
|
-
addPatch(prototype, fieldName, (
|
223
|
+
addPatch(prototype, fieldName, function (this: object, oldValue, newValue) {
|
221
224
|
if (allowUsageTracking) {
|
222
|
-
updateUsers($objectUsersKey,
|
223
|
-
updateUsers($objectUsersKey,
|
225
|
+
updateUsers($objectUsersKey, this, oldValue, false);
|
226
|
+
updateUsers($objectUsersKey, this, newValue, true);
|
224
227
|
}
|
225
228
|
});
|
226
229
|
}
|
@@ -234,29 +237,26 @@
|
|
234
237
|
// }
|
235
238
|
|
236
239
|
|
237
|
-
|
238
|
-
trackValueChange(Mesh.prototype, "
|
239
|
-
trackValueChange(
|
240
|
-
trackValueChange(Material.prototype, "
|
241
|
-
trackValueChange(Material.prototype, "
|
242
|
-
trackValueChange(Material.prototype, "
|
243
|
-
trackValueChange(Material.prototype, "
|
244
|
-
trackValueChange(Material.prototype, "
|
245
|
-
trackValueChange(Material.prototype, "
|
246
|
-
trackValueChange(Material.prototype, "
|
247
|
-
trackValueChange(Material.prototype, "
|
248
|
-
trackValueChange(Material.prototype, "
|
249
|
-
trackValueChange(Material.prototype, "
|
250
|
-
trackValueChange(Material.prototype, "
|
251
|
-
trackValueChange(Material.prototype, "
|
252
|
-
|
240
|
+
if (allowUsageTracking) {
|
241
|
+
trackValueChange(Mesh.prototype, "material");
|
242
|
+
trackValueChange(Mesh.prototype, "geometry");
|
243
|
+
trackValueChange(Material.prototype, "map");
|
244
|
+
trackValueChange(Material.prototype, "bumpMap");
|
245
|
+
trackValueChange(Material.prototype, "alphaMap");
|
246
|
+
trackValueChange(Material.prototype, "normalMap");
|
247
|
+
trackValueChange(Material.prototype, "displacementMap");
|
248
|
+
trackValueChange(Material.prototype, "roughnessMap");
|
249
|
+
trackValueChange(Material.prototype, "metalnessMap");
|
250
|
+
trackValueChange(Material.prototype, "emissiveMap");
|
251
|
+
trackValueChange(Material.prototype, "specularMap");
|
252
|
+
trackValueChange(Material.prototype, "envMap");
|
253
|
+
trackValueChange(Material.prototype, "lightMap");
|
254
|
+
trackValueChange(Material.prototype, "aoMap");
|
255
|
+
trackValueChange(Material.prototype, "gradientMap");
|
256
|
+
}
|
253
257
|
|
254
258
|
|
255
|
-
// setTimeout(()=>{
|
256
|
-
// stopTracking(Mesh.prototype, "material");
|
257
|
-
// },100);
|
258
259
|
|
259
|
-
|
260
260
|
// TODO: patch dispose?
|
261
261
|
|
262
262
|
|
@@ -270,18 +270,17 @@
|
|
270
270
|
}
|
271
271
|
}
|
272
272
|
|
273
|
-
|
274
|
-
addPatch(prototype,
|
275
|
-
onDispose(instance);
|
276
|
-
});
|
273
|
+
if (allowUsageTracking) {
|
274
|
+
addPatch(Material.prototype, "dispose", function (this: object) { onDispose(this) });
|
277
275
|
}
|
278
276
|
|
279
|
-
trackDispose(Material.prototype, "dispose");
|
280
277
|
|
281
278
|
|
282
279
|
|
283
|
-
|
284
|
-
|
280
|
+
// This variable is crucial for performance:
|
281
|
+
// it is incremented during rendering to prevent usage updates during the three.js render loop
|
282
|
+
// where materials and properties are updated every frame (e.g. the DepthMaterial)
|
283
|
+
// and we don't care about those
|
285
284
|
let noUpdateScope = 0;
|
286
285
|
|
287
286
|
// Main method called by wrapped fields/properties to update the users for an object
|
@@ -331,21 +330,16 @@
|
|
331
330
|
}
|
332
331
|
|
333
332
|
|
334
|
-
|
335
|
-
|
336
333
|
// We dont want to update users during rendering
|
337
|
-
|
338
|
-
|
339
334
|
try {
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
// );
|
335
|
+
addPatch(WebGLRenderer.prototype, "render",
|
336
|
+
function () {
|
337
|
+
noUpdateScope++;
|
338
|
+
},
|
339
|
+
function () {
|
340
|
+
noUpdateScope--;
|
341
|
+
}
|
342
|
+
);
|
349
343
|
}
|
350
344
|
catch (e) {
|
351
345
|
console.warn("Could not wrap WebGLRenderer.render", e);
|
@@ -1,9 +1,11 @@
|
|
1
1
|
|
2
2
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
|
3
3
|
|
4
|
-
|
4
|
+
import { getParam } from "./engine_utils";
|
5
5
|
|
6
|
+
// const _wrappedMethods = new WeakSet();
|
6
7
|
|
8
|
+
|
7
9
|
// export function wrap<T>(prototype: object, methodName: string, before: (t: T) => void, after: (t: T) => void) {
|
8
10
|
|
9
11
|
// const $key = Symbol(methodName + "-patched");
|
@@ -40,34 +42,43 @@
|
|
40
42
|
export type Prefix = (...args) => any;
|
41
43
|
export type Postfix = (...args) => any;
|
42
44
|
|
45
|
+
const debugPatch = getParam("debugpatch");
|
46
|
+
|
43
47
|
/**
|
44
48
|
* Use patcher for patching properties insteadof calling Object.defineProperty individually
|
45
49
|
* since this will cause conflicts if multiple patches need to be applied to the same property
|
46
50
|
*/
|
47
|
-
export function addPatch<T extends object>(prototype: T, fieldName: string, beforeCallback?: Prefix, afterCallback?: Postfix) {
|
51
|
+
export function addPatch<T extends object>(prototype: T, fieldName: string, beforeCallback?: Prefix | null, afterCallback?: Postfix | null) {
|
52
|
+
const debug = debugPatch === fieldName;
|
48
53
|
|
49
|
-
//
|
50
|
-
|
54
|
+
// If no callbacks are provided, we don't need to do anything
|
55
|
+
if (!beforeCallback && !afterCallback) {
|
56
|
+
return;
|
57
|
+
}
|
51
58
|
|
52
59
|
// TODO: we probably want to turn this into a symbol to prevent anyone from overriding it
|
53
60
|
// But when we need to store the symbol per prototype to allow e.g. material disposing to iterate those and dispose all
|
54
61
|
const backingField = Symbol(fieldName + "__needle");// Symbol(fieldName);// + " (patched)";
|
55
62
|
|
56
|
-
internalAddPatch(prototype, fieldName,
|
63
|
+
internalAddPatch(prototype, fieldName, beforeCallback, afterCallback);
|
57
64
|
|
58
65
|
const desc = Object.getOwnPropertyDescriptor(prototype, fieldName);
|
59
|
-
|
60
66
|
const existing = prototype[fieldName];
|
61
|
-
console.log(prototype);
|
67
|
+
if (debug) console.log("Patch", prototype.constructor.name, fieldName, desc, existing);
|
62
68
|
|
63
69
|
if (desc) {
|
70
|
+
if (debug) console.log("Apply patch with existing descriptor", prototype.constructor.name, fieldName, desc);
|
71
|
+
if (typeof desc.value === "function") {
|
72
|
+
prototype[fieldName] = ensureFunctionWrapped(desc.value, prototype, fieldName);
|
73
|
+
}
|
64
74
|
}
|
65
75
|
else {
|
76
|
+
if (debug) console.log("Create patch with new property", prototype.constructor.name, fieldName, desc);
|
66
77
|
Object.defineProperty(prototype, fieldName, {
|
67
78
|
set: function (this: object, value: any) {
|
68
|
-
console.log("setting", fieldName, value);
|
69
79
|
if (typeof value === "function") {
|
70
|
-
this
|
80
|
+
// TODO: not sure if this is correct (if the value that is set is a function)
|
81
|
+
this[backingField] = ensureFunctionWrapped(value, prototype, fieldName);
|
71
82
|
}
|
72
83
|
else {
|
73
84
|
const prev = this[backingField];
|
@@ -77,7 +88,6 @@
|
|
77
88
|
}
|
78
89
|
},
|
79
90
|
get: function (this: any) {
|
80
|
-
console.log("GET", fieldName);
|
81
91
|
const value = this[backingField];
|
82
92
|
if (typeof value === "function") {
|
83
93
|
if (value[backingField]) {
|
@@ -90,20 +100,21 @@
|
|
90
100
|
}
|
91
101
|
}
|
92
102
|
|
93
|
-
|
94
|
-
|
95
|
-
executePrefixes(prototype, fieldname, this, ...args);
|
96
|
-
const result = originalFunction.apply(this, args);
|
97
|
-
executePostFixes(prototype, fieldname, this, result, ...args);
|
98
|
-
return result;
|
99
|
-
}
|
100
|
-
}
|
101
|
-
|
102
|
-
export function removePatch(prototype: object, fieldName: string, cb: Function) {
|
103
|
+
/** Removes prefix or postfix */
|
104
|
+
export function removePatch(prototype: object, fieldName: string, prefixOrPostfix: Prefix | Postfix) {
|
103
105
|
const patches = getPatches(prototype, fieldName);
|
104
106
|
if (patches) {
|
105
107
|
for (let i = patches.length - 1; i >= 0; i--) {
|
106
|
-
|
108
|
+
const patch = patches[i];
|
109
|
+
// Remove either the prefix or postfix
|
110
|
+
if (patch.prefix === prefixOrPostfix) {
|
111
|
+
patch.prefix = null;
|
112
|
+
}
|
113
|
+
if (patch.postfix === prefixOrPostfix) {
|
114
|
+
patch.postfix = null;
|
115
|
+
}
|
116
|
+
// If the patch is empty, remove it from the list
|
117
|
+
if (!patch.prefix && !patch.postfix) {
|
107
118
|
patches.splice(i, 1);
|
108
119
|
}
|
109
120
|
}
|
@@ -111,11 +122,29 @@
|
|
111
122
|
}
|
112
123
|
|
113
124
|
|
125
|
+
|
126
|
+
const $wrappedFunctionSymbol = Symbol("Needle:Patches:WrappedFunction");
|
127
|
+
|
128
|
+
function ensureFunctionWrapped(originalFunction: Function, prototype, fieldname) {
|
129
|
+
if (originalFunction[$wrappedFunctionSymbol]) {
|
130
|
+
return originalFunction;
|
131
|
+
}
|
132
|
+
const wrappedFunction = function (this: object, ...args: any[]) {
|
133
|
+
executePrefixes(prototype, fieldname, this, ...args);
|
134
|
+
const result = originalFunction.apply(this, args);
|
135
|
+
executePostFixes(prototype, fieldname, this, result, ...args);
|
136
|
+
return result;
|
137
|
+
}
|
138
|
+
wrappedFunction[$wrappedFunctionSymbol] = true;
|
139
|
+
return wrappedFunction;
|
140
|
+
}
|
141
|
+
|
142
|
+
|
114
143
|
export const NeedlePatchesKey = "Needle:Patches";
|
115
144
|
|
116
145
|
declare type PatchInfo = {
|
117
|
-
prefix?: Prefix;
|
118
|
-
postfix?: Postfix;
|
146
|
+
prefix?: Prefix | null;
|
147
|
+
postfix?: Postfix | null;
|
119
148
|
}
|
120
149
|
function patches(): WeakMap<object, Map<string, PatchInfo[]>> {
|
121
150
|
if (!globalThis[NeedlePatchesKey]) {
|
@@ -132,7 +161,7 @@
|
|
132
161
|
return patchesMap.get(fieldName);;
|
133
162
|
}
|
134
163
|
|
135
|
-
function internalAddPatch(prototype, fieldName: string,
|
164
|
+
function internalAddPatch(prototype, fieldName: string, prefix?: Prefix | null, postfix?: Postfix | null) {
|
136
165
|
let patchesMap = patches().get(prototype);
|
137
166
|
if (!patchesMap) {
|
138
167
|
patchesMap = new Map();
|
@@ -4,7 +4,7 @@
|
|
4
4
|
import { Behaviour, Component, GameObject } from "../engine-components/Component";
|
5
5
|
import { debugExtension } from "./engine_default_parameters";
|
6
6
|
import { CallInfo, EventList } from "../engine-components/EventList";
|
7
|
-
import { Color, Object3D, Texture, WebGLRenderTarget } from "three";
|
7
|
+
import { Color, CompressedTexture, Object3D, Texture, WebGLRenderTarget } from "three";
|
8
8
|
import { RenderTexture } from "./engine_texture";
|
9
9
|
import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../engine/debug/debug";
|
10
10
|
import { resolveUrl } from "./engine_utils";
|
@@ -350,6 +350,18 @@
|
|
350
350
|
const tex = data as Texture;
|
351
351
|
const rt = new RenderTexture(tex.image.width, tex.image.height);
|
352
352
|
rt.texture = tex;
|
353
|
+
|
354
|
+
if (tex instanceof CompressedTexture) {
|
355
|
+
//@ts-ignore
|
356
|
+
tex["isCompressedTexture"] = false;
|
357
|
+
//@ts-ignore
|
358
|
+
tex.format = THREE.RGBAFormat;
|
359
|
+
}
|
360
|
+
|
361
|
+
rt.texture.flipY = true;
|
362
|
+
rt.texture.offset.y = 1;
|
363
|
+
rt.texture.repeat.y = -1;
|
364
|
+
|
353
365
|
return rt;
|
354
366
|
}
|
355
367
|
return undefined;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Camera, Mesh, Object3D, Texture, WebGLRenderer, WebGLRenderTarget } from "three";
|
2
2
|
import { EffectComposer } from "postprocessing";
|
3
|
-
import {
|
3
|
+
import { findResourceUsers } from "./engine_assetdatabase";
|
4
4
|
|
5
5
|
|
6
6
|
const _prevVisible = Symbol("previous-visibility");
|
@@ -34,7 +34,7 @@
|
|
34
34
|
|
35
35
|
private onBeforeRender() {
|
36
36
|
RenderTexture._userSet.clear();
|
37
|
-
const users =
|
37
|
+
const users = findResourceUsers(this.texture, true, null, RenderTexture._userSet);
|
38
38
|
for (const user of users) {
|
39
39
|
if (user instanceof Mesh) {
|
40
40
|
user[_prevVisible] = user.visible;
|
@@ -14,7 +14,7 @@
|
|
14
14
|
import { NEEDLE_render_objects } from "./NEEDLE_render_objects";
|
15
15
|
import { NEEDLE_progressive } from "./NEEDLE_progressive";
|
16
16
|
import { InternalUsageTrackerPlugin } from "./usage_tracker";
|
17
|
-
import {
|
17
|
+
import { isResourceTrackingEnabled } from "../engine_assetdatabase";
|
18
18
|
import { GLTFLoaderPlugin } from "three/examples/jsm/loaders/GLTFLoader.js";
|
19
19
|
import { getParam } from "../engine_utils";
|
20
20
|
import { isDevEnvironment } from "../debug";
|
@@ -79,7 +79,7 @@
|
|
79
79
|
loader.register(p => new NEEDLE_render_objects(p, sourceId));
|
80
80
|
loader.register(p => new NEEDLE_progressive(p, sourceId, context));
|
81
81
|
loader.register(p => new EXT_texture_exr(p));
|
82
|
-
if (
|
82
|
+
if (isResourceTrackingEnabled()) loader.register(p => new InternalUsageTrackerPlugin(p))
|
83
83
|
|
84
84
|
for (const ext of _addedCustomExtension)
|
85
85
|
loader.register(p => new ext(p));
|
@@ -1027,7 +1027,12 @@
|
|
1027
1027
|
if (this._subEmitterSystems && this._particleSystem) {
|
1028
1028
|
for (const sys of this._subEmitterSystems) {
|
1029
1029
|
// Make sure the particle system is created
|
1030
|
-
if (sys.particleSystem)
|
1030
|
+
if (sys.particleSystem) {
|
1031
|
+
if (sys.particleSystem.__internalAwake)
|
1032
|
+
sys.particleSystem.__internalAwake();
|
1033
|
+
else if (isDevEnvironment())
|
1034
|
+
console.warn("SubParticleSystem serialization issue(?)", sys.particleSystem, sys);
|
1035
|
+
}
|
1031
1036
|
const system = sys.particleSystem?._particleSystem;
|
1032
1037
|
if (system) {
|
1033
1038
|
sys.particleSystem!._isUsedAsSubsystem = true;
|
@@ -1037,6 +1042,7 @@
|
|
1037
1042
|
sub.emitterProbability = sys.emitProbability;
|
1038
1043
|
this._particleSystem.addBehavior(sub);
|
1039
1044
|
}
|
1045
|
+
else if (debug) console.warn("Could not add SubParticleSystem", sys, this);
|
1040
1046
|
}
|
1041
1047
|
}
|
1042
1048
|
}
|
@@ -365,8 +365,8 @@
|
|
365
365
|
// ignore nested groups or objects that have their own renderer (aka their own render order settings)
|
366
366
|
if (!this.isMeshOrSkinnedMesh(ch) || GameObject.getComponent(ch, Renderer)) continue;
|
367
367
|
if (this.renderOrder.length <= index) {
|
368
|
-
console.
|
369
|
-
|
368
|
+
console.warn("Incorrect renderOrder element count", this, this.renderOrder.length + " but expected " + this.gameObject.children.length, "Index: " + index, "ChildElement:", ch);
|
369
|
+
continue;
|
370
370
|
}
|
371
371
|
// if(debugRenderer) console.log("Setting render order", ch, this.renderOrder[index])
|
372
372
|
ch.renderOrder = this.renderOrder[index];
|