@needle-tools/engine 2.59.1-pre.1 → 2.60.0-pre
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 +12 -0
- package/dist/needle-engine.js +7382 -7345
- package/dist/needle-engine.umd.cjs +222 -222
- package/lib/engine/engine_gltf_builtin_components.js +13 -5
- package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.js +14 -0
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_types.d.ts +1 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_render_objects.js +19 -13
- package/lib/engine/extensions/NEEDLE_render_objects.js.map +1 -1
- package/lib/engine-components/ParticleSystem.d.ts +11 -1
- package/lib/engine-components/ParticleSystem.js +35 -8
- package/lib/engine-components/ParticleSystem.js.map +1 -1
- package/lib/engine-components/ParticleSystemModules.d.ts +2 -0
- package/lib/engine-components/ParticleSystemModules.js +2 -2
- package/lib/engine-components/ParticleSystemModules.js.map +1 -1
- package/lib/engine-components/ParticleSystemSubEmitter.d.ts +5 -1
- package/lib/engine-components/ParticleSystemSubEmitter.js +25 -5
- package/lib/engine-components/ParticleSystemSubEmitter.js.map +1 -1
- package/lib/engine-components/Skybox.d.ts +0 -1
- package/lib/engine-components/Skybox.js +2 -5
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js +11 -2
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/TimelineModels.d.ts +3 -2
- package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +1 -0
- package/lib/engine-components/timeline/TimelineTracks.js +29 -7
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/engine/engine_gltf_builtin_components.ts +14 -6
- package/src/engine/engine_serialization_builtin_serializer.ts +17 -1
- package/src/engine/engine_types.ts +1 -0
- package/src/engine/extensions/NEEDLE_render_objects.ts +22 -18
- package/src/engine-components/ParticleSystem.ts +36 -8
- package/src/engine-components/ParticleSystemModules.ts +3 -3
- package/src/engine-components/ParticleSystemSubEmitter.ts +32 -7
- package/src/engine-components/Skybox.ts +2 -4
- package/src/engine-components/timeline/PlayableDirector.ts +10 -2
- package/src/engine-components/timeline/TimelineModels.ts +4 -3
- package/src/engine-components/timeline/TimelineTracks.ts +31 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.60.0-pre",
|
|
4
4
|
"description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
|
|
5
5
|
"main": "dist/needle-engine.umd.cjs",
|
|
6
6
|
"module": "dist/needle-engine.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@babel/runtime": "^7.16.0",
|
|
58
58
|
"@luncheon/esbuild-plugin-gzip": "^0.1.0",
|
|
59
|
-
"@needle-tools/gltf-transform-extensions": "^0.10.
|
|
59
|
+
"@needle-tools/gltf-transform-extensions": "^0.10.10-pre",
|
|
60
60
|
"@needle-tools/needle-component-compiler": "1.9.3",
|
|
61
61
|
"@needle-tools/helper": "^0.2.1-pre",
|
|
62
62
|
"@types/three": "0.146.0",
|
|
@@ -9,7 +9,7 @@ import { assign, ImplementationInformation, ISerializable, SerializationContext
|
|
|
9
9
|
import { NEEDLE_components } from "./extensions/NEEDLE_components";
|
|
10
10
|
import { debugExtension } from "./engine_default_parameters";
|
|
11
11
|
import { builtinComponentKeyName } from "./engine_constants";
|
|
12
|
-
import { SourceIdentifier } from "./engine_types";
|
|
12
|
+
import { GuidsMap, SourceIdentifier } from "./engine_types";
|
|
13
13
|
import { UIDProvider } from "./engine_types";
|
|
14
14
|
import { addNewComponent } from "./engine_components";
|
|
15
15
|
import { getParam } from "./engine_utils";
|
|
@@ -83,9 +83,10 @@ export async function createBuiltinComponents(context: Context, gltfId: SourceId
|
|
|
83
83
|
// and this should run before awake and onenable of newly created components
|
|
84
84
|
if (idProvider) {
|
|
85
85
|
// TODO: should we do this after setup callbacks now?
|
|
86
|
-
|
|
86
|
+
const guidsMap: GuidsMap = {};
|
|
87
|
+
recursiveCreateGuids(gltf, idProvider, guidsMap);
|
|
87
88
|
for (const scene of gltf.scenes)
|
|
88
|
-
recursiveCreateGuids(scene, idProvider);
|
|
89
|
+
recursiveCreateGuids(scene, idProvider, guidsMap);
|
|
89
90
|
}
|
|
90
91
|
});
|
|
91
92
|
|
|
@@ -94,22 +95,29 @@ export async function createBuiltinComponents(context: Context, gltfId: SourceId
|
|
|
94
95
|
// console.log("finished creating builtin components", gltf.scene?.name, gltf);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
function recursiveCreateGuids(obj: GameObject, idProvider: UIDProvider | null) {
|
|
98
|
+
function recursiveCreateGuids(obj: GameObject, idProvider: UIDProvider | null, guidsMap: GuidsMap) {
|
|
98
99
|
if (idProvider === null) return;
|
|
99
100
|
if (!obj) return;
|
|
101
|
+
const prev = obj.guid;
|
|
100
102
|
obj.guid = idProvider.generateUUID();
|
|
103
|
+
if (prev && prev !== "invalid")
|
|
104
|
+
guidsMap[prev] = obj.guid;
|
|
101
105
|
// console.log(obj);
|
|
102
106
|
if (obj && obj.userData && obj.userData.components) {
|
|
103
107
|
for (const comp of obj.userData.components) {
|
|
104
108
|
if (comp === null) continue;
|
|
109
|
+
const prev = comp.guid;
|
|
105
110
|
comp.guid = idProvider.generateUUID();
|
|
106
|
-
|
|
111
|
+
if (prev && prev !== "invalid")
|
|
112
|
+
guidsMap[prev] = comp.guid;
|
|
113
|
+
if (comp.resolveGuids)
|
|
114
|
+
comp.resolveGuids(guidsMap);
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
}
|
|
110
118
|
if (obj.children) {
|
|
111
119
|
for (const child of obj.children) {
|
|
112
|
-
recursiveCreateGuids(child as GameObject, idProvider);
|
|
120
|
+
recursiveCreateGuids(child as GameObject, idProvider, guidsMap);
|
|
113
121
|
}
|
|
114
122
|
}
|
|
115
123
|
}
|
|
@@ -70,7 +70,23 @@ class ObjectSerializer extends TypeSerializer {
|
|
|
70
70
|
return undefined;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
onDeserialize(data: ObjectData, context: SerializationContext) {
|
|
73
|
+
onDeserialize(data: ObjectData | string | null, context: SerializationContext) {
|
|
74
|
+
|
|
75
|
+
if (typeof data === "string") {
|
|
76
|
+
// ACTUALLY: this is already handled by the extension_utils where we resolve json pointers recursively
|
|
77
|
+
// if(data.startsWith("/nodes/")){
|
|
78
|
+
// const node = parseInt(data.substring("/nodes/".length));
|
|
79
|
+
// if (context.nodeToObject) {
|
|
80
|
+
// const res = context.nodeToObject[node];
|
|
81
|
+
// if (debugExtension)
|
|
82
|
+
// console.log("Deserialized object reference?", data, res, context?.nodeToObject);
|
|
83
|
+
// if (!res) console.warn("Did not find node: " + data, context.nodeToObject, context.object);
|
|
84
|
+
// return res;
|
|
85
|
+
// }
|
|
86
|
+
// }
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
if (data) {
|
|
75
91
|
if (data.node !== undefined && context.nodeToObject) {
|
|
76
92
|
const res = context.nodeToObject[data.node];
|
|
@@ -151,6 +151,7 @@ export declare interface ICamera extends IComponent {
|
|
|
151
151
|
nearClipPlane: number;
|
|
152
152
|
farClipPlane: number;
|
|
153
153
|
backgroundColor: RGBAColor | null;
|
|
154
|
+
backgroundBlurriness: number | undefined;
|
|
154
155
|
clearFlags: number;
|
|
155
156
|
aspect: number;
|
|
156
157
|
fieldOfView: number;
|
|
@@ -45,6 +45,8 @@ declare type StencilSettingsModel = {
|
|
|
45
45
|
zFailOp: number;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
const $stencils = Symbol("stencils");
|
|
49
|
+
|
|
48
50
|
export class NEEDLE_render_objects implements GLTFLoaderPlugin {
|
|
49
51
|
|
|
50
52
|
private static stencils: { [key: string]: StencilSettingsModel[] } = {};
|
|
@@ -52,31 +54,33 @@ export class NEEDLE_render_objects implements GLTFLoaderPlugin {
|
|
|
52
54
|
static applyStencil(obj?: IRenderer | null) {
|
|
53
55
|
if (!obj) return;
|
|
54
56
|
const source = obj.sourceId;
|
|
55
|
-
if (debug)
|
|
56
|
-
console.log(source, NEEDLE_render_objects.stencils);
|
|
57
|
+
if (debug) console.log(source, NEEDLE_render_objects.stencils);
|
|
57
58
|
if (!source) return;
|
|
58
59
|
const settings = NEEDLE_render_objects.stencils[source];
|
|
59
60
|
if (!settings) return;
|
|
60
61
|
for (let i = settings.length - 1; i >= 0; i--) {
|
|
61
62
|
const stencil: StencilSettingsModel = settings[i];
|
|
62
63
|
if (matchesLayer(stencil.layer, obj)) {
|
|
63
|
-
if (debug)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
if (debug) console.log(stencil);
|
|
65
|
+
for (let i = 0; i < obj.sharedMaterials.length; i++) {
|
|
66
|
+
let mat = obj.sharedMaterials[i];
|
|
67
|
+
if (mat) {
|
|
68
|
+
// if (!mat[$stencils])
|
|
69
|
+
mat = mat.clone();
|
|
70
|
+
mat[$stencils] = true;
|
|
71
|
+
mat.stencilWrite = true;
|
|
72
|
+
mat.stencilWriteMask = 255;
|
|
73
|
+
mat.stencilFuncMask = 255;
|
|
74
|
+
mat.stencilRef = stencil.value;
|
|
75
|
+
mat.stencilFunc = stencil.compareFunc;
|
|
76
|
+
mat.stencilZPass = stencil.passOp;
|
|
77
|
+
mat.stencilFail = stencil.failOp;
|
|
78
|
+
mat.stencilZFail = stencil.zFailOp;
|
|
79
|
+
obj.sharedMaterials[i] = mat;
|
|
80
|
+
}
|
|
79
81
|
}
|
|
82
|
+
// you can have 50 renderer features per event until this breaks
|
|
83
|
+
obj.gameObject.renderOrder = stencil.event * 1000 + stencil.index * 50;
|
|
80
84
|
break;
|
|
81
85
|
}
|
|
82
86
|
}
|
|
@@ -16,15 +16,26 @@ import { createFlatTexture } from "../engine/engine_shaders";
|
|
|
16
16
|
import { Mathf } from "../engine/engine_math";
|
|
17
17
|
import { Context } from "../engine/engine_setup";
|
|
18
18
|
import { ParticleSubEmitter } from "./ParticleSystemSubEmitter";
|
|
19
|
+
import { copyFile } from "fs";
|
|
19
20
|
|
|
20
21
|
const debug = getParam("debugparticles");
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
export enum SubEmitterType {
|
|
24
|
+
Birth = 0,
|
|
25
|
+
Collision = 1,
|
|
26
|
+
Death = 2,
|
|
27
|
+
Trigger = 3,
|
|
28
|
+
Manual = 4,
|
|
29
|
+
}
|
|
23
30
|
|
|
24
31
|
export class SubEmitterSystem {
|
|
25
32
|
|
|
26
33
|
particleSystem?: ParticleSystem;
|
|
27
34
|
|
|
35
|
+
emitProbability: number = 1;
|
|
36
|
+
properties?: number;
|
|
37
|
+
type?: SubEmitterType;
|
|
38
|
+
|
|
28
39
|
_deserialize(context: Context) {
|
|
29
40
|
const ps = this.particleSystem;
|
|
30
41
|
if (ps && !(ps instanceof ParticleSystem) && typeof ps["guid"] === "string") {
|
|
@@ -62,12 +73,22 @@ export class ParticleSystemRenderer extends Behaviour {
|
|
|
62
73
|
return this.particleMaterial;
|
|
63
74
|
}
|
|
64
75
|
|
|
65
|
-
getMesh() {
|
|
76
|
+
getMesh(renderMode?: ParticleSystemRenderMode) {
|
|
66
77
|
let geo: BufferGeometry | null = null;
|
|
67
|
-
if (
|
|
68
|
-
geo =
|
|
78
|
+
if (renderMode === ParticleSystemRenderMode.HorizontalBillboard) {
|
|
79
|
+
geo = new THREE.BoxGeometry(1, 1, 0);
|
|
69
80
|
}
|
|
70
|
-
if (
|
|
81
|
+
else if (renderMode === ParticleSystemRenderMode.VerticalBillboard) {
|
|
82
|
+
geo = new THREE.BoxGeometry(1, 0, 1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!geo) {
|
|
86
|
+
if (this.particleMesh instanceof Mesh) {
|
|
87
|
+
geo = this.particleMesh.geometry;
|
|
88
|
+
}
|
|
89
|
+
if (geo === null) geo = new THREE.BoxGeometry(1, 1, 0);
|
|
90
|
+
}
|
|
91
|
+
|
|
71
92
|
const res = new Mesh(geo, this.getMaterial());
|
|
72
93
|
return res;
|
|
73
94
|
}
|
|
@@ -483,7 +504,7 @@ class ParticleSystemInterface implements ParticleSystemParameters {
|
|
|
483
504
|
onlyUsedByOther?: boolean | undefined;
|
|
484
505
|
readonly behaviors: Behavior[] = [];
|
|
485
506
|
get instancingGeometry() {
|
|
486
|
-
return this.system.renderer.getMesh().geometry;
|
|
507
|
+
return this.system.renderer.getMesh(this.system.renderer.renderMode).geometry;
|
|
487
508
|
}
|
|
488
509
|
get renderMode() {
|
|
489
510
|
if (this.system.trails["enabled"] === true) {
|
|
@@ -492,8 +513,8 @@ class ParticleSystemInterface implements ParticleSystemParameters {
|
|
|
492
513
|
switch (this.system.renderer.renderMode) {
|
|
493
514
|
case ParticleSystemRenderMode.Billboard: return RenderMode.BillBoard;
|
|
494
515
|
// case ParticleSystemRenderMode.Stretch: return RenderMode.Stretch;
|
|
495
|
-
|
|
496
|
-
|
|
516
|
+
case ParticleSystemRenderMode.HorizontalBillboard: return RenderMode.LocalSpace;
|
|
517
|
+
case ParticleSystemRenderMode.VerticalBillboard: return RenderMode.LocalSpace;
|
|
497
518
|
case ParticleSystemRenderMode.Mesh: return RenderMode.LocalSpace;
|
|
498
519
|
}
|
|
499
520
|
return RenderMode.BillBoard;
|
|
@@ -726,6 +747,9 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
|
|
|
726
747
|
arr[i] = instance;
|
|
727
748
|
}
|
|
728
749
|
}
|
|
750
|
+
if (debug && arr.length > 0) {
|
|
751
|
+
console.log("SubEmitters: ", arr, this)
|
|
752
|
+
}
|
|
729
753
|
this._subEmitterSystems = arr;
|
|
730
754
|
}
|
|
731
755
|
private _subEmitterSystems?: SubEmitterSystem[];
|
|
@@ -839,11 +863,15 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
|
|
|
839
863
|
private addSubParticleSystems() {
|
|
840
864
|
if (this._subEmitterSystems && this._particleSystem) {
|
|
841
865
|
for (const sys of this._subEmitterSystems) {
|
|
866
|
+
// Make sure the particle system is created
|
|
867
|
+
if (sys.particleSystem) sys.particleSystem.__internalAwake();
|
|
842
868
|
const system = sys.particleSystem?._particleSystem;
|
|
843
869
|
if (system) {
|
|
844
870
|
sys.particleSystem!._isUsedAsSubsystem = true;
|
|
845
871
|
// sys.particleSystem!.main.simulationSpace = ParticleSystemSimulationSpace.World;
|
|
846
872
|
const sub = new ParticleSubEmitter(this, this._particleSystem, sys.particleSystem!, system);
|
|
873
|
+
sub.emitterType = sys.type;
|
|
874
|
+
sub.emitterProbability = sys.emitProbability;
|
|
847
875
|
this._particleSystem.addBehavior(sub);
|
|
848
876
|
}
|
|
849
877
|
}
|
|
@@ -36,8 +36,8 @@ export interface IParticleSystem {
|
|
|
36
36
|
export enum ParticleSystemRenderMode {
|
|
37
37
|
Billboard = 0,
|
|
38
38
|
// Stretch = 1,
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
HorizontalBillboard = 2,
|
|
40
|
+
VerticalBillboard = 3,
|
|
41
41
|
Mesh = 4,
|
|
42
42
|
// None = 5,
|
|
43
43
|
}
|
|
@@ -1117,7 +1117,7 @@ export class RotationOverLifetimeModule {
|
|
|
1117
1117
|
@serializable()
|
|
1118
1118
|
zMultiplier!: number;
|
|
1119
1119
|
|
|
1120
|
-
evaluate(t01: number, t:number): number {
|
|
1120
|
+
evaluate(t01: number, t: number): number {
|
|
1121
1121
|
if (!this.enabled) return 0;
|
|
1122
1122
|
if (!this.separateAxes) {
|
|
1123
1123
|
const rot = this.z.evaluate(t01, t) * -1;
|
|
@@ -2,6 +2,7 @@ import { Behavior, Particle, EmissionState, ParticleSystem, ParticleEmitter } fr
|
|
|
2
2
|
import { Vector3, Quaternion, Matrix4 } from "three";
|
|
3
3
|
import { IParticleSystem } from "./ParticleSystemModules";
|
|
4
4
|
import { CircularBuffer } from "../engine/engine_utils";
|
|
5
|
+
import { SubEmitterType } from "./ParticleSystem";
|
|
5
6
|
|
|
6
7
|
const VECTOR_ONE = new Vector3(1, 1, 1);
|
|
7
8
|
const VECTOR_Z = new Vector3(0, 0, 1);
|
|
@@ -11,6 +12,9 @@ export class ParticleSubEmitter implements Behavior {
|
|
|
11
12
|
|
|
12
13
|
type = "NeedleParticleSubEmitter";
|
|
13
14
|
|
|
15
|
+
emitterType?: SubEmitterType;
|
|
16
|
+
emitterProbability?: number;
|
|
17
|
+
|
|
14
18
|
//private matrix_ = new Matrix4();
|
|
15
19
|
private q_ = new Quaternion();
|
|
16
20
|
private v_ = new Vector3();
|
|
@@ -47,14 +51,41 @@ export class ParticleSubEmitter implements Behavior {
|
|
|
47
51
|
} as EmissionState;
|
|
48
52
|
// particle[$emitterMatrix] = new Matrix4();
|
|
49
53
|
this._emitterMatrix.copy(this.subSystem.matrixWorld).invert().premultiply(this.system.matrixWorld)
|
|
54
|
+
|
|
55
|
+
if (this.emitterType === SubEmitterType.Birth) {
|
|
56
|
+
this.run(particle);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
update(particle: Particle, _delta: number): void {
|
|
61
|
+
this.run(particle);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
frameUpdate(_delta: number): void {
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
toJSON(): any {
|
|
50
68
|
}
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
private run(particle: Particle) {
|
|
53
71
|
if (this.subSystem.currentParticles >= this.subSystem.main.maxParticles)
|
|
54
72
|
return;
|
|
55
73
|
if (!this.subParticleSystem || !particle.emissionState)
|
|
56
74
|
return;
|
|
57
75
|
|
|
76
|
+
if(this.emitterProbability && Math.random() > this.emitterProbability)
|
|
77
|
+
return;
|
|
78
|
+
|
|
79
|
+
const delta = this.system.deltaTime;
|
|
80
|
+
|
|
81
|
+
if (this.emitterType === SubEmitterType.Death) {
|
|
82
|
+
const willDie = particle.age + delta * 1.2 >= particle.life;
|
|
83
|
+
if (!willDie) return;
|
|
84
|
+
// Just emit all for now, we should probably add a way to get the amount from the subsystem emission module
|
|
85
|
+
const maxAmount = this.subSystem.main.maxParticles - this.subSystem.currentParticles;
|
|
86
|
+
particle.emissionState.waitEmiting = maxAmount;
|
|
87
|
+
}
|
|
88
|
+
|
|
58
89
|
// TODO: figure out how to re-use matrices
|
|
59
90
|
const m = new Matrix4();// this._circularBuffer.get();// new Matrix4();// particle[$emitterMatrix];
|
|
60
91
|
|
|
@@ -71,10 +102,4 @@ export class ParticleSubEmitter implements Behavior {
|
|
|
71
102
|
|
|
72
103
|
this.subParticleSystem!.emit(delta, particle.emissionState!, m);
|
|
73
104
|
}
|
|
74
|
-
|
|
75
|
-
frameUpdate(_delta: number): void {
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
toJSON(): any {
|
|
79
|
-
}
|
|
80
105
|
}
|
|
@@ -18,9 +18,6 @@ export class RemoteSkybox extends Behaviour {
|
|
|
18
18
|
@serializable()
|
|
19
19
|
background: boolean = true;
|
|
20
20
|
|
|
21
|
-
@serializable()
|
|
22
|
-
backgroundBlurriness: number = 0;
|
|
23
|
-
|
|
24
21
|
@serializable()
|
|
25
22
|
environment: boolean = true;
|
|
26
23
|
|
|
@@ -84,7 +81,8 @@ export class RemoteSkybox extends Behaviour {
|
|
|
84
81
|
this._prevLoadedEnvironment = envMap;
|
|
85
82
|
const nameIndex = url.lastIndexOf("/");
|
|
86
83
|
envMap.name = url.substring(nameIndex >= 0 ? nameIndex + 1 : 0);
|
|
87
|
-
this.context.
|
|
84
|
+
if (this.context.mainCameraComponent?.backgroundBlurriness !== undefined)
|
|
85
|
+
this.context.scene.backgroundBlurriness = this.context.mainCameraComponent.backgroundBlurriness;
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
|
|
@@ -8,6 +8,7 @@ import * as Models from "./TimelineModels";
|
|
|
8
8
|
import * as Tracks from "./TimelineTracks";
|
|
9
9
|
import { deepClone, getParam } from '../../engine/engine_utils';
|
|
10
10
|
import { GuidsMap } from '../../engine/engine_types';
|
|
11
|
+
import { Object3D } from 'three';
|
|
11
12
|
|
|
12
13
|
const debug = getParam("debugtimeline");
|
|
13
14
|
|
|
@@ -371,10 +372,17 @@ export class PlayableDirector extends Behaviour {
|
|
|
371
372
|
}
|
|
372
373
|
// only handle animation tracks
|
|
373
374
|
if (track.type === Models.TrackType.Animation) {
|
|
374
|
-
if (track.clips.length <= 0)
|
|
375
|
+
if (track.clips.length <= 0) {
|
|
376
|
+
if(debug) console.warn("Animation track has no clips", track);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
375
379
|
// loop outputs / bindings, they should contain animator references
|
|
376
380
|
for (let i = track.outputs.length - 1; i >= 0; i--) {
|
|
377
|
-
|
|
381
|
+
let binding = track.outputs[i] as Animator;
|
|
382
|
+
if(binding instanceof Object3D){
|
|
383
|
+
const anim = GameObject.getOrAddComponent(binding, Animator);
|
|
384
|
+
if(anim) binding = anim;
|
|
385
|
+
}
|
|
378
386
|
if (typeof binding.enabled === "boolean") binding.enabled = false;
|
|
379
387
|
const animationClips = binding?.gameObject?.animations;
|
|
380
388
|
if (animationClips) {
|
|
@@ -51,7 +51,8 @@ export declare type ClipModel = {
|
|
|
51
51
|
easeInDuration: number;
|
|
52
52
|
easeOutDuration: number;
|
|
53
53
|
preExtrapolationMode: ClipExtrapolation;
|
|
54
|
-
postExtrapolationMode: ClipExtrapolation
|
|
54
|
+
postExtrapolationMode: ClipExtrapolation;
|
|
55
|
+
reversed?: boolean;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
export declare type AnimationClipModel = {
|
|
@@ -59,8 +60,8 @@ export declare type AnimationClipModel = {
|
|
|
59
60
|
loop: boolean;
|
|
60
61
|
duration: number;
|
|
61
62
|
removeStartOffset: boolean;
|
|
62
|
-
position
|
|
63
|
-
rotation
|
|
63
|
+
position?: Vec3 | THREE.Vector3;
|
|
64
|
+
rotation?: Quat | THREE.Quaternion;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export declare type AudioClipModel = {
|
|
@@ -213,7 +213,7 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
213
213
|
const clipData = model.asset as Models.AnimationClipModel;
|
|
214
214
|
const pos = clipData.position as any;
|
|
215
215
|
const rot = clipData.rotation as any;
|
|
216
|
-
if (pos.x !== undefined) {
|
|
216
|
+
if (pos && pos.x !== undefined) {
|
|
217
217
|
if (!pos.isVector3) {
|
|
218
218
|
clipData.position = new Vector3(pos.x, pos.y, pos.z);
|
|
219
219
|
}
|
|
@@ -254,6 +254,7 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
254
254
|
private _tempPos = new THREE.Vector3();
|
|
255
255
|
private _summedRot = new THREE.Quaternion();
|
|
256
256
|
private _tempRot = new THREE.Quaternion();
|
|
257
|
+
private _clipRotQuat = new THREE.Quaternion();
|
|
257
258
|
|
|
258
259
|
evaluate(time: number) {
|
|
259
260
|
if (this.track.muted) return;
|
|
@@ -315,7 +316,9 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
318
|
|
|
318
|
-
action.time = t;
|
|
319
|
+
if(model.reversed === true) action.time = action.getClip().duration - t;
|
|
320
|
+
else action.time = t;
|
|
321
|
+
|
|
319
322
|
action.timeScale = 0;
|
|
320
323
|
const effectiveWeight = weight * this.director.weight;
|
|
321
324
|
action.weight = effectiveWeight;
|
|
@@ -335,8 +338,10 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
335
338
|
const tempRot = this._tempRot.identity();
|
|
336
339
|
|
|
337
340
|
const clipOffsetRot = clipModel.rotation as Quaternion;
|
|
338
|
-
|
|
339
|
-
|
|
341
|
+
if (clipOffsetRot) {
|
|
342
|
+
this._clipRotQuat.identity();
|
|
343
|
+
this._clipRotQuat.slerp(clipOffsetRot, weight);
|
|
344
|
+
}
|
|
340
345
|
|
|
341
346
|
const offsets = this._actionOffsets[i];
|
|
342
347
|
if (offsets.hasOffsets) {
|
|
@@ -345,7 +350,10 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
345
350
|
tempPos.copy(offsets.rootPositionOffset);
|
|
346
351
|
else tempPos.set(0, 0, 0);
|
|
347
352
|
|
|
348
|
-
tempPos.applyQuaternion(summedRot)
|
|
353
|
+
tempPos.applyQuaternion(summedRot);
|
|
354
|
+
if (this._clipRotQuat)
|
|
355
|
+
tempPos.applyQuaternion(this._clipRotQuat);
|
|
356
|
+
|
|
349
357
|
if (offsets.rootQuaternionOffset) {
|
|
350
358
|
// console.log(new THREE.Euler().setFromQuaternion(offsets.rootQuaternionOffset).y.toFixed(2));
|
|
351
359
|
tempRot.copy(offsets.rootQuaternionOffset);
|
|
@@ -355,10 +363,12 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
355
363
|
}
|
|
356
364
|
}
|
|
357
365
|
|
|
358
|
-
|
|
366
|
+
if (this._clipRotQuat)
|
|
367
|
+
totalRotation.multiply(this._clipRotQuat);
|
|
359
368
|
totalRotation.multiply(summedRot);
|
|
360
369
|
|
|
361
|
-
|
|
370
|
+
if (clipModel.position)
|
|
371
|
+
summedPos.add(clipModel.position as THREE.Vector3);
|
|
362
372
|
totalPosition.add(summedPos);
|
|
363
373
|
}
|
|
364
374
|
|
|
@@ -506,7 +516,7 @@ export class AudioTrackHandler extends TrackHandler {
|
|
|
506
516
|
}
|
|
507
517
|
}
|
|
508
518
|
evaluate(time: number) {
|
|
509
|
-
if(muteAudioTracks) return;
|
|
519
|
+
if (muteAudioTracks) return;
|
|
510
520
|
if (this.track.muted) return;
|
|
511
521
|
for (let i = 0; i < this.models.length; i++) {
|
|
512
522
|
const model = this.models[i];
|
|
@@ -521,6 +531,19 @@ export class AudioTrackHandler extends TrackHandler {
|
|
|
521
531
|
audio.offset = model.clipIn + (time - model.start) * model.timeScale;
|
|
522
532
|
audio.play();
|
|
523
533
|
}
|
|
534
|
+
else {
|
|
535
|
+
const targetOffset = model.clipIn + (time - model.start) * model.timeScale;
|
|
536
|
+
// seems it's non-trivial to get the right time from audio sources;
|
|
537
|
+
// https://github.com/mrdoob/three.js/blob/master/src/audio/Audio.js#L170
|
|
538
|
+
const currentTime = audio.context.currentTime - audio["_startedAt"] + audio.offset;
|
|
539
|
+
const diff = Math.abs(targetOffset - currentTime);
|
|
540
|
+
|
|
541
|
+
if (diff > 0.3) {
|
|
542
|
+
audio.offset = targetOffset;
|
|
543
|
+
audio.stop();
|
|
544
|
+
audio.play();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
524
547
|
let vol = model.asset.volume;
|
|
525
548
|
if (model.easeInDuration > 0) {
|
|
526
549
|
const easeIn = Math.min((time - model.start) / model.easeInDuration, 1);
|