@needle-tools/engine 4.11.0-beta → 4.11.0-beta.1
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/CHANGELOG.md +1 -0
- package/dist/{gltf-progressive-B63NpN_i.js → gltf-progressive-CXVECA3a.js} +1 -1
- package/dist/{needle-engine.bundle-BJ2hrLWW.min.js → needle-engine.bundle-6j5gE-aQ.min.js} +67 -67
- package/dist/{needle-engine.bundle-DZS0TuER.js → needle-engine.bundle-BDZ09xyt.js} +1062 -1059
- package/dist/{needle-engine.bundle-JCl0_y_J.umd.cjs → needle-engine.bundle-CFc4UIqz.umd.cjs} +73 -73
- package/dist/needle-engine.js +3 -3
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/engine_physics.js +25 -2
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +2 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.js +1 -2
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/package.json +1 -1
- package/plugins/common/needle-engine.js +41 -0
- package/plugins/common/worker.js +129 -0
- package/plugins/vite/asap.js +5 -23
- package/plugins/vite/dependencies.js +21 -11
- package/plugins/vite/index.js +7 -0
- package/plugins/vite/needle-app.js +194 -0
- package/src/engine/engine_physics.ts +27 -2
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +3 -1
- package/src/engine-components/web/ScrollFollow.ts +1 -1
|
@@ -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,129 @@
|
|
|
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
|
+
}
|
|
76
|
+
return res;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fix worker self.location to import.meta.url
|
|
86
|
+
* @param {string} filename
|
|
87
|
+
* @param {string} code
|
|
88
|
+
*/
|
|
89
|
+
function fixWorkerSelfLocation(filename, code) {
|
|
90
|
+
let lastIndex = 0;
|
|
91
|
+
while (true) {
|
|
92
|
+
const startIndex = code.indexOf("new Worker", lastIndex);
|
|
93
|
+
if (startIndex < 0) break;
|
|
94
|
+
let index = startIndex + 1;
|
|
95
|
+
let endIndex = -1;
|
|
96
|
+
let openingBraceCount = 0;
|
|
97
|
+
let foundAnyOpening = false;
|
|
98
|
+
while (true) {
|
|
99
|
+
const char = code[index];
|
|
100
|
+
if (char === "(") {
|
|
101
|
+
openingBraceCount++;
|
|
102
|
+
foundAnyOpening = true;
|
|
103
|
+
}
|
|
104
|
+
if (char === ")") openingBraceCount--;
|
|
105
|
+
if (openingBraceCount === 0 && foundAnyOpening) {
|
|
106
|
+
endIndex = index;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
// console.log(openingBraceCount, char, index, code.length);
|
|
110
|
+
index++;
|
|
111
|
+
if (index >= code.length) break;
|
|
112
|
+
}
|
|
113
|
+
if (endIndex > startIndex) {
|
|
114
|
+
const workerCode = code.substring(startIndex, endIndex + 1);
|
|
115
|
+
if (workerCode.indexOf("self.location") >= 0) {
|
|
116
|
+
const fixedCode = workerCode.replace("self.location", "import.meta.url");
|
|
117
|
+
code = code.substring(0, startIndex) + fixedCode + code.substring(endIndex + 1);
|
|
118
|
+
lastIndex = startIndex + fixedCode.length;
|
|
119
|
+
console.log(`[rollup] Rewrite worker 'self.location' to 'import.meta.url' in ${filename}`);
|
|
120
|
+
} else {
|
|
121
|
+
lastIndex = endIndex;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
lastIndex = startIndex + "new Worker".length;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return code;
|
|
129
|
+
}
|
package/plugins/vite/asap.js
CHANGED
|
@@ -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
|
|
175
|
-
if (needleEngineMatches) {
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
package/plugins/vite/index.js
CHANGED
|
@@ -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
|
-
|
|
650
|
-
|
|
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;
|
|
@@ -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
|
|
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
|
|
|
@@ -473,7 +473,7 @@ function tryGetElementsForSelector(index: number): Element | null {
|
|
|
473
473
|
|
|
474
474
|
if (!needsScrollMarkerRefresh) {
|
|
475
475
|
const element = needleScrollMarkerCache[index] || null;
|
|
476
|
-
|
|
476
|
+
return element;
|
|
477
477
|
}
|
|
478
478
|
needsScrollMarkerRefresh = false;
|
|
479
479
|
needleScrollMarkerCache.length = 0;
|