Needle Engine

Changes between version 3.27.0-beta and 3.27.1-beta
Files changed (4) hide show
  1. src/engine/extensions/extensions.ts +30 -16
  2. src/engine-components/export/gltf/GltfExport.ts +11 -17
  3. src/engine-components/export/usdz/index.ts +1 -1
  4. src/engine-components/Renderer.ts +4 -1
src/engine/extensions/extensions.ts CHANGED
@@ -1,12 +1,9 @@
1
1
  import { NEEDLE_techniques_webgl } from "./NEEDLE_techniques_webgl.js";
2
- import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
2
+ import { GLTFLoader, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { NEEDLE_components } from "./NEEDLE_components.js";
4
4
  import { EXT_texture_exr } from "./EXT_texture_exr.js";
5
5
  import { NEEDLE_gameobject_data } from "./NEEDLE_gameobject_data.js";
6
- // import { NEEDLE_timeline } from "./NEEDLE_timeline.js";
7
- // import { NEEDLE_animator_controller } from "./NEEDLE_animator_controller.js";
8
6
  import { NEEDLE_persistent_assets } from "./NEEDLE_persistent_assets.js";
9
- // import { KHR_animation_pointer } from "./KHR_animation_pointer.js";
10
7
  import { NEEDLE_lightmaps } from "../extensions/NEEDLE_lightmaps.js";
11
8
  import { type ConstructorConcrete, type SourceIdentifier } from "../engine_types.js";
12
9
  import { Context } from "../engine_setup.js";
@@ -15,10 +12,9 @@
15
12
  import { NEEDLE_progressive } from "./NEEDLE_progressive.js";
16
13
  import { InternalUsageTrackerPlugin } from "./usage_tracker.js";
17
14
  import { isResourceTrackingEnabled } from "../engine_assetdatabase.js";
18
- import { type GLTFLoaderPlugin } from "three/examples/jsm/loaders/GLTFLoader.js";
19
15
  import { getParam } from "../engine_utils.js";
20
16
  import { isDevEnvironment } from "../debug/index.js";
21
- // import { GLTFAnimationPointerExtension } from "three/examples/jsm/loaders/GLTFLoaderAnimationPointer.js";
17
+ import { GLTFExporter, GLTFExporterPlugin, GLTFWriter } from "three/examples/jsm/exporters/GLTFExporter.js";
22
18
 
23
19
  const debug = getParam("debugextensions");
24
20
 
@@ -31,20 +27,32 @@
31
27
  console.warn("Failed to import GLTFLoaderAnimationPointer. Please use @needle-tools/three for full KHR_animation support", e);
32
28
  });
33
29
 
34
- const _addedCustomExtension = new Array<ConstructorConcrete<GLTFLoaderPlugin>>();
35
30
 
36
- export function addCustomExtension(ext: ConstructorConcrete<GLTFLoaderPlugin>) {
37
- if (!_addedCustomExtension.includes(ext)) {
38
- if(!ext.name) console.warn("Custom extension has no name", ext);
39
- _addedCustomExtension.push(ext);
31
+ declare type OnImportCallback = (loader: GLTFLoader, sourceId: SourceIdentifier, context: Context) => void;
32
+ declare type OnExportCallback = (exp: GLTFExporter, context: Context) => void;
33
+
34
+ export interface INeedleGLTFExtensionPlugin {
35
+ name: string;
36
+ onImport?: OnImportCallback;
37
+ onExport?: OnExportCallback
38
+ }
39
+
40
+ /** Register callbacks for registering custom gltf importer or exporter plugins */
41
+ export function addCustomExtensionPlugin(ext: INeedleGLTFExtensionPlugin) {
42
+ if (!_plugins.includes(ext)) {
43
+ _plugins.push(ext);
40
44
  }
41
45
  }
42
- export function removeCustomExtension(ext: ConstructorConcrete<GLTFLoaderPlugin>) {
43
- const index = _addedCustomExtension.indexOf(ext);
46
+ /** Unregister callbacks for registering custom gltf importer or exporter plugins */
47
+ export function removeCustomImportExtensionType(ext: INeedleGLTFExtensionPlugin) {
48
+ const index = _plugins.indexOf(ext);
44
49
  if (index >= 0)
45
- _addedCustomExtension.splice(index, 1);
50
+ _plugins.splice(index, 1);
46
51
  }
52
+ const _plugins = new Array<INeedleGLTFExtensionPlugin>();
47
53
 
54
+
55
+ /** Registers the Needle Engine components extension */
48
56
  export function registerComponentExtension(loader: GLTFLoader): NEEDLE_components {
49
57
  const ext = new NEEDLE_components();
50
58
  loader.register(p => {
@@ -83,8 +91,9 @@
83
91
  loader.register(p => new EXT_texture_exr(p));
84
92
  if (isResourceTrackingEnabled()) loader.register(p => new InternalUsageTrackerPlugin(p))
85
93
 
86
- for (const ext of _addedCustomExtension)
87
- loader.register(p => new ext(p));
94
+ for (const plugin of _plugins) {
95
+ if (plugin.onImport) plugin.onImport(loader, sourceId, context);
96
+ }
88
97
 
89
98
  await KHR_ANIMATIONPOINTER_IMPORT.catch(_ => { })
90
99
  loader.register(p => {
@@ -102,4 +111,9 @@
102
111
  }
103
112
  });
104
113
 
114
+ }
115
+
116
+ export function registerExportExtensions(exp: GLTFExporter, context: Context) {
117
+ for (const ext of _plugins)
118
+ if (ext.onExport) ext.onExport(exp, context);
105
119
  }
src/engine-components/export/gltf/GltfExport.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Object3D, Vector3 } from "three";
2
- import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
2
+ import { GLTFExporter, type GLTFExporterOptions } from 'three/examples/jsm/exporters/GLTFExporter.js';
3
3
 
4
4
  import { Behaviour, GameObject } from "../../Component.js";
5
5
  import GLTFMeshGPUInstancingExtension from '../../../include/three/EXT_mesh_gpu_instancing_exporter.js';
@@ -11,11 +11,11 @@
11
11
  import { BoxHelperComponent } from "../../BoxHelperComponent.js";
12
12
  import { AnimationClip } from "three";
13
13
  import { getParam } from "../../../engine/engine_utils.js";
14
+ import { registerExportExtensions } from "../../../engine/extensions/index.js";
14
15
 
15
16
  const debugExport = getParam("debuggltfexport");
16
17
 
17
- declare type ExportOptions = {
18
- binary: boolean,
18
+ declare type ExportOptions = GLTFExporterOptions & {
19
19
  pivot?: THREE.Vector3
20
20
  }
21
21
 
@@ -34,7 +34,6 @@
34
34
  @serializable(Object3D)
35
35
  objects: Object3D[] = [];
36
36
 
37
- private exporter?: GLTFExporter;
38
37
  private ext?: NEEDLE_components;
39
38
 
40
39
  async exportNow(name: string) {
@@ -70,16 +69,11 @@
70
69
  return;
71
70
  }
72
71
 
73
- if (!this.exporter) {
74
- // Instantiate a exporter
75
- this.exporter = new GLTFExporter();
76
- this.exporter.register(writer => new GLTFMeshGPUInstancingExtension(writer));
72
+ // Instantiate a exporter
73
+ const exporter = new GLTFExporter();
74
+ exporter.register(writer => new GLTFMeshGPUInstancingExtension(writer));
75
+ registerExportExtensions(exporter, this.context);
77
76
 
78
- // TODO
79
- // this.ext = new NEEDLE_components();
80
- // this.ext.registerExport(this.exporter);
81
- }
82
-
83
77
  GltfExport.filterTopmostParent(objectsToExport);
84
78
 
85
79
  // TODO export only worldglb BUT exclude "World" child which contains all build tools
@@ -90,11 +84,12 @@
90
84
  trs: false,
91
85
  onlyVisible: true,
92
86
  truncateDrawRange: false,
93
- binary: opts?.binary ?? true,
87
+ binary: true,
94
88
  maxTextureSize: Infinity, // To prevent NaN value,
95
89
  embedImages: true,
96
90
  includeCustomExtensions: true,
97
- animations: GltfExport.collectAnimations(objectsToExport),
91
+ animations: opts?.animations || GltfExport.collectAnimations(objectsToExport),
92
+ ...opts
98
93
  };
99
94
 
100
95
  // hide objects that we don't want to export
@@ -128,7 +123,7 @@
128
123
  if (debugExport) console.log("Starting glTF export.")
129
124
  try {
130
125
  // Parse the input and generate the glTF output
131
- this.exporter?.parse(
126
+ exporter?.parse(
132
127
  exportScene,
133
128
  // called when the gltf has been generated
134
129
  res => {
@@ -140,7 +135,6 @@
140
135
  cleanup();
141
136
  reject(err);
142
137
  },
143
- //@ts-ignore
144
138
  options
145
139
  );
146
140
  }
src/engine-components/export/usdz/index.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { USDZExporter } from "./USDZExporter.js";
2
- export { USDObject } from "./ThreeUSDZExporter.js";
2
+ export { USDObject, imageToCanvas } from "./ThreeUSDZExporter.js";
3
3
  export { type UsdzBehaviour } from "./extensions/behavior/Behaviour.js";
src/engine-components/Renderer.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  import { AxesHelper, Material, Matrix4, Mesh, Object3D, SkinnedMesh, Texture, Vector4 } from "three";
9
9
  import { NEEDLE_render_objects } from "../engine/extensions/NEEDLE_render_objects.js";
10
10
  import { NEEDLE_progressive } from "../engine/extensions/NEEDLE_progressive.js";
11
- import { NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing.js";
11
+ import { InstancingUtil, NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing.js";
12
12
  import type { IRenderer, ISharedMaterials } from "../engine/engine_types.js";
13
13
  import { ReflectionProbe } from "./ReflectionProbe.js";
14
14
  import { setCustomVisibility } from "../engine/js-extensions/Layers.js";
@@ -505,6 +505,9 @@
505
505
  start() {
506
506
  if (this.enableInstancing && !suppressInstancing) {
507
507
  this.setInstancingEnabled(true);
508
+ // make sure the instance is marked dirty once for cases where e.g. an animator animates the instanced object
509
+ // in the first frame we want the updated matrix then to be applied immediately to the instancing
510
+ InstancingUtil.markDirty(this.gameObject);
508
511
  }
509
512
  this.gameObject.frustumCulled = this.allowOcclusionWhenDynamic;
510
513
  if (this.isMultiMaterialObject(this.gameObject)) {