Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
车家大少爷
three.js
提交
5e55ef65
T
three.js
项目概览
车家大少爷
/
three.js
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
three.js
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
5e55ef65
编写于
1月 25, 2019
作者:
M
Mr.doob
提交者:
GitHub
1月 25, 2019
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #15011 from donmccurdy/feat-gltfexporter-multimorphanimations
GLTFExporter: Support individual morph target animation.
上级
dc59d012
4b93acc9
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
431 addition
and
14 deletion
+431
-14
docs/api/en/animation/AnimationClip.html
docs/api/en/animation/AnimationClip.html
+5
-0
docs/api/en/animation/KeyframeTrack.html
docs/api/en/animation/KeyframeTrack.html
+5
-0
docs/api/zh/animation/AnimationClip.html
docs/api/zh/animation/AnimationClip.html
+3
-0
docs/api/zh/animation/AnimationUtils.html
docs/api/zh/animation/AnimationUtils.html
+6
-0
docs/api/zh/animation/KeyframeTrack.html
docs/api/zh/animation/KeyframeTrack.html
+3
-0
examples/js/exporters/GLTFExporter.js
examples/js/exporters/GLTFExporter.js
+200
-14
src/animation/AnimationClip.js
src/animation/AnimationClip.js
+15
-0
src/animation/KeyframeTrack.js
src/animation/KeyframeTrack.js
+15
-0
test/unit/example/exporters/GLTFExporter.tests.js
test/unit/example/exporters/GLTFExporter.tests.js
+179
-0
未找到文件。
docs/api/en/animation/AnimationClip.html
浏览文件 @
5e55ef65
...
...
@@ -66,6 +66,11 @@
<h2>
Methods
</h2>
<h3>
[method:AnimationClip clone]()
</h3>
<p>
Returns a copy of this clip.
</p>
<h3>
[method:this optimize]()
</h3>
<p>
Optimizes each track by removing equivalent sequential keys (which are common in morph target
...
...
docs/api/en/animation/KeyframeTrack.html
浏览文件 @
5e55ef65
...
...
@@ -160,6 +160,11 @@
<h2>
Methods
</h2>
<h3>
[method:KeyframeTrack clone]()
</h3>
<p>
Returns a copy of this track.
</p>
<h3>
[method:null createInterpolant]()
</h3>
<p>
Creates a [page:LinearInterpolant LinearInterpolant], [page:CubicInterpolant CubicInterpolant]
...
...
docs/api/zh/animation/AnimationClip.html
浏览文件 @
5e55ef65
...
...
@@ -62,6 +62,9 @@
<h2>
方法
</h2>
<h3>
[method:AnimationClip clone]()
</h3>
<p></p>
<h3>
[method:this optimize]()
</h3>
<p>
通过移除等效的顺序键(在变形目标序列中很常见)来优化每一个轨道
...
...
docs/api/zh/animation/AnimationUtils.html
浏览文件 @
5e55ef65
...
...
@@ -38,12 +38,18 @@
返回一个数组,时间和值可以根据此数组排序。
</p>
<h3>
[method:Number insertKeyframe]( [param:KeyframeTrack track], [param:Number time] )
</h3>
<p></p>
<h3>
[method:Boolean isTypedArray]( object )
</h3>
<p>
如果该对象是类型化数组,返回*true*
</p>
<h3>
[method:AnimationClip mergeMorphTargetTracks]( [param:AnimationClip clip], [param:Object3D root] )
</h3>
<p></p>
<h3>
[method:Array sortedArray]( values, stride, order )
</h3>
<p>
将[page:AnimationUtils.getKeyframeOrder getKeyframeOrder]方法返回的数组排序。
...
...
docs/api/zh/animation/KeyframeTrack.html
浏览文件 @
5e55ef65
...
...
@@ -137,6 +137,9 @@
<h2>
方法
</h2>
<h3>
[method:KeyframeTrack clone]()
</h3>
<p></p>
<h3>
[method:null createInterpolant]()
</h3>
<p>
根据传入构造器中的插值类型参数,创建线性插值([page:LinearInterpolant LinearInterpolant]),立方插值([page:CubicInterpolant CubicInterpolant])或离散插值
...
...
examples/js/exporters/GLTFExporter.js
浏览文件 @
5e55ef65
...
...
@@ -1227,9 +1227,9 @@ THREE.GLTFExporter.prototype = {
var
baseAttribute
=
geometry
.
attributes
[
attributeName
];
if
(
cachedData
.
attributes
.
has
(
baseA
ttribute
)
)
{
if
(
cachedData
.
attributes
.
has
(
a
ttribute
)
)
{
target
[
gltfAttributeName
]
=
cachedData
.
attributes
.
get
(
baseA
ttribute
);
target
[
gltfAttributeName
]
=
cachedData
.
attributes
.
get
(
a
ttribute
);
continue
;
}
...
...
@@ -1443,12 +1443,15 @@ THREE.GLTFExporter.prototype = {
}
clip
=
THREE
.
GLTFExporter
.
Utils
.
mergeMorphTargetTracks
(
clip
.
clone
(),
root
);
var
tracks
=
clip
.
tracks
;
var
channels
=
[];
var
samplers
=
[];
for
(
var
i
=
0
;
i
<
clip
.
tracks
.
length
;
++
i
)
{
for
(
var
i
=
0
;
i
<
tracks
.
length
;
++
i
)
{
var
track
=
clip
.
tracks
[
i
];
var
track
=
tracks
[
i
];
var
trackBinding
=
THREE
.
PropertyBinding
.
parseTrackName
(
track
.
name
);
var
trackNode
=
THREE
.
PropertyBinding
.
findNode
(
root
,
trackBinding
.
nodeName
);
var
trackProperty
=
PATH_PROPERTIES
[
trackBinding
.
propertyName
];
...
...
@@ -1479,16 +1482,6 @@ THREE.GLTFExporter.prototype = {
if
(
trackProperty
===
PATH_PROPERTIES
.
morphTargetInfluences
)
{
if
(
trackNode
.
morphTargetInfluences
.
length
!==
1
&&
trackBinding
.
propertyIndex
!==
undefined
)
{
console
.
warn
(
'
THREE.GLTFExporter: Skipping animation track "%s".
'
+
'
Morph target keyframe tracks must target all available morph targets
'
+
'
for the given mesh.
'
,
track
.
name
);
continue
;
}
outputItemSize
/=
trackNode
.
morphTargetInfluences
.
length
;
}
...
...
@@ -2006,3 +1999,196 @@ THREE.GLTFExporter.prototype = {
}
};
THREE
.
GLTFExporter
.
Utils
=
{
insertKeyframe
:
function
(
track
,
time
)
{
var
tolerance
=
0.001
;
// 1ms
var
valueSize
=
track
.
getValueSize
();
var
times
=
new
track
.
TimeBufferType
(
track
.
times
.
length
+
1
);
var
values
=
new
track
.
ValueBufferType
(
track
.
values
.
length
+
valueSize
);
var
interpolant
=
track
.
createInterpolant
(
new
track
.
ValueBufferType
(
valueSize
)
);
var
index
;
if
(
track
.
times
.
length
===
0
)
{
times
[
0
]
=
time
;
for
(
var
i
=
0
;
i
<
valueSize
;
i
++
)
{
values
[
i
]
=
0
;
}
index
=
0
;
}
else
if
(
time
<
track
.
times
[
0
]
)
{
if
(
Math
.
abs
(
track
.
times
[
0
]
-
time
)
<
tolerance
)
return
0
;
times
[
0
]
=
time
;
times
.
set
(
track
.
times
,
1
);
values
.
set
(
interpolant
.
evaluate
(
time
),
0
);
values
.
set
(
track
.
values
,
valueSize
);
index
=
0
;
}
else
if
(
time
>
track
.
times
[
track
.
times
.
length
-
1
]
)
{
if
(
Math
.
abs
(
track
.
times
[
track
.
times
.
length
-
1
]
-
time
)
<
tolerance
)
{
return
track
.
times
.
length
-
1
;
}
times
[
times
.
length
-
1
]
=
time
;
times
.
set
(
track
.
times
,
0
);
values
.
set
(
track
.
values
,
0
);
values
.
set
(
interpolant
.
evaluate
(
time
),
track
.
values
.
length
);
index
=
times
.
length
-
1
;
}
else
{
for
(
var
i
=
0
;
i
<
track
.
times
.
length
;
i
++
)
{
if
(
Math
.
abs
(
track
.
times
[
i
]
-
time
)
<
tolerance
)
return
i
;
if
(
track
.
times
[
i
]
<
time
&&
track
.
times
[
i
+
1
]
>
time
)
{
times
.
set
(
track
.
times
.
slice
(
0
,
i
+
1
),
0
);
times
[
i
+
1
]
=
time
;
times
.
set
(
track
.
times
.
slice
(
i
+
1
),
i
+
2
);
values
.
set
(
track
.
values
.
slice
(
0
,
(
i
+
1
)
*
valueSize
),
0
);
values
.
set
(
interpolant
.
evaluate
(
time
),
(
i
+
1
)
*
valueSize
);
values
.
set
(
track
.
values
.
slice
(
(
i
+
1
)
*
valueSize
),
(
i
+
2
)
*
valueSize
);
index
=
i
+
1
;
break
;
}
}
}
track
.
times
=
times
;
track
.
values
=
values
;
return
index
;
},
mergeMorphTargetTracks
:
function
(
clip
,
root
)
{
var
tracks
=
[];
var
mergedTracks
=
{};
var
sourceTracks
=
clip
.
tracks
;
for
(
var
i
=
0
;
i
<
sourceTracks
.
length
;
++
i
)
{
var
sourceTrack
=
sourceTracks
[
i
];
var
sourceTrackBinding
=
THREE
.
PropertyBinding
.
parseTrackName
(
sourceTrack
.
name
);
var
sourceTrackNode
=
THREE
.
PropertyBinding
.
findNode
(
root
,
sourceTrackBinding
.
nodeName
);
if
(
sourceTrackBinding
.
propertyName
!==
'
morphTargetInfluences
'
||
sourceTrackBinding
.
propertyIndex
===
undefined
)
{
// Tracks that don't affect morph targets, or that affect all morph targets together, can be left as-is.
tracks
.
push
(
sourceTrack
);
continue
;
}
if
(
sourceTrack
.
createInterpolant
!==
sourceTrack
.
InterpolantFactoryMethodDiscrete
&&
sourceTrack
.
createInterpolant
!==
sourceTrack
.
InterpolantFactoryMethodLinear
)
{
if
(
sourceTrack
.
createInterpolant
.
isInterpolantFactoryMethodGLTFCubicSpline
)
{
// This should never happen, because glTF morph target animations
// affect all targets already.
throw
new
Error
(
'
THREE.GLTFExporter: Cannot merge tracks with glTF CUBICSPLINE interpolation.
'
);
}
console
.
warn
(
'
THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.
'
);
sourceTrack
=
sourceTrack
.
clone
();
sourceTrack
.
setInterpolation
(
InterpolateLinear
);
}
var
targetCount
=
sourceTrackNode
.
morphTargetInfluences
.
length
;
var
targetIndex
=
sourceTrackNode
.
morphTargetDictionary
[
sourceTrackBinding
.
propertyIndex
];
if
(
targetIndex
===
undefined
)
{
throw
new
Error
(
'
THREE.GLTFExporter: Morph target name not found:
'
+
sourceTrackBinding
.
propertyIndex
);
}
var
mergedTrack
;
// If this is the first time we've seen this object, create a new
// track to store merged keyframe data for each morph target.
if
(
mergedTracks
[
sourceTrackNode
.
uuid
]
===
undefined
)
{
mergedTrack
=
sourceTrack
.
clone
();
var
values
=
new
mergedTrack
.
ValueBufferType
(
targetCount
*
mergedTrack
.
times
.
length
);
for
(
var
j
=
0
;
j
<
mergedTrack
.
times
.
length
;
j
++
)
{
values
[
j
*
targetCount
+
targetIndex
]
=
mergedTrack
.
values
[
j
];
}
mergedTrack
.
name
=
'
.morphTargetInfluences
'
;
mergedTrack
.
values
=
values
;
mergedTracks
[
sourceTrackNode
.
uuid
]
=
mergedTrack
;
tracks
.
push
(
mergedTrack
);
continue
;
}
var
mergedKeyframeIndex
=
0
;
var
sourceKeyframeIndex
=
0
;
var
sourceInterpolant
=
sourceTrack
.
createInterpolant
(
new
sourceTrack
.
ValueBufferType
(
1
)
);
mergedTrack
=
mergedTracks
[
sourceTrackNode
.
uuid
];
// For every existing keyframe of the merged track, write a (possibly
// interpolated) value from the source track.
for
(
var
j
=
0
;
j
<
mergedTrack
.
times
.
length
;
j
++
)
{
mergedTrack
.
values
[
j
*
targetCount
+
targetIndex
]
=
sourceInterpolant
.
evaluate
(
mergedTrack
.
times
[
j
]
);
}
// For every existing keyframe of the source track, write a (possibly
// new) keyframe to the merged track. Values from the previous loop may
// be written again, but keyframes are de-duplicated.
for
(
var
j
=
0
;
j
<
sourceTrack
.
times
.
length
;
j
++
)
{
var
keyframeIndex
=
this
.
insertKeyframe
(
mergedTrack
,
sourceTrack
.
times
[
j
]
);
mergedTrack
.
values
[
keyframeIndex
*
targetCount
+
targetIndex
]
=
sourceTrack
.
values
[
j
];
}
}
clip
.
tracks
=
tracks
;
return
clip
;
}
};
src/animation/AnimationClip.js
浏览文件 @
5e55ef65
...
...
@@ -445,6 +445,21 @@ Object.assign( AnimationClip.prototype, {
return
this
;
},
clone
:
function
()
{
var
tracks
=
[];
for
(
var
i
=
0
;
i
<
this
.
tracks
.
length
;
i
++
)
{
tracks
.
push
(
this
.
tracks
[
i
].
clone
()
);
}
return
new
AnimationClip
(
this
.
name
,
this
.
duration
,
tracks
);
}
}
);
...
...
src/animation/KeyframeTrack.js
浏览文件 @
5e55ef65
...
...
@@ -447,6 +447,21 @@ Object.assign( KeyframeTrack.prototype, {
return
this
;
},
clone
:
function
()
{
var
times
=
AnimationUtils
.
arraySlice
(
this
.
times
,
0
);
var
values
=
AnimationUtils
.
arraySlice
(
this
.
values
,
0
);
var
TypedKeyframeTrack
=
this
.
constructor
;
var
track
=
new
TypedKeyframeTrack
(
this
.
name
,
times
,
values
);
// Interpolant argument to constructor is not saved, so copy the factory method directly.
track
.
createInterpolant
=
this
.
createInterpolant
;
return
track
;
}
}
);
...
...
test/unit/example/exporters/GLTFExporter.tests.js
浏览文件 @
5e55ef65
...
...
@@ -5,6 +5,19 @@
import
*
as
GLTFExporter
from
'
../../../../examples/js/exporters/GLTFExporter
'
;
import
{
AnimationClip
}
from
'
../../../../src/animation/AnimationClip
'
;
import
{
BufferAttribute
}
from
'
../../../../src/core/BufferAttribute
'
;
import
{
BufferGeometry
}
from
'
../../../../src/core/BufferGeometry
'
;
import
{
Mesh
}
from
'
../../../../src/objects/Mesh
'
;
import
{
Object3D
}
from
'
../../../../src/core/Object3D
'
;
import
{
NumberKeyframeTrack
}
from
'
../../../../src/animation/tracks/NumberKeyframeTrack
'
;
import
{
VectorKeyframeTrack
}
from
'
../../../../src/animation/tracks/VectorKeyframeTrack
'
;
import
{
InterpolateLinear
,
InterpolateSmooth
,
InterpolateDiscrete
}
from
'
../../../../src/constants.js
'
;
export
default
QUnit
.
module
(
'
Exporters
'
,
()
=>
{
QUnit
.
module
(
'
GLTFExporter
'
,
()
=>
{
...
...
@@ -156,6 +169,172 @@ export default QUnit.module( 'Exporters', () => {
}
);
QUnit
.
test
(
'
parse - individual morph targets
'
,
(
assert
)
=>
{
var
done
=
assert
.
async
();
// Creates a geometry with four (4) morph targets, three (3) of which are
// animated by an animation clip. Because glTF requires all morph targets
// to be animated in unison, the exporter should write an empty track for
// the fourth target.
var
geometry
=
new
THREE
.
BufferGeometry
();
var
position
=
new
THREE
.
BufferAttribute
(
new
Float32Array
(
[
0
,
0
,
0
,
0
,
0
,
1
,
1
,
0
,
1
]
),
3
);
geometry
.
addAttribute
(
'
position
'
,
position
);
geometry
.
morphAttributes
.
position
=
[
position
,
position
,
position
,
position
];
var
mesh
=
new
THREE
.
Mesh
(
geometry
);
mesh
.
morphTargetDictionary
.
a
=
0
;
mesh
.
morphTargetDictionary
.
b
=
1
;
mesh
.
morphTargetDictionary
.
c
=
2
;
mesh
.
morphTargetDictionary
.
d
=
3
;
var
timesA
=
[
0
,
1
,
2
];
var
timesB
=
[
2
,
3
,
4
];
var
timesC
=
[
4
,
5
,
6
];
var
valuesA
=
[
0
,
1
,
0
];
var
valuesB
=
[
0
,
1
,
0
];
var
valuesC
=
[
0
,
1
,
0
];
var
trackA
=
new
THREE
.
VectorKeyframeTrack
(
'
.morphTargetInfluences[a]
'
,
timesA
,
valuesA
,
THREE
.
InterpolateLinear
);
var
trackB
=
new
THREE
.
VectorKeyframeTrack
(
'
.morphTargetInfluences[b]
'
,
timesB
,
valuesB
,
THREE
.
InterpolateLinear
);
var
trackC
=
new
THREE
.
VectorKeyframeTrack
(
'
.morphTargetInfluences[c]
'
,
timesC
,
valuesC
,
THREE
.
InterpolateLinear
);
var
clip
=
new
THREE
.
AnimationClip
(
'
clip1
'
,
undefined
,
[
trackA
,
trackB
,
trackC
]
);
var
exporter
=
new
THREE
.
GLTFExporter
();
exporter
.
parse
(
mesh
,
function
(
gltf
)
{
assert
.
equal
(
1
,
gltf
.
animations
.
length
,
'
one animation
'
);
assert
.
equal
(
1
,
gltf
.
animations
[
0
].
channels
.
length
,
'
one channel
'
);
assert
.
equal
(
1
,
gltf
.
animations
[
0
].
samplers
.
length
,
'
one sampler
'
);
var
channel
=
gltf
.
animations
[
0
].
channels
[
0
];
var
sampler
=
gltf
.
animations
[
0
].
samplers
[
0
];
assert
.
smartEqual
(
channel
,
{
sampler
:
0
,
target
:
{
node
:
0
,
path
:
'
weights
'
}
}
);
assert
.
equal
(
sampler
.
interpolation
,
'
LINEAR
'
);
var
input
=
gltf
.
accessors
[
sampler
.
input
];
var
output
=
gltf
.
accessors
[
sampler
.
output
];
assert
.
equal
(
input
.
count
,
7
);
assert
.
equal
(
input
.
type
,
'
SCALAR
'
);
assert
.
smartEqual
(
input
.
min
,
[
0
]
);
assert
.
smartEqual
(
input
.
max
,
[
6
]
);
assert
.
equal
(
output
.
count
,
28
);
// 4 targets * 7 frames
assert
.
equal
(
output
.
type
,
'
SCALAR
'
);
assert
.
smartEqual
(
output
.
min
,
[
0
]
);
assert
.
smartEqual
(
output
.
max
,
[
1
]
);
done
();
},
{
animations
:
[
clip
]
}
);
}
);
QUnit
.
test
(
'
utils - insertKeyframe
'
,
(
assert
)
=>
{
var
track
;
var
index
;
function
createTrack
()
{
return
new
VectorKeyframeTrack
(
'
foo.bar
'
,
[
5
,
10
,
15
,
20
,
25
,
30
],
[
0
,
5
,
1
,
4
,
2
,
3
,
3
,
2
,
4
,
1
,
5
,
0
],
InterpolateLinear
);
}
track
=
createTrack
();
index
=
THREE
.
GLTFExporter
.
Utils
.
insertKeyframe
(
track
,
0
);
assert
.
equal
(
index
,
0
,
'
prepend - index
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
times
),
[
0
,
5
,
10
,
15
,
20
,
25
,
30
],
'
prepend - time
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
values
),
[
0
,
5
,
0
,
5
,
1
,
4
,
2
,
3
,
3
,
2
,
4
,
1
,
5
,
0
],
'
prepend - value
'
);
track
=
createTrack
();
index
=
THREE
.
GLTFExporter
.
Utils
.
insertKeyframe
(
track
,
7.5
);
assert
.
equal
(
index
,
1
,
'
insert - index (linear)
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
times
),
[
5
,
7.5
,
10
,
15
,
20
,
25
,
30
],
'
insert - time (linear)
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
values
),
[
0
,
5
,
0.5
,
4.5
,
1
,
4
,
2
,
3
,
3
,
2
,
4
,
1
,
5
,
0
],
'
insert - value (linear)
'
);
track
=
createTrack
();
track
.
setInterpolation
(
InterpolateDiscrete
);
index
=
THREE
.
GLTFExporter
.
Utils
.
insertKeyframe
(
track
,
16
);
assert
.
equal
(
index
,
3
,
'
insert - index (linear)
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
times
),
[
5
,
10
,
15
,
16
,
20
,
25
,
30
],
'
insert - time (discrete)
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
values
),
[
0
,
5
,
1
,
4
,
2
,
3
,
2
,
3
,
3
,
2
,
4
,
1
,
5
,
0
],
'
insert - value (discrete)
'
);
track
=
createTrack
();
index
=
THREE
.
GLTFExporter
.
Utils
.
insertKeyframe
(
track
,
100
);
assert
.
equal
(
index
,
6
,
'
append - index
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
times
),
[
5
,
10
,
15
,
20
,
25
,
30
,
100
],
'
append time
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
values
),
[
0
,
5
,
1
,
4
,
2
,
3
,
3
,
2
,
4
,
1
,
5
,
0
,
5
,
0
],
'
append value
'
);
track
=
createTrack
();
index
=
THREE
.
GLTFExporter
.
Utils
.
insertKeyframe
(
track
,
15
);
assert
.
equal
(
index
,
2
,
'
existing - index
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
times
),
[
5
,
10
,
15
,
20
,
25
,
30
],
'
existing - time
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
values
),
[
0
,
5
,
1
,
4
,
2
,
3
,
3
,
2
,
4
,
1
,
5
,
0
],
'
existing - value
'
);
track
=
createTrack
();
index
=
THREE
.
GLTFExporter
.
Utils
.
insertKeyframe
(
track
,
20.000005
);
assert
.
equal
(
index
,
3
,
'
tolerance - index
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
times
),
[
5
,
10
,
15
,
20
,
25
,
30
],
'
tolerance - time
'
);
assert
.
smartEqual
(
Array
.
from
(
track
.
values
),
[
0
,
5
,
1
,
4
,
2
,
3
,
3
,
2
,
4
,
1
,
5
,
0
],
'
tolerance - value
'
);
}
);
QUnit
.
test
(
'
utils - mergeMorphTargetTracks
'
,
(
assert
)
=>
{
var
trackA
=
new
NumberKeyframeTrack
(
'
foo.morphTargetInfluences[a]
'
,
[
5
,
10
,
15
,
20
,
25
,
30
],
[
0
,
0.2
,
0.4
,
0.6
,
0.8
,
1.0
],
InterpolateLinear
);
var
trackB
=
new
NumberKeyframeTrack
(
'
foo.morphTargetInfluences[b]
'
,
[
10
,
50
],
[
0.25
,
0.75
],
InterpolateLinear
);
var
geometry
=
new
BufferGeometry
();
var
position
=
new
BufferAttribute
(
new
Float32Array
(
[
0
,
0
,
0
,
0
,
0
,
1
,
1
,
0
,
1
]
),
3
);
geometry
.
addAttribute
(
'
position
'
,
position
);
geometry
.
morphAttributes
.
position
=
[
position
,
position
];
var
mesh
=
new
Mesh
(
geometry
);
mesh
.
name
=
'
foo
'
;
mesh
.
morphTargetDictionary
.
a
=
0
;
mesh
.
morphTargetDictionary
.
b
=
1
;
var
root
=
new
Object3D
();
root
.
add
(
mesh
);
var
clip
=
new
AnimationClip
(
'
waltz
'
,
undefined
,
[
trackA
,
trackB
]
);
clip
=
THREE
.
GLTFExporter
.
Utils
.
mergeMorphTargetTracks
(
clip
,
root
);
assert
.
equal
(
clip
.
tracks
.
length
,
1
,
'
tracks are merged
'
);
var
track
=
clip
.
tracks
[
0
];
assert
.
smartEqual
(
Array
.
from
(
track
.
times
),
[
5
,
10
,
15
,
20
,
25
,
30
,
50
],
'
all keyframes are present
'
);
var
expectedValues
=
[
0
,
0.25
,
0.2
,
0.25
,
0.4
,
0.3125
,
0.6
,
0.375
,
0.8
,
0.4375
,
1.0
,
0.5
,
1.0
,
0.75
];
for
(
var
i
=
0
;
i
<
track
.
values
.
length
;
i
++
)
{
assert
.
numEqual
(
track
.
values
[
i
],
expectedValues
[
i
],
'
all values are merged or interpolated -
'
+
i
);
}
}
);
}
);
}
);
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录