@zephyr3d/scene 0.4.0 → 0.5.0
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/dist/animation/animation.js +25 -117
- package/dist/animation/animation.js.map +1 -1
- package/dist/animation/animationset.js +164 -24
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/animationtrack.js +6 -18
- package/dist/animation/animationtrack.js.map +1 -1
- package/dist/animation/eulerrotationtrack.js +16 -6
- package/dist/animation/eulerrotationtrack.js.map +1 -1
- package/dist/animation/morphtarget.js +104 -0
- package/dist/animation/morphtarget.js.map +1 -0
- package/dist/animation/morphtrack.js +68 -0
- package/dist/animation/morphtrack.js.map +1 -0
- package/dist/animation/rotationtrack.js +15 -7
- package/dist/animation/rotationtrack.js.map +1 -1
- package/dist/animation/scaletrack.js +15 -7
- package/dist/animation/scaletrack.js.map +1 -1
- package/dist/animation/skeleton.js +101 -1
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/translationtrack.js +15 -7
- package/dist/animation/translationtrack.js.map +1 -1
- package/dist/animation/usertrack.js +2 -2
- package/dist/app.js +4 -26
- package/dist/app.js.map +1 -1
- package/dist/asset/assetmanager.js +58 -107
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/loaders/dds/dds.js +77 -3
- package/dist/asset/loaders/dds/dds.js.map +1 -1
- package/dist/asset/loaders/dds/dds_loader.js +1 -1
- package/dist/asset/loaders/gltf/gltf_loader.js +280 -37
- package/dist/asset/loaders/gltf/gltf_loader.js.map +1 -1
- package/dist/asset/loaders/image/tga_Loader.js +1 -1
- package/dist/asset/model.js +13 -0
- package/dist/asset/model.js.map +1 -1
- package/dist/blitter/blitter.js +2 -2
- package/dist/camera/camera.js +58 -3
- package/dist/camera/camera.js.map +1 -1
- package/dist/index.d.ts +3026 -2839
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/material/blinn.js +9 -4
- package/dist/material/blinn.js.map +1 -1
- package/dist/material/lambert.js +22 -17
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +11 -2
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +36 -4
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/albedocolor.js +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +3 -3
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
- package/dist/material/mixins/lit.js +2 -2
- package/dist/material/mixins/pbr/common.js +433 -10
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/pbrmr.js +17 -6
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/pbrsg.js +16 -9
- package/dist/material/pbrsg.js.map +1 -1
- package/dist/material/shader/helper.js +94 -4
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/unlit.js +8 -4
- package/dist/material/unlit.js.map +1 -1
- package/dist/posteffect/bloom.js +33 -43
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/compositor.js +8 -37
- package/dist/posteffect/compositor.js.map +1 -1
- package/dist/posteffect/sao.js +11 -24
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/water.js +2 -4
- package/dist/posteffect/water.js.map +1 -1
- package/dist/render/cull_visitor.js +3 -3
- package/dist/render/depthpass.js +13 -13
- package/dist/render/drawable_mixin.js +48 -6
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/envlight.js +165 -31
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/lightpass.js +35 -27
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/objectcolorpass.js +50 -0
- package/dist/render/objectcolorpass.js.map +1 -0
- package/dist/render/objectpool.js +295 -0
- package/dist/render/objectpool.js.map +1 -0
- package/dist/render/render_queue.js +189 -156
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +97 -20
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/renderpass.js +18 -14
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/shadowmap_pass.js +14 -14
- package/dist/render/weightedblended_oit.js +11 -28
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/environment.js +22 -1
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/graph_node.js +0 -9
- package/dist/scene/graph_node.js.map +1 -1
- package/dist/scene/mesh.js +32 -1
- package/dist/scene/mesh.js.map +1 -1
- package/dist/scene/scene.js +5 -8
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +2 -3
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/terrain/grass.js +9 -0
- package/dist/scene/terrain/grass.js.map +1 -1
- package/dist/scene/terrain/patch.js +9 -0
- package/dist/scene/terrain/patch.js.map +1 -1
- package/dist/scene/terrain/quadtree.js +2 -2
- package/dist/shadow/esm.js +4 -22
- package/dist/shadow/esm.js.map +1 -1
- package/dist/shadow/shadowmapper.js +45 -20
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shadow/vsm.js +4 -24
- package/dist/shadow/vsm.js.map +1 -1
- package/dist/utility/draco/decoder.js +116 -0
- package/dist/utility/draco/decoder.js.map +1 -0
- package/dist/values.js +18 -1
- package/dist/values.js.map +1 -1
- package/package.json +7 -6
|
@@ -1,47 +1,22 @@
|
|
|
1
|
-
import { Quaternion, Vector3, Matrix4x4 } from '@zephyr3d/base';
|
|
2
|
-
import { BoundingBox } from '../utility/bounding_volume.js';
|
|
3
|
-
import { Application } from '../app.js';
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
2
|
* Animation that contains multiple tracks
|
|
7
3
|
* @public
|
|
8
4
|
*/ class AnimationClip {
|
|
9
5
|
/** @internal */ _name;
|
|
10
|
-
/** @internal */ _model;
|
|
11
|
-
/** @internal */ _repeat;
|
|
12
|
-
/** @internal */ _speedRatio;
|
|
13
|
-
/** @internal */ _repeatCounter;
|
|
14
6
|
/** @internal */ _duration;
|
|
15
|
-
/** @internal */ _isPlaying;
|
|
16
|
-
/** @internal */ _lastUpdateFrame;
|
|
17
|
-
/** @internal */ _currentPlayTime;
|
|
18
7
|
/** @internal */ _tracks;
|
|
19
8
|
/** @internal */ _skeletons;
|
|
20
|
-
/** @internal */ _tmpPosition;
|
|
21
|
-
/** @internal */ _tmpRotation;
|
|
22
|
-
/** @internal */ _tmpScale;
|
|
23
9
|
/**
|
|
24
10
|
* Creates an animation instance
|
|
25
11
|
* @param name - Name of the animation
|
|
26
12
|
* @param model - Parent node if this is a skeleton animation
|
|
27
|
-
*/ constructor(name
|
|
13
|
+
*/ constructor(name){
|
|
28
14
|
this._name = name;
|
|
29
|
-
this._model = model ?? null;
|
|
30
15
|
this._tracks = new Map();
|
|
31
16
|
this._duration = 0;
|
|
32
|
-
this.
|
|
33
|
-
this._repeatCounter = 0;
|
|
34
|
-
this._speedRatio = 1;
|
|
35
|
-
this._isPlaying = false;
|
|
36
|
-
this._currentPlayTime = 0;
|
|
37
|
-
this._lastUpdateFrame = 0;
|
|
38
|
-
this._skeletons = new Map();
|
|
39
|
-
this._tmpRotation = new Quaternion();
|
|
40
|
-
this._tmpPosition = new Vector3();
|
|
41
|
-
this._tmpScale = new Vector3();
|
|
17
|
+
this._skeletons = new Set();
|
|
42
18
|
}
|
|
43
19
|
/** Disposes self */ dispose() {
|
|
44
|
-
this._model = null;
|
|
45
20
|
this._tracks = null;
|
|
46
21
|
this._skeletons?.forEach((val, key)=>key.dispose());
|
|
47
22
|
this._skeletons = null;
|
|
@@ -52,6 +27,9 @@ import { Application } from '../app.js';
|
|
|
52
27
|
/** Gets all the tracks of this animation */ get tracks() {
|
|
53
28
|
return this._tracks;
|
|
54
29
|
}
|
|
30
|
+
/** Gets all skeletons */ get skeletons() {
|
|
31
|
+
return this._skeletons;
|
|
32
|
+
}
|
|
55
33
|
/** The duration of the animation */ get timeDuration() {
|
|
56
34
|
return this._duration;
|
|
57
35
|
}
|
|
@@ -60,19 +38,8 @@ import { Application } from '../app.js';
|
|
|
60
38
|
* @param skeleton - The skeleton to be added
|
|
61
39
|
* @param meshList - The meshes controlled by the skeleton
|
|
62
40
|
* @param boundingBoxInfo - Bounding box information for the skeleton
|
|
63
|
-
*/ addSkeleton(skeleton
|
|
64
|
-
|
|
65
|
-
if (!meshes) {
|
|
66
|
-
meshes = [];
|
|
67
|
-
this._skeletons.set(skeleton, meshes);
|
|
68
|
-
}
|
|
69
|
-
for(let i = 0; i < meshList.length; i++){
|
|
70
|
-
meshes.push({
|
|
71
|
-
mesh: meshList[i],
|
|
72
|
-
bounding: boundingBoxInfo[i],
|
|
73
|
-
box: new BoundingBox()
|
|
74
|
-
});
|
|
75
|
-
}
|
|
41
|
+
*/ addSkeleton(skeleton) {
|
|
42
|
+
this._skeletons.add(skeleton);
|
|
76
43
|
}
|
|
77
44
|
/**
|
|
78
45
|
* Adds an animation track to the animation
|
|
@@ -83,90 +50,31 @@ import { Application } from '../app.js';
|
|
|
83
50
|
if (!track) {
|
|
84
51
|
return;
|
|
85
52
|
}
|
|
53
|
+
if (track.animation) {
|
|
54
|
+
if (track.animation === this) {
|
|
55
|
+
return;
|
|
56
|
+
} else {
|
|
57
|
+
console.error('Track is already in another animation');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const blendId = track.getBlendId();
|
|
62
|
+
const tracks = this._tracks.get(node);
|
|
63
|
+
if (tracks && tracks.findIndex((track)=>track.getBlendId() === blendId) >= 0) {
|
|
64
|
+
console.error('Tracks with same BlendId could not be added to same animation');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
track.animation = this;
|
|
86
68
|
let trackInfo = this._tracks.get(node);
|
|
87
69
|
if (!trackInfo) {
|
|
88
|
-
trackInfo =
|
|
89
|
-
poseTranslation: new Vector3(node.position),
|
|
90
|
-
poseRotation: new Quaternion(node.rotation),
|
|
91
|
-
poseScaling: new Vector3(node.scale),
|
|
92
|
-
tracks: []
|
|
93
|
-
};
|
|
70
|
+
trackInfo = [];
|
|
94
71
|
this._tracks.set(node, trackInfo);
|
|
95
72
|
}
|
|
96
|
-
trackInfo.
|
|
73
|
+
trackInfo.push(track);
|
|
97
74
|
this._duration = Math.max(this._duration, track.interpolator.maxTime);
|
|
75
|
+
track.reset(node);
|
|
98
76
|
return this;
|
|
99
77
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Check if the animation is playing
|
|
102
|
-
* @returns true if the animation is playing, otherwise false
|
|
103
|
-
*/ isPlaying() {
|
|
104
|
-
return this._isPlaying;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Updates the animation state
|
|
108
|
-
*/ update() {
|
|
109
|
-
const device = Application.instance.device;
|
|
110
|
-
if (!this._isPlaying || this._lastUpdateFrame === device.frameInfo.frameCounter) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
this._lastUpdateFrame = device.frameInfo.frameCounter;
|
|
114
|
-
this._tracks.forEach((trackInfo, node)=>{
|
|
115
|
-
for (const track of trackInfo.tracks){
|
|
116
|
-
track.apply(node, this._currentPlayTime, this._duration);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
this._skeletons.forEach((meshes, skeleton)=>{
|
|
120
|
-
skeleton.computeJoints();
|
|
121
|
-
for (const mesh of meshes){
|
|
122
|
-
skeleton.computeBoundingBox(mesh.bounding, mesh.mesh.invWorldMatrix);
|
|
123
|
-
mesh.mesh.setBoneMatrices(skeleton.jointTexture);
|
|
124
|
-
mesh.mesh.setInvBindMatrix(mesh.mesh.invWorldMatrix);
|
|
125
|
-
mesh.mesh.setAnimatedBoundingBox(mesh.bounding.boundingBox);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
const timeAdvance = device.frameInfo.elapsedFrame * 0.001 * this._speedRatio;
|
|
129
|
-
this._currentPlayTime += timeAdvance;
|
|
130
|
-
if (this._currentPlayTime > this._duration) {
|
|
131
|
-
this._repeatCounter++;
|
|
132
|
-
this._currentPlayTime = 0;
|
|
133
|
-
} else if (this._currentPlayTime < 0) {
|
|
134
|
-
this._repeatCounter++;
|
|
135
|
-
this._currentPlayTime = this._duration;
|
|
136
|
-
}
|
|
137
|
-
if (this._repeat !== 0 && this._repeatCounter >= this._repeat) {
|
|
138
|
-
this.stop();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Starts playing the animation
|
|
143
|
-
*/ play(repeat, speedRatio) {
|
|
144
|
-
this._isPlaying = true;
|
|
145
|
-
this._repeat = repeat;
|
|
146
|
-
this._speedRatio = speedRatio;
|
|
147
|
-
this._currentPlayTime = speedRatio < 0 ? this._duration : 0;
|
|
148
|
-
this.update();
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Stops the animation
|
|
152
|
-
*/ stop() {
|
|
153
|
-
this._isPlaying = false;
|
|
154
|
-
this._skeletons.forEach((meshes, skeleton)=>{
|
|
155
|
-
skeleton.computeBindPose();
|
|
156
|
-
for (const mesh of meshes){
|
|
157
|
-
const invWorldMatrix = Matrix4x4.multiply(mesh.mesh.invWorldMatrix, this._model.worldMatrix);
|
|
158
|
-
skeleton.computeBoundingBox(mesh.bounding, invWorldMatrix);
|
|
159
|
-
mesh.mesh.setBoneMatrices(skeleton.jointTexture);
|
|
160
|
-
mesh.mesh.setInvBindMatrix(invWorldMatrix);
|
|
161
|
-
mesh.mesh.setAnimatedBoundingBox(mesh.bounding.boundingBox);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Rewind the animation to the first frame
|
|
167
|
-
*/ rewind() {
|
|
168
|
-
this._currentPlayTime = 0;
|
|
169
|
-
}
|
|
170
78
|
}
|
|
171
79
|
|
|
172
80
|
export { AnimationClip };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animation.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"animation.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,16 +1,27 @@
|
|
|
1
|
+
import { weightedAverage } from '@zephyr3d/base';
|
|
2
|
+
import { Application } from '../app.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Animation set
|
|
3
6
|
* @public
|
|
4
7
|
*/ class AnimationSet {
|
|
8
|
+
/** @internal */ _model;
|
|
5
9
|
/** @internal */ _animations;
|
|
6
10
|
/** @internal */ _scene;
|
|
11
|
+
/** @internal */ _activeTracks;
|
|
12
|
+
/** @internal */ _activeSkeletons;
|
|
13
|
+
/** @internal */ _activeAnimations;
|
|
7
14
|
/**
|
|
8
15
|
* Creates an instance of AnimationSet
|
|
9
16
|
* @param scene - The scene to which the animation set belongs
|
|
10
|
-
*/ constructor(scene){
|
|
17
|
+
*/ constructor(scene, model){
|
|
11
18
|
this._scene = scene;
|
|
19
|
+
this._model = model;
|
|
12
20
|
this._scene.animationSet.push(this);
|
|
13
21
|
this._animations = {};
|
|
22
|
+
this._activeTracks = new Map();
|
|
23
|
+
this._activeSkeletons = new Map();
|
|
24
|
+
this._activeAnimations = new Map();
|
|
14
25
|
}
|
|
15
26
|
/**
|
|
16
27
|
* How many animations in this set
|
|
@@ -37,47 +48,173 @@
|
|
|
37
48
|
/**
|
|
38
49
|
* Updates all animations of the model
|
|
39
50
|
*/ update() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
this._activeAnimations.forEach((v, k)=>{
|
|
52
|
+
if (v.fadeOut > 0 && v.fadeOutStart < 0) {
|
|
53
|
+
v.fadeOutStart = v.animateTime;
|
|
54
|
+
}
|
|
55
|
+
// Update animation time
|
|
56
|
+
if (v.firstFrame) {
|
|
57
|
+
v.firstFrame = false;
|
|
58
|
+
} else {
|
|
59
|
+
const timeAdvance = Application.instance.device.frameInfo.elapsedFrame * 0.001 * v.speedRatio;
|
|
60
|
+
v.currentTime += timeAdvance;
|
|
61
|
+
v.animateTime += timeAdvance;
|
|
62
|
+
if (v.currentTime > k.timeDuration) {
|
|
63
|
+
v.repeatCounter++;
|
|
64
|
+
v.currentTime = 0;
|
|
65
|
+
} else if (v.currentTime < 0) {
|
|
66
|
+
v.repeatCounter++;
|
|
67
|
+
v.currentTime = k.timeDuration;
|
|
68
|
+
}
|
|
69
|
+
if (v.repeat !== 0 && v.repeatCounter >= v.repeat) {
|
|
70
|
+
this.stopAnimation(k.name);
|
|
71
|
+
} else if (v.fadeOut > 0) {
|
|
72
|
+
if (v.animateTime - v.fadeOutStart >= v.fadeOut) {
|
|
73
|
+
this.stopAnimation(k.name);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
// Update tracks
|
|
79
|
+
this._activeTracks.forEach((v, k)=>{
|
|
80
|
+
v.forEach((tracks)=>{
|
|
81
|
+
if (tracks.length > 0) {
|
|
82
|
+
const weights = tracks.map((track)=>{
|
|
83
|
+
const info = this._activeAnimations.get(track.animation);
|
|
84
|
+
const weight = info.weight;
|
|
85
|
+
const fadeIn = info.fadeIn === 0 ? 1 : Math.min(1, info.animateTime / info.fadeIn);
|
|
86
|
+
let fadeOut = 1;
|
|
87
|
+
if (info.fadeOut !== 0) {
|
|
88
|
+
fadeOut = 1 - (info.animateTime - info.fadeOutStart) / info.fadeOut;
|
|
89
|
+
}
|
|
90
|
+
return weight * fadeIn * fadeOut;
|
|
91
|
+
});
|
|
92
|
+
const states = tracks.map((track)=>track.calculateState(this._activeAnimations.get(track.animation).currentTime));
|
|
93
|
+
const state = weightedAverage(weights, states, (a, b, t)=>{
|
|
94
|
+
return tracks[0].mixState(a, b, t);
|
|
95
|
+
});
|
|
96
|
+
tracks[0].applyState(k, state);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
// Update skeletons
|
|
101
|
+
this._activeSkeletons.forEach((v, k)=>{
|
|
102
|
+
k.apply();
|
|
103
|
+
});
|
|
43
104
|
}
|
|
44
105
|
/**
|
|
45
106
|
* Checks whether an animation is playing
|
|
46
107
|
* @param name - Name of the animation to be checked
|
|
47
108
|
* @returns true if the animation is playing, otherwise false
|
|
48
109
|
*/ isPlayingAnimation(name) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
110
|
+
return name ? this._activeAnimations.has(this._animations[name]) : this._activeAnimations.size > 0;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Gets the weight of specific animation which is currently playing
|
|
114
|
+
* @param name - Name of the animation
|
|
115
|
+
* @returns Weight of the animation or 0 if this animation is not playing
|
|
116
|
+
*/ getAnimationWeight(name) {
|
|
117
|
+
const ani = this._animations[name];
|
|
118
|
+
const info = this._activeAnimations.get(ani);
|
|
119
|
+
return info?.weight ?? 0;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Sets the weight of specific animation which is currently playing
|
|
123
|
+
* @param name - Name of the animation
|
|
124
|
+
* @param weight - New weight value
|
|
125
|
+
*/ setAnimationWeight(name, weight) {
|
|
126
|
+
const ani = this._animations[name];
|
|
127
|
+
const info = this._activeAnimations.get(ani);
|
|
128
|
+
if (info) {
|
|
129
|
+
info.weight = weight;
|
|
58
130
|
}
|
|
59
131
|
}
|
|
60
132
|
/**
|
|
61
133
|
* Starts playing an animation of the model
|
|
62
134
|
* @param name - Name of the animation to play
|
|
63
|
-
* @param
|
|
64
|
-
|
|
65
|
-
*/ playAnimation(name, repeat = 0, speedRatio = 1) {
|
|
135
|
+
* @param options - Playing options
|
|
136
|
+
*/ playAnimation(name, options) {
|
|
66
137
|
const ani = this._animations[name];
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
138
|
+
if (!ani) {
|
|
139
|
+
console.error(`Animation ${name} not exists`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const fadeIn = Math.max(options?.fadeIn ?? 0, 0);
|
|
143
|
+
const info = this._activeAnimations.get(ani);
|
|
144
|
+
if (info) {
|
|
145
|
+
info.fadeOut = 0;
|
|
146
|
+
info.fadeIn = fadeIn;
|
|
147
|
+
} else {
|
|
148
|
+
const repeat = options?.repeat ?? 0;
|
|
149
|
+
const speedRatio = options?.speedRatio ?? 1;
|
|
150
|
+
const weight = options?.weight ?? 1;
|
|
151
|
+
this._activeAnimations.set(ani, {
|
|
152
|
+
repeat,
|
|
153
|
+
weight,
|
|
154
|
+
speedRatio,
|
|
155
|
+
fadeIn,
|
|
156
|
+
fadeOut: 0,
|
|
157
|
+
repeatCounter: 0,
|
|
158
|
+
currentTime: speedRatio < 0 ? ani.timeDuration : 0,
|
|
159
|
+
animateTime: 0,
|
|
160
|
+
fadeOutStart: 0,
|
|
161
|
+
firstFrame: true
|
|
162
|
+
});
|
|
163
|
+
ani.tracks?.forEach((v, k)=>{
|
|
164
|
+
let nodeTracks = this._activeTracks.get(k);
|
|
165
|
+
if (!nodeTracks) {
|
|
166
|
+
nodeTracks = new Map();
|
|
167
|
+
this._activeTracks.set(k, nodeTracks);
|
|
71
168
|
}
|
|
72
|
-
|
|
73
|
-
|
|
169
|
+
for (const track of v){
|
|
170
|
+
const blendId = track.getBlendId();
|
|
171
|
+
let blendedTracks = nodeTracks.get(blendId);
|
|
172
|
+
if (!blendedTracks) {
|
|
173
|
+
blendedTracks = [];
|
|
174
|
+
nodeTracks.set(blendId, blendedTracks);
|
|
175
|
+
}
|
|
176
|
+
blendedTracks.push(track);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
ani.skeletons?.forEach((v, k)=>{
|
|
180
|
+
const refcount = this._activeSkeletons.get(k);
|
|
181
|
+
if (refcount) {
|
|
182
|
+
this._activeSkeletons.set(k, refcount + 1);
|
|
183
|
+
} else {
|
|
184
|
+
this._activeSkeletons.set(k, 1);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
74
187
|
}
|
|
75
188
|
}
|
|
76
189
|
/**
|
|
77
190
|
* Stops playing an animation of the model
|
|
78
191
|
* @param name - Name of the animation to stop playing
|
|
79
|
-
*/ stopAnimation(name) {
|
|
80
|
-
this._animations[name]
|
|
192
|
+
*/ stopAnimation(name, options) {
|
|
193
|
+
const ani = this._animations[name];
|
|
194
|
+
const info = this._activeAnimations.get(ani);
|
|
195
|
+
if (info) {
|
|
196
|
+
const fadeOut = Math.max(options?.fadeOut ?? 0, 0);
|
|
197
|
+
if (fadeOut !== 0) {
|
|
198
|
+
info.fadeOut = fadeOut;
|
|
199
|
+
info.fadeOutStart = -1;
|
|
200
|
+
} else {
|
|
201
|
+
this._activeAnimations.delete(ani);
|
|
202
|
+
this._activeTracks.forEach((v, k)=>{
|
|
203
|
+
v.forEach((tracks, id)=>{
|
|
204
|
+
v.set(id, tracks.filter((track)=>track.animation !== ani));
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
ani.skeletons?.forEach((v, k)=>{
|
|
208
|
+
const refcount = this._activeSkeletons.get(k);
|
|
209
|
+
if (refcount === 1) {
|
|
210
|
+
k.reset(this._model);
|
|
211
|
+
this._activeSkeletons.delete(k);
|
|
212
|
+
} else {
|
|
213
|
+
this._activeSkeletons.set(k, refcount - 1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
81
218
|
}
|
|
82
219
|
dispose() {
|
|
83
220
|
const index = this._scene.animationSet.indexOf(this);
|
|
@@ -88,6 +225,9 @@
|
|
|
88
225
|
this._animations[k].dispose();
|
|
89
226
|
}
|
|
90
227
|
this._animations = {};
|
|
228
|
+
this._activeAnimations.clear();
|
|
229
|
+
this._activeSkeletons.clear();
|
|
230
|
+
this._activeTracks.clear();
|
|
91
231
|
}
|
|
92
232
|
}
|
|
93
233
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animationset.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"animationset.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -3,35 +3,23 @@
|
|
|
3
3
|
* @public
|
|
4
4
|
*/ class AnimationTrack {
|
|
5
5
|
/** @internal */ _interpolator;
|
|
6
|
-
/** @internal */
|
|
7
|
-
/** @internal */ _playing;
|
|
6
|
+
/** @internal */ _animation;
|
|
8
7
|
/**
|
|
9
8
|
* Creates a new animation track
|
|
10
9
|
* @param interpolator - Interpolator for the track
|
|
11
10
|
*/ constructor(interpolator){
|
|
12
|
-
this._currentPlayTime = 0;
|
|
13
|
-
this._playing = false;
|
|
14
11
|
this._interpolator = interpolator;
|
|
15
12
|
}
|
|
16
13
|
/** Gets the interpolator of the track */ get interpolator() {
|
|
17
14
|
return this._interpolator;
|
|
18
15
|
}
|
|
19
|
-
/**
|
|
20
|
-
return this.
|
|
16
|
+
/** Animation this track belongs to */ get animation() {
|
|
17
|
+
return this._animation;
|
|
21
18
|
}
|
|
22
|
-
|
|
23
|
-
this.
|
|
24
|
-
}
|
|
25
|
-
/** Stops playing the track */ stop() {
|
|
26
|
-
this._playing = false;
|
|
27
|
-
}
|
|
28
|
-
/** Rewinds the track to the first frame */ rewind() {
|
|
29
|
-
this._currentPlayTime = 0;
|
|
30
|
-
}
|
|
31
|
-
/** Stops playing the track and rewind to the first frame */ reset() {
|
|
32
|
-
this.stop();
|
|
33
|
-
this._currentPlayTime = 0;
|
|
19
|
+
set animation(ani) {
|
|
20
|
+
this._animation = ani;
|
|
34
21
|
}
|
|
22
|
+
/** Stops playing the track and rewind to the first frame */ reset(node) {}
|
|
35
23
|
}
|
|
36
24
|
|
|
37
25
|
export { AnimationTrack };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animationtrack.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"animationtrack.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Vector3,
|
|
1
|
+
import { Vector3, Interpolator, Quaternion } from '@zephyr3d/base';
|
|
2
2
|
import { AnimationTrack } from './animationtrack.js';
|
|
3
3
|
|
|
4
4
|
const tmpVec3 = new Vector3();
|
|
5
|
-
const tmpQuat = new Quaternion();
|
|
6
5
|
/**
|
|
7
6
|
* Euler angle rotation animation track
|
|
8
7
|
* @public
|
|
9
8
|
*/ class EulerRotationTrack extends AnimationTrack {
|
|
9
|
+
_state;
|
|
10
10
|
/**
|
|
11
11
|
* Create an instance of EulerRotationTrack from keyframe values
|
|
12
12
|
* @param mode - The interpolation mode of keyframes
|
|
@@ -21,11 +21,21 @@ const tmpQuat = new Quaternion();
|
|
|
21
21
|
}
|
|
22
22
|
const interpolator = new Interpolator(mode, 'vec3', inputs, outputs);
|
|
23
23
|
super(interpolator);
|
|
24
|
+
this._state = new Quaternion();
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
this._interpolator.interpolate(currentTime,
|
|
27
|
-
|
|
28
|
-
return
|
|
26
|
+
calculateState(currentTime) {
|
|
27
|
+
this._interpolator.interpolate(currentTime, tmpVec3);
|
|
28
|
+
this._state.fromEulerAngle(tmpVec3.x, tmpVec3.y, tmpVec3.z, 'ZYX');
|
|
29
|
+
return this._state;
|
|
30
|
+
}
|
|
31
|
+
applyState(node, state) {
|
|
32
|
+
node.rotation.set(state);
|
|
33
|
+
}
|
|
34
|
+
mixState(a, b, t) {
|
|
35
|
+
return Quaternion.slerp(a, b, t);
|
|
36
|
+
}
|
|
37
|
+
getBlendId() {
|
|
38
|
+
return 'node-rotation';
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eulerrotationtrack.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"eulerrotationtrack.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { PBStructTypeInfo, PBArrayTypeInfo, PBPrimitiveTypeInfo, PBPrimitiveType } from '@zephyr3d/device';
|
|
2
|
+
import { Application } from '../app.js';
|
|
3
|
+
import { MAX_MORPH_ATTRIBUTES, MAX_MORPH_TARGETS, MORPH_WEIGHTS_VECTOR_COUNT, MORPH_ATTRIBUTE_VECTOR_COUNT } from '../values.js';
|
|
4
|
+
import { ShaderHelper } from '../material/shader/helper.js';
|
|
5
|
+
import '../material/lambert.js';
|
|
6
|
+
import '../material/blinn.js';
|
|
7
|
+
import '../material/unlit.js';
|
|
8
|
+
import '../material/meshmaterial.js';
|
|
9
|
+
import '../material/grassmaterial.js';
|
|
10
|
+
import '../material/terrainmaterial.js';
|
|
11
|
+
import '../material/pbrmr.js';
|
|
12
|
+
import '../material/pbrsg.js';
|
|
13
|
+
import '@zephyr3d/base';
|
|
14
|
+
import '../utility/pmrem.js';
|
|
15
|
+
import '../utility/panorama.js';
|
|
16
|
+
import '../utility/shprojection.js';
|
|
17
|
+
import { BoundingBox } from '../utility/bounding_volume.js';
|
|
18
|
+
|
|
19
|
+
function processMorphData(subMesh, morphWeights) {
|
|
20
|
+
const numTargets = subMesh.numTargets;
|
|
21
|
+
if (numTargets === 0) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const attributes = Object.getOwnPropertyNames(subMesh.targets);
|
|
25
|
+
const numVertices = subMesh.primitive.getNumVertices();
|
|
26
|
+
const weightsAndOffsets = new Float32Array(4 + MAX_MORPH_TARGETS + MAX_MORPH_ATTRIBUTES);
|
|
27
|
+
for(let i = 0; i < numTargets; i++){
|
|
28
|
+
weightsAndOffsets[4 + i] = morphWeights?.[i] ?? 0;
|
|
29
|
+
}
|
|
30
|
+
const textureSize = Math.ceil(Math.sqrt(numVertices * attributes.length * numTargets));
|
|
31
|
+
if (textureSize > Application.instance.device.getDeviceCaps().textureCaps.maxTextureSize) {
|
|
32
|
+
// TODO: reduce morph attributes
|
|
33
|
+
throw new Error(`Morph target data too large`);
|
|
34
|
+
}
|
|
35
|
+
weightsAndOffsets[0] = textureSize;
|
|
36
|
+
weightsAndOffsets[1] = textureSize;
|
|
37
|
+
weightsAndOffsets[2] = numVertices;
|
|
38
|
+
weightsAndOffsets[3] = numTargets;
|
|
39
|
+
let offset = 0;
|
|
40
|
+
const textureData = new Float32Array(textureSize * textureSize * 4);
|
|
41
|
+
for(let attrib = 0; attrib < MAX_MORPH_ATTRIBUTES; attrib++){
|
|
42
|
+
const index = attributes.indexOf(String(attrib));
|
|
43
|
+
if (index < 0) {
|
|
44
|
+
weightsAndOffsets[4 + MAX_MORPH_TARGETS + attrib] = -1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
weightsAndOffsets[4 + MAX_MORPH_TARGETS + attrib] = offset >> 2;
|
|
48
|
+
const info = subMesh.targets[attrib];
|
|
49
|
+
if (info.data.length !== numTargets) {
|
|
50
|
+
console.error(`Invalid morph target data`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
for(let t = 0; t < numTargets; t++){
|
|
54
|
+
const data = info.data[t];
|
|
55
|
+
for(let i = 0; i < numVertices; i++){
|
|
56
|
+
for(let j = 0; j < 4; j++){
|
|
57
|
+
textureData[offset++] = j < info.numComponents ? data[i * info.numComponents + j] : 1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const morphTexture = Application.instance.device.createTexture2D('rgba32f', textureSize, textureSize, {
|
|
63
|
+
samplerOptions: {
|
|
64
|
+
minFilter: 'nearest',
|
|
65
|
+
magFilter: 'nearest',
|
|
66
|
+
mipFilter: 'none'
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
morphTexture.update(textureData, 0, 0, textureSize, textureSize);
|
|
70
|
+
const bufferType = new PBStructTypeInfo('dummy', 'std140', [
|
|
71
|
+
{
|
|
72
|
+
name: ShaderHelper.getMorphInfoUniformName(),
|
|
73
|
+
type: new PBArrayTypeInfo(new PBPrimitiveTypeInfo(PBPrimitiveType.F32VEC4), 1 + MORPH_WEIGHTS_VECTOR_COUNT + MORPH_ATTRIBUTE_VECTOR_COUNT)
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
const morphUniformBuffer = Application.instance.device.createStructuredBuffer(bufferType, {
|
|
77
|
+
usage: 'uniform'
|
|
78
|
+
}, weightsAndOffsets);
|
|
79
|
+
const morphBoundingBox = new BoundingBox();
|
|
80
|
+
calculateMorphBoundingBox(morphBoundingBox, subMesh.targetBox, weightsAndOffsets.subarray(4, 4 + MAX_MORPH_TARGETS), numTargets);
|
|
81
|
+
const meshAABB = subMesh.mesh.getBoundingVolume().toAABB();
|
|
82
|
+
morphBoundingBox.minPoint.addBy(meshAABB.minPoint);
|
|
83
|
+
morphBoundingBox.maxPoint.addBy(meshAABB.maxPoint);
|
|
84
|
+
subMesh.mesh.setMorphData(morphTexture);
|
|
85
|
+
subMesh.mesh.setMorphInfo(morphUniformBuffer);
|
|
86
|
+
subMesh.mesh.setAnimatedBoundingBox(morphBoundingBox);
|
|
87
|
+
}
|
|
88
|
+
/** @internal */ function calculateMorphBoundingBox(morphBoundingBox, keyframeBoundingBox, weights, numTargets) {
|
|
89
|
+
morphBoundingBox.minPoint.setXYZ(0, 0, 0);
|
|
90
|
+
morphBoundingBox.maxPoint.setXYZ(0, 0, 0);
|
|
91
|
+
for(let i = 0; i < numTargets; i++){
|
|
92
|
+
const weight = weights[i];
|
|
93
|
+
const keyframeBox = keyframeBoundingBox[i];
|
|
94
|
+
morphBoundingBox.minPoint.x += keyframeBox.minPoint.x * weight;
|
|
95
|
+
morphBoundingBox.minPoint.y += keyframeBox.minPoint.y * weight;
|
|
96
|
+
morphBoundingBox.minPoint.y += keyframeBox.minPoint.z * weight;
|
|
97
|
+
morphBoundingBox.maxPoint.x += keyframeBox.maxPoint.x * weight;
|
|
98
|
+
morphBoundingBox.maxPoint.y += keyframeBox.maxPoint.y * weight;
|
|
99
|
+
morphBoundingBox.maxPoint.y += keyframeBox.maxPoint.z * weight;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { calculateMorphBoundingBox, processMorphData };
|
|
104
|
+
//# sourceMappingURL=morphtarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"morphtarget.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|