@needle-tools/engine 2.59.2-pre → 2.60.1-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 +5443 -5422
- package/dist/needle-engine.umd.cjs +99 -99
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- 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 +1 -1
- package/lib/engine-components/ParticleSystem.js +16 -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/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/api.ts +2 -1
- 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 +17 -7
- package/src/engine-components/ParticleSystemModules.ts +3 -3
- 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.1-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.
|
|
59
|
+
"@needle-tools/gltf-transform-extensions": "^0.11.0-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",
|
package/src/engine/api.ts
CHANGED
|
@@ -7,4 +7,5 @@ export { Context, FrameEvent } from "./engine_setup";
|
|
|
7
7
|
export * from "./debug/debug";
|
|
8
8
|
export { validate } from "./engine_util_decorator"
|
|
9
9
|
export { Gizmos } from "./engine_gizmos"
|
|
10
|
-
export * from "./engine_scenetools";
|
|
10
|
+
export * from "./engine_scenetools";
|
|
11
|
+
export * from "./engine_math"
|
|
@@ -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
|
}
|
|
@@ -73,12 +73,22 @@ export class ParticleSystemRenderer extends Behaviour {
|
|
|
73
73
|
return this.particleMaterial;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
getMesh() {
|
|
76
|
+
getMesh(renderMode?: ParticleSystemRenderMode) {
|
|
77
77
|
let geo: BufferGeometry | null = null;
|
|
78
|
-
if (
|
|
79
|
-
geo =
|
|
78
|
+
if (renderMode === ParticleSystemRenderMode.HorizontalBillboard) {
|
|
79
|
+
geo = new THREE.BoxGeometry(1, 1, 0);
|
|
80
80
|
}
|
|
81
|
-
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
|
+
|
|
82
92
|
const res = new Mesh(geo, this.getMaterial());
|
|
83
93
|
return res;
|
|
84
94
|
}
|
|
@@ -494,7 +504,7 @@ class ParticleSystemInterface implements ParticleSystemParameters {
|
|
|
494
504
|
onlyUsedByOther?: boolean | undefined;
|
|
495
505
|
readonly behaviors: Behavior[] = [];
|
|
496
506
|
get instancingGeometry() {
|
|
497
|
-
return this.system.renderer.getMesh().geometry;
|
|
507
|
+
return this.system.renderer.getMesh(this.system.renderer.renderMode).geometry;
|
|
498
508
|
}
|
|
499
509
|
get renderMode() {
|
|
500
510
|
if (this.system.trails["enabled"] === true) {
|
|
@@ -503,8 +513,8 @@ class ParticleSystemInterface implements ParticleSystemParameters {
|
|
|
503
513
|
switch (this.system.renderer.renderMode) {
|
|
504
514
|
case ParticleSystemRenderMode.Billboard: return RenderMode.BillBoard;
|
|
505
515
|
// case ParticleSystemRenderMode.Stretch: return RenderMode.Stretch;
|
|
506
|
-
|
|
507
|
-
|
|
516
|
+
case ParticleSystemRenderMode.HorizontalBillboard: return RenderMode.LocalSpace;
|
|
517
|
+
case ParticleSystemRenderMode.VerticalBillboard: return RenderMode.LocalSpace;
|
|
508
518
|
case ParticleSystemRenderMode.Mesh: return RenderMode.LocalSpace;
|
|
509
519
|
}
|
|
510
520
|
return RenderMode.BillBoard;
|
|
@@ -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;
|
|
@@ -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);
|