@needle-tools/engine 3.5.4-alpha → 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 +15 -0
- package/dist/needle-engine.js +7669 -7648
- package/dist/needle-engine.min.js +296 -296
- package/dist/needle-engine.umd.cjs +294 -294
- package/lib/engine/engine_license.js +1 -1
- package/lib/engine/engine_license.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/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugins/vite/copyfiles.js +49 -2
- package/plugins/vite/license.js +16 -3
- package/src/engine/codegen/register_types.js +2 -2
- package/src/engine/engine_license.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/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/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 {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TypeStore } from "./../engine_typestore"
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
// Import types
|
|
4
4
|
import { __Ignore } from "../../engine-components/codegen/components";
|
|
5
5
|
import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder";
|
|
@@ -215,7 +215,7 @@ import { XRGrabModel } from "../../engine-components/webxr/WebXRGrabRendering";
|
|
|
215
215
|
import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering";
|
|
216
216
|
import { XRRig } from "../../engine-components/webxr/WebXRRig";
|
|
217
217
|
import { XRState } from "../../engine-components/XRFlag";
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
// Register types
|
|
220
220
|
TypeStore.add("__Ignore", __Ignore);
|
|
221
221
|
TypeStore.add("ActionBuilder", ActionBuilder);
|
|
@@ -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() {
|
|
@@ -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
|
}
|