@needle-tools/engine 4.11.4-next.1fca70f → 4.11.4-next.a568de7
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/dist/{needle-engine.bundle-mXN93lWd.js → needle-engine.bundle-DpWrB4yf.js} +3394 -3382
- package/dist/{needle-engine.bundle-BXLvEjNl.min.js → needle-engine.bundle-lEVjhicZ.min.js} +115 -115
- package/dist/{needle-engine.bundle-C-av6GAF.umd.cjs → needle-engine.bundle-nX51yB5a.umd.cjs} +120 -120
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/engine_addressables.d.ts +74 -11
- package/lib/engine/engine_addressables.js +74 -11
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_camera.fit.d.ts +48 -3
- package/lib/engine/engine_camera.fit.js +29 -0
- package/lib/engine/engine_camera.fit.js.map +1 -1
- package/lib/engine/engine_context.d.ts +18 -3
- package/lib/engine/engine_context.js +18 -3
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_components.d.ts +4 -4
- package/lib/engine/extensions/NEEDLE_components.js +36 -17
- package/lib/engine/extensions/NEEDLE_components.js.map +1 -1
- package/lib/engine-components/Renderer.js +14 -40
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/RendererLightmap.d.ts +1 -0
- package/lib/engine-components/RendererLightmap.js +24 -18
- package/lib/engine-components/RendererLightmap.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.d.ts +2 -1
- package/lib/engine-components/timeline/PlayableDirector.js +16 -9
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_addressables.ts +76 -11
- package/src/engine/engine_camera.fit.ts +50 -4
- package/src/engine/engine_context.ts +18 -3
- package/src/engine/extensions/NEEDLE_components.ts +47 -26
- package/src/engine-components/Renderer.ts +14 -42
- package/src/engine-components/RendererLightmap.ts +27 -17
- package/src/engine-components/timeline/PlayableDirector.ts +16 -10
|
@@ -10,6 +10,8 @@ import { SerializationContext, TypeSerializer } from "./engine_serialization_cor
|
|
|
10
10
|
import { Context } from "./engine_setup.js";
|
|
11
11
|
import type { GLTF, IComponent, IGameObject, Model, SourceIdentifier } from "./engine_types.js";
|
|
12
12
|
|
|
13
|
+
import type { loadAsset } from "./engine_loaders.js";
|
|
14
|
+
|
|
13
15
|
const debug = getParam("debugaddressables");
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -79,13 +81,50 @@ export type ProgressCallback = (asset: AssetReference, prog: ProgressEvent) => v
|
|
|
79
81
|
|
|
80
82
|
const $assetReference = Symbol("assetReference");
|
|
81
83
|
|
|
82
|
-
/** ### AssetReferences can be used to
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* -
|
|
87
|
-
* -
|
|
88
|
-
* -
|
|
84
|
+
/** ### AssetReferences can be used to load glTF or GLB assets
|
|
85
|
+
* Use {@link AssetReference.getOrCreateFromUrl} to get an AssetReference for a URL to be easily loaded. When using the same URL multiple times the same AssetReference will be returned, this avoids loading or creating the same asset multiple times.
|
|
86
|
+
*
|
|
87
|
+
* **Important methods:**
|
|
88
|
+
* - {@link preload} to load the asset binary without creating an instance yet.
|
|
89
|
+
* - {@link loadAssetAsync} to load the asset and create an instance.
|
|
90
|
+
* - {@link instantiate} to load the asset and create another instance.
|
|
91
|
+
* - {@link unload} to dispose allocated memory and destroy the asset instance.
|
|
92
|
+
*
|
|
93
|
+
* @example Loading an asset from a URL
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { AssetReference } from '@needle-tools/engine';
|
|
96
|
+
* const assetRef = AssetReference.getOrCreateFromUrl("https://example.com/myModel.glb");
|
|
97
|
+
* const instance = await assetRef.loadAssetAsync();
|
|
98
|
+
* scene.add(instance);
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @example Referencing an asset in a component and loading it on start
|
|
102
|
+
* ```ts
|
|
103
|
+
* import { Behaviour, serializable, AssetReference } from '@needle-tools/engine';
|
|
104
|
+
*
|
|
105
|
+
* export class MyComponent extends Behaviour {
|
|
106
|
+
*
|
|
107
|
+
* @serializable(AssetReference)
|
|
108
|
+
* myModel?: AssetReference;
|
|
109
|
+
*
|
|
110
|
+
* // Load the model on start. Start is called after awake and onEnable
|
|
111
|
+
* start() {
|
|
112
|
+
* if (this.myModel) {
|
|
113
|
+
* this.myModel.loadAssetAsync().then(instance => {
|
|
114
|
+
* if (instance) {
|
|
115
|
+
* // add the loaded model to this component's game object
|
|
116
|
+
* this.gameObject.add(instance);
|
|
117
|
+
* }
|
|
118
|
+
* });
|
|
119
|
+
* }
|
|
120
|
+
* }
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* ### Related:
|
|
125
|
+
* - {@link ImageReference} to load external image URLs
|
|
126
|
+
* - {@link FileReference} to load external file URLs
|
|
127
|
+
* - {@link loadAsset} to load assets directly without using AssetReferences
|
|
89
128
|
*/
|
|
90
129
|
export class AssetReference {
|
|
91
130
|
|
|
@@ -524,12 +563,33 @@ new AddressableSerializer();
|
|
|
524
563
|
|
|
525
564
|
const failedTexturePromise = Promise.resolve(null);
|
|
526
565
|
|
|
527
|
-
/**
|
|
566
|
+
/**
|
|
567
|
+
* Load images or textures from external URLs.
|
|
568
|
+
*
|
|
569
|
+
* **Important methods:**
|
|
570
|
+
* - {@link createHTMLImage} to create an HTMLImageElement from the URL
|
|
571
|
+
* - {@link createTexture} to create a Three.js Texture from the URL
|
|
572
|
+
*
|
|
528
573
|
* @example
|
|
529
574
|
* ```ts
|
|
530
|
-
* @
|
|
531
|
-
*
|
|
575
|
+
* import { ImageReference, serializable } from '@needle-tools/engine';
|
|
576
|
+
*
|
|
577
|
+
* export class MyComponent extends Behaviour {
|
|
578
|
+
* @serializable(ImageReference)
|
|
579
|
+
* myImage?:ImageReference;
|
|
580
|
+
* async start() {
|
|
581
|
+
* if(this.myImage) {
|
|
582
|
+
* const texture = await this.myImage.createTexture();
|
|
583
|
+
* if(texture) {
|
|
584
|
+
* // use the texture
|
|
585
|
+
* }
|
|
586
|
+
* }
|
|
587
|
+
* }
|
|
532
588
|
* ```
|
|
589
|
+
*
|
|
590
|
+
* ### Related:
|
|
591
|
+
* - {@link AssetReference} to load glTF or GLB assets
|
|
592
|
+
* - {@link FileReference} to load external file URLs
|
|
533
593
|
*/
|
|
534
594
|
export class ImageReference {
|
|
535
595
|
|
|
@@ -637,7 +697,12 @@ new ImageReferenceSerializer();
|
|
|
637
697
|
|
|
638
698
|
|
|
639
699
|
|
|
640
|
-
/**
|
|
700
|
+
/**
|
|
701
|
+
* Use this if a file is a external file URL. The file can be any arbitrary binary data like a videofile or a text asset.
|
|
702
|
+
*
|
|
703
|
+
* ### Related:
|
|
704
|
+
* - {@link AssetReference} to load glTF or GLB assets
|
|
705
|
+
* - {@link ImageReference} to load external image URLs
|
|
641
706
|
*/
|
|
642
707
|
export class FileReference {
|
|
643
708
|
|
|
@@ -7,9 +7,14 @@ import { Gizmos } from "./engine_gizmos.js";
|
|
|
7
7
|
import { getBoundingBox } from "./engine_three_utils.js";
|
|
8
8
|
import { NeedleXRSession } from "./xr/NeedleXRSession.js";
|
|
9
9
|
|
|
10
|
+
import type { OrbitControls } from "../engine-components/OrbitControls.js";
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
|
-
* Options for fitting
|
|
14
|
+
* Options for fitting a camera to the scene or specific objects.
|
|
15
|
+
*
|
|
16
|
+
* Used by {@link OrbitControls.fitCamera} and the {@link fitCamera}.
|
|
17
|
+
*
|
|
13
18
|
*/
|
|
14
19
|
export type FitCameraOptions = {
|
|
15
20
|
/** When enabled debug rendering will be shown */
|
|
@@ -31,8 +36,17 @@ export type FitCameraOptions = {
|
|
|
31
36
|
*/
|
|
32
37
|
camera?: Camera,
|
|
33
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The current zoom level of the camera (used to avoid clipping when fitting)
|
|
41
|
+
*/
|
|
34
42
|
currentZoom?: number,
|
|
43
|
+
/**
|
|
44
|
+
* Minimum and maximum zoom levels for the camera (e.g. if zoom is constrained by OrbitControls)
|
|
45
|
+
*/
|
|
35
46
|
minZoom?: number,
|
|
47
|
+
/**
|
|
48
|
+
* Maximum zoom level for the camera (e.g. if zoom is constrained by OrbitControls)
|
|
49
|
+
*/
|
|
36
50
|
maxZoom?: number,
|
|
37
51
|
|
|
38
52
|
/**
|
|
@@ -40,7 +54,11 @@ export type FitCameraOptions = {
|
|
|
40
54
|
*/
|
|
41
55
|
objects?: Object3D[] | Object3D;
|
|
42
56
|
|
|
43
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* A factor to control padding around the fitted objects.
|
|
59
|
+
*
|
|
60
|
+
* Values > 1 will add more space around the fitted objects, values < 1 will zoom in closer.
|
|
61
|
+
*
|
|
44
62
|
* @default 1.1
|
|
45
63
|
*/
|
|
46
64
|
fitOffset?: number,
|
|
@@ -74,7 +92,7 @@ export type FitCameraOptions = {
|
|
|
74
92
|
relativeTargetOffset?: Partial<Vector3Like>,
|
|
75
93
|
|
|
76
94
|
/**
|
|
77
|
-
*
|
|
95
|
+
* Target field of view (FOV) for the camera
|
|
78
96
|
*/
|
|
79
97
|
fov?: number,
|
|
80
98
|
}
|
|
@@ -86,7 +104,35 @@ export type FitCameraReturnType = {
|
|
|
86
104
|
fov: number | undefined
|
|
87
105
|
}
|
|
88
106
|
|
|
89
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Fit the camera to the specified objects or the whole scene.
|
|
109
|
+
* Adjusts the camera position and optionally the FOV to ensure all objects are visible.
|
|
110
|
+
*
|
|
111
|
+
* @example Fit the main camera to the entire scene:
|
|
112
|
+
* ```ts
|
|
113
|
+
* import { fitCamera } from '@needle-tools/engine';
|
|
114
|
+
*
|
|
115
|
+
* // Fit the main camera to the entire scene
|
|
116
|
+
* fitCamera();
|
|
117
|
+
* ```
|
|
118
|
+
* @example Fit a specific camera to specific objects with custom options:
|
|
119
|
+
* ```ts
|
|
120
|
+
* import { fitCamera } from '@needle-tools/engine';
|
|
121
|
+
*
|
|
122
|
+
* // Fit a specific camera to specific objects with custom options
|
|
123
|
+
* const myCamera = ...; // your camera
|
|
124
|
+
* const objectsToFit = [...]; // array of objects to fit
|
|
125
|
+
* fitCamera({
|
|
126
|
+
* camera: myCamera,
|
|
127
|
+
* objects: objectsToFit,
|
|
128
|
+
* fitOffset: 1,
|
|
129
|
+
* fov: 20,
|
|
130
|
+
* });
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* @param options Options for fitting the camera
|
|
134
|
+
* @returns
|
|
135
|
+
*/
|
|
90
136
|
export function fitCamera(options?: FitCameraOptions): null | FitCameraReturnType {
|
|
91
137
|
|
|
92
138
|
if (NeedleXRSession.active) {
|
|
@@ -129,9 +129,12 @@ export function registerComponent(script: IComponent, context?: Context) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
|
-
* The context is the main
|
|
133
|
-
* It can be used to access the scene, renderer,
|
|
134
|
-
*
|
|
132
|
+
* The Needle Engine context is the main access point that holds all the data and state of a Needle Engine application.
|
|
133
|
+
* It can be used to access the {@link Context.scene}, {@link Context.renderer}, {@link Context.mainCamera}, {@link Context.input}, {@link Context.physics}, {@link Context.time}, {@link Context.connection} (networking), and more.
|
|
134
|
+
*
|
|
135
|
+
* The context is automatically created when using the `<needle-engine>` web component.
|
|
136
|
+
*
|
|
137
|
+
* @example Accessing the context from a [component](https://engine.needle.tools/docs/api/Behaviour):
|
|
135
138
|
* ```typescript
|
|
136
139
|
* import { Behaviour } from "@needle-tools/engine";
|
|
137
140
|
* import { Mesh, BoxGeometry, MeshBasicMaterial } from "three";
|
|
@@ -142,6 +145,18 @@ export function registerComponent(script: IComponent, context?: Context) {
|
|
|
142
145
|
* }
|
|
143
146
|
* }
|
|
144
147
|
* ```
|
|
148
|
+
*
|
|
149
|
+
* @example Accessing the context from a [hook](https://engine.needle.tools/docs/scripting.html#hooks) without a component e.g. from a javascript module or svelte or react component.
|
|
150
|
+
*
|
|
151
|
+
* ```typescript
|
|
152
|
+
* import { onStart } from "@needle-tools/engine";
|
|
153
|
+
*
|
|
154
|
+
* onStart((context) => {
|
|
155
|
+
* console.log("Hello from onStart hook");
|
|
156
|
+
* context.scene.add(new Mesh(new BoxGeometry(), new MeshBasicMaterial()));
|
|
157
|
+
* });
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
145
160
|
*/
|
|
146
161
|
export class Context implements IContext {
|
|
147
162
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Object3D } from "three";
|
|
2
2
|
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
|
|
3
3
|
import { type GLTF, type GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
4
|
+
import { isDevEnvironment } from "../debug/debug.js";
|
|
4
5
|
|
|
5
6
|
import { builtinComponentKeyName } from "../engine_constants.js";
|
|
6
7
|
import { debugExtension } from "../engine_default_parameters.js";
|
|
7
8
|
import { getLoader } from "../engine_gltf.js";
|
|
8
9
|
import { type NodeToObjectMap, type ObjectToNodeMap, SerializationContext } from "../engine_serialization_core.js";
|
|
9
10
|
import { apply } from "../js-extensions/index.js";
|
|
10
|
-
import { maskGltfAssociation,resolveReferences } from "./extension_utils.js";
|
|
11
|
+
import { maskGltfAssociation, resolveReferences } from "./extension_utils.js";
|
|
11
12
|
|
|
12
13
|
export const debug = debugExtension
|
|
13
14
|
const componentsArrayExportKey = "$___Export_Components";
|
|
@@ -15,7 +16,7 @@ const componentsArrayExportKey = "$___Export_Components";
|
|
|
15
16
|
export const EXTENSION_NAME = "NEEDLE_components";
|
|
16
17
|
|
|
17
18
|
class ExtensionData {
|
|
18
|
-
[builtinComponentKeyName]?: Array<
|
|
19
|
+
[builtinComponentKeyName]?: Array<Record<string, any> | null>
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
class ExportData {
|
|
@@ -35,14 +36,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
35
36
|
get name(): string {
|
|
36
37
|
return EXTENSION_NAME;
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
// import
|
|
40
|
-
parser?: GLTFParser;
|
|
41
|
-
nodeToObjectMap: NodeToObjectMap = {};
|
|
42
|
-
/** The loaded gltf */
|
|
43
|
-
gltf: GLTF | null = null;
|
|
44
|
-
|
|
45
|
-
// export
|
|
39
|
+
// #region export
|
|
46
40
|
exportContext!: { [nodeIndex: number]: ExportData };
|
|
47
41
|
objectToNodeMap: ObjectToNodeMap = {};
|
|
48
42
|
context!: SerializationContext;
|
|
@@ -112,7 +106,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
112
106
|
|
|
113
107
|
writeNode(node: Object3D, nodeDef) {
|
|
114
108
|
const nodeIndex = this.writer.json.nodes.length;
|
|
115
|
-
if (debug)
|
|
109
|
+
if (debug)
|
|
116
110
|
console.log(node.name, nodeIndex, node.uuid);
|
|
117
111
|
const context = new ExportData(node, nodeIndex, nodeDef);
|
|
118
112
|
this.exportContext[nodeIndex] = context;
|
|
@@ -158,8 +152,13 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
158
152
|
|
|
159
153
|
|
|
160
154
|
// -------------------------------------
|
|
161
|
-
//
|
|
162
|
-
|
|
155
|
+
// #region import
|
|
156
|
+
parser?: GLTFParser;
|
|
157
|
+
nodeToObjectMap: NodeToObjectMap = {};
|
|
158
|
+
/** The loaded gltf */
|
|
159
|
+
gltf: GLTF | null = null;
|
|
160
|
+
|
|
161
|
+
|
|
163
162
|
beforeRoot() {
|
|
164
163
|
if (debug)
|
|
165
164
|
console.log("BEGIN LOAD");
|
|
@@ -167,7 +166,6 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
167
166
|
return null;
|
|
168
167
|
}
|
|
169
168
|
|
|
170
|
-
// called by GLTFLoader
|
|
171
169
|
async afterRoot(result: GLTF): Promise<void> {
|
|
172
170
|
this.gltf = result;
|
|
173
171
|
|
|
@@ -175,8 +173,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
175
173
|
const ext = parser?.extensions;
|
|
176
174
|
if (!ext) return;
|
|
177
175
|
const hasExtension = ext[this.name];
|
|
178
|
-
if (debug)
|
|
179
|
-
console.log("After root", result, this.parser, ext);
|
|
176
|
+
if (debug) console.log("After root", result, this.parser, ext);
|
|
180
177
|
|
|
181
178
|
const loadComponents: Array<Promise<void>> = [];
|
|
182
179
|
if (hasExtension === true) {
|
|
@@ -204,7 +201,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
204
201
|
|
|
205
202
|
apply(obj);
|
|
206
203
|
|
|
207
|
-
loadComponents.push(this.createComponents(obj, data));
|
|
204
|
+
loadComponents.push(this.createComponents(result, node, obj, data));
|
|
208
205
|
}
|
|
209
206
|
}
|
|
210
207
|
}
|
|
@@ -220,7 +217,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
220
217
|
}
|
|
221
218
|
}
|
|
222
219
|
|
|
223
|
-
private async createComponents(obj: Object3D, data: ExtensionData) {
|
|
220
|
+
private async createComponents(result: GLTF, node: Node, obj: Object3D, data: ExtensionData) {
|
|
224
221
|
if (!data) return;
|
|
225
222
|
const componentData = data[builtinComponentKeyName];
|
|
226
223
|
if (componentData) {
|
|
@@ -228,20 +225,42 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
228
225
|
if (debug)
|
|
229
226
|
console.log(obj.name, componentData);
|
|
230
227
|
for (const i in componentData) {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
228
|
+
const data = componentData[i];
|
|
229
|
+
|
|
230
|
+
if (debug) console.log("Serialized data", JSON.parse(JSON.stringify(data)));
|
|
231
|
+
|
|
232
|
+
// Fix for https://linear.app/needle/issue/NE-6779/blender-export-has-missing-sharedmaterials
|
|
233
|
+
if (data?.name === "MeshRenderer" || data?.name === "SkinnedMeshRenderer") {
|
|
234
|
+
if (!data.sharedMaterials) {
|
|
235
|
+
let success = false;
|
|
236
|
+
if ("mesh" in node) {
|
|
237
|
+
const meshIndex = node.mesh;
|
|
238
|
+
if (typeof meshIndex === "number" && result.parser) {
|
|
239
|
+
const meshDef = result.parser.json.meshes?.[meshIndex];
|
|
240
|
+
if (meshDef?.primitives) {
|
|
241
|
+
data.sharedMaterials = meshDef.primitives.map(prim => {
|
|
242
|
+
return "/materials/" + (prim.material ?? 0);
|
|
243
|
+
});
|
|
244
|
+
success = true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if(!success && (debug || isDevEnvironment())) {
|
|
249
|
+
console.warn(`[NEEDLE_components] Component '${data.name}' on object '${obj.name}' is not added to a mesh or failed to retrieve materials from glTF.`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
234
253
|
|
|
235
|
-
if (
|
|
254
|
+
if (data && this.parser) {
|
|
236
255
|
tasks.push(
|
|
237
|
-
resolveReferences(this.parser,
|
|
238
|
-
.catch(e => console.error(`Error while resolving references (see console for details)\n`, e, obj,
|
|
256
|
+
resolveReferences(this.parser, data)
|
|
257
|
+
.catch(e => console.error(`Error while resolving references (see console for details)\n`, e, obj, data))
|
|
239
258
|
);
|
|
240
259
|
}
|
|
241
260
|
|
|
242
261
|
obj.userData = obj.userData || {};
|
|
243
262
|
obj.userData[builtinComponentKeyName] = obj.userData[builtinComponentKeyName] || [];
|
|
244
|
-
obj.userData[builtinComponentKeyName].push(
|
|
263
|
+
obj.userData[builtinComponentKeyName].push(data);
|
|
245
264
|
}
|
|
246
265
|
await Promise.all(tasks).catch((e) => {
|
|
247
266
|
console.error("Error while loading components", e);
|
|
@@ -266,4 +285,6 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
|
|
|
266
285
|
// // console.log(components);
|
|
267
286
|
// return null;
|
|
268
287
|
// }
|
|
269
|
-
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
@@ -69,7 +69,7 @@ class SharedMaterialArray implements ISharedMaterials {
|
|
|
69
69
|
set changed(value: boolean) {
|
|
70
70
|
if (value === true) {
|
|
71
71
|
if (debugRenderer)
|
|
72
|
-
console.warn("SharedMaterials have changed: " + this._renderer.name
|
|
72
|
+
console.warn("SharedMaterials have changed: " + this._renderer.name);
|
|
73
73
|
}
|
|
74
74
|
this._changed = value;
|
|
75
75
|
}
|
|
@@ -358,11 +358,15 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
358
358
|
//@ts-ignore
|
|
359
359
|
get sharedMaterials(): SharedMaterialArray {
|
|
360
360
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
361
|
+
if (this._originalMaterials === undefined) {
|
|
362
|
+
if (!this.__didAwake) {
|
|
363
|
+
// @ts-ignore (original materials will be set during deserialization)
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
this._originalMaterials = [];
|
|
368
|
+
}
|
|
369
|
+
}
|
|
366
370
|
|
|
367
371
|
if (!this._sharedMaterials || !this._sharedMaterials.is(this)) {
|
|
368
372
|
if (!this._originalMaterials) this._originalMaterials = [];
|
|
@@ -454,6 +458,7 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
454
458
|
this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
|
|
455
459
|
}
|
|
456
460
|
|
|
461
|
+
this._lightmaps = undefined;
|
|
457
462
|
this.applyLightmapping();
|
|
458
463
|
|
|
459
464
|
if (showWireframe) {
|
|
@@ -468,8 +473,8 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
468
473
|
}
|
|
469
474
|
|
|
470
475
|
private applyLightmapping() {
|
|
471
|
-
if (this.lightmapIndex >= 0) {
|
|
472
|
-
const type = this.gameObject.type;
|
|
476
|
+
if (this.lightmapIndex >= 0 && !this._lightmaps) {
|
|
477
|
+
// const type = this.gameObject.type;
|
|
473
478
|
|
|
474
479
|
// use the override lightmap if its not undefined
|
|
475
480
|
const tex = this._lightmapTextureOverride !== undefined
|
|
@@ -477,45 +482,12 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
477
482
|
: this.context.lightmaps.tryGetLightmap(this.sourceId, this.lightmapIndex);
|
|
478
483
|
if (tex) {
|
|
479
484
|
if (!this._lightmaps) this._lightmaps = [];
|
|
480
|
-
|
|
481
|
-
|
|
482
485
|
const rm = new RendererLightmap(this);
|
|
483
486
|
rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
|
|
484
487
|
this._lightmaps.push(rm);
|
|
485
|
-
|
|
486
|
-
// if (type === "Mesh") {
|
|
487
|
-
// const mat = this.gameObject["material"];
|
|
488
|
-
// if (!mat?.isMeshBasicMaterial) {
|
|
489
|
-
// if (this._lightmaps.length <= 0) {
|
|
490
|
-
// }
|
|
491
|
-
// const rm = this._lightmaps[0];
|
|
492
|
-
// rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
|
|
493
|
-
// }
|
|
494
|
-
// else {
|
|
495
|
-
// if (mat)
|
|
496
|
-
// console.warn("Lightmapping is not supported on MeshBasicMaterial", mat.name)
|
|
497
|
-
// }
|
|
498
|
-
// }
|
|
499
|
-
// // for multi materials we need to loop through children
|
|
500
|
-
// // and then we add a lightmap renderer component to each of them
|
|
501
|
-
// else if (this.isMultiMaterialObject(this.gameObject) && this.sharedMaterials.length > 0) {
|
|
502
|
-
// for (let i = 0; i < this.gameObject.children.length; i++) {
|
|
503
|
-
// const child = this.gameObject.children[i];
|
|
504
|
-
// if (!child["material"]?.isMeshBasicMaterial) {
|
|
505
|
-
// let rm: RendererLightmap | undefined = undefined;
|
|
506
|
-
// if (i >= this._lightmaps.length) {
|
|
507
|
-
// rm = new RendererLightmap(child as Mesh, this.context);
|
|
508
|
-
// this._lightmaps.push(rm);
|
|
509
|
-
// }
|
|
510
|
-
// else
|
|
511
|
-
// rm = this._lightmaps[i];
|
|
512
|
-
// rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
|
|
513
|
-
// }
|
|
514
|
-
// }
|
|
515
|
-
// }
|
|
516
488
|
}
|
|
517
489
|
else {
|
|
518
|
-
if (debugRenderer) console.warn(
|
|
490
|
+
if (debugRenderer) console.warn(`[Renderer] No lightmaps found ${this.name} (${this.sourceId}, ${this.lightmapIndex})`);
|
|
519
491
|
}
|
|
520
492
|
}
|
|
521
493
|
|
|
@@ -9,6 +9,10 @@ const debug = getParam("debuglightmaps");
|
|
|
9
9
|
|
|
10
10
|
declare type MaterialWithLightmap = Material & { lightMap?: Texture | null };
|
|
11
11
|
|
|
12
|
+
let cloningCounter = 0;
|
|
13
|
+
|
|
14
|
+
const $lightmapVersion = Symbol("lightmap-material-version");
|
|
15
|
+
|
|
12
16
|
|
|
13
17
|
/**
|
|
14
18
|
* This component is automatically added by the {@link Renderer} component if the object has lightmap uvs AND we have a lightmap.
|
|
@@ -37,6 +41,8 @@ export class RendererLightmap {
|
|
|
37
41
|
private lightmapScaleOffset: Vector4 = new Vector4(1, 1, 0, 0);
|
|
38
42
|
|
|
39
43
|
private readonly renderer: Renderer;
|
|
44
|
+
private readonly clonedMaterials = new Array<Material>();
|
|
45
|
+
|
|
40
46
|
private get context(): Context { return this.renderer.context; }
|
|
41
47
|
private get gameObject() { return this.renderer.gameObject; }
|
|
42
48
|
private lightmapTexture: Texture | null = null;
|
|
@@ -89,8 +95,7 @@ export class RendererLightmap {
|
|
|
89
95
|
|
|
90
96
|
const mat = this.renderer.sharedMaterials[i];
|
|
91
97
|
if (!mat) continue;
|
|
92
|
-
|
|
93
|
-
const newMat = this.ensureLightmapMaterial(mat);
|
|
98
|
+
const newMat = this.ensureLightmapMaterial(mat, i);
|
|
94
99
|
if (mat !== newMat) {
|
|
95
100
|
this.renderer.sharedMaterials[i] = newMat;
|
|
96
101
|
}
|
|
@@ -118,23 +123,28 @@ export class RendererLightmap {
|
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
private ensureLightmapMaterial(material: Material) {
|
|
126
|
+
private ensureLightmapMaterial(material: Material, index: number) {
|
|
122
127
|
if (!material.userData) material.userData = {};
|
|
123
128
|
// if (material instanceof MeshPhysicalMaterial) {
|
|
124
129
|
// return material;
|
|
125
130
|
// }
|
|
126
131
|
// check if the material version has changed and only then clone the material
|
|
127
|
-
if (
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// we need to clone the material
|
|
132
|
+
if (this.clonedMaterials[index] !== material) {
|
|
133
|
+
if (debug) {
|
|
134
|
+
++cloningCounter;
|
|
135
|
+
if (cloningCounter++ < 1000) {
|
|
136
|
+
console.warn(`Cloning material for lightmap ${this.renderer.name}: '${material.name}'`);
|
|
137
|
+
}
|
|
138
|
+
else if (cloningCounter === 1000) {
|
|
139
|
+
console.warn(`Further material cloning for lightmaps suppressed to avoid flooding the console.`);
|
|
140
|
+
}
|
|
137
141
|
}
|
|
142
|
+
const mat: Material = material.clone();
|
|
143
|
+
if (!mat.name?.includes("(lightmap)")) mat.name = material.name + " (lightmap)";
|
|
144
|
+
material = mat;
|
|
145
|
+
material.onBeforeCompile = this.onBeforeCompile;
|
|
146
|
+
this.clonedMaterials[index] = material;
|
|
147
|
+
|
|
138
148
|
}
|
|
139
149
|
return material;
|
|
140
150
|
}
|
|
@@ -144,22 +154,22 @@ export class RendererLightmap {
|
|
|
144
154
|
if (material instanceof MeshPhysicalMaterial && material.transmission > 0) {
|
|
145
155
|
return;
|
|
146
156
|
}
|
|
147
|
-
const hasChanged = material.lightMap !== this.lightmapTexture || material[
|
|
157
|
+
const hasChanged = material.lightMap !== this.lightmapTexture || material[$lightmapVersion] !== material.version;
|
|
148
158
|
if (!hasChanged) {
|
|
149
159
|
return;
|
|
150
160
|
}
|
|
151
161
|
|
|
152
|
-
if (debug) console.log(
|
|
162
|
+
if (debug) console.log(`Assigning lightmap texture ${this.renderer.name}: '${material.name}' (${material.version} ${material[$lightmapVersion]})`);
|
|
153
163
|
|
|
154
164
|
// assign the lightmap
|
|
155
165
|
material.lightMap = this.lightmapTexture;
|
|
156
166
|
material.needsUpdate = true;
|
|
157
167
|
// store the version of the material
|
|
158
|
-
material[
|
|
168
|
+
material[$lightmapVersion] = material.version;
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
private onBeforeCompile = (shader: WebGLProgramParametersWithUniforms, _) => {
|
|
162
|
-
if (debug) console.log("Lightmaps, before compile\n", shader)
|
|
172
|
+
if (debug === "verbose") console.log("Lightmaps, before compile\n", shader)
|
|
163
173
|
this.lightmapScaleOffsetUniform.value = this.lightmapScaleOffset;
|
|
164
174
|
this.lightmapUniform.value = this.lightmapTexture;
|
|
165
175
|
shader.uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
|
|
@@ -114,8 +114,7 @@ export class PlayableDirector extends Behaviour {
|
|
|
114
114
|
|
|
115
115
|
/** @internal */
|
|
116
116
|
awake(): void {
|
|
117
|
-
if (debug)
|
|
118
|
-
console.log(this, this.playableAsset);
|
|
117
|
+
if (debug) console.log(`[Timeline] Awake '${this.name}'`, this);
|
|
119
118
|
|
|
120
119
|
this.rebuildGraph();
|
|
121
120
|
|
|
@@ -134,6 +133,8 @@ export class PlayableDirector extends Behaviour {
|
|
|
134
133
|
|
|
135
134
|
/** @internal */
|
|
136
135
|
onEnable() {
|
|
136
|
+
if (debug) console.log("[Timeline] OnEnable", this.name, this.playOnAwake);
|
|
137
|
+
|
|
137
138
|
for (const track of this._audioTracks) {
|
|
138
139
|
track.onEnable?.();
|
|
139
140
|
}
|
|
@@ -162,6 +163,8 @@ export class PlayableDirector extends Behaviour {
|
|
|
162
163
|
|
|
163
164
|
/** @internal */
|
|
164
165
|
onDisable(): void {
|
|
166
|
+
if (debug) console.log("[Timeline] OnDisable", this.name);
|
|
167
|
+
|
|
165
168
|
this.stop();
|
|
166
169
|
for (const track of this._audioTracks) {
|
|
167
170
|
track.onDisable?.();
|
|
@@ -360,14 +363,17 @@ export class PlayableDirector extends Behaviour {
|
|
|
360
363
|
private readonly _controlTracks: Array<Tracks.ControlTrackHandler> = [];
|
|
361
364
|
private readonly _customTracks: Array<Tracks.TrackHandler> = [];
|
|
362
365
|
|
|
363
|
-
private readonly
|
|
364
|
-
|
|
365
|
-
this.
|
|
366
|
-
this.
|
|
367
|
-
this.
|
|
368
|
-
this.
|
|
369
|
-
this.
|
|
370
|
-
|
|
366
|
+
private readonly _tracksArray: Array<Array<Tracks.TrackHandler>> = [];
|
|
367
|
+
private get _allTracks(): Array<Array<Tracks.TrackHandler>> {
|
|
368
|
+
this._tracksArray.length = 0;
|
|
369
|
+
this._tracksArray.push(this._animationTracks);
|
|
370
|
+
this._tracksArray.push(this._audioTracks);
|
|
371
|
+
this._tracksArray.push(this._signalTracks);
|
|
372
|
+
this._tracksArray.push(this._markerTracks);
|
|
373
|
+
this._tracksArray.push(this._controlTracks);
|
|
374
|
+
this._tracksArray.push(this._customTracks);
|
|
375
|
+
return this._tracksArray;
|
|
376
|
+
}
|
|
371
377
|
|
|
372
378
|
/** should be called after evaluate if the director was playing */
|
|
373
379
|
private invokePauseChangedMethodsOnTracks() {
|