@@ -36,6 +36,7 @@
|
|
36
36
|
const needleConfig = tryLoadProjectConfig();
|
37
37
|
if (needleConfig) {
|
38
38
|
assetsDirName = needleConfig.assetsDirectory;
|
39
|
+
while(assetsDirName.startsWith('/')) assetsDirName = assetsDirName.substring(1);
|
39
40
|
}
|
40
41
|
|
41
42
|
if (copyIncludesFromEngine !== false) {
|
@@ -84,6 +85,7 @@
|
|
84
85
|
const targetDir = resolve(outDir, 'assets');
|
85
86
|
copyRecursiveSync(assetsDir, targetDir);
|
86
87
|
}
|
88
|
+
else console.log(`WARN: No assets directory found. Skipping copy of ${assetsDirName} resolved to ${assetsDir}`)
|
87
89
|
// copy include dir
|
88
90
|
const includeDir = resolve(baseDir, 'include');
|
89
91
|
if (existsSync(includeDir)) {
|
@@ -29,8 +29,17 @@
|
|
29
29
|
apply: 'build',
|
30
30
|
transform(src, id) {
|
31
31
|
if (id.endsWith(codegenDirectory + "/gen.js")) {
|
32
|
-
|
32
|
+
let assetsDir = builtAssetsDirectory();
|
33
33
|
if (assetsDir !== configuredAssetsDirectory) {
|
34
|
+
// check if the the assets directory is expected to be relative to the root
|
35
|
+
// like for example "/assets"
|
36
|
+
// then we want to add the leading dash also to the new path
|
37
|
+
// if (configuredAssetsDirectory.startsWith("/")) assetsDir = "/" + assetsDir;
|
38
|
+
// // are they now the same? if so well we dont have to do anything
|
39
|
+
// if (assetsDir === configuredAssetsDirectory) {
|
40
|
+
// return;
|
41
|
+
// }
|
42
|
+
|
34
43
|
console.log(`[needle-transform-files] - Transform codegen paths \"${configuredAssetsDirectory}\" → \"${assetsDir}\"`)
|
35
44
|
// replace codegen paths
|
36
45
|
src = src.replaceAll(configuredAssetsDirectory, assetsDir);
|
@@ -1,14 +1,79 @@
|
|
1
1
|
import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
|
2
2
|
import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry";
|
3
|
-
import {
|
3
|
+
import { addNewComponent } from "../engine/engine_components";
|
4
|
+
import { Animator } from "./Animator";
|
5
|
+
import { Animation } from "./Animation";
|
6
|
+
import { GameObject } from "./Component";
|
7
|
+
import { PlayableDirector } from "./api";
|
4
8
|
|
5
|
-
|
6
9
|
ContextRegistry.registerCallback(ContextEvent.ContextCreated, args => {
|
7
10
|
const autoplay = args.context.domElement.getAttribute("autoplay");
|
8
11
|
if (autoplay !== undefined && (autoplay === "" || autoplay === "true")) {
|
9
12
|
if (args.files) {
|
10
|
-
for (const file of args.files)
|
11
|
-
|
13
|
+
for (const file of args.files) {
|
14
|
+
const hasAnimation = GameObject.foreachComponent(file.file.scene, comp => {
|
15
|
+
if (comp.enabled === false) return undefined;
|
16
|
+
if (comp instanceof Animation && comp.playAutomatically || comp instanceof Animator || comp instanceof PlayableDirector && comp.playOnAwake === true) {
|
17
|
+
return true;
|
18
|
+
}
|
19
|
+
else if (comp instanceof Animation) {
|
20
|
+
comp.playAutomatically = true;
|
21
|
+
return true;
|
22
|
+
}
|
23
|
+
else if (comp instanceof PlayableDirector) {
|
24
|
+
comp.playOnAwake = true;
|
25
|
+
return true;
|
26
|
+
}
|
27
|
+
return undefined;
|
28
|
+
}, true);
|
29
|
+
if (hasAnimation !== true)
|
30
|
+
findAnimations(file.file as GLTF);
|
31
|
+
}
|
12
32
|
}
|
13
33
|
}
|
14
|
-
});
|
34
|
+
});
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
function findAnimations(gltf: GLTF) {
|
39
|
+
if (!gltf || !gltf.animations) return;
|
40
|
+
|
41
|
+
|
42
|
+
for (let i = 0; i < gltf.animations.length; i++) {
|
43
|
+
const animation = gltf.animations[i];
|
44
|
+
if (!animation.tracks || animation.tracks.length <= 0) continue;
|
45
|
+
for (const t in animation.tracks) {
|
46
|
+
const track = animation.tracks[t];
|
47
|
+
const objectName = track["__objectName"] ?? track.name.substring(0, track.name.indexOf("."));
|
48
|
+
const obj = gltf.scene.getObjectByName(objectName);
|
49
|
+
if (!obj) {
|
50
|
+
// console.warn("could not find " + objectName, animation, gltf.scene);
|
51
|
+
continue;
|
52
|
+
}
|
53
|
+
let animationComponent = findAnimationGameObjectInParent(obj);
|
54
|
+
if (!animationComponent) {
|
55
|
+
animationComponent = addNewComponent(gltf.scene, new Animation());
|
56
|
+
}
|
57
|
+
const animations = animationComponent.animations = animationComponent.animations || [];
|
58
|
+
animation["name_animator"] = animationComponent.name;
|
59
|
+
// console.log(objectName, obj, animator.name, animations.length);
|
60
|
+
if (animations.indexOf(animation) < 0) {
|
61
|
+
animations.push(animation);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
function findAnimationGameObjectInParent(obj) {
|
66
|
+
if (!obj) return;
|
67
|
+
const components = obj.userData?.components;
|
68
|
+
if (components && components.length > 0) {
|
69
|
+
for (let i = 0; i < components.length; i++) {
|
70
|
+
const component = components[i];
|
71
|
+
// console.log(component);
|
72
|
+
if (component instanceof Animator || component instanceof Animation) {
|
73
|
+
return obj;;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
return findAnimationGameObjectInParent(obj.parent);
|
78
|
+
}
|
79
|
+
}
|
@@ -1,12 +1,13 @@
|
|
1
1
|
|
2
|
-
export * from "./extensions"
|
2
|
+
export * from "./extensions";
|
3
3
|
export * from "./engine_addressables";
|
4
|
-
export * from "./engine_application"
|
5
|
-
export * from "./engine_assetdatabase"
|
4
|
+
export * from "./engine_application";
|
5
|
+
export * from "./engine_assetdatabase";
|
6
|
+
export * from "./engine_create_objects";
|
6
7
|
export * from "./engine_components_internal";
|
7
8
|
export * from "./engine_components";
|
8
9
|
export * from "./engine_components_internal";
|
9
|
-
export * from "./engine_context_registry"
|
10
|
+
export * from "./engine_context_registry";
|
10
11
|
export * from "./engine_context";
|
11
12
|
export * from "./engine_coroutine"
|
12
13
|
export * from "./engine_constants";
|
@@ -24,12 +25,12 @@
|
|
24
25
|
export * from "./engine_networking_files";
|
25
26
|
export * from "./engine_networking_instantiate";
|
26
27
|
export * from "./engine_networking_utils";
|
27
|
-
export * from "./engine_patcher"
|
28
|
-
export * from "./engine_playerview"
|
29
|
-
export * from "./engine_physics"
|
30
|
-
export * from "./engine_physics.types"
|
31
|
-
export * from "./engine_physics_rapier"
|
32
|
-
export * from "./engine_scenelighting"
|
28
|
+
export * from "./engine_patcher";
|
29
|
+
export * from "./engine_playerview";
|
30
|
+
export * from "./engine_physics";
|
31
|
+
export * from "./engine_physics.types";
|
32
|
+
export * from "./engine_physics_rapier";
|
33
|
+
export * from "./engine_scenelighting";
|
33
34
|
export * from "./engine_input";
|
34
35
|
export * from "./engine_math";
|
35
36
|
export * from "./js-extensions";
|
@@ -47,5 +48,5 @@
|
|
47
48
|
export { TypeStore, registerType } from "./engine_typestore";
|
48
49
|
|
49
50
|
export { InstancingUtil } from "./engine_instancing";
|
50
|
-
export { validate, prefix } from "./engine_util_decorator"
|
51
|
+
export { validate, prefix } from "./engine_util_decorator";
|
51
52
|
export { hasProLicense, hasIndieLicense } from "./engine_license";
|
@@ -63,8 +63,9 @@
|
|
63
63
|
|
64
64
|
/** Run a callback for all components of the provided type on the provided object and its children (if recursive is true)
|
65
65
|
* @param instance object to run the method on
|
66
|
-
* @param cb callback to run on each component
|
66
|
+
* @param cb callback to run on each component, "return undefined;" to continue and "return <anything>;" to break the loop
|
67
67
|
* @param recursive if true, the method will be run on all children as well
|
68
|
+
* @returns the last return value of the callback
|
68
69
|
*/
|
69
70
|
public static foreachComponent(instance: Object3D, cb: (comp: Component) => any, recursive: boolean = true): any {
|
70
71
|
return foreachComponent(instance, cb as (comp: IComponent) => any, recursive);
|
@@ -1,8 +1,10 @@
|
|
1
|
-
import { PlaneGeometry, MeshBasicMaterial, DoubleSide, Mesh, Material } from "three"
|
1
|
+
import { PlaneGeometry, MeshBasicMaterial, DoubleSide, Mesh, Material, MeshStandardMaterial, BoxGeometry, SphereGeometry } from "three"
|
2
2
|
|
3
3
|
|
4
4
|
export enum PrimitiveType {
|
5
|
-
Quad = 0
|
5
|
+
Quad = 0,
|
6
|
+
Cube = 1,
|
7
|
+
Sphere = 2,
|
6
8
|
}
|
7
9
|
|
8
10
|
export type ObjectOptions = {
|
@@ -16,9 +18,20 @@
|
|
16
18
|
let obj: Mesh;
|
17
19
|
switch (type) {
|
18
20
|
case PrimitiveType.Quad:
|
19
|
-
const
|
20
|
-
const
|
21
|
-
obj = new Mesh(
|
21
|
+
const quadGeo = new PlaneGeometry(1, 1, 1, 1);
|
22
|
+
const quadMat = opts?.material ?? new MeshBasicMaterial({ color: 0xffffff });
|
23
|
+
obj = new Mesh(quadGeo, quadMat);
|
24
|
+
break;
|
25
|
+
case PrimitiveType.Cube:
|
26
|
+
const boxGeo = new BoxGeometry(1, 1, 1);
|
27
|
+
const boxMat = opts?.material ?? new MeshStandardMaterial({ color: 0xdddddd });
|
28
|
+
obj = new Mesh(boxGeo, boxMat);
|
29
|
+
break;
|
30
|
+
case PrimitiveType.Sphere:
|
31
|
+
const sphereGeo = new SphereGeometry(.5, 16, 16);
|
32
|
+
const sphereMat = opts?.material ?? new MeshStandardMaterial({ color: 0xdddddd });
|
33
|
+
obj = new Mesh(sphereGeo, sphereMat);
|
34
|
+
break;
|
22
35
|
}
|
23
36
|
if (opts?.name)
|
24
37
|
obj.name = opts.name;
|
@@ -142,18 +142,16 @@
|
|
142
142
|
background-image:url('${logo}');
|
143
143
|
background-max-size: 40px;
|
144
144
|
margin-bottom: 5px;
|
145
|
-
margin-top:
|
146
|
-
margin-bottom:
|
145
|
+
margin-top: .3em;
|
146
|
+
margin-bottom: .5em;
|
147
147
|
padding: .2em;
|
148
148
|
padding-left: 37px;
|
149
|
-
background-color: rgba(250,160,160,.2);
|
150
149
|
border-radius: .5em;
|
151
|
-
border: 2px solid rgba(
|
152
|
-
color: rgba(90,20,20,1);
|
150
|
+
border: 2px solid rgba(160,160,160,.5);
|
153
151
|
`;
|
154
152
|
// url must contain https for firefox to make it clickable
|
155
153
|
let licenseText = "Needle Engine — No license active, commercial use is not allowed. Visit https://needle.tools/pricing for more information and licensing options.";
|
156
|
-
console.log("%c" + licenseText, style);
|
154
|
+
console.log("%c " + licenseText, style);
|
157
155
|
}
|
158
156
|
|
159
157
|
function createLicenseElement() {
|
@@ -1,6 +1,4 @@
|
|
1
1
|
import { Context } from "./engine_setup"
|
2
|
-
import { Animator } from '../engine-components/Animator';
|
3
|
-
import { Animation } from '../engine-components/Animation';
|
4
2
|
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
5
3
|
// import * as object from "./engine_gltf_builtin_components";
|
6
4
|
import * as loaders from "./engine_loaders"
|
@@ -11,8 +9,8 @@
|
|
11
9
|
import { createBuiltinComponents, writeBuiltinComponentData } from "./engine_gltf_builtin_components";
|
12
10
|
import { SerializationContext } from "./engine_serialization_core";
|
13
11
|
import { NEEDLE_components } from "./extensions/NEEDLE_components";
|
14
|
-
import { addNewComponent, getComponentInChildren } from "./engine_components";
|
15
12
|
import { registerPrewarmObject } from "./engine_mainloop_utils";
|
13
|
+
import { Object3D } from "three";
|
16
14
|
|
17
15
|
|
18
16
|
export class NeedleGltfLoader implements INeedleGltfLoader {
|
@@ -24,10 +22,10 @@
|
|
24
22
|
return writeBuiltinComponentData(comp, context);
|
25
23
|
}
|
26
24
|
|
27
|
-
parseSync(context: Context, data
|
25
|
+
parseSync(context: Context, data: string | ArrayBuffer, path: string, seed: number | UIDProvider | null): Promise<GLTF | undefined> {
|
28
26
|
return parseSync(context, data, path, seed);
|
29
27
|
}
|
30
|
-
loadSync(context: Context, url: string, sourceId:string, seed: number | UIDProvider | null, prog?: ((ProgressEvent: any) => void) | undefined): Promise<GLTF | undefined> {
|
28
|
+
loadSync(context: Context, url: string, sourceId: string, seed: number | UIDProvider | null, prog?: ((ProgressEvent: any) => void) | undefined): Promise<GLTF | undefined> {
|
31
29
|
return loadSync(context, url, sourceId, seed, prog);
|
32
30
|
}
|
33
31
|
}
|
@@ -98,7 +96,7 @@
|
|
98
96
|
return loader;
|
99
97
|
}
|
100
98
|
|
101
|
-
export function parseSync(context: Context, data
|
99
|
+
export function parseSync(context: Context, data: string | ArrayBuffer, path: string, seed: number | UIDProvider | null): Promise<GLTF | undefined> {
|
102
100
|
if (typeof path !== "string") {
|
103
101
|
console.warn("Parse gltf binary without path, this might lead to errors in resolving extensions. Please provide the source path of the gltf/glb file", path, typeof path);
|
104
102
|
}
|
@@ -127,7 +125,7 @@
|
|
127
125
|
});
|
128
126
|
}
|
129
127
|
|
130
|
-
export function loadSync(context: Context, url: string, sourceId:string, seed: number | UIDProvider | null, prog?: (ProgressEvent) => void): Promise<GLTF | undefined> {
|
128
|
+
export function loadSync(context: Context, url: string, sourceId: string, seed: number | UIDProvider | null, prog?: (ProgressEvent) => void): Promise<GLTF | undefined> {
|
131
129
|
// better to create new loaders every time
|
132
130
|
// (maybe we can cache them...)
|
133
131
|
// but due to the async nature and potentially triggering multiple loads at the same time
|
@@ -160,75 +158,13 @@
|
|
160
158
|
});
|
161
159
|
}
|
162
160
|
|
163
|
-
export function findAnimationsLate(_context: Context, gltf, callbackarray, allowAddingAnimator: boolean = false) {
|
164
|
-
if (gltf && gltf.animations && gltf.animations.length > 0) {
|
165
|
-
callbackarray.push(() => {
|
166
|
-
// console.trace("callback", gltf);
|
167
|
-
findAnimations(gltf, allowAddingAnimator);
|
168
|
-
});
|
169
|
-
}
|
170
|
-
}
|
171
161
|
|
172
|
-
export function findAnimations(gltf: GLTF, allowAddingAnimator: boolean = false) {
|
173
|
-
console.log(gltf);
|
174
|
-
if (!gltf || !gltf.animations) return;
|
175
|
-
|
176
|
-
if (!allowAddingAnimator) {
|
177
|
-
// we only need to search if any animation component is in the scene
|
178
|
-
// otherwise if we dont add anything there is no reason to search and log anything
|
179
|
-
if (!getComponentInChildren(gltf.scene, Animation)) return;
|
180
|
-
}
|
181
|
-
|
182
|
-
for (let i = 0; i < gltf.animations.length; i++) {
|
183
|
-
const animation = gltf.animations[i];
|
184
|
-
if (!animation.tracks || animation.tracks.length <= 0) continue;
|
185
|
-
for (const t in animation.tracks) {
|
186
|
-
const track = animation.tracks[t];
|
187
|
-
const objectName = track["__objectName"] ?? track.name.substring(0, track.name.indexOf("."));
|
188
|
-
const obj = gltf.scene.getObjectByName(objectName);
|
189
|
-
if (!obj) {
|
190
|
-
// console.warn("could not find " + objectName, animation, gltf.scene);
|
191
|
-
continue;
|
192
|
-
}
|
193
|
-
let animationComponent = findAnimationGameObjectInParent(obj);
|
194
|
-
if (!animationComponent) {
|
195
|
-
if (allowAddingAnimator)
|
196
|
-
animationComponent = addNewComponent(gltf.scene, new Animation());
|
197
|
-
else {
|
198
|
-
console.warn("Failed finding animator for", track.name, objectName);
|
199
|
-
continue;
|
200
|
-
}
|
201
|
-
}
|
202
|
-
const animations = animationComponent.animations = animationComponent.animations || [];
|
203
|
-
animation["name_animator"] = animationComponent.name;
|
204
|
-
// console.log(objectName, obj, animator.name, animations.length);
|
205
|
-
if (animations.indexOf(animation) < 0) {
|
206
|
-
animations.push(animation);
|
207
|
-
}
|
208
|
-
}
|
209
|
-
}
|
210
|
-
function findAnimationGameObjectInParent(obj) {
|
211
|
-
if (!obj) return;
|
212
|
-
const components = obj.userData?.components;
|
213
|
-
if (components && components.length > 0) {
|
214
|
-
for (let i = 0; i < components.length; i++) {
|
215
|
-
const component = components[i];
|
216
|
-
// console.log(component);
|
217
|
-
if (component instanceof Animator || component instanceof Animation) {
|
218
|
-
return obj;;
|
219
|
-
}
|
220
|
-
}
|
221
|
-
}
|
222
|
-
return findAnimationGameObjectInParent(obj.parent);
|
223
|
-
}
|
224
|
-
}
|
225
|
-
|
226
|
-
|
227
162
|
// TODO: save references in guid map
|
228
163
|
// const guidMap = {};
|
229
164
|
|
230
165
|
|
231
|
-
|
166
|
+
/** @deprecated */
|
167
|
+
export function tryFindObjectByName(name: string, obj: Object3D, recursive = true) {
|
232
168
|
if (obj.userData && obj.userData.name === name) return obj;
|
233
169
|
if (obj.children && obj.children.length > 0) {
|
234
170
|
for (let i = 0; i < obj.children.length; i++) {
|
@@ -239,7 +175,8 @@
|
|
239
175
|
}
|
240
176
|
}
|
241
177
|
|
242
|
-
|
178
|
+
/** @deprecated */
|
179
|
+
export function tryFindScript(globalObjectIdentifier: string, list = null) {
|
243
180
|
const arr = list ?? Context.Current.scripts;
|
244
181
|
for (const i in arr) {
|
245
182
|
const script = arr[i];
|
@@ -21,7 +21,7 @@
|
|
21
21
|
onEnable(): void {
|
22
22
|
if (this.isGizmo && !params.showGizmos) return;
|
23
23
|
if (!this._gizmoObject) {
|
24
|
-
if (this.objectBounds
|
24
|
+
if (this.objectBounds) {
|
25
25
|
this._gizmoObject = new THREE.BoxHelper(this.gameObject, this.color ?? 0xffff00);
|
26
26
|
}
|
27
27
|
else {
|