@needle-tools/engine 3.5.3-alpha.1 → 3.5.5-alpha
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 +21 -0
- package/dist/needle-engine.js +8168 -8135
- package/dist/needle-engine.min.js +310 -310
- package/dist/needle-engine.umd.cjs +296 -296
- package/lib/engine/codegen/register_types.js +2 -2
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_addressables.js +3 -0
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_constants.d.ts +2 -0
- package/lib/engine/engine_constants.js +2 -0
- package/lib/engine/engine_constants.js.map +1 -1
- package/lib/engine/engine_context.d.ts +2 -0
- package/lib/engine/engine_context.js +5 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_license.js +1 -1
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_serialization_core.js +1 -1
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_three_utils.d.ts +5 -0
- package/lib/engine/engine_three_utils.js +29 -12
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +12 -0
- package/lib/engine/engine_utils.js +20 -0
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine-components/AnimatorController.js +1 -2
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -1
- package/lib/engine-components/codegen/components.js +1 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +2 -2
- package/lib/engine-components/export/usdz/USDZExporter.js +23 -11
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/ui/Graphic.js +3 -2
- package/lib/engine-components/ui/Graphic.js.map +1 -1
- package/lib/engine-components/ui/Text.js +5 -2
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/needle-engine.js +5 -0
- package/lib/needle-engine.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugins/vite/copyfiles.js +49 -2
- package/plugins/vite/defines.js +12 -6
- package/plugins/vite/license.js +16 -3
- package/plugins/vite/meta.js +16 -3
- package/plugins/vite/utils.js +11 -0
- package/src/engine/codegen/register_types.js +2 -2
- package/src/engine/engine_addressables.ts +2 -1
- package/src/engine/engine_constants.ts +3 -0
- package/src/engine/engine_context.ts +6 -0
- package/src/engine/engine_license.ts +1 -1
- package/src/engine/engine_serialization_core.ts +1 -1
- package/src/engine/engine_three_utils.ts +28 -12
- package/src/engine/engine_utils.ts +34 -0
- package/src/engine-components/AnimatorController.ts +1 -1
- package/src/engine-components/codegen/components.ts +1 -1
- package/src/engine-components/export/usdz/USDZExporter.ts +19 -7
- package/src/engine-components/ui/Graphic.ts +3 -1
- package/src/engine-components/ui/Text.ts +5 -4
- package/src/needle-engine.ts +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.5-alpha",
|
|
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.umd.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { resolve, join } from 'path'
|
|
2
|
+
import { resolve, join, isAbsolute } from 'path'
|
|
3
3
|
import { existsSync, statSync, mkdirSync, readdirSync, copyFileSync, mkdir } from 'fs';
|
|
4
4
|
import { builtAssetsDirectory, tryLoadProjectConfig } from './config.js';
|
|
5
5
|
|
|
@@ -51,6 +51,30 @@ async function run(isBuild, config) {
|
|
|
51
51
|
if (!existsSync(outDir)) {
|
|
52
52
|
mkdirSync(outDir);
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
// copy a list of files or directories declared in build.copy = [] in the needle.config.json
|
|
56
|
+
/*
|
|
57
|
+
"build": {
|
|
58
|
+
"copy": ["myFolder", "myFile.txt"]
|
|
59
|
+
}
|
|
60
|
+
*/
|
|
61
|
+
if (needleConfig?.build?.copy) {
|
|
62
|
+
const arr = needleConfig.build.copy;
|
|
63
|
+
for (let i = 0; i < arr.length; i++) {
|
|
64
|
+
const entry = arr[i];
|
|
65
|
+
if (Array.isArray(entry)) {
|
|
66
|
+
console.log("WARN: build.copy can only contain string paths to copy to. Found array instead.");
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const src = resolve(baseDir, entry);
|
|
70
|
+
const dest = resolvePath(outDir, entry);
|
|
71
|
+
if (existsSync(src)) {
|
|
72
|
+
console.log(`[${pluginName}] - Copy ${entry} to ${outdirName}/${entry}`)
|
|
73
|
+
copyRecursiveSync(src, dest);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
54
78
|
// copy assets dir
|
|
55
79
|
const assetsDir = resolve(baseDir, assetsDirName);
|
|
56
80
|
if (existsSync(assetsDir)) {
|
|
@@ -68,13 +92,36 @@ async function run(isBuild, config) {
|
|
|
68
92
|
}
|
|
69
93
|
}
|
|
70
94
|
|
|
95
|
+
/** resolves relative or absolute paths to a path inside the out directory
|
|
96
|
+
* for example D:/myFile.txt would resolve to outDir/myFile.txt
|
|
97
|
+
* wherereas "some/relative/path" would become outDir/some/relative/path
|
|
98
|
+
*/
|
|
99
|
+
function resolvePath(outDir, pathValue) {
|
|
100
|
+
if (isAbsolute(pathValue)) {
|
|
101
|
+
var exists = existsSync(pathValue);
|
|
102
|
+
if (!exists) return null;
|
|
103
|
+
var stats = exists && statSync(pathValue);
|
|
104
|
+
if (stats.isDirectory()) {
|
|
105
|
+
const dirName = pathValue.replaceAll('\\', '/').split('/').pop();
|
|
106
|
+
return resolve(outDir, dirName);
|
|
107
|
+
}
|
|
108
|
+
const fileName = pathValue.replaceAll('\\', '/').split('/').pop();
|
|
109
|
+
return resolve(outDir, fileName);
|
|
110
|
+
}
|
|
111
|
+
return resolve(outDir, pathValue);
|
|
112
|
+
}
|
|
113
|
+
|
|
71
114
|
function copyRecursiveSync(src, dest) {
|
|
115
|
+
if (dest === null) {
|
|
116
|
+
console.log(`[${pluginName}] - Copy ${src} to ${dest} - dest is null`)
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
72
119
|
var exists = existsSync(src);
|
|
73
120
|
var stats = exists && statSync(src);
|
|
74
121
|
var isDirectory = exists && stats.isDirectory();
|
|
75
122
|
if (isDirectory) {
|
|
76
123
|
if (!existsSync(dest))
|
|
77
|
-
mkdirSync(dest);
|
|
124
|
+
mkdirSync(dest, { recursive: true });
|
|
78
125
|
readdirSync(src).forEach(function (childItemName) {
|
|
79
126
|
copyRecursiveSync(join(src, childItemName), join(dest, childItemName));
|
|
80
127
|
});
|
package/plugins/vite/defines.js
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import { loadConfig } from "./config.js";
|
|
2
|
+
import { tryGetNeedleEngineVersion } from "./utils.js";
|
|
2
3
|
|
|
3
4
|
/** used to pass config variables into vite.config.define
|
|
4
5
|
* for example "useRapier"
|
|
5
6
|
*/
|
|
6
|
-
export const needleDefines = (command,
|
|
7
|
+
export const needleDefines = (command, needleEngineConfig, userSettings) => {
|
|
7
8
|
|
|
8
9
|
if (!userSettings) userSettings = {};
|
|
9
10
|
|
|
10
11
|
let useRapier = true;
|
|
11
|
-
if (
|
|
12
|
+
if (needleEngineConfig.useRapier === false || userSettings?.useRapier === false) useRapier = false;
|
|
12
13
|
|
|
13
14
|
return {
|
|
14
15
|
name: 'needle-defines',
|
|
15
16
|
enforce: 'pre',
|
|
16
|
-
config(
|
|
17
|
+
config(viteConfig) {
|
|
18
|
+
if (!viteConfig.define) viteConfig.define = {};
|
|
19
|
+
viteConfig.define.NEEDLE_ENGINE_META = {
|
|
20
|
+
version: tryGetNeedleEngineVersion(),
|
|
21
|
+
generator: needleEngineConfig.generator,
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
if (useRapier && userSettings?.useRapier !== true) {
|
|
18
25
|
const meta = loadConfig();
|
|
19
26
|
if (meta?.useRapier === false) {
|
|
@@ -21,9 +28,8 @@ export const needleDefines = (command, config, userSettings) => {
|
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
console.log("UseRapier?", useRapier);
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
config.define.NEEDLE_USE_RAPIER = useRapier;
|
|
31
|
+
if (viteConfig.define.NEEDLE_USE_RAPIER === undefined) {
|
|
32
|
+
viteConfig.define.NEEDLE_USE_RAPIER = useRapier;
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
35
|
}
|
package/plugins/vite/license.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from './config.js';
|
|
2
2
|
|
|
3
|
+
let didLog = false;
|
|
3
4
|
|
|
4
5
|
export const needleLicense = (command, config, userSettings) => {
|
|
5
6
|
|
|
@@ -7,15 +8,27 @@ export const needleLicense = (command, config, userSettings) => {
|
|
|
7
8
|
name: "needle-license",
|
|
8
9
|
enforce: 'pre',
|
|
9
10
|
async transform(src, id) {
|
|
10
|
-
const isNeedleEngineFile = id.includes("engine/engine_license") || id.includes("needle-tools_engine
|
|
11
|
+
const isNeedleEngineFile = id.includes("engine/engine_license") || id.includes("needle-tools_engine");
|
|
11
12
|
// sometimes the actual license parameter is in a unnamed chunk file
|
|
12
13
|
const isViteChunkFile = id.includes("chunk") && id.includes(".vite");
|
|
13
14
|
if (isNeedleEngineFile || isViteChunkFile) {
|
|
14
15
|
const needleConfig = await loadConfig();
|
|
15
16
|
if (needleConfig) {
|
|
16
17
|
if (typeof needleConfig.license === "string") {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
if (!didLog) {
|
|
19
|
+
didLog = true;
|
|
20
|
+
console.log("Applying license: " + needleConfig.license);
|
|
21
|
+
}
|
|
22
|
+
const index = src.indexOf("NEEDLE_ENGINE_LICENSE_TYPE");
|
|
23
|
+
if (index >= 0) {
|
|
24
|
+
const end = src.indexOf(";", index);
|
|
25
|
+
if (end >= 0) {
|
|
26
|
+
const line = src.substring(index, end);
|
|
27
|
+
const replaced = "NEEDLE_ENGINE_LICENSE_TYPE = \"" + needleConfig.license + "\"";
|
|
28
|
+
src = src.replace(line, replaced);
|
|
29
|
+
return { code: src, map: null }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
19
32
|
}
|
|
20
33
|
}
|
|
21
34
|
else {
|
package/plugins/vite/meta.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { loadConfig } from './config.js';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import { getPosterPath } from './poster.js';
|
|
4
|
+
import { tryGetNeedleEngineVersion } from './utils.js';
|
|
4
5
|
|
|
5
6
|
export const needleMeta = (command, config, userSettings) => {
|
|
6
7
|
|
|
@@ -15,7 +16,7 @@ export const needleMeta = (command, config, userSettings) => {
|
|
|
15
16
|
|
|
16
17
|
return {
|
|
17
18
|
// replace meta tags
|
|
18
|
-
name: 'needle-meta
|
|
19
|
+
name: 'needle-meta',
|
|
19
20
|
transformIndexHtml: {
|
|
20
21
|
enforce: 'pre',
|
|
21
22
|
transform(html, _ctx) {
|
|
@@ -91,8 +92,19 @@ export const needleMeta = (command, config, userSettings) => {
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
let generator = "Needle";
|
|
96
|
+
if (config.generator?.length > 5) {
|
|
97
|
+
generator = config.generator;
|
|
98
|
+
}
|
|
99
|
+
tags.push({ tag: 'meta', attrs: { name: 'generator', content: generator } });
|
|
100
|
+
|
|
101
|
+
const needleEngineVersion = tryGetNeedleEngineVersion();
|
|
102
|
+
if (needleEngineVersion) {
|
|
103
|
+
if (command === "build")
|
|
104
|
+
console.log("Needle Engine version: " + needleEngineVersion);
|
|
105
|
+
tags.push({ tag: 'meta', attrs: { name: 'needle-engine', content: needleEngineVersion } });
|
|
106
|
+
}
|
|
107
|
+
else console.log("WARN: could not find needle engine package.json")
|
|
96
108
|
|
|
97
109
|
return { html, tags }
|
|
98
110
|
},
|
|
@@ -100,6 +112,7 @@ export const needleMeta = (command, config, userSettings) => {
|
|
|
100
112
|
}
|
|
101
113
|
}
|
|
102
114
|
|
|
115
|
+
|
|
103
116
|
function updateUrlMetaTag(html, url) {
|
|
104
117
|
html = html.replace(`<meta name="url" content="http://needle.tools">`, `<meta name="url" content="${url}">`);
|
|
105
118
|
return html;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
|
|
3
|
+
export function tryGetNeedleEngineVersion() {
|
|
4
|
+
const needleEnginePackageJsonPath = process.cwd() + "/node_modules/@needle-tools/engine/package.json";
|
|
5
|
+
if (existsSync(needleEnginePackageJsonPath)) {
|
|
6
|
+
const json = JSON.parse(readFileSync(needleEnginePackageJsonPath));
|
|
7
|
+
const version = json.version;
|
|
8
|
+
return version;
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
@@ -54,6 +54,7 @@ import { ColorBySpeedModule } from "../../engine-components/ParticleSystemModule
|
|
|
54
54
|
import { ColorOverLifetimeModule } from "../../engine-components/ParticleSystemModules";
|
|
55
55
|
import { Component } from "../../engine-components/Component";
|
|
56
56
|
import { ControlTrackHandler } from "../../engine-components/timeline/TimelineTracks";
|
|
57
|
+
import { CustomBranding } from "../../engine-components/export/usdz/USDZExporter";
|
|
57
58
|
import { Deletable } from "../../engine-components/DeleteBox";
|
|
58
59
|
import { DeleteBox } from "../../engine-components/DeleteBox";
|
|
59
60
|
import { DepthOfField } from "../../engine-components/postprocessing/Effects/DepthOfField";
|
|
@@ -126,7 +127,6 @@ import { PostProcessingHandler } from "../../engine-components/postprocessing/Po
|
|
|
126
127
|
import { PreliminaryAction } from "../../engine-components/export/usdz/extensions/behavior/BehaviourComponents";
|
|
127
128
|
import { PreliminaryTrigger } from "../../engine-components/export/usdz/extensions/behavior/BehaviourComponents";
|
|
128
129
|
import { PresentationMode } from "../../engine-components-experimental/Presentation";
|
|
129
|
-
import { QuickLookOverlay } from "../../engine-components/export/usdz/USDZExporter";
|
|
130
130
|
import { RawImage } from "../../engine-components/ui/Image";
|
|
131
131
|
import { Raycaster } from "../../engine-components/ui/Raycaster";
|
|
132
132
|
import { Rect } from "../../engine-components/ui/RectTransform";
|
|
@@ -270,6 +270,7 @@ TypeStore.add("ColorBySpeedModule", ColorBySpeedModule);
|
|
|
270
270
|
TypeStore.add("ColorOverLifetimeModule", ColorOverLifetimeModule);
|
|
271
271
|
TypeStore.add("Component", Component);
|
|
272
272
|
TypeStore.add("ControlTrackHandler", ControlTrackHandler);
|
|
273
|
+
TypeStore.add("CustomBranding", CustomBranding);
|
|
273
274
|
TypeStore.add("Deletable", Deletable);
|
|
274
275
|
TypeStore.add("DeleteBox", DeleteBox);
|
|
275
276
|
TypeStore.add("DepthOfField", DepthOfField);
|
|
@@ -342,7 +343,6 @@ TypeStore.add("PostProcessingHandler", PostProcessingHandler);
|
|
|
342
343
|
TypeStore.add("PreliminaryAction", PreliminaryAction);
|
|
343
344
|
TypeStore.add("PreliminaryTrigger", PreliminaryTrigger);
|
|
344
345
|
TypeStore.add("PresentationMode", PresentationMode);
|
|
345
|
-
TypeStore.add("QuickLookOverlay", QuickLookOverlay);
|
|
346
346
|
TypeStore.add("RawImage", RawImage);
|
|
347
347
|
TypeStore.add("Raycaster", Raycaster);
|
|
348
348
|
TypeStore.add("Rect", Rect);
|
|
@@ -353,7 +353,7 @@ new AddressableSerializer();
|
|
|
353
353
|
|
|
354
354
|
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
const failedTexturePromise = Promise.resolve(null);
|
|
357
357
|
|
|
358
358
|
export class ImageReference {
|
|
359
359
|
|
|
@@ -392,6 +392,7 @@ export class ImageReference {
|
|
|
392
392
|
|
|
393
393
|
private loader: TextureLoader | null = null;
|
|
394
394
|
createTexture(): Promise<Texture | null> {
|
|
395
|
+
if (!this.url) return failedTexturePromise;
|
|
395
396
|
if (!this.loader) this.loader = new TextureLoader();
|
|
396
397
|
this.loader.setCrossOrigin("anonymous");
|
|
397
398
|
return this.loader.loadAsync(this.url);
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
declare const NEEDLE_ENGINE_META: { version: string, generator: string };
|
|
2
|
+
export const NEEDLE_ENGINE_VERSION = NEEDLE_ENGINE_META.version;
|
|
3
|
+
export const NEEDLE_ENGINE_GENERATOR = NEEDLE_ENGINE_META.generator;
|
|
1
4
|
|
|
2
5
|
export const activeInHierarchyFieldName = "needle_isActiveInHierarchy";
|
|
3
6
|
export const builtinComponentKeyName = "builtin_components";
|
|
@@ -29,6 +29,7 @@ import { CoroutineData, ICamera, IComponent, IContext, ILight } from "./engine_t
|
|
|
29
29
|
import { destroy, foreachComponent } from './engine_gameobject';
|
|
30
30
|
import { ContextEvent, ContextRegistry } from './engine_context_registry';
|
|
31
31
|
import { delay } from './engine_utils';
|
|
32
|
+
import { NEEDLE_ENGINE_VERSION } from './engine_constants';
|
|
32
33
|
// import { createCameraWithOrbitControl } from '../engine-components/CameraUtils';
|
|
33
34
|
|
|
34
35
|
|
|
@@ -93,6 +94,11 @@ export function registerComponent(script: IComponent, context?: Context) {
|
|
|
93
94
|
|
|
94
95
|
export class Context implements IContext {
|
|
95
96
|
|
|
97
|
+
/** the needle engine version */
|
|
98
|
+
get version() {
|
|
99
|
+
return NEEDLE_ENGINE_VERSION;
|
|
100
|
+
}
|
|
101
|
+
|
|
96
102
|
static get Current(): Context {
|
|
97
103
|
return ContextRegistry.Current as Context;
|
|
98
104
|
}
|
|
@@ -7,7 +7,7 @@ const debug = getParam("debuglicense");
|
|
|
7
7
|
|
|
8
8
|
// This is modified by a bundler (e.g. vite)
|
|
9
9
|
// Do not edit manually
|
|
10
|
-
const NEEDLE_ENGINE_LICENSE_TYPE: string = "";
|
|
10
|
+
const NEEDLE_ENGINE_LICENSE_TYPE: string = "basic";
|
|
11
11
|
if (debug) console.log("License Type: " + NEEDLE_ENGINE_LICENSE_TYPE)
|
|
12
12
|
|
|
13
13
|
export function hasProLicense() {
|
|
@@ -436,7 +436,7 @@ function implictlyAssignPrimitiveTypes(obj: any, serializedData: any) {
|
|
|
436
436
|
if (isPrimitiveType(data[key]) && !isPrimitiveType(member)) {
|
|
437
437
|
|
|
438
438
|
const prop = tryFindPropertyDescriptor(member, key);
|
|
439
|
-
if (prop?.writable ===
|
|
439
|
+
if (prop && (prop?.writable === undefined || prop?.writable === false) && (prop.set === undefined)) {
|
|
440
440
|
if (debug)
|
|
441
441
|
console.warn("Property is not writable \"" + key + "\"", member, prop, data[key], member[key]);
|
|
442
442
|
continue;
|
|
@@ -50,13 +50,13 @@ export function setWorldPositionXYZ(obj: Object3D, x: number, y: number, z: numb
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
const _worldQuaternions = new CircularBuffer(() => new Quaternion(), 100);
|
|
53
54
|
const _worldQuaternionBuffer: Quaternion = new Quaternion();
|
|
54
|
-
const _worldQuaternion: Quaternion = new Quaternion();
|
|
55
55
|
const _tempQuaternionBuffer2: Quaternion = new Quaternion();
|
|
56
56
|
|
|
57
57
|
export function getWorldQuaternion(obj: Object3D, target: Quaternion | null = null): Quaternion {
|
|
58
|
-
if (!obj) return
|
|
59
|
-
const quat = target ??
|
|
58
|
+
if (!obj) return _worldQuaternions.get().identity();
|
|
59
|
+
const quat = target ?? _worldQuaternions.get();
|
|
60
60
|
if (!obj.parent) return quat.copy(obj.quaternion);
|
|
61
61
|
obj.getWorldQuaternion(quat);
|
|
62
62
|
return quat;
|
|
@@ -79,14 +79,16 @@ export function setWorldQuaternionXYZW(obj: Object3D, x: number, y: number, z: n
|
|
|
79
79
|
setWorldQuaternion(obj, _worldQuaternionBuffer);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
const _worldScaleBuffer = new CircularBuffer(() => new Vector3(), 100);
|
|
82
83
|
const _worldScale: Vector3 = new Vector3();
|
|
83
|
-
const _worldScale2: Vector3 = new Vector3();
|
|
84
84
|
|
|
85
85
|
export function getWorldScale(obj: Object3D, vec: Vector3 | null = null): Vector3 {
|
|
86
|
-
if (!
|
|
87
|
-
|
|
88
|
-
obj.
|
|
89
|
-
return vec
|
|
86
|
+
if (!vec)
|
|
87
|
+
vec = _worldScaleBuffer.get();
|
|
88
|
+
if (!obj) return vec.set(0, 0, 0);
|
|
89
|
+
if (!obj.parent) return vec.copy(obj.scale);
|
|
90
|
+
obj.getWorldScale(vec);
|
|
91
|
+
return vec;
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
export function setWorldScale(obj: Object3D, vec: Vector3) {
|
|
@@ -95,7 +97,7 @@ export function setWorldScale(obj: Object3D, vec: Vector3) {
|
|
|
95
97
|
obj.scale.copy(vec);
|
|
96
98
|
return;
|
|
97
99
|
}
|
|
98
|
-
const tempVec =
|
|
100
|
+
const tempVec = _worldScale;
|
|
99
101
|
const obj2 = obj.parent;
|
|
100
102
|
obj2.getWorldScale(tempVec);
|
|
101
103
|
obj.scale.copy(vec);
|
|
@@ -109,6 +111,18 @@ export function forward(obj: Object3D): Vector3 {
|
|
|
109
111
|
return _forward.set(0, 0, 1).applyQuaternion(_forwardQuat);
|
|
110
112
|
}
|
|
111
113
|
|
|
114
|
+
const _worldDirectionBuffer = new CircularBuffer(() => new Vector3(), 100);
|
|
115
|
+
const _worldDirectionQuat = new Quaternion();
|
|
116
|
+
/** Get the world direction. Returns world forward if nothing is passed in.
|
|
117
|
+
* Pass in a relative direction to get it converted to world space (e.g. dir = new Vector3(0, 1, 1))
|
|
118
|
+
* The returned vector will not be normalized
|
|
119
|
+
*/
|
|
120
|
+
export function getWorldDirection(obj: Object3D, dir?: Vector3) {
|
|
121
|
+
// If no direction is passed in set the direction to the forward vector
|
|
122
|
+
if (!dir) dir = _worldDirectionBuffer.get().set(0, 0, 1);
|
|
123
|
+
getWorldQuaternion(obj, _worldDirectionQuat);
|
|
124
|
+
return dir.applyQuaternion(_worldDirectionQuat);
|
|
125
|
+
}
|
|
112
126
|
|
|
113
127
|
|
|
114
128
|
const _worldEulerBuffer: Euler = new Euler();
|
|
@@ -119,14 +133,16 @@ const _worldRotation: Vector3 = new Vector3();
|
|
|
119
133
|
|
|
120
134
|
// world euler (in radians)
|
|
121
135
|
export function getWorldEuler(obj: Object3D): Euler {
|
|
122
|
-
|
|
123
|
-
|
|
136
|
+
const quat = _worldQuaternions.get();
|
|
137
|
+
obj.getWorldQuaternion(quat);
|
|
138
|
+
_worldEuler.setFromQuaternion(quat);
|
|
124
139
|
return _worldEuler;
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
// world euler (in radians)
|
|
128
143
|
export function setWorldEuler(obj: Object3D, val: Euler) {
|
|
129
|
-
|
|
144
|
+
const quat = _worldQuaternions.get();
|
|
145
|
+
setWorldQuaternion(obj, quat.setFromEuler(val));;
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
// returns rotation in degrees
|
|
@@ -408,4 +408,38 @@ export function isSafari() {
|
|
|
408
408
|
|
|
409
409
|
export function isQuest() {
|
|
410
410
|
return navigator.userAgent.includes("OculusBrowser");
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
const cloudflareIPRegex = /ip=(?<ip>.+?)\n/s;
|
|
416
|
+
export async function getIpCloudflare() {
|
|
417
|
+
const data = await fetch('https://www.cloudflare.com/cdn-cgi/trace');
|
|
418
|
+
const body = await data.text();
|
|
419
|
+
// we are only interested in the ip= part:
|
|
420
|
+
const match = cloudflareIPRegex.exec(body);
|
|
421
|
+
if (match)
|
|
422
|
+
return match[1];
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export async function getIp() {
|
|
427
|
+
const res = await fetch("https://api.db-ip.com/v2/free/self");
|
|
428
|
+
const json = await res.json();
|
|
429
|
+
return json.ipAddress;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export type IpAndLocation = {
|
|
433
|
+
ipAddress: string;
|
|
434
|
+
continentCode: string;
|
|
435
|
+
continentName: string;
|
|
436
|
+
countryCode: string;
|
|
437
|
+
countryName: string;
|
|
438
|
+
stateProv: string;
|
|
439
|
+
city: string;
|
|
440
|
+
}
|
|
441
|
+
export async function getIpAndLocation(): Promise<IpAndLocation> {
|
|
442
|
+
const res = (await fetch("https://api.db-ip.com/v2/free/self").catch(() => null))!;
|
|
443
|
+
const json = await res.json() as IpAndLocation;
|
|
444
|
+
return json;
|
|
411
445
|
}
|
|
@@ -52,6 +52,7 @@ export { ColorBySpeedModule } from "../ParticleSystemModules";
|
|
|
52
52
|
export { ColorOverLifetimeModule } from "../ParticleSystemModules";
|
|
53
53
|
export { Component } from "../Component";
|
|
54
54
|
export { ControlTrackHandler } from "../timeline/TimelineTracks";
|
|
55
|
+
export { CustomBranding } from "../export/usdz/USDZExporter";
|
|
55
56
|
export { Deletable } from "../DeleteBox";
|
|
56
57
|
export { DeleteBox } from "../DeleteBox";
|
|
57
58
|
export { DepthOfField } from "../postprocessing/Effects/DepthOfField";
|
|
@@ -121,7 +122,6 @@ export { PointerEventData } from "../ui/PointerEvents";
|
|
|
121
122
|
export { PostProcessingHandler } from "../postprocessing/PostProcessingHandler";
|
|
122
123
|
export { PreliminaryAction } from "../export/usdz/extensions/behavior/BehaviourComponents";
|
|
123
124
|
export { PreliminaryTrigger } from "../export/usdz/extensions/behavior/BehaviourComponents";
|
|
124
|
-
export { QuickLookOverlay } from "../export/usdz/USDZExporter";
|
|
125
125
|
export { RawImage } from "../ui/Image";
|
|
126
126
|
export { Raycaster } from "../ui/Raycaster";
|
|
127
127
|
export { Rect } from "../ui/RectTransform";
|
|
@@ -18,7 +18,7 @@ import { AudioExtension } from "./extensions/behavior/AudioExtension";
|
|
|
18
18
|
|
|
19
19
|
const debug = getParam("debugusdz");
|
|
20
20
|
|
|
21
|
-
export class
|
|
21
|
+
export class CustomBranding {
|
|
22
22
|
@serializable()
|
|
23
23
|
callToAction?: string;
|
|
24
24
|
@serializable()
|
|
@@ -45,8 +45,8 @@ export class USDZExporter extends Behaviour {
|
|
|
45
45
|
@serializable(URL)
|
|
46
46
|
customUsdzFile?: string;
|
|
47
47
|
|
|
48
|
-
@serializable(
|
|
49
|
-
|
|
48
|
+
@serializable(CustomBranding)
|
|
49
|
+
customBranding?: CustomBranding;
|
|
50
50
|
|
|
51
51
|
// Currently not exposed to integrations - not fully tested. Set from code (e.g. image tracking)
|
|
52
52
|
@serializable()
|
|
@@ -66,6 +66,7 @@ export class USDZExporter extends Behaviour {
|
|
|
66
66
|
private webARSessionRoot: WebARSessionRoot | undefined;
|
|
67
67
|
|
|
68
68
|
start() {
|
|
69
|
+
console.log(this.customUsdzFile);
|
|
69
70
|
if (debug) {
|
|
70
71
|
console.log(this);
|
|
71
72
|
console.log("Debug USDZ, press 't' to export")
|
|
@@ -249,16 +250,27 @@ export class USDZExporter extends Behaviour {
|
|
|
249
250
|
if (debug)
|
|
250
251
|
showBalloonMessage("Quicklook url: " + callToActionURL);
|
|
251
252
|
if (callToActionURL) {
|
|
252
|
-
|
|
253
|
+
if (!hasProLicense()) {
|
|
254
|
+
console.warn("Quicklook closed: custom redirects require a Needle Engine Pro license: https://needle.tools/pricing", callToActionURL)
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
globalThis.open(callToActionURL, "_blank");
|
|
258
|
+
}
|
|
253
259
|
}
|
|
254
260
|
}
|
|
255
261
|
}
|
|
256
262
|
}
|
|
257
263
|
}
|
|
258
264
|
|
|
259
|
-
private buildQuicklookOverlay():
|
|
260
|
-
const obj:
|
|
261
|
-
if (this.
|
|
265
|
+
private buildQuicklookOverlay(): CustomBranding {
|
|
266
|
+
const obj: CustomBranding = {};
|
|
267
|
+
if (this.customBranding) Object.assign(obj, this.customBranding);
|
|
268
|
+
if (!hasProLicense()) {
|
|
269
|
+
console.log("Custom Quicklook banner text requires pro license: https://needle.tools/pricing");
|
|
270
|
+
obj.callToAction = "Close";
|
|
271
|
+
obj.checkoutTitle = "🌵 Made with Needle";
|
|
272
|
+
obj.checkoutSubtitle = "_";
|
|
273
|
+
}
|
|
262
274
|
if (!obj.callToAction?.length)
|
|
263
275
|
obj.callToAction = "Close";
|
|
264
276
|
if (!obj.checkoutTitle?.length)
|
|
@@ -174,7 +174,6 @@ export class Graphic extends BaseUIComponent implements IGraphic, IRectTransform
|
|
|
174
174
|
static textureCache: Map<Texture, Texture> = new Map();
|
|
175
175
|
|
|
176
176
|
protected async setTexture(tex: Texture | null | undefined) {
|
|
177
|
-
if (!tex) return;
|
|
178
177
|
this.setOptions({ backgroundOpacity: 0 });
|
|
179
178
|
if (tex) {
|
|
180
179
|
// workaround for https://github.com/needle-tools/needle-engine-support/issues/109
|
|
@@ -190,6 +189,9 @@ export class Graphic extends BaseUIComponent implements IGraphic, IRectTransform
|
|
|
190
189
|
}
|
|
191
190
|
this.setOptions({ backgroundImage: tex, borderRadius: 0, backgroundOpacity: this.color.alpha, backgroundSize: "stretch" });
|
|
192
191
|
}
|
|
192
|
+
else {
|
|
193
|
+
this.setOptions({ backgroundImage: null, borderRadius: 0, backgroundOpacity: this.color.alpha });
|
|
194
|
+
}
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
protected onAfterAddedToScene(): void {
|
|
@@ -61,10 +61,11 @@ export class Text extends Graphic {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
set text(val: string) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
if (val !== this._text) {
|
|
65
|
+
this._text = val;
|
|
66
|
+
this.feedText(this.text, this.supportRichText);
|
|
67
|
+
this.markDirty();
|
|
68
|
+
}
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
private set_text(val: string) {
|
package/src/needle-engine.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { makeErrorsVisibleForDevelopment } from "./engine/debug/debug_overlay";
|
|
2
2
|
makeErrorsVisibleForDevelopment();
|
|
3
3
|
|
|
4
|
+
import { NEEDLE_ENGINE_GENERATOR, NEEDLE_ENGINE_VERSION } from "./engine/engine_constants";
|
|
5
|
+
|
|
4
6
|
import "./engine/engine_element";
|
|
5
7
|
import "./engine/engine_setup";
|
|
6
8
|
import "./engine-components/CameraUtils"
|
|
@@ -9,6 +11,7 @@ export * from "./engine/api";
|
|
|
9
11
|
export * from "./engine-components/api";
|
|
10
12
|
export * from "./engine-components-experimental/api";
|
|
11
13
|
|
|
14
|
+
|
|
12
15
|
// make accessible for external javascript
|
|
13
16
|
import { Context } from "./engine/engine_setup";
|
|
14
17
|
const Needle = { Context: Context };
|
|
@@ -27,6 +30,12 @@ registerGlobal(Component);
|
|
|
27
30
|
import * as Components from "./engine-components/codegen/components";
|
|
28
31
|
registerGlobal(Components);
|
|
29
32
|
|
|
33
|
+
Needle["$meta"] = {
|
|
34
|
+
version: NEEDLE_ENGINE_VERSION,
|
|
35
|
+
generator: NEEDLE_ENGINE_GENERATOR
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
|
|
30
39
|
import { GameObject } from "./engine-components/Component";
|
|
31
40
|
for (const method of Object.getOwnPropertyNames(GameObject)) {
|
|
32
41
|
switch (method) {
|
|
@@ -50,4 +59,4 @@ else console.warn("Threejs is already imported");
|
|
|
50
59
|
|
|
51
60
|
|
|
52
61
|
|
|
53
|
-
import "./engine/engine_license";
|
|
62
|
+
import "./engine/engine_license";
|