Needle Engine

Changes between version 3.5.8-alpha and 3.5.9-alpha
Files changed (25) hide show
  1. plugins/vite/alias.js +3 -0
  2. plugins/vite/config.js +4 -2
  3. plugins/vite/copyfiles.js +3 -1
  4. plugins/vite/defines.js +3 -0
  5. plugins/vite/dependency-watcher.js +3 -0
  6. plugins/vite/editor-connection.js +3 -0
  7. plugins/vite/index.js +4 -0
  8. plugins/vite/meta.js +3 -0
  9. plugins/vite/peer.js +3 -0
  10. plugins/vite/reload.js +3 -0
  11. plugins/vite/transform-codegen.js +1 -0
  12. src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +10 -0
  13. src/engine/engine_element_loading.ts +1 -1
  14. src/engine-components/export/usdz/index.ts +3 -1
  15. src/engine-components/utils/LookAt.ts +3 -2
  16. src/engine-components/export/usdz/ThreeUSDZExporter.ts +12 -6
  17. plugins/common/config.cjs +15 -0
  18. plugins/common/license.cjs +31 -0
  19. plugins/next/license.cjs +5 -0
  20. plugins/next/index.js +1 -0
  21. plugins/next/next.js +64 -0
  22. plugins/types/index.d.ts +2 -0
  23. plugins/types/needleConfig.d.ts +12 -0
  24. plugins/types/next.d.ts +0 -0
  25. plugins/types/userconfig.d.ts +27 -0
plugins/vite/alias.js CHANGED
@@ -25,6 +25,9 @@
25
25
  'md5': null,
26
26
  }
27
27
 
28
+ /**
29
+ * @param {import('../types').userSettings} userSettings
30
+ */
28
31
  export const needleViteAlias = (command, config, userSettings) => {
29
32
 
30
33
  if (config?.noAlias === true || userSettings?.noAlias === true)
plugins/vite/config.js CHANGED
@@ -17,7 +17,9 @@
17
17
  if (existsSync(path)) {
18
18
  const text = readFileSync(path, 'utf8');
19
19
  if (!text) return null;
20
- return JSON.parse(text);
20
+ /**@type {import("../types/needleConfig").needleConfig} */
21
+ const meta = JSON.parse(text);
22
+ return meta;
21
23
  }
22
24
  else console.error("Could not find config file at " + path);
23
25
  return null;
@@ -51,7 +53,7 @@
51
53
 
52
54
 
53
55
  /** "assets" -> the directory name inside the output directory to put e.g. glb files into */
54
- export function builtAssetsDirectory(){
56
+ export function builtAssetsDirectory() {
55
57
  return "assets";
56
58
  }
57
59
 
plugins/vite/copyfiles.js CHANGED
@@ -4,7 +4,9 @@
4
4
  import { builtAssetsDirectory, tryLoadProjectConfig } from './config.js';
5
5
 
6
6
 
7
- /** copy files on build from assets to dist */
7
+ /** copy files on build from assets to dist
8
+ * @param {import('../types').userSettings} userSettings
9
+ */
8
10
  export const needleCopyFiles = (command, config, userSettings) => {
9
11
 
10
12
  if (config?.noCopy === true || userSettings?.noCopy === true) {
plugins/vite/defines.js CHANGED
@@ -9,6 +9,9 @@
9
9
 
10
10
  // https://vitejs.dev/config/#using-environment-variables-in-config
11
11
 
12
+ /**
13
+ * @param {import('../types').userSettings} userSettings
14
+ */
12
15
  export const needleDefines = (command, needleEngineConfig, userSettings) => {
13
16
 
14
17
  if (!userSettings) userSettings = {};
plugins/vite/dependency-watcher.js CHANGED
@@ -8,6 +8,9 @@
8
8
  console.log(prefix, ...msg)
9
9
  }
10
10
 
11
+ /**
12
+ * @param {import('../types').userSettings} userSettings
13
+ */
11
14
  export const needleDependencyWatcher = (command, config, userSettings) => {
12
15
  if (command === "build") return;
13
16
 
plugins/vite/editor-connection.js CHANGED
@@ -12,6 +12,9 @@
12
12
  */
13
13
  let editorSyncEnabled = false;
14
14
 
15
+ /**
16
+ * @param {import('../types').userSettings} userSettings
17
+ */
15
18
  export const editorConnection = async (command, config, userSettings, pluginsArray) => {
16
19
  if (command === "build") return;
17
20
 
plugins/vite/index.js CHANGED
@@ -20,7 +20,11 @@
20
20
  allowHotReload: true,
21
21
  }
22
22
 
23
+ /**
24
+ * @param {import('../types').userSettings} userSettings
25
+ */
23
26
  export const needlePlugins = async (command, config, userSettings) => {
27
+
24
28
  // ensure we have user settings initialized with defaults
25
29
  userSettings = { ...defaultUserSettings, ...userSettings }
26
30
  const array = [
plugins/vite/meta.js CHANGED
@@ -3,6 +3,9 @@
3
3
  import { getPosterPath } from './poster.js';
4
4
  import { tryGetNeedleEngineVersion } from './utils.js';
5
5
 
6
+ /**
7
+ * @param {import('../types').userSettings} userSettings
8
+ */
6
9
  export const needleMeta = (command, config, userSettings) => {
7
10
 
8
11
  // we can check if this is a build
plugins/vite/peer.js CHANGED
@@ -3,6 +3,9 @@
3
3
  window.global = window;
4
4
  var parcelRequire;`
5
5
 
6
+ /**
7
+ * @param {import('../types').userSettings} userSettings
8
+ */
6
9
  export const needlePeerjs = (command, config, userSettings) => {
7
10
 
8
11
  if (userSettings.noPeer === true) return;
plugins/vite/reload.js CHANGED
@@ -11,6 +11,9 @@
11
11
  const filesUsingHotReload = new Set();
12
12
  let assetsDirectory = "";
13
13
 
14
+ /**
15
+ * @param {import('../types').userSettings} userSettings
16
+ */
14
17
  export const needleReload = (command, config, userSettings) => {
15
18
  if (command === "build") return;
16
19
 
plugins/vite/transform-codegen.js CHANGED
@@ -3,6 +3,7 @@
3
3
  /**
4
4
  * modify the glb load path in codegen files
5
5
  * this is necessary if the assets directory is not the default (changed by the user in needle.config.json)
6
+ * @param {import('../types').userSettings} userSettings
6
7
  */
7
8
  export const needleTransformCodegen = (command, config, userSettings) => {
8
9
 
src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts CHANGED
@@ -110,6 +110,7 @@
110
110
  targetId?: string | Target;
111
111
  tokenId?: string;
112
112
  type?: string;
113
+ distance?: number;
113
114
 
114
115
  constructor(targetId?: string | Target, id?: string) {
115
116
  if (targetId) this.targetId = targetId;
@@ -127,6 +128,8 @@
127
128
  writer.appendLine(`token info:id = "${this.tokenId}"`);
128
129
  if (this.type)
129
130
  writer.appendLine(`token type = "${this.type}"`);
131
+ if (typeof this.distance === "number")
132
+ writer.appendLine(`double distance = ${this.distance}`);
130
133
  writer.closeBlock();
131
134
  }
132
135
  }
@@ -150,6 +153,13 @@
150
153
  static isTapTrigger(trigger?: TriggerModel) {
151
154
  return trigger?.tokenId === "TapGesture";
152
155
  }
156
+
157
+ static proximityToCameraTrigger(targetObject: Target, distance: number): TriggerModel {
158
+ const trigger = new TriggerModel(targetObject);
159
+ trigger.tokenId = "ProximityToCamera";
160
+ trigger.distance = distance;
161
+ return trigger;
162
+ }
153
163
  }
154
164
 
155
165
  export class GroupActionModel implements IBehaviorElement {
src/engine/engine_element_loading.ts CHANGED
@@ -178,7 +178,7 @@
178
178
 
179
179
  const hasLicense = hasProLicense();
180
180
  if (!existing) {
181
- this._loadingElement.style.position = "fixed";
181
+ this._loadingElement.style.position = "absolute";
182
182
  this._loadingElement.style.width = "100%";
183
183
  this._loadingElement.style.height = "100%";
184
184
  this._loadingElement.style.left = "0";
src/engine-components/export/usdz/index.ts CHANGED
@@ -1,1 +1,3 @@
1
- export { USDZExporter } from "./USDZExporter"
1
+ export { USDZExporter } from "./USDZExporter";
2
+ export { USDObject } from "./ThreeUSDZExporter";
3
+ export { type UsdzBehaviour } from "./extensions/behavior/Behaviour";
src/engine-components/utils/LookAt.ts CHANGED
@@ -55,8 +55,9 @@
55
55
 
56
56
  // rotate by 90° - counter-rotation on the parent makes sure
57
57
  // that without Preliminary Behaviours it still looks right
58
- parent.matrix.multiply(new Matrix4().makeRotationZ(Math.PI / 2));
59
- model.matrix.multiply(new Matrix4().makeRotationZ(-Math.PI / 2));
58
+ const flip = this.invertForward ? -1 : 1;
59
+ parent.matrix.multiply(new Matrix4().makeRotationZ(Math.PI / 2 * flip));
60
+ model.matrix.multiply(new Matrix4().makeRotationZ(-Math.PI / 2 * flip));
60
61
  }
61
62
 
62
63
  const lookAt = new BehaviorModel("lookat " + this.name,
src/engine-components/export/usdz/ThreeUSDZExporter.ts CHANGED
@@ -66,6 +66,13 @@
66
66
 
67
67
  }
68
68
 
69
+ static createEmpty() {
70
+
71
+ const empty = new USDObject( MathUtils.generateUUID(), 'Empty_' + ( USDObject.USDObject_export_id ++ ), new Matrix4() );
72
+ empty.isDynamic = true;
73
+ return empty;
74
+ }
75
+
69
76
  constructor( id, name, matrix, mesh: BufferGeometry | null = null, material: Material | null = null, camera: Camera | null = null ) {
70
77
 
71
78
  this.uuid = id;
@@ -443,7 +450,7 @@
443
450
 
444
451
  await invokeAll( context, 'onAfterSerialize' );
445
452
 
446
- context.output += buildMaterials( materials, textures );
453
+ context.output += buildMaterials( materials, textures, options.quickLookCompatible );
447
454
 
448
455
  const header = context.document.buildHeader();
449
456
  const final = header + '\n' + context.output;
@@ -1100,7 +1107,7 @@
1100
1107
 
1101
1108
  // Materials
1102
1109
 
1103
- function buildMaterials( materials, textures ) {
1110
+ function buildMaterials( materials, textures, quickLookCompatible = false ) {
1104
1111
 
1105
1112
  const array: Array<string> = [];
1106
1113
 
@@ -1108,7 +1115,7 @@
1108
1115
 
1109
1116
  const material = materials[ uuid ];
1110
1117
 
1111
- array.push( buildMaterial( material, textures ) );
1118
+ array.push( buildMaterial( material, textures, quickLookCompatible ) );
1112
1119
 
1113
1120
  }
1114
1121
 
@@ -1121,14 +1128,13 @@
1121
1128
 
1122
1129
  }
1123
1130
 
1124
- function buildMaterial( material: MeshStandardMaterial, textures ) {
1131
+ function buildMaterial( material: MeshStandardMaterial, textures, quickLookCompatible = false ) {
1125
1132
 
1126
1133
  // https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
1127
1134
 
1128
1135
  const pad = ' ';
1129
1136
  const inputs: Array<string> = [];
1130
1137
  const samplers: Array<string> = [];
1131
- const exportForQuickLook = false;
1132
1138
 
1133
1139
  function buildTexture( texture, mapType, color: Color | undefined = undefined, opacity: number | undefined = undefined ) {
1134
1140
 
@@ -1151,7 +1157,7 @@
1151
1157
 
1152
1158
  // turns out QuickLook is buggy and interprets texture repeat inverted.
1153
1159
  // Apple Feedback: FB10036297 and FB11442287
1154
- if ( exportForQuickLook ) {
1160
+ if ( quickLookCompatible ) {
1155
1161
 
1156
1162
  // This is NOT correct yet in QuickLook, but comes close for a range of models.
1157
1163
  // It becomes more incorrect the bigger the offset is
plugins/common/config.cjs ADDED
@@ -0,0 +1,15 @@
1
+
2
+
3
+
4
+ module.exports.getMeta = function () {
5
+ const workingDirectory = process.cwd();
6
+ const needleConfig = require(workingDirectory + "/needle.config.json");
7
+ if (needleConfig.codegenDirectory) {
8
+ const metaPath = workingDirectory + "/" + needleConfig.codegenDirectory + "/meta.json";
9
+
10
+ /**@type {import("../types").needleConfig} */
11
+ const meta = require(metaPath);
12
+ return meta;
13
+ }
14
+ return null;
15
+ }
plugins/common/license.cjs ADDED
@@ -0,0 +1,31 @@
1
+ const { getMeta } = require("./config.cjs");
2
+
3
+ /**
4
+ * @param {string} code
5
+ * @param {string | null | undefined} licenseType
6
+ */
7
+ module.exports.replaceLicense = function (code, licenseType) {
8
+
9
+ if (!licenseType) {
10
+ const meta = getMeta();
11
+ if (meta) {
12
+ licenseType = meta.license;
13
+ }
14
+ }
15
+
16
+
17
+ if (!licenseType) {
18
+ return code;
19
+ }
20
+
21
+ const index = code.indexOf("NEEDLE_ENGINE_LICENSE_TYPE");
22
+ if (index >= 0) {
23
+ const end = code.indexOf(";", index);
24
+ if (end >= 0) {
25
+ const line = code.substring(index, end);
26
+ const replaced = "NEEDLE_ENGINE_LICENSE_TYPE = \"" + licenseType + "\"";
27
+ code = code.replace(line, replaced);
28
+ return code;
29
+ }
30
+ }
31
+ }
plugins/next/license.cjs ADDED
@@ -0,0 +1,5 @@
1
+ const { replaceLicense } = require("../common/license.cjs");
2
+
3
+ module.exports = function (source, _map) {
4
+ return replaceLicense(source);
5
+ };
plugins/next/index.js ADDED
@@ -0,0 +1,1 @@
1
+ export * from "./next.js"
plugins/next/next.js ADDED
@@ -0,0 +1,64 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { dirname, resolve } from 'path';
3
+ // import { ApplyLicensePlugin } from './license.js';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+
9
+ /** Pass a nextConfig object in to add the needle specific settings.
10
+ * Optionally omit nextConfig and it will be created for you.
11
+ * @param {import('next').NextConfig} nextConfig
12
+ * @param {import('@needle-tools/engine/plugins/types').userSettings} userSettings
13
+ * @returns {import('next').NextConfig}
14
+ */
15
+ export const needleNext = (nextConfig, userSettings) => {
16
+ if (!nextConfig) nextConfig = {
17
+ reactStrictMode: true,
18
+ };
19
+
20
+ // add transpile packages
21
+ if (!nextConfig.transpilePackages) {
22
+ nextConfig.transpilePackages = [];
23
+ }
24
+ nextConfig.transpilePackages.push("three", "peerjs", "@needle-tools/engine", "three-mesh-ui");
25
+
26
+ // add webpack config
27
+ if (!nextConfig.webpack) nextConfig.webpack = nextWebPack;
28
+ else {
29
+ const webpackFn = nextConfig.webpack;
30
+ nextConfig.webpack = (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
31
+ nextWebPack(config, { buildId, dev, isServer, defaultLoaders, webpack });
32
+ return webpackFn(config, { buildId, dev, isServer, defaultLoaders, webpack });
33
+ }
34
+ }
35
+ /** @param {import ('next').NextConfig config } */
36
+ function nextWebPack(config, { buildId, dev, isServer, defaultLoaders, webpack }) {
37
+ // add defines
38
+ const webpackModule = userSettings.modules?.webpack;
39
+ const definePlugin = webpackModule && new webpackModule.DefinePlugin({
40
+ NEEDLE_ENGINE_META: {},
41
+ NEEDLE_USE_RAPIER: true,
42
+ parcelRequire: undefined,
43
+ });
44
+ if (!definePlugin) console.log("WARN: no define plugin provided. Did you miss adding the webpack module to the next config? You can pass it to the Needle plugins via `nextConfig.modules = { webpack };`");
45
+ else
46
+ config.plugins.push(definePlugin);
47
+
48
+
49
+
50
+
51
+ if (!config.module) config.module = {};
52
+ if (!config.module.rules) config.module.rules = [];
53
+ // add license plugin
54
+ config.module.rules.push({
55
+ test: /engine_license\.(ts|js)$/,
56
+ loader: resolve(__dirname, 'license.cjs')
57
+ });
58
+
59
+ return config;
60
+ }
61
+
62
+
63
+ return nextConfig;
64
+ };
plugins/types/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './userconfig';
2
+ export * from "./needleConfig";
plugins/types/needleConfig.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export type needleConfig = {
2
+ needleEditor: string | null,
3
+ meta: string | null;
4
+ absolutePath: string | null,
5
+ sceneName: string | null,
6
+ deployOnly: boolean,
7
+ generator: string | null,
8
+ gzip: boolean,
9
+ allowHotReload: boolean,
10
+ license: string | null,
11
+ useRapier: boolean,
12
+ }
plugins/types/next.d.ts ADDED
File without changes
plugins/types/userconfig.d.ts ADDED
@@ -0,0 +1,27 @@
1
+
2
+
3
+ export type needleModules = {
4
+ webpack: object | undefined
5
+ }
6
+
7
+ export type userSettings = {
8
+ /** disable vite.alias modification */
9
+ noAlias: boolean;
10
+ /** disable automatic copying of files to include and output directory (dist) */
11
+ noCopy: boolean;
12
+ /** set to false to tree-shake rapier physics engine to the reduce bundle size */
13
+ useRapier: boolean;
14
+ noDependencyWatcher: boolean;
15
+ /** set to false to suppress editor-sync package installation and connection */
16
+ dontInstallEditor: boolean;
17
+ /** set to false to prevent meta.html modifications (vite only) */
18
+ allowMetaPlugin: boolean;
19
+ /** set to true to prevent injecting peerjs `parcelRequire` global variable declaration */
20
+ noPeer: boolean;
21
+ /** set to true to disable reload plugin */
22
+ noReload: boolean;
23
+ noCodegenTransform: boolean;
24
+
25
+ /** used by nextjs config to forward the webpack module */
26
+ modules: needleModules
27
+ }