@needle-tools/engine 4.11.0-next.91b9cf1 → 4.11.0-next.cc37c71

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.
Files changed (130) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +3 -1
  3. package/components.needle.json +1 -1
  4. package/dist/{gltf-progressive-B63NpN_i.js → gltf-progressive-BvlZQAkt.js} +4 -4
  5. package/dist/{gltf-progressive-D4Z_Khp3.min.js → gltf-progressive-CftVUJy3.min.js} +1 -1
  6. package/dist/{gltf-progressive-CHeORqEv.umd.cjs → gltf-progressive-GwdQV1Qx.umd.cjs} +1 -1
  7. package/dist/{needle-engine.bundle-D4dsuq2U.js → needle-engine.bundle-BPZ6emFK.js} +7858 -7724
  8. package/dist/{needle-engine.bundle-DtfAXDjU.umd.cjs → needle-engine.bundle-CTY0RgBZ.umd.cjs} +150 -150
  9. package/dist/needle-engine.bundle-JV2ghuCa.min.js +1652 -0
  10. package/dist/needle-engine.d.ts +6 -0
  11. package/dist/needle-engine.js +4 -4
  12. package/dist/needle-engine.min.js +1 -1
  13. package/dist/needle-engine.umd.cjs +1 -1
  14. package/dist/{postprocessing-DQ2pynXW.js → postprocessing-CJC0Npcd.js} +2 -2
  15. package/dist/{postprocessing-BsnRNRRS.umd.cjs → postprocessing-DrM4PWU3.umd.cjs} +1 -1
  16. package/dist/{postprocessing-BHMVuZQ1.min.js → postprocessing-l7zsdO_Q.min.js} +1 -1
  17. package/dist/{three-qw28ZtTy.min.js → three-BDW9I486.min.js} +13 -13
  18. package/dist/{three-CJSAehtG.js → three-MHVqtJYj.js} +1 -0
  19. package/dist/{three-examples-Doq0rvFU.js → three-examples-C5Ht-QFN.js} +1 -1
  20. package/dist/{three-examples-Deqc1bNw.umd.cjs → three-examples-CgwGHSgz.umd.cjs} +1 -1
  21. package/dist/{three-examples-BivkhnvN.min.js → three-examples-fvEPSC8L.min.js} +1 -1
  22. package/dist/{three-B-jwTHao.umd.cjs → three-iFaDq9U3.umd.cjs} +13 -13
  23. package/dist/{three-mesh-ui-CktOi6oI.js → three-mesh-ui-BjWTTk1R.js} +1 -1
  24. package/dist/{three-mesh-ui-CsHwj9cJ.umd.cjs → three-mesh-ui-Bm32sS2a.umd.cjs} +1 -1
  25. package/dist/{three-mesh-ui-DhYXcXZe.min.js → three-mesh-ui-CLdkp21K.min.js} +1 -1
  26. package/dist/{vendor-BcsPRUmt.umd.cjs → vendor-CAWj5cBK.umd.cjs} +2 -2
  27. package/dist/{vendor-CyfN5nor.js → vendor-DJBpoQcM.js} +608 -599
  28. package/dist/{vendor-DyavoogU.min.js → vendor-DWGd3dEf.min.js} +20 -20
  29. package/lib/engine/engine_physics.js +25 -2
  30. package/lib/engine/engine_physics.js.map +1 -1
  31. package/lib/engine/js-extensions/Object3D.d.ts +6 -0
  32. package/lib/engine/js-extensions/Object3D.js +15 -0
  33. package/lib/engine/js-extensions/Object3D.js.map +1 -1
  34. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +2 -1
  35. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  36. package/lib/engine-components/Collider.d.ts +26 -0
  37. package/lib/engine-components/Collider.js +26 -0
  38. package/lib/engine-components/Collider.js.map +1 -1
  39. package/lib/engine-components/ContactShadows.d.ts +11 -2
  40. package/lib/engine-components/ContactShadows.js +11 -2
  41. package/lib/engine-components/ContactShadows.js.map +1 -1
  42. package/lib/engine-components/DropListener.d.ts +3 -0
  43. package/lib/engine-components/DropListener.js +44 -21
  44. package/lib/engine-components/DropListener.js.map +1 -1
  45. package/lib/engine-components/Duplicatable.d.ts +2 -2
  46. package/lib/engine-components/Duplicatable.js +2 -2
  47. package/lib/engine-components/EventList.d.ts +18 -1
  48. package/lib/engine-components/EventList.js +18 -1
  49. package/lib/engine-components/EventList.js.map +1 -1
  50. package/lib/engine-components/GroundProjection.d.ts +3 -0
  51. package/lib/engine-components/GroundProjection.js +3 -0
  52. package/lib/engine-components/GroundProjection.js.map +1 -1
  53. package/lib/engine-components/Interactable.d.ts +4 -0
  54. package/lib/engine-components/Interactable.js +4 -0
  55. package/lib/engine-components/Interactable.js.map +1 -1
  56. package/lib/engine-components/OrbitControls.d.ts +4 -2
  57. package/lib/engine-components/OrbitControls.js +33 -3
  58. package/lib/engine-components/OrbitControls.js.map +1 -1
  59. package/lib/engine-components/RigidBody.d.ts +5 -0
  60. package/lib/engine-components/RigidBody.js +5 -0
  61. package/lib/engine-components/RigidBody.js.map +1 -1
  62. package/lib/engine-components/SeeThrough.js +20 -0
  63. package/lib/engine-components/SeeThrough.js.map +1 -1
  64. package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +4 -2
  65. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +69 -14
  66. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  67. package/lib/engine-components/splines/SplineWalker.d.ts +43 -4
  68. package/lib/engine-components/splines/SplineWalker.js +88 -12
  69. package/lib/engine-components/splines/SplineWalker.js.map +1 -1
  70. package/lib/engine-components/ui/Text.js +6 -1
  71. package/lib/engine-components/ui/Text.js.map +1 -1
  72. package/lib/engine-components/utils/LookAt.d.ts +3 -0
  73. package/lib/engine-components/utils/LookAt.js +3 -0
  74. package/lib/engine-components/utils/LookAt.js.map +1 -1
  75. package/lib/engine-components/utils/OpenURL.d.ts +2 -1
  76. package/lib/engine-components/utils/OpenURL.js +2 -1
  77. package/lib/engine-components/utils/OpenURL.js.map +1 -1
  78. package/lib/engine-components/web/Clickthrough.d.ts +2 -0
  79. package/lib/engine-components/web/Clickthrough.js +23 -1
  80. package/lib/engine-components/web/Clickthrough.js.map +1 -1
  81. package/lib/engine-components/web/ScrollFollow.d.ts +1 -9
  82. package/lib/engine-components/web/ScrollFollow.js +13 -30
  83. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  84. package/lib/engine-components/web/ViewBox.d.ts +16 -0
  85. package/lib/engine-components/web/ViewBox.js +35 -3
  86. package/lib/engine-components/web/ViewBox.js.map +1 -1
  87. package/lib/engine-components/webxr/WebARCameraBackground.d.ts +2 -0
  88. package/lib/engine-components/webxr/WebARCameraBackground.js +2 -0
  89. package/lib/engine-components/webxr/WebARCameraBackground.js.map +1 -1
  90. package/lib/engine-components/webxr/WebARSessionRoot.d.ts +1 -1
  91. package/lib/engine-components/webxr/WebARSessionRoot.js +1 -1
  92. package/lib/engine-components/webxr/WebXR.d.ts +2 -0
  93. package/lib/engine-components/webxr/WebXR.js +2 -0
  94. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  95. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +29 -0
  96. package/lib/engine-components/webxr/WebXRImageTracking.js +29 -0
  97. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  98. package/package.json +2 -2
  99. package/plugins/common/needle-engine.js +41 -0
  100. package/plugins/common/worker.js +129 -0
  101. package/plugins/vite/asap.js +5 -23
  102. package/plugins/vite/dependencies.js +21 -11
  103. package/plugins/vite/index.js +7 -0
  104. package/plugins/vite/needle-app.js +194 -0
  105. package/src/engine/engine_physics.ts +27 -2
  106. package/src/engine/js-extensions/Object3D.ts +24 -0
  107. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +3 -1
  108. package/src/engine-components/Collider.ts +27 -1
  109. package/src/engine-components/ContactShadows.ts +12 -4
  110. package/src/engine-components/DropListener.ts +45 -24
  111. package/src/engine-components/Duplicatable.ts +2 -2
  112. package/src/engine-components/EventList.ts +18 -1
  113. package/src/engine-components/GroundProjection.ts +4 -1
  114. package/src/engine-components/Interactable.ts +4 -1
  115. package/src/engine-components/OrbitControls.ts +32 -5
  116. package/src/engine-components/RigidBody.ts +6 -1
  117. package/src/engine-components/SeeThrough.ts +42 -2
  118. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +117 -17
  119. package/src/engine-components/splines/SplineWalker.ts +99 -14
  120. package/src/engine-components/ui/Text.ts +11 -2
  121. package/src/engine-components/utils/LookAt.ts +3 -0
  122. package/src/engine-components/utils/OpenURL.ts +3 -2
  123. package/src/engine-components/web/Clickthrough.ts +28 -1
  124. package/src/engine-components/web/ScrollFollow.ts +16 -34
  125. package/src/engine-components/web/ViewBox.ts +35 -5
  126. package/src/engine-components/webxr/WebARCameraBackground.ts +2 -0
  127. package/src/engine-components/webxr/WebARSessionRoot.ts +1 -1
  128. package/src/engine-components/webxr/WebXR.ts +2 -0
  129. package/src/engine-components/webxr/WebXRImageTracking.ts +30 -3
  130. package/dist/needle-engine.bundle-B8HfDBoL.min.js +0 -1652
@@ -8,23 +8,26 @@ export const preloadScriptPaths = [];
8
8
 
9
9
  /**
10
10
  * @param {import('../types').userSettings} userSettings
11
+ * @returns {import('vite').Plugin[]}
11
12
  */
12
13
  export const needleDependencies = (command, config, userSettings) => {
13
14
 
14
15
  /**
15
16
  * @type {import('vite').Plugin}
16
17
  */
17
- return {
18
- name: 'needle:dependencies',
19
- enforce: 'pre',
20
- /**
21
- * @param {import('vite').UserConfig} config
22
- */
23
- config: (config, env) => {
24
- handleOptimizeDeps(config);
25
- handleManualChunks(config);
26
- }
27
- }
18
+ return [
19
+ {
20
+ name: 'needle:dependencies',
21
+ enforce: 'pre',
22
+ /**
23
+ * @param {import('vite').UserConfig} config
24
+ */
25
+ config: (config, env) => {
26
+ handleOptimizeDeps(config);
27
+ handleManualChunks(config);
28
+ },
29
+ },
30
+ ]
28
31
  }
29
32
 
30
33
  const excludeDependencies = [
@@ -167,6 +170,13 @@ function handleManualChunks(config) {
167
170
  return name;
168
171
  }
169
172
  }
173
+ // else if(chunk.name === 'index') {
174
+ // console.log(chunk);
175
+ // debugger
176
+ // // this is the main chunk
177
+ // // we don't want to add a hash here to be able to easily import the main script
178
+ // return `index.js`;
179
+ // }
170
180
  return `assets/[name].[hash].js`;
171
181
  }
172
182
 
@@ -63,11 +63,16 @@ export { needleImportsLogger } from "./imports-logger.js";
63
63
  import { needleBuildInfo } from "./buildinfo.js";
64
64
  export { needleBuildInfo } from "./buildinfo.js";
65
65
 
66
+ import { needleApp } from "./needle-app.js";
67
+ export { needleApp } from "./needle-app.js";
68
+
66
69
  import { needleServer } from "./server.js";
67
70
  import { needleNPM } from "./npm.js";
68
71
  import { needleTransformCode } from "./transform.js";
69
72
  import { needleMaterialXLoader } from "./materialx.js";
70
73
  import { needleLogger } from "./logger.js";
74
+ import { viteFixWorkerImport } from "../common/worker.js";
75
+
71
76
  export { needleServer } from "./server.js";
72
77
 
73
78
 
@@ -137,6 +142,8 @@ export const needlePlugins = async (command, config = undefined, userSettings =
137
142
  needleServer(command, config, userSettings),
138
143
  needleNPM(command, config, userSettings),
139
144
  needleMaterialXLoader(command, config, userSettings),
145
+ needleApp(command, config, userSettings),
146
+ viteFixWorkerImport()
140
147
  ];
141
148
 
142
149
  const asap = await needleAsap(command, config, userSettings);
@@ -0,0 +1,194 @@
1
+ import { writeFile } from 'fs';
2
+ import { tryParseNeedleEngineSrcAttributeFromHtml } from '../common/needle-engine.js';
3
+
4
+
5
+
6
+ /**
7
+ * @param {'serve' | 'build'} command
8
+ * @param {{} | undefined | null} config
9
+ * @param {import('../types').userSettings} userSettings
10
+ * @returns {import('vite').Plugin[] | null}
11
+ */
12
+ export const needleApp = (command, config, userSettings) => {
13
+
14
+ if (command !== "build") {
15
+ return null;
16
+ }
17
+
18
+ /** @type {Array<import("rollup").OutputChunk>} */
19
+ const entryFiles = new Array();
20
+
21
+ let outputDir = "dist";
22
+
23
+ /**
24
+ * @type {import('vite').Plugin}
25
+ */
26
+ return [
27
+ {
28
+ name: 'needle:app',
29
+ enforce: "post",
30
+ configResolved(config) {
31
+ outputDir = config.build.outDir || "dist";
32
+ },
33
+ transformIndexHtml: {
34
+ handler: async function (html, context) {
35
+ const name = context.filename;
36
+ if (name.includes("index.html")) {
37
+ if (context.chunk?.isEntry) {
38
+ try {
39
+ entryFiles.push(context.chunk);
40
+ const path = context.chunk.fileName;
41
+ // console.log("[needle-dependencies] entry chunk imports", {
42
+ // name: context.chunk.fileName,
43
+ // imports: context.chunk.imports,
44
+ // dynamicImports: context.chunk.dynamicImports,
45
+ // refs: context.chunk.referencedFiles,
46
+ // });
47
+
48
+ // TODO: here we try to find the main asset (entrypoint) for this web app. It's a bit hacky right now but we have to assign this url directly to make it work with `gen.js` where multiple needle-apps are on different (or the same) pages.
49
+ const attribute_src = tryParseNeedleEngineSrcAttributeFromHtml(html);
50
+ const imported_glbs = Array.from(context.chunk.viteMetadata?.importedAssets?.values() || [])?.filter(f => f.endsWith(".glb") || f.endsWith(".gltf"));
51
+
52
+ const main_asset = attribute_src?.[0] || imported_glbs?.[0];
53
+
54
+ const webComponent = generateNeedleEmbedWebComponent(path, main_asset);
55
+ await writeFile(`${outputDir}/needle-app.js`, webComponent, (err) => {
56
+ if (err) {
57
+ console.error("[needle-app] could not create needle-app.js", err);
58
+ }
59
+ else {
60
+ console.log("[needle-app] created needle-app.js");
61
+ }
62
+ });
63
+ }
64
+ catch (e) {
65
+ console.warn("WARN: could not create needle-app.js\n", e);
66
+ }
67
+ }
68
+ }
69
+ }
70
+ },
71
+
72
+ }
73
+ ]
74
+ }
75
+
76
+
77
+ /**
78
+ * @param {string} filepath
79
+ * @param {string | null} src
80
+ * @returns {string}
81
+ */
82
+ function generateNeedleEmbedWebComponent(filepath, src) {
83
+
84
+
85
+ // filepath is e.g. `assets/index-XXXXXXXX.js`
86
+ // we want to make sure the path is correct relative to where the component will be used
87
+ // this script will be emitted in the output directory root (e.g. needle-embed.js)
88
+
89
+ const componentDefaultName = 'needle-app';
90
+
91
+ return `
92
+
93
+ // Needle Engine attributes we want to allow to be overriden
94
+ const knownAttributes = [
95
+ "src",
96
+ "background-color",
97
+ "background-image",
98
+ "environment-image",
99
+ "focus-rect",
100
+ ];
101
+
102
+ const scriptUrl = new URL(import.meta.url);
103
+ const componentName = scriptUrl.searchParams.get("component-name") || '${componentDefaultName}';
104
+
105
+
106
+ if (!customElements.get(componentName)) {
107
+ console.debug(\`Defining needle-app as <\${componentName}>\`);
108
+ customElements.define(componentName, class extends HTMLElement {
109
+
110
+ static get observedAttributes() {
111
+ return knownAttributes;
112
+ }
113
+
114
+ constructor() {
115
+ super();
116
+ this.attachShadow({ mode: 'open' });
117
+ const template = document.createElement('template');
118
+ template.innerHTML = \`
119
+ <style>
120
+ :host {
121
+ position: relative;
122
+ display: block;
123
+ width: max(360px 100%);
124
+ height: max(240px, 100%);
125
+ margin: 0;
126
+ padding: 0;
127
+ }
128
+ needle-engine {
129
+ position: absolute;
130
+ top: 0;
131
+ left: 0;
132
+ width: 100%;
133
+ height: 100%;
134
+ }
135
+ </style>
136
+ \`;
137
+ this.shadowRoot.appendChild(template.content.cloneNode(true));
138
+
139
+ const script = document.createElement('script');
140
+ script.type = 'module';
141
+ const url = new URL('.', import.meta.url);
142
+ this.basePath = this.getAttribute('base-path') || \`\${url.protocol}//\${url.host}\${url.pathname}\`;
143
+ while(this.basePath.endsWith('/')) {
144
+ this.basePath = this.basePath.slice(0, -1);
145
+ }
146
+ script.src = this.getAttribute('script-src') || \`\${this.basePath}/${filepath}\`;
147
+ this.shadowRoot.appendChild(script);
148
+
149
+ this.needleEngine = document.createElement('needle-engine');
150
+ this.updateAttributes();
151
+ this.shadowRoot.appendChild(this.needleEngine);
152
+
153
+ console.debug(this.basePath, script.src, this.needleEngine.getAttribute("src"));
154
+ }
155
+
156
+ onConnectedCallback() {
157
+ console.debug('NeedleEmbed connected to the DOM');
158
+ }
159
+
160
+ disconnectedCallback() {
161
+ console.debug('NeedleEmbed disconnected from the DOM');
162
+ }
163
+
164
+ attributeChangedCallback(name, oldValue, newValue) {
165
+ console.debug(\`NeedleApp attribute changed: \${name} from \${oldValue} to \${newValue}\`);
166
+ this.updateAttributes();
167
+ }
168
+
169
+ updateAttributes() {
170
+ console.debug("NeedleApp updating attributes");
171
+
172
+ const src = this.getAttribute('src') || ${src?.length ? `\`\${this.basePath}/${src}\`` : null};
173
+ if(src) this.needleEngine.setAttribute("src", src);
174
+ else this.needleEngine.removeAttribute("src");
175
+
176
+ for(const attr of knownAttributes) {
177
+
178
+ if(attr === "src") continue; // already handled above
179
+
180
+ if(this.hasAttribute(attr)) {
181
+ this.needleEngine.setAttribute(attr, this.getAttribute(attr));
182
+ }
183
+ else {
184
+ this.needleEngine.removeAttribute(attr);
185
+ }
186
+ }
187
+ }
188
+ });
189
+ }
190
+ else {
191
+ console.warn(\`needle-app <\${componentName}> already defined.\`);
192
+ }
193
+ `
194
+ }
@@ -572,6 +572,9 @@ declare module 'three' {
572
572
 
573
573
 
574
574
  namespace NEMeshBVH {
575
+
576
+ let failedToCreateMeshBVHWorker = 0;
577
+
575
578
  export function runMeshBVHRaycast(method: Raycaster | Sphere, mesh: Mesh, results: Intersection[], context: Pick<Context, "xr">, options: { allowSlowRaycastFallback?: boolean }): boolean {
576
579
  if (!mesh.geometry) {
577
580
  return false;
@@ -630,6 +633,10 @@ namespace NEMeshBVH {
630
633
  || geom.index && geom.index?.["isInterleavedBufferAttribute"]) {
631
634
  canUseWorker = false;
632
635
  }
636
+ else if (failedToCreateMeshBVHWorker > 10) {
637
+ // if we failed to create a worker multiple times, don't try again
638
+ canUseWorker = false;
639
+ }
633
640
 
634
641
  // if we have a worker use that
635
642
  if (canUseWorker && _GenerateMeshBVHWorker) {
@@ -646,8 +653,23 @@ namespace NEMeshBVH {
646
653
  }
647
654
  // if there are no workers available, create a new one
648
655
  if (!workerInstance && workerInstances.length < 3) {
649
- workerInstance = new _GenerateMeshBVHWorker();
650
- workerInstances.push(workerInstance);
656
+ try {
657
+ workerInstance = new _GenerateMeshBVHWorker();
658
+ workerInstances.push(workerInstance);
659
+ }
660
+ catch (err) {
661
+ const isSecurityError = err instanceof DOMException && err.name === "SecurityError";
662
+ if (isSecurityError) {
663
+ console.warn("Failed to create MeshBVH worker, falling back to main thread generation. This can happen when running from file://, if the browser does not support workers or if the browser is blocking workers for other reasons.");
664
+ console.debug(err);
665
+ failedToCreateMeshBVHWorker += 10;
666
+ }
667
+ else {
668
+ console.error("Failed to create MeshBVH worker");
669
+ console.debug(err);
670
+ }
671
+ failedToCreateMeshBVHWorker++;
672
+ }
651
673
  }
652
674
 
653
675
  if (workerInstance != null && !workerInstance.running) {
@@ -806,6 +828,9 @@ namespace NEMeshBVH {
806
828
  if (debugPhysics || isDevEnvironment()) {
807
829
  console.warn("Failed to setup mesh bvh worker");
808
830
  }
831
+ else {
832
+ console.debug("Failed to setup mesh bvh worker", _err);
833
+ }
809
834
  })
810
835
  .finally(() => {
811
836
  isRequestingWorker = false;
@@ -157,6 +157,14 @@ declare module 'three' {
157
157
  * Added by Needle Engine.
158
158
  */
159
159
  get worldUp(): Vector3;
160
+
161
+
162
+ /**
163
+ * Check if the given object is contained in the hierarchy of this object or if it's the same object.
164
+ * @param object The object to check.
165
+ * @returns True if the object is contained in the hierarchy, false otherwise.
166
+ */
167
+ contains(object: Object3D | null | undefined): boolean;
160
168
  }
161
169
  }
162
170
 
@@ -356,5 +364,21 @@ if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldUp")) {
356
364
 
357
365
 
358
366
 
367
+ if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "contains")) {
368
+ Object.defineProperty(Object3D.prototype, "contains", {
369
+ value: function (object: Object3D | null | undefined): boolean {
370
+ if (!object) return false;
371
+ if (this === object) return true;
372
+ for (const child of this.children) {
373
+ if (child.contains(object)) return true;
374
+ }
375
+ return false;
376
+ }
377
+ });
378
+ }
379
+
380
+
381
+
382
+
359
383
  // do this after adding the component extensions
360
384
  registerPrototypeExtensions(Object3D);
@@ -4,14 +4,16 @@ import { MeshBVH } from 'three-mesh-bvh';
4
4
  // Modified according to https://github.com/gkjohnson/three-mesh-bvh/issues/636#issuecomment-2209571751
5
5
  import { WorkerBase } from "three-mesh-bvh/src/workers/utils/WorkerBase.js";
6
6
 
7
+
7
8
  export class GenerateMeshBVHWorker extends WorkerBase {
8
9
 
9
10
  constructor() {
10
11
  // TODO: make mesh bvh worker "work" for prebundled CDN loading
11
12
  // https://linear.app/needle/issue/NE-6572
12
13
  // Also we don't use toplevel imports to not completely fail to load needle-engine where loading the worker fails
13
- // const url = new URL('three-mesh-bvh/src/workers/generateMeshBVH.worker.js', import.meta.url)
14
+ // const meta_url = import.meta.url;
14
15
  // const getWorker = () => new Worker(url, { type: 'module' })
16
+ // console.log(meta_url, url, getWorker());
15
17
  super(new Worker(new URL('three-mesh-bvh/src/workers/generateMeshBVH.worker.js', import.meta.url), { type: 'module' }));
16
18
  this.name = 'GenerateMeshBVHWorker';
17
19
 
@@ -16,7 +16,13 @@ import { Rigidbody } from "./RigidBody.js";
16
16
  /**
17
17
  * Collider is the base class for all colliders. A collider is a physical shape that is used to detect collisions with other objects in the scene.
18
18
  * Colliders are used in combination with a {@link Rigidbody} to create physical interactions between objects.
19
- * Colliders are registered with the physics engine when they are enabled and removed when they are disabled.
19
+ * Colliders are registered with the physics engine when they are enabled and removed when they are disabled.
20
+ *
21
+ * - Example: https://samples.needle.tools/physics-basic
22
+ * - Example: https://samples.needle.tools/physics-playground
23
+ * - Example: https://samples.needle.tools/physics-&-animation
24
+ *
25
+ *
20
26
  * @category Physics
21
27
  * @group Components
22
28
  */
@@ -112,6 +118,11 @@ export class Collider extends Behaviour implements ICollider {
112
118
  /**
113
119
  * SphereCollider represents a sphere-shaped collision volume.
114
120
  * Useful for objects that are roughly spherical in shape or need a simple collision boundary.
121
+ *
122
+ * - Example: https://samples.needle.tools/physics-basic
123
+ * - Example: https://samples.needle.tools/physics-playground
124
+ * - Example: https://samples.needle.tools/physics-&-animation
125
+ *
115
126
  * @category Physics
116
127
  * @group Components
117
128
  */
@@ -158,6 +169,11 @@ export class SphereCollider extends Collider implements ISphereCollider {
158
169
  /**
159
170
  * BoxCollider represents a box-shaped collision volume.
160
171
  * Ideal for rectangular objects or objects that need a simple cuboid collision boundary.
172
+ *
173
+ * - Example: https://samples.needle.tools/physics-basic
174
+ * - Example: https://samples.needle.tools/physics-playground
175
+ * - Example: https://samples.needle.tools/physics-&-animation
176
+ *
161
177
  * @category Physics
162
178
  * @group Components
163
179
  */
@@ -260,6 +276,11 @@ export class BoxCollider extends Collider implements IBoxCollider {
260
276
  /**
261
277
  * MeshCollider creates a collision shape from a mesh geometry.
262
278
  * Allows for complex collision shapes that match the exact geometry of an object.
279
+ *
280
+ * - Example: https://samples.needle.tools/physics-basic
281
+ * - Example: https://samples.needle.tools/physics-playground
282
+ * - Example: https://samples.needle.tools/physics-&-animation
283
+ *
263
284
  * @category Physics
264
285
  * @group Components
265
286
  */
@@ -343,6 +364,11 @@ export class MeshCollider extends Collider {
343
364
  /**
344
365
  * CapsuleCollider represents a capsule-shaped collision volume (cylinder with hemispherical ends).
345
366
  * Ideal for character controllers and objects that need a rounded collision shape.
367
+ *
368
+ * - Example: https://samples.needle.tools/physics-basic
369
+ * - Example: https://samples.needle.tools/physics-playground
370
+ * - Example: https://samples.needle.tools/physics-&-animation
371
+ *
346
372
  * @category Physics
347
373
  * @group Components
348
374
  */
@@ -44,10 +44,19 @@ type FitParameters = {
44
44
  // - node can simply be scaled in Y to adjust max. ground height
45
45
 
46
46
  /**
47
- * ContactShadows is a component that allows to display contact shadows in the scene.
47
+ * ContactShadows is a component that allows to display contact shadows in the scene. It has options for darkness, opacity and blur of the shadows.
48
+ *
49
+ * - Example: https://samples.needle.tools/contact-shadows
50
+ *
51
+ * ## Usage
52
+ * You can use {@link ContactShadows.auto} to automatically create a ContactShadows instance for the scene or you can add the component to an object in the scene. Note that the scale of the object will be used to define the size of the shadow area.
53
+ *
54
+ * ContactShadows can also be enabled on the `<needle-engine>` web component directly by adding the `contactshadows` attribute. The value of the attribute will be used as opacity and darkness of the shadows: `<needle-engine contactshadows="0.7">`.
55
+ *
56
+ *
48
57
  * @category Rendering
49
58
  * @group Components
50
- */
59
+ */
51
60
  export class ContactShadows extends Behaviour {
52
61
 
53
62
  private static readonly _instances: Map<Context, ContactShadows> = new Map();
@@ -362,8 +371,7 @@ export class ContactShadows extends Behaviour {
362
371
  !this.depthMaterial || !this.shadowCamera ||
363
372
  !this.blurPlane || !this.shadowGroup || !this.plane ||
364
373
  !this.horizontalBlurMaterial || !this.verticalBlurMaterial) {
365
- if (debug)
366
- console.error("ContactShadows: not initialized yet");
374
+ if (debug) console.error("ContactShadows: not initialized yet");
367
375
  return;
368
376
  }
369
377
 
@@ -1,23 +1,22 @@
1
- import { AxesHelper, Box3, Cache, Object3D, Vector2, Vector3 } from "three";
1
+ import { Box3, Object3D, Vector2, Vector3 } from "three";
2
2
 
3
3
  import { isDevEnvironment } from "../engine/debug/index.js";
4
4
  import { AnimationUtils } from "../engine/engine_animation.js";
5
- import { addComponent } from "../engine/engine_components.js";
6
5
  import { Context } from "../engine/engine_context.js";
7
6
  import { destroy } from "../engine/engine_gameobject.js";
8
7
  import { Gizmos } from "../engine/engine_gizmos.js";
9
8
  import { getLoader } from "../engine/engine_gltf.js";
10
9
  import { BlobStorage } from "../engine/engine_networking_blob.js";
11
10
  import { PreviewHelper } from "../engine/engine_networking_files.js";
12
- import { generateSeed, InstantiateIdProvider } from "../engine/engine_networking_instantiate.js";
11
+ import { InstantiateIdProvider } from "../engine/engine_networking_instantiate.js";
13
12
  import { serializable } from "../engine/engine_serialization_decorator.js";
14
13
  import { fitObjectIntoVolume, getBoundingBox, placeOnSurface } from "../engine/engine_three_utils.js";
15
- import { IGameObject, Model, Vec3 } from "../engine/engine_types.js";
14
+ import { Model, Vec3 } from "../engine/engine_types.js";
16
15
  import { getParam, setParamWithoutReload } from "../engine/engine_utils.js";
17
16
  import { determineMimeTypeFromExtension } from "../engine/engine_utils_format.js";
18
- import { Animation } from "./Animation.js";
19
17
  import { Behaviour } from "./Component.js";
20
18
  import { EventList } from "./EventList.js";
19
+ import { Renderer } from "./Renderer.js";
21
20
 
22
21
  /**
23
22
  * Debug mode can be enabled with the URL parameter `?debugdroplistener`, which
@@ -116,6 +115,8 @@ const blobKeyName = "blob";
116
115
  * If {@link useNetworking} is enabled, the DropListener will automatically synchronize dropped files to other connected clients.
117
116
  * Enable {@link fitIntoVolume} to automatically scale dropped objects to fit within the volume defined by {@link fitVolumeSize}.
118
117
  *
118
+ * - Example: [DropListener Sample](https://droplistener-zubcksz1veaoo.needle.run/)
119
+ *
119
120
  * The following events are dispatched by the DropListener:
120
121
  * - **object-added** - dispatched when a new object is added to the scene
121
122
  * - **file-dropped** - dispatched when a file is dropped into the scene
@@ -214,7 +215,14 @@ export class DropListener extends Behaviour {
214
215
  }
215
216
 
216
217
 
217
-
218
+ awake() {
219
+ for (const ch of this.gameObject.children) {
220
+ if (this.dropArea && ch.contains(this.dropArea)) {
221
+ continue;
222
+ }
223
+ this._addedObjects.push(ch);
224
+ }
225
+ }
218
226
 
219
227
  // #region internals
220
228
 
@@ -223,7 +231,15 @@ export class DropListener extends Behaviour {
223
231
  this.context.renderer.domElement.addEventListener("dragover", this.onDrag);
224
232
  this.context.renderer.domElement.addEventListener("drop", this.onDrop);
225
233
  window.addEventListener("paste", this.handlePaste);
226
- this.context.connection.beginListen("droplistener", this.onNetworkEvent)
234
+ this.context.connection.beginListen("droplistener", this.onNetworkEvent);
235
+ if (isDevEnvironment()) {
236
+ if (this.dropArea) {
237
+ const anyRenderer = this.dropArea.getComponentInChildren(Renderer);
238
+ if (!anyRenderer) {
239
+ console.warn("[DropListener] The assigned DropArea does not seem to have a renderer/mesh. Drag and Drop events will not be detected.");
240
+ }
241
+ }
242
+ }
227
243
  }
228
244
  /** @internal */
229
245
  onDisable(): void {
@@ -287,6 +303,7 @@ export class DropListener extends Behaviour {
287
303
  * @param evt The drag event
288
304
  */
289
305
  private onDrag = (evt: DragEvent) => {
306
+ if(debug) console.debug("DropListener Drag", evt, this.context.connection.allowEditing);
290
307
  if (this.context.connection.allowEditing === false) return;
291
308
  // necessary to get drop event
292
309
  evt.preventDefault();
@@ -298,9 +315,9 @@ export class DropListener extends Behaviour {
298
315
  * @param evt The drop event
299
316
  */
300
317
  private onDrop = async (evt: DragEvent) => {
318
+ if (debug) console.debug("DropListener Drop", evt, this.context.connection.allowEditing);
301
319
  if (this.context.connection.allowEditing === false) return;
302
320
 
303
- if (debug) console.log(evt);
304
321
  if (!evt?.dataTransfer) return;
305
322
  // If the event is marked as handled for droplisteners then ignore it
306
323
  if (evt["droplistener:handled"]) return;
@@ -473,7 +490,7 @@ export class DropListener extends Behaviour {
473
490
  if (doDestroy) {
474
491
  for (const prev of this._addedObjects) {
475
492
  if (prev.parent === this.gameObject) {
476
- destroy(prev, true, true);
493
+ prev.destroy();
477
494
  }
478
495
  }
479
496
  }
@@ -596,24 +613,28 @@ export class DropListener extends Behaviour {
596
613
  * @returns True if the drop is valid (either no drop area is set or the drop occurred inside it)
597
614
  */
598
615
  private testIfIsInDropArea(ctx: DropContext): boolean {
616
+ const screenPoint = this.context.input.convertScreenspaceToRaycastSpace(ctx.screenposition.clone());
617
+ const hits = this.context.physics.raycast({
618
+ screenPoint,
619
+ recursive: true,
620
+ testObject: obj => {
621
+ // Ignore hits on the already added objects, they don't count as part of the dropzone
622
+ if (this._addedObjects.some(o => o.contains(obj))) return false;
623
+ return true;
624
+ }
625
+ });
626
+ if (!hits.length) {
627
+ if (isDevEnvironment()) console.log(`Dropped outside of drop area for DropListener \"${this.name}\".`);
628
+ return false;
629
+ }
630
+
631
+ const hit = hits[0];
599
632
  if (this.dropArea) {
600
- const screenPoint = this.context.input.convertScreenspaceToRaycastSpace(ctx.screenposition.clone());
601
- const hits = this.context.physics.raycast({
602
- targets: [this.dropArea],
603
- screenPoint,
604
- recursive: true,
605
- testObject: obj => {
606
- // Ignore hits on the already added objects, they don't count as part of the dropzone
607
- if (this._addedObjects.includes(obj)) return false;
608
- return true;
609
- }
610
- });
611
- if (!hits.length) {
612
- if (isDevEnvironment()) console.log(`Dropped outside of drop area for DropListener \"${this.name}\".`);
613
- return false;
633
+ if (this.dropArea.contains(hit.object)) {
634
+ return true;
614
635
  }
615
636
  }
616
- return true;
637
+ return false;
617
638
  }
618
639
 
619
640
  }
@@ -12,8 +12,8 @@ import { type IPointerEventHandler, PointerEventData } from "./ui/PointerEvents.
12
12
  import { ObjectRaycaster } from "./ui/Raycaster.js";
13
13
 
14
14
  /**
15
- * The Duplicatable component is used to duplicate a assigned {@link GameObject} when a pointer event occurs on the GameObject.
16
- * It implements the {@link IPointerEventHandler} interface and can be used to expose duplication to the user in the editor without writing code.
15
+ * The Duplicatable component is used to duplicate a assigned {@link GameObject} when a pointer event occurs on the object.
16
+ *
17
17
  * @category Interactivity
18
18
  * @group Components
19
19
  */
@@ -97,7 +97,24 @@ export class EventListEvent<TArgs extends any> extends Event { //implements Arra
97
97
 
98
98
 
99
99
  /**
100
- * The EventList is a class that can be used to create a list of event listeners that can be invoked
100
+ * The EventList is a class that can be used to create a list of event listeners that can be invoked.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * // create an event list
105
+ * const onClick = new EventList<(event: MouseEvent) => void>();
106
+ *
107
+ * // add an event listener
108
+ * onClick.addEventListener((event) => {
109
+ * console.log("Clicked!", event);
110
+ * });
111
+ *
112
+ * // invoke the event list
113
+ * onClick.invoke(new MouseEvent("click"));
114
+ * ```
115
+ *
116
+ * @category Events
117
+ * @group Utilities
101
118
  */
102
119
  export class EventList<TArgs extends any = any> implements IEventList {
103
120
 
@@ -10,7 +10,10 @@ import { Behaviour } from "./Component.js";
10
10
  const debug = getParam("debuggroundprojection");
11
11
 
12
12
  /**
13
- * GroundProjectedEnv creates a ground projection of the current environment map.
13
+ * GroundProjectedEnv creates a ground projection of the current environment map.
14
+ *
15
+ * - Example https://engine.needle.tools/samples/ground-projection
16
+ *
14
17
  * @category Rendering
15
18
  * @group Components
16
19
  */