@needle-tools/engine 4.10.5-next.a5d5bf4 → 4.11.0-next.358bed1

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 (65) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/components.needle.json +1 -1
  3. package/dist/{gltf-progressive-B63NpN_i.js → gltf-progressive-CXVECA3a.js} +1 -1
  4. package/dist/{needle-engine.bundle-DPHrCUDs.umd.cjs → needle-engine.bundle-BX6JeEif.umd.cjs} +139 -139
  5. package/dist/{needle-engine.bundle-D56E0HeK.min.js → needle-engine.bundle-CDMrKOoV.min.js} +137 -137
  6. package/dist/{needle-engine.bundle-B2qX4saI.js → needle-engine.bundle-Dy5wgRLd.js} +6591 -6436
  7. package/dist/needle-engine.d.ts +14 -0
  8. package/dist/needle-engine.js +321 -320
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/lib/engine/codegen/register_types.js +2 -0
  12. package/lib/engine/codegen/register_types.js.map +1 -1
  13. package/lib/engine/engine_gizmos.js +2 -2
  14. package/lib/engine/engine_gizmos.js.map +1 -1
  15. package/lib/engine/engine_physics.js +44 -14
  16. package/lib/engine/engine_physics.js.map +1 -1
  17. package/lib/engine/js-extensions/Object3D.d.ts +14 -0
  18. package/lib/engine/js-extensions/Object3D.js +13 -0
  19. package/lib/engine/js-extensions/Object3D.js.map +1 -1
  20. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +2 -1
  21. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  22. package/lib/engine-components/Renderer.js +33 -32
  23. package/lib/engine-components/Renderer.js.map +1 -1
  24. package/lib/engine-components/RendererLightmap.d.ts +7 -5
  25. package/lib/engine-components/RendererLightmap.js +29 -30
  26. package/lib/engine-components/RendererLightmap.js.map +1 -1
  27. package/lib/engine-components/SeeThrough.d.ts +70 -0
  28. package/lib/engine-components/SeeThrough.js +223 -0
  29. package/lib/engine-components/SeeThrough.js.map +1 -0
  30. package/lib/engine-components/codegen/components.d.ts +1 -0
  31. package/lib/engine-components/codegen/components.js +1 -0
  32. package/lib/engine-components/codegen/components.js.map +1 -1
  33. package/lib/engine-components/ui/Graphic.js +13 -1
  34. package/lib/engine-components/ui/Graphic.js.map +1 -1
  35. package/lib/engine-components/ui/RaycastUtils.js +5 -3
  36. package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
  37. package/lib/engine-components/utils/LookAt.js +4 -2
  38. package/lib/engine-components/utils/LookAt.js.map +1 -1
  39. package/lib/engine-components/web/Clickthrough.d.ts +2 -1
  40. package/lib/engine-components/web/Clickthrough.js +2 -1
  41. package/lib/engine-components/web/Clickthrough.js.map +1 -1
  42. package/lib/engine-components/web/CursorFollow.d.ts +7 -1
  43. package/lib/engine-components/web/CursorFollow.js +35 -2
  44. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  45. package/package.json +1 -1
  46. package/plugins/common/needle-engine.js +41 -0
  47. package/plugins/common/worker.js +130 -0
  48. package/plugins/vite/asap.js +5 -23
  49. package/plugins/vite/dependencies.js +21 -11
  50. package/plugins/vite/index.js +4 -0
  51. package/plugins/vite/needle-app.js +148 -0
  52. package/src/engine/codegen/register_types.ts +2 -0
  53. package/src/engine/engine_gizmos.ts +3 -3
  54. package/src/engine/engine_physics.ts +51 -14
  55. package/src/engine/js-extensions/Object3D.ts +32 -0
  56. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +3 -1
  57. package/src/engine-components/Renderer.ts +38 -37
  58. package/src/engine-components/RendererLightmap.ts +31 -33
  59. package/src/engine-components/SeeThrough.ts +256 -0
  60. package/src/engine-components/codegen/components.ts +1 -0
  61. package/src/engine-components/ui/Graphic.ts +13 -1
  62. package/src/engine-components/ui/RaycastUtils.ts +9 -8
  63. package/src/engine-components/utils/LookAt.ts +4 -1
  64. package/src/engine-components/web/Clickthrough.ts +2 -1
  65. package/src/engine-components/web/CursorFollow.ts +48 -7
@@ -4,9 +4,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
+ import { Ray } from "three";
8
+ import { Gizmos } from "../../engine/engine_gizmos.js";
7
9
  import { serializable } from "../../engine/engine_serialization_decorator.js";
8
10
  import { getTempVector } from "../../engine/engine_three_utils.js";
11
+ import { getParam } from "../../engine/engine_utils.js";
9
12
  import { Behaviour } from "../Component.js";
13
+ const debug = getParam("debugcursor");
10
14
  /**
11
15
  * The CursorFollow component makes the object follow the cursor (or touch) position on screen.
12
16
  *
@@ -32,6 +36,10 @@ export class CursorFollow extends Behaviour {
32
36
  * @default true
33
37
  */
34
38
  keepDistance = true;
39
+ /**
40
+ * If true, the object will attempt to snap to the surface of other objects in the scene using a raycast.
41
+ */
42
+ snapToSurface = false;
35
43
  _distance = -1;
36
44
  updateDistance(force = false) {
37
45
  if (!force && (this.keepDistance && this._distance !== -1)) {
@@ -43,10 +51,12 @@ export class CursorFollow extends Behaviour {
43
51
  awake() {
44
52
  this._distance = -1;
45
53
  }
54
+ /** @internal */
46
55
  onEnable() {
47
56
  this._distance = -1;
48
57
  window.addEventListener('pointermove', this._onPointerMove);
49
58
  }
59
+ /** @internal */
50
60
  onDisable() {
51
61
  window.removeEventListener('pointermove', this._onPointerMove);
52
62
  }
@@ -65,7 +75,7 @@ export class CursorFollow extends Behaviour {
65
75
  this._ndc_y = -(y - domy) / domh * 2 + 1;
66
76
  };
67
77
  /** @internal */
68
- update() {
78
+ lateUpdate() {
69
79
  // continuously update distance in case camera or object moves
70
80
  this.updateDistance();
71
81
  const x = this.useFullPage ? this._ndc_x : this.context.input.mousePositionRC.x;
@@ -77,15 +87,34 @@ export class CursorFollow extends Behaviour {
77
87
  const rayDirection = getTempVector(x, y, 1).unproject(camera);
78
88
  rayDirection.sub(cameraPosition).normalize();
79
89
  // position object at initial distance along the ray
80
- const newPosition = rayDirection.multiplyScalar(this._distance).add(cameraPosition);
90
+ const newPosition = getTempVector(rayDirection).multiplyScalar(this._distance).add(cameraPosition);
91
+ let _position = newPosition;
81
92
  if (this.damping > 0) {
82
93
  const pos = this.gameObject.worldPosition;
83
94
  pos.lerp(newPosition, this.context.time.deltaTime / this.damping);
84
95
  this.gameObject.worldPosition = pos;
96
+ _position = pos;
85
97
  }
86
98
  else {
87
99
  this.gameObject.worldPosition = newPosition;
88
100
  }
101
+ if (this.snapToSurface) {
102
+ ray.origin = _position;
103
+ ray.direction = rayDirection.multiplyScalar(-1);
104
+ const hits = this.context.physics.raycastFromRay(ray);
105
+ if (hits?.length) {
106
+ const hit = hits[0];
107
+ if (this.damping > 0) {
108
+ this.gameObject.worldPosition = _position.lerp(hit.point, this.context.time.deltaTime / this.damping);
109
+ }
110
+ else {
111
+ this.gameObject.worldPosition = hit.point;
112
+ }
113
+ if (debug) {
114
+ Gizmos.DrawLine(hit.point, hit.normal.add(hit.point), 0x00FF00);
115
+ }
116
+ }
117
+ }
89
118
  }
90
119
  }
91
120
  __decorate([
@@ -97,4 +126,8 @@ __decorate([
97
126
  __decorate([
98
127
  serializable()
99
128
  ], CursorFollow.prototype, "keepDistance", void 0);
129
+ __decorate([
130
+ serializable()
131
+ ], CursorFollow.prototype, "snapToSurface", void 0);
132
+ const ray = new Ray();
100
133
  //# sourceMappingURL=CursorFollow.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "4.10.5-next.a5d5bf4",
3
+ "version": "4.11.0-next.358bed1",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
5
5
  "main": "dist/needle-engine.min.js",
6
6
  "exports": {
@@ -0,0 +1,41 @@
1
+
2
+
3
+ // https://regex101.com/r/SVhzzD/1
4
+ // @ts-ignore
5
+ const needleEngineRegex = /<needle-engine.*?src=["'](?<src>[\w\d]+?)["']>/gm;
6
+
7
+
8
+ /**
9
+ * @param {string} html
10
+ * @returns {string[]} urls
11
+ */
12
+ export function tryParseNeedleEngineSrcAttributeFromHtml(html) {
13
+ const needleEngineMatches = html.matchAll(needleEngineRegex);
14
+
15
+ /**
16
+ * @type {string[]}
17
+ */
18
+ const results = [];
19
+
20
+ if (needleEngineMatches) {
21
+ while (true) {
22
+ const match = needleEngineMatches.next();
23
+ if (match.done) break;
24
+ /** @type {undefined | null | string} */
25
+ const value = match.value?.groups?.src;
26
+ if (value) {
27
+ if (value.startsWith("[")) {
28
+ // we have an array assigned
29
+ const arr = JSON.parse(value);
30
+ for (const item of arr) {
31
+ results.push(item);
32
+ }
33
+ }
34
+ else {
35
+ results.push(value);
36
+ }
37
+ }
38
+ }
39
+ }
40
+ return results;
41
+ }
@@ -0,0 +1,130 @@
1
+
2
+
3
+
4
+
5
+ /**
6
+ * @type {() => import("vite").Plugin}
7
+ */
8
+ export function viteFixWorkerImport() {
9
+ return {
10
+ name: 'vite-rewriteWorkerImport',
11
+ config: function (config, env) {
12
+ if (!config.build) {
13
+ config.build = {};
14
+ }
15
+ if (!config.build.rollupOptions) {
16
+ config.build.rollupOptions = {};
17
+ }
18
+ if (!config.build.rollupOptions.plugins) {
19
+ config.build.rollupOptions.plugins = [];
20
+ }
21
+ if (!Array.isArray(config.build.rollupOptions.plugins)) {
22
+ const value = config.build.rollupOptions.plugins;
23
+ config.build.rollupOptions.plugins = [];
24
+ config.build.rollupOptions.plugins.push(value);
25
+ }
26
+ config.build.rollupOptions.plugins.push(rollupFixWorkerImport({ logFail: false }));
27
+ }
28
+ }
29
+ }
30
+
31
+
32
+
33
+ // https://regex101.com/r/hr01H4/1
34
+ const regex = /new\s+Worker\s*\(\s*new\s+URL\s*\(\s*(?:\/\*.*?\*\/\s*)?\"(?<url>[^"]+)\"\s*,\s*(?<base>import\.meta\.url|self\.location)[^)]*\)/gm;
35
+
36
+
37
+
38
+ /**
39
+ * @type {(opts?: {logFail:boolean}) => import("vite").Plugin}
40
+ */
41
+ export function rollupFixWorkerImport(opts = { logFail: true }) {
42
+ return {
43
+ name: 'rewriteWorkerImport',
44
+ renderChunk: {
45
+ order: 'post',
46
+ async handler(code, chunk, outputOptions) {
47
+ let regexMatchedWorkerCode = false;
48
+ const newWorkerStartIndex = code.indexOf("new Worker");
49
+ if (newWorkerStartIndex >= 0) {
50
+ const res = code.replace(regex, (match, url, _base) => {
51
+ regexMatchedWorkerCode = true;
52
+ // console.log("WORKER?", url)
53
+ if (url?.startsWith("/")) {
54
+ console.log(`[rollup] Rewrite worker import in ${chunk.fileName}`);
55
+ // Make url file-relative
56
+ const newUrl = url.replace(/^\//, "");
57
+ // For CORS issues we need to use importScripts: https://linear.app/needle/issue/NE-6572#comment-ea5dc65e
58
+ const output = `/* new-worker */ new Worker(URL.createObjectURL(new Blob(["import '" + \`\${new URL('./${newUrl}', import.meta.url).toString()}\` + "';"], { type: 'text/javascript' }))`;
59
+ console.log("[rollup] Did rewrite worker output to:", output);
60
+ return output;
61
+ // return `new Worker(new URL("./${newUrl}", import.meta.url)`;
62
+ }
63
+ return match;
64
+ });
65
+ if (!regexMatchedWorkerCode) {
66
+
67
+ const fixedCode = fixWorkerSelfLocation(chunk.fileName, code);
68
+ if (fixedCode !== code) {
69
+ return fixedCode;
70
+ }
71
+ if (opts?.logFail !== false) {
72
+ const str = `[...]${code.substring(newWorkerStartIndex, newWorkerStartIndex + 200)}[...]`
73
+ console.warn(`\n[rollup] Worker import in ${chunk.fileName} was not rewritten: ${str}`);
74
+ }
75
+ else console.log(`[rollup] Worker import in ${chunk.fileName}: ${str}`);
76
+ }
77
+ return res;
78
+ }
79
+ },
80
+ }
81
+ };
82
+ }
83
+
84
+
85
+ /**
86
+ * Fix worker self.location to import.meta.url
87
+ * @param {string} filename
88
+ * @param {string} code
89
+ */
90
+ function fixWorkerSelfLocation(filename, code) {
91
+ let lastIndex = 0;
92
+ while (true) {
93
+ const startIndex = code.indexOf("new Worker", lastIndex);
94
+ if (startIndex < 0) break;
95
+ let index = startIndex + 1;
96
+ let endIndex = -1;
97
+ let openingBraceCount = 0;
98
+ let foundAnyOpening = false;
99
+ while (true) {
100
+ const char = code[index];
101
+ if (char === "(") {
102
+ openingBraceCount++;
103
+ foundAnyOpening = true;
104
+ }
105
+ if (char === ")") openingBraceCount--;
106
+ if (openingBraceCount === 0 && foundAnyOpening) {
107
+ endIndex = index;
108
+ break;
109
+ }
110
+ // console.log(openingBraceCount, char, index, code.length);
111
+ index++;
112
+ if (index >= code.length) break;
113
+ }
114
+ if (endIndex > startIndex) {
115
+ const workerCode = code.substring(startIndex, endIndex + 1);
116
+ if (workerCode.indexOf("self.location") >= 0) {
117
+ const fixedCode = workerCode.replace("self.location", "import.meta.url");
118
+ code = code.substring(0, startIndex) + fixedCode + code.substring(endIndex + 1);
119
+ lastIndex = startIndex + fixedCode.length;
120
+ console.log(`[rollup] Rewrite worker 'self.location' to 'import.meta.url' in ${filename}`);
121
+ } else {
122
+ lastIndex = endIndex;
123
+ }
124
+ }
125
+ else {
126
+ lastIndex = startIndex + "new Worker".length;
127
+ }
128
+ }
129
+ return code;
130
+ }
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs';
2
2
  import path from 'path';
3
+ import { tryParseNeedleEngineSrcAttributeFromHtml } from '../common/needle-engine.js';
3
4
  import { preloadScriptPaths } from './dependencies.js';
4
5
  import { makeFilesLocalIsEnabled } from './local-files.js';
5
6
 
@@ -159,10 +160,6 @@ function generateScriptPreloadLinks(_config, tags) {
159
160
  // @ts-ignore
160
161
  const codegenRegex = /\"(?<gltf>.+(.glb|.gltf)(\?.*)?)\"/gm;
161
162
 
162
- // https://regex101.com/r/SVhzzD/1
163
- // @ts-ignore
164
- const needleEngineRegex = /<needle-engine.*?src=["'](?<src>[\w\d]+?)["']>/gm;
165
-
166
163
  /**
167
164
  * @param {import('../types').needleConfig} config
168
165
  * @param {string} html
@@ -171,25 +168,10 @@ const needleEngineRegex = /<needle-engine.*?src=["'](?<src>[\w\d]+?)["']>/gm;
171
168
  function generateGltfPreloadLinks(config, html, tags) {
172
169
 
173
170
  // TODO: try to get the <needle-engine src> element src attribute and preload that
174
- const needleEngineMatches = html.matchAll(needleEngineRegex);
175
- if (needleEngineMatches) {
176
- while (true) {
177
- const match = needleEngineMatches.next();
178
- if (match.done) break;
179
- /** @type {undefined | null | string} */
180
- const value = match.value?.groups?.src;
181
- if (value) {
182
- if (value.startsWith("[")) {
183
- // we have an array assigned
184
- const arr = JSON.parse(value);
185
- for (const item of arr) {
186
- insertPreloadLink(tags, item, "model/gltf+json");
187
- }
188
- }
189
- else {
190
- insertPreloadLink(tags, value, "model/gltf+json");
191
- }
192
- }
171
+ const needleEngineMatches = tryParseNeedleEngineSrcAttributeFromHtml(html);
172
+ if (needleEngineMatches?.length) {
173
+ for (const item of needleEngineMatches) {
174
+ insertPreloadLink(tags, item, "model/gltf+json");
193
175
  }
194
176
  }
195
177
 
@@ -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
 
@@ -68,6 +68,8 @@ import { needleNPM } from "./npm.js";
68
68
  import { needleTransformCode } from "./transform.js";
69
69
  import { needleMaterialXLoader } from "./materialx.js";
70
70
  import { needleLogger } from "./logger.js";
71
+ import { needleApp } from "./needle-app.js";
72
+ import { viteFixWorkerImport } from "../common/worker.js";
71
73
  export { needleServer } from "./server.js";
72
74
 
73
75
 
@@ -137,6 +139,8 @@ export const needlePlugins = async (command, config = undefined, userSettings =
137
139
  needleServer(command, config, userSettings),
138
140
  needleNPM(command, config, userSettings),
139
141
  needleMaterialXLoader(command, config, userSettings),
142
+ needleApp(command, config, userSettings),
143
+ viteFixWorkerImport()
140
144
  ];
141
145
 
142
146
  const asap = await needleAsap(command, config, userSettings);
@@ -0,0 +1,148 @@
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
+ const referencedGlbs = tryParseNeedleEngineSrcAttributeFromHtml(html);
49
+ const webComponent = generateNeedleEmbedWebComponent(path, referencedGlbs);
50
+ await writeFile(`${outputDir}/needle-app.js`, webComponent, (err) => {
51
+ if (err) {
52
+ console.error("[needle-app] could not create needle-app.js", err);
53
+ }
54
+ else {
55
+ console.log("[needle-app] created needle-app.js");
56
+ }
57
+ });
58
+ }
59
+ catch (e) {
60
+ console.warn("WARN: could not create needle-app.js\n", e);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ },
66
+
67
+ }
68
+ ]
69
+ }
70
+
71
+
72
+ /**
73
+ * @param {string} filepath
74
+ * @param {string[]} needleEngineSrcPaths
75
+ * @returns {string}
76
+ */
77
+ function generateNeedleEmbedWebComponent(filepath, needleEngineSrcPaths) {
78
+
79
+
80
+ // filepath is e.g. `assets/index-XXXXXXXX.js`
81
+ // we want to make sure the path is correct relative to where the component will be used
82
+ // this script will be emitted in the output directory root (e.g. needle-embed.js)
83
+
84
+ const src = needleEngineSrcPaths?.length ? `${needleEngineSrcPaths[0]}` : "";
85
+
86
+ const componentName = 'needle-app';
87
+ const className = 'NeedleApp';
88
+
89
+ return `
90
+ class ${className} extends HTMLElement {
91
+ constructor() {
92
+ super();
93
+ this.attachShadow({ mode: 'open' });
94
+ const template = document.createElement('template');
95
+ template.innerHTML = \`
96
+ <style>
97
+ :host {
98
+ position: relative;
99
+ display: block;
100
+ width: 100%;
101
+ height: 100%;
102
+ margin: 0;
103
+ padding: 0;
104
+ }
105
+ needle-engine {
106
+ position: absolute;
107
+ top: 0;
108
+ left: 0;
109
+ width: 100%;
110
+ height: 100%;
111
+ }
112
+ </style>
113
+ \`;
114
+ this.shadowRoot.appendChild(template.content.cloneNode(true));
115
+
116
+ const script = document.createElement('script');
117
+ script.type = 'module';
118
+ const url = new URL('.', import.meta.url);
119
+ let basePath = this.getAttribute('base-path') || \`\${url.protocol}//\${url.host}\${url.pathname}\`;
120
+ while(basePath.endsWith('/')) {
121
+ basePath = basePath.slice(0, -1);
122
+ }
123
+ script.src = this.getAttribute('script-src') || \`\${basePath}/${filepath}\`;
124
+ this.shadowRoot.appendChild(script);
125
+
126
+ const needleEngine = document.createElement('needle-engine');
127
+ needleEngine.src = this.getAttribute('src') || ${src?.length ? `\${basePath}/${needleEngineSrcPaths}` : undefined};
128
+ this.shadowRoot.appendChild(needleEngine);
129
+
130
+ console.debug(basePath, script.src, needleEngine.getAttribute("src"));
131
+ }
132
+
133
+ onConnectedCallback() {
134
+ console.debug('NeedleEmbed connected to the DOM');
135
+ }
136
+
137
+ disconnectedCallback() {
138
+ console.debug('NeedleEmbed disconnected from the DOM');
139
+ }
140
+ }
141
+
142
+
143
+ if (!customElements.get('${componentName}')) {
144
+ console.debug("Defining ${componentName}");
145
+ customElements.define('${componentName}', ${className});
146
+ }
147
+ `
148
+ }
@@ -90,6 +90,7 @@ import { SkinnedMeshRenderer } from "../../engine-components/Renderer.js";
90
90
  import { Rigidbody } from "../../engine-components/RigidBody.js";
91
91
  import { SceneSwitcher } from "../../engine-components/SceneSwitcher.js";
92
92
  import { ScreenCapture } from "../../engine-components/ScreenCapture.js";
93
+ import { SeeThrough } from "../../engine-components/SeeThrough.js";
93
94
  import { ShadowCatcher } from "../../engine-components/ShadowCatcher.js";
94
95
  import { RemoteSkybox } from "../../engine-components/Skybox.js";
95
96
  import { SmoothFollow } from "../../engine-components/SmoothFollow.js";
@@ -249,6 +250,7 @@ TypeStore.add("SkinnedMeshRenderer", SkinnedMeshRenderer);
249
250
  TypeStore.add("Rigidbody", Rigidbody);
250
251
  TypeStore.add("SceneSwitcher", SceneSwitcher);
251
252
  TypeStore.add("ScreenCapture", ScreenCapture);
253
+ TypeStore.add("SeeThrough", SeeThrough);
252
254
  TypeStore.add("ShadowCatcher", ShadowCatcher);
253
255
  TypeStore.add("RemoteSkybox", RemoteSkybox);
254
256
  TypeStore.add("SmoothFollow", SmoothFollow);
@@ -1,4 +1,4 @@
1
- import { AxesHelper, Box3, BoxGeometry, BufferAttribute, BufferGeometry, Color, type ColorRepresentation, CylinderGeometry, EdgesGeometry, Line, LineBasicMaterial, LineSegments, Material,Matrix4, Mesh, MeshBasicMaterial, Object3D, Quaternion, SphereGeometry, Vector3 } from 'three';
1
+ import { AxesHelper, Box3, BoxGeometry, BufferAttribute, BufferGeometry, Color, type ColorRepresentation, CylinderGeometry, EdgesGeometry, Line, LineBasicMaterial, LineSegments, Material, Matrix4, Mesh, MeshBasicMaterial, Object3D, Quaternion, SphereGeometry, Vector3 } from 'three';
2
2
  import ThreeMeshUI, { Inline, Text } from "three-mesh-ui"
3
3
  import { type Options } from 'three-mesh-ui/build/types/core/elements/MeshUIBaseElement.js';
4
4
 
@@ -289,11 +289,11 @@ export class Gizmos {
289
289
  const mesh = Internal.getMesh(options.duration ?? 0);
290
290
  if ("mesh" in options) {
291
291
  mesh.geometry = options.mesh.geometry;
292
- mesh.matrix.copy(options.mesh.matrixWorld);
292
+ mesh.matrixWorld.copy(options.mesh.matrixWorld);
293
293
  }
294
294
  else {
295
295
  mesh.geometry = options.geometry;
296
- mesh.matrix.copy(options.matrix);
296
+ mesh.matrixWorld.copy(options.matrix);
297
297
  }
298
298
  mesh.matrixAutoUpdate = false;
299
299
  mesh.matrixWorldAutoUpdate = false;