@@ -1227,9 +1227,9 @@ THREE.GLTFExporter.prototype = {
12271227
12281228 var baseAttribute = geometry . attributes [ attributeName ] ;
12291229
1230- if ( cachedData . attributes . has ( baseAttribute ) ) {
1230+ if ( cachedData . attributes . has ( attribute ) ) {
12311231
1232- target [ gltfAttributeName ] = cachedData . attributes . get ( baseAttribute ) ;
1232+ target [ gltfAttributeName ] = cachedData . attributes . get ( attribute ) ;
12331233 continue ;
12341234
12351235 }
@@ -1443,12 +1443,15 @@ THREE.GLTFExporter.prototype = {
14431443
14441444 }
14451445
1446+ clip = THREE . GLTFExporter . Utils . mergeMorphTargetTracks ( clip . clone ( ) , root ) ;
1447+
1448+ var tracks = clip . tracks ;
14461449 var channels = [ ] ;
14471450 var samplers = [ ] ;
14481451
1449- for ( var i = 0 ; i < clip . tracks . length ; ++ i ) {
1452+ for ( var i = 0 ; i < tracks . length ; ++ i ) {
14501453
1451- var track = clip . tracks [ i ] ;
1454+ var track = tracks [ i ] ;
14521455 var trackBinding = THREE . PropertyBinding . parseTrackName ( track . name ) ;
14531456 var trackNode = THREE . PropertyBinding . findNode ( root , trackBinding . nodeName ) ;
14541457 var trackProperty = PATH_PROPERTIES [ trackBinding . propertyName ] ;
@@ -1479,16 +1482,6 @@ THREE.GLTFExporter.prototype = {
14791482
14801483 if ( trackProperty === PATH_PROPERTIES . morphTargetInfluences ) {
14811484
1482- if ( trackNode . morphTargetInfluences . length !== 1 &&
1483- trackBinding . propertyIndex !== undefined ) {
1484-
1485- console . warn ( 'THREE.GLTFExporter: Skipping animation track "%s". ' +
1486- 'Morph target keyframe tracks must target all available morph targets ' +
1487- 'for the given mesh.' , track . name ) ;
1488- continue ;
1489-
1490- }
1491-
14921485 outputItemSize /= trackNode . morphTargetInfluences . length ;
14931486
14941487 }
@@ -2006,3 +1999,196 @@ THREE.GLTFExporter.prototype = {
20061999 }
20072000
20082001} ;
2002+
2003+ THREE . GLTFExporter . Utils = {
2004+
2005+ insertKeyframe : function ( track , time ) {
2006+
2007+ var tolerance = 0.001 ; // 1ms
2008+ var valueSize = track . getValueSize ( ) ;
2009+
2010+ var times = new track . TimeBufferType ( track . times . length + 1 ) ;
2011+ var values = new track . ValueBufferType ( track . values . length + valueSize ) ;
2012+ var interpolant = track . createInterpolant ( new track . ValueBufferType ( valueSize ) ) ;
2013+
2014+ var index ;
2015+
2016+ if ( track . times . length === 0 ) {
2017+
2018+ times [ 0 ] = time ;
2019+
2020+ for ( var i = 0 ; i < valueSize ; i ++ ) {
2021+
2022+ values [ i ] = 0 ;
2023+
2024+ }
2025+
2026+ index = 0 ;
2027+
2028+ } else if ( time < track . times [ 0 ] ) {
2029+
2030+ if ( Math . abs ( track . times [ 0 ] - time ) < tolerance ) return 0 ;
2031+
2032+ times [ 0 ] = time ;
2033+ times . set ( track . times , 1 ) ;
2034+
2035+ values . set ( interpolant . evaluate ( time ) , 0 ) ;
2036+ values . set ( track . values , valueSize ) ;
2037+
2038+ index = 0 ;
2039+
2040+ } else if ( time > track . times [ track . times . length - 1 ] ) {
2041+
2042+ if ( Math . abs ( track . times [ track . times . length - 1 ] - time ) < tolerance ) {
2043+
2044+ return track . times . length - 1 ;
2045+
2046+ }
2047+
2048+ times [ times . length - 1 ] = time ;
2049+ times . set ( track . times , 0 ) ;
2050+
2051+ values . set ( track . values , 0 ) ;
2052+ values . set ( interpolant . evaluate ( time ) , track . values . length ) ;
2053+
2054+ index = times . length - 1 ;
2055+
2056+ } else {
2057+
2058+ for ( var i = 0 ; i < track . times . length ; i ++ ) {
2059+
2060+ if ( Math . abs ( track . times [ i ] - time ) < tolerance ) return i ;
2061+
2062+ if ( track . times [ i ] < time && track . times [ i + 1 ] > time ) {
2063+
2064+ times . set ( track . times . slice ( 0 , i + 1 ) , 0 ) ;
2065+ times [ i + 1 ] = time ;
2066+ times . set ( track . times . slice ( i + 1 ) , i + 2 ) ;
2067+
2068+ values . set ( track . values . slice ( 0 , ( i + 1 ) * valueSize ) , 0 ) ;
2069+ values . set ( interpolant . evaluate ( time ) , ( i + 1 ) * valueSize ) ;
2070+ values . set ( track . values . slice ( ( i + 1 ) * valueSize ) , ( i + 2 ) * valueSize ) ;
2071+
2072+ index = i + 1 ;
2073+
2074+ break ;
2075+
2076+ }
2077+
2078+ }
2079+
2080+ }
2081+
2082+ track . times = times ;
2083+ track . values = values ;
2084+
2085+ return index ;
2086+
2087+ } ,
2088+
2089+ mergeMorphTargetTracks : function ( clip , root ) {
2090+
2091+ var tracks = [ ] ;
2092+ var mergedTracks = { } ;
2093+ var sourceTracks = clip . tracks ;
2094+
2095+ for ( var i = 0 ; i < sourceTracks . length ; ++ i ) {
2096+
2097+ var sourceTrack = sourceTracks [ i ] ;
2098+ var sourceTrackBinding = THREE . PropertyBinding . parseTrackName ( sourceTrack . name ) ;
2099+ var sourceTrackNode = THREE . PropertyBinding . findNode ( root , sourceTrackBinding . nodeName ) ;
2100+
2101+ if ( sourceTrackBinding . propertyName !== 'morphTargetInfluences' || sourceTrackBinding . propertyIndex === undefined ) {
2102+
2103+ // Tracks that don't affect morph targets, or that affect all morph targets together, can be left as-is.
2104+ tracks . push ( sourceTrack ) ;
2105+ continue ;
2106+
2107+ }
2108+
2109+ if ( sourceTrack . createInterpolant !== sourceTrack . InterpolantFactoryMethodDiscrete
2110+ && sourceTrack . createInterpolant !== sourceTrack . InterpolantFactoryMethodLinear ) {
2111+
2112+ if ( sourceTrack . createInterpolant . isInterpolantFactoryMethodGLTFCubicSpline ) {
2113+
2114+ // This should never happen, because glTF morph target animations
2115+ // affect all targets already.
2116+ throw new Error ( 'THREE.GLTFExporter: Cannot merge tracks with glTF CUBICSPLINE interpolation.' ) ;
2117+
2118+ }
2119+
2120+ console . warn ( 'THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.' ) ;
2121+
2122+ sourceTrack = sourceTrack . clone ( ) ;
2123+ sourceTrack . setInterpolation ( InterpolateLinear ) ;
2124+
2125+ }
2126+
2127+ var targetCount = sourceTrackNode . morphTargetInfluences . length ;
2128+ var targetIndex = sourceTrackNode . morphTargetDictionary [ sourceTrackBinding . propertyIndex ] ;
2129+
2130+ if ( targetIndex === undefined ) {
2131+
2132+ throw new Error ( 'THREE.GLTFExporter: Morph target name not found: ' + sourceTrackBinding . propertyIndex ) ;
2133+
2134+ }
2135+
2136+ var mergedTrack ;
2137+
2138+ // If this is the first time we've seen this object, create a new
2139+ // track to store merged keyframe data for each morph target.
2140+ if ( mergedTracks [ sourceTrackNode . uuid ] === undefined ) {
2141+
2142+ mergedTrack = sourceTrack . clone ( ) ;
2143+
2144+ var values = new mergedTrack . ValueBufferType ( targetCount * mergedTrack . times . length ) ;
2145+
2146+ for ( var j = 0 ; j < mergedTrack . times . length ; j ++ ) {
2147+
2148+ values [ j * targetCount + targetIndex ] = mergedTrack . values [ j ] ;
2149+
2150+ }
2151+
2152+ mergedTrack . name = '.morphTargetInfluences' ;
2153+ mergedTrack . values = values ;
2154+
2155+ mergedTracks [ sourceTrackNode . uuid ] = mergedTrack ;
2156+ tracks . push ( mergedTrack ) ;
2157+
2158+ continue ;
2159+
2160+ }
2161+
2162+ var mergedKeyframeIndex = 0 ;
2163+ var sourceKeyframeIndex = 0 ;
2164+ var sourceInterpolant = sourceTrack . createInterpolant ( new sourceTrack . ValueBufferType ( 1 ) ) ;
2165+
2166+ mergedTrack = mergedTracks [ sourceTrackNode . uuid ] ;
2167+
2168+ // For every existing keyframe of the merged track, write a (possibly
2169+ // interpolated) value from the source track.
2170+ for ( var j = 0 ; j < mergedTrack . times . length ; j ++ ) {
2171+
2172+ mergedTrack . values [ j * targetCount + targetIndex ] = sourceInterpolant . evaluate ( mergedTrack . times [ j ] ) ;
2173+
2174+ }
2175+
2176+ // For every existing keyframe of the source track, write a (possibly
2177+ // new) keyframe to the merged track. Values from the previous loop may
2178+ // be written again, but keyframes are de-duplicated.
2179+ for ( var j = 0 ; j < sourceTrack . times . length ; j ++ ) {
2180+
2181+ var keyframeIndex = this . insertKeyframe ( mergedTrack , sourceTrack . times [ j ] ) ;
2182+ mergedTrack . values [ keyframeIndex * targetCount + targetIndex ] = sourceTrack . values [ j ] ;
2183+
2184+ }
2185+
2186+ }
2187+
2188+ clip . tracks = tracks ;
2189+
2190+ return clip ;
2191+
2192+ }
2193+
2194+ } ;
0 commit comments