private void ReadCurveData(ImportedKeyframedAnimation iAnim, AnimationClipBindingConstant m_ClipBindingConstant, int index, float time, float[] data, int offset, ref int curveIndex) { var binding = m_ClipBindingConstant.FindBinding(index); if (binding.path == 0) { curveIndex++; return; } var path = FixBonePath(GetPathFromHash(binding.path)); var track = iAnim.FindTrack(path); switch (binding.attribute) { case 1: track.Translations.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( -data[curveIndex++ + offset], data[curveIndex++ + offset], data[curveIndex++ + offset] ))); break; case 2: var value = Fbx.QuaternionToEuler(new Quaternion ( data[curveIndex++ + offset], -data[curveIndex++ + offset], -data[curveIndex++ + offset], data[curveIndex++ + offset] )); track.Rotations.Add(new ImportedKeyframe <Vector3>(time, value)); break; case 3: track.Scalings.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( data[curveIndex++ + offset], data[curveIndex++ + offset], data[curveIndex++ + offset] ))); break; case 4: track.Rotations.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( data[curveIndex++ + offset], -data[curveIndex++ + offset], -data[curveIndex++ + offset] ))); break; default: //track.Curve.Add(new ImportedKeyframe<float>(time, data[curveIndex++])); curveIndex++; break; } }
private void ConvertAnimations() { foreach (var assetPreloadData in animationClipHashSet) { var animationClip = new AnimationClip(assetPreloadData); var iAnim = new ImportedKeyframedAnimation(); AnimationList.Add(iAnim); iAnim.Name = animationClip.m_Name; iAnim.TrackList = new List <ImportedAnimationKeyframedTrack>(); if (animationClip.m_Legacy) { foreach (var m_CompressedRotationCurve in animationClip.m_CompressedRotationCurves) { var path = m_CompressedRotationCurve.m_Path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); var numKeys = m_CompressedRotationCurve.m_Times.m_NumItems; var data = m_CompressedRotationCurve.m_Times.UnpackInts(); var times = new float[numKeys]; int t = 0; for (int i = 0; i < numKeys; i++) { t += data[i]; times[i] = t * 0.01f; } var quats = m_CompressedRotationCurve.m_Values.UnpackQuats(); for (int i = 0; i < numKeys; i++) { var quat = quats[i]; var value = Fbx.QuaternionToEuler(new Quaternion(quat.X, -quat.Y, -quat.Z, quat.W)); track.Rotations.Add(new ImportedKeyframe <Vector3>(times[i], value)); } } foreach (var m_RotationCurve in animationClip.m_RotationCurves) { var path = m_RotationCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); foreach (var m_Curve in m_RotationCurve.curve.m_Curve) { var value = Fbx.QuaternionToEuler(new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W)); track.Rotations.Add(new ImportedKeyframe <Vector3>(m_Curve.time, value)); } } foreach (var m_PositionCurve in animationClip.m_PositionCurves) { var path = m_PositionCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); foreach (var m_Curve in m_PositionCurve.curve.m_Curve) { track.Translations.Add(new ImportedKeyframe <Vector3>(m_Curve.time, new Vector3(-m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z))); } } foreach (var m_ScaleCurve in animationClip.m_ScaleCurves) { var path = m_ScaleCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); foreach (var m_Curve in m_ScaleCurve.curve.m_Curve) { track.Scalings.Add(new ImportedKeyframe <Vector3>(m_Curve.time, new Vector3(m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z))); } } if (animationClip.m_EulerCurves != null) { foreach (var m_EulerCurve in animationClip.m_EulerCurves) { var path = m_EulerCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); foreach (var m_Curve in m_EulerCurve.curve.m_Curve) { track.Rotations.Add(new ImportedKeyframe <Vector3>(m_Curve.time, new Vector3(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z))); } } } foreach (var m_FloatCurve in animationClip.m_FloatCurves) { var path = m_FloatCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); foreach (var m_Curve in m_FloatCurve.curve.m_Curve) { track.Curve.Add(new ImportedKeyframe <float>(m_Curve.time, m_Curve.value)); } } } else { var m_Clip = animationClip.m_MuscleClip.m_Clip; var streamedFrames = m_Clip.m_StreamedClip.ReadData(); var m_ClipBindingConstant = animationClip.m_ClipBindingConstant; for (int frameIndex = 1; frameIndex < streamedFrames.Count - 1; frameIndex++) { var frame = streamedFrames[frameIndex]; var streamedValues = frame.keyList.Select(x => x.value).ToArray(); for (int curveIndex = 0; curveIndex < frame.keyList.Count;) { ReadCurveData(iAnim, m_ClipBindingConstant, frame.keyList[curveIndex].index, frame.time, streamedValues, 0, ref curveIndex); } } var m_DenseClip = m_Clip.m_DenseClip; var streamCount = m_Clip.m_StreamedClip.curveCount; for (int frameIndex = 0; frameIndex < m_DenseClip.m_FrameCount; frameIndex++) { var time = frameIndex / m_DenseClip.m_SampleRate; var frameOffset = frameIndex * m_DenseClip.m_CurveCount; for (int curveIndex = 0; curveIndex < m_DenseClip.m_CurveCount;) { var index = streamCount + curveIndex; ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time, m_DenseClip.m_SampleArray, (int)frameOffset, ref curveIndex); } } var m_ConstantClip = m_Clip.m_ConstantClip; var denseCount = m_Clip.m_DenseClip.m_CurveCount; var time2 = 0.0f; for (int i = 0; i < 2; i++) { for (int curveIndex = 0; curveIndex < m_ConstantClip.data.Length;) { var index = streamCount + denseCount + curveIndex; ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time2, m_ConstantClip.data, 0, ref curveIndex); } time2 = animationClip.m_MuscleClip.m_StopTime; } } if ((bool)Properties.Settings.Default["FixRotation"]) { foreach (var track in iAnim.TrackList) { var prevKey = new Vector3(); foreach (var rotation in track.Rotations) { var value = rotation.value; ReplaceOutOfBound(ref prevKey, ref value); prevKey = value; rotation.value = value; } } } } }
private void ConvertAnimations() { foreach (var animationClip in animationClipHashSet) { var iAnim = new ImportedKeyframedAnimation(); var name = animationClip.m_Name; if (AnimationList.Exists(x => x.Name == name)) { for (int i = 1; ; i++) { var fixName = name + $"_{i}"; if (!AnimationList.Exists(x => x.Name == fixName)) { name = fixName; break; } } } iAnim.Name = name; iAnim.SampleRate = animationClip.m_SampleRate; iAnim.TrackList = new List <ImportedAnimationKeyframedTrack>(); AnimationList.Add(iAnim); if (animationClip.m_Legacy) { foreach (var m_CompressedRotationCurve in animationClip.m_CompressedRotationCurves) { var track = iAnim.FindTrack(FixBonePath(m_CompressedRotationCurve.m_Path)); var numKeys = m_CompressedRotationCurve.m_Times.m_NumItems; var data = m_CompressedRotationCurve.m_Times.UnpackInts(); var times = new float[numKeys]; int t = 0; for (int i = 0; i < numKeys; i++) { t += data[i]; times[i] = t * 0.01f; } var quats = m_CompressedRotationCurve.m_Values.UnpackQuats(); for (int i = 0; i < numKeys; i++) { var quat = quats[i]; var value = Fbx.QuaternionToEuler(new Quaternion(quat.X, -quat.Y, -quat.Z, quat.W)); track.Rotations.Add(new ImportedKeyframe <Vector3>(times[i], value)); } } foreach (var m_RotationCurve in animationClip.m_RotationCurves) { var track = iAnim.FindTrack(FixBonePath(m_RotationCurve.path)); foreach (var m_Curve in m_RotationCurve.curve.m_Curve) { var value = Fbx.QuaternionToEuler(new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W)); track.Rotations.Add(new ImportedKeyframe <Vector3>(m_Curve.time, value)); } } foreach (var m_PositionCurve in animationClip.m_PositionCurves) { var track = iAnim.FindTrack(FixBonePath(m_PositionCurve.path)); foreach (var m_Curve in m_PositionCurve.curve.m_Curve) { track.Translations.Add(new ImportedKeyframe <Vector3>(m_Curve.time, new Vector3(-m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z))); } } foreach (var m_ScaleCurve in animationClip.m_ScaleCurves) { var track = iAnim.FindTrack(FixBonePath(m_ScaleCurve.path)); foreach (var m_Curve in m_ScaleCurve.curve.m_Curve) { track.Scalings.Add(new ImportedKeyframe <Vector3>(m_Curve.time, new Vector3(m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z))); } } if (animationClip.m_EulerCurves != null) { foreach (var m_EulerCurve in animationClip.m_EulerCurves) { var track = iAnim.FindTrack(FixBonePath(m_EulerCurve.path)); foreach (var m_Curve in m_EulerCurve.curve.m_Curve) { track.Rotations.Add(new ImportedKeyframe <Vector3>(m_Curve.time, new Vector3(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z))); } } } } else { var m_Clip = animationClip.m_MuscleClip.m_Clip; var streamedFrames = m_Clip.m_StreamedClip.ReadData(); var m_ClipBindingConstant = animationClip.m_ClipBindingConstant; for (int frameIndex = 1; frameIndex < streamedFrames.Count - 1; frameIndex++) { var frame = streamedFrames[frameIndex]; var streamedValues = frame.keyList.Select(x => x.value).ToArray(); for (int curveIndex = 0; curveIndex < frame.keyList.Length;) { ReadCurveData(iAnim, m_ClipBindingConstant, frame.keyList[curveIndex].index, frame.time, streamedValues, 0, ref curveIndex); } } var m_DenseClip = m_Clip.m_DenseClip; var streamCount = m_Clip.m_StreamedClip.curveCount; for (int frameIndex = 0; frameIndex < m_DenseClip.m_FrameCount; frameIndex++) { var time = m_DenseClip.m_BeginTime + frameIndex / m_DenseClip.m_SampleRate; var frameOffset = frameIndex * m_DenseClip.m_CurveCount; for (int curveIndex = 0; curveIndex < m_DenseClip.m_CurveCount;) { var index = streamCount + curveIndex; ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time, m_DenseClip.m_SampleArray, (int)frameOffset, ref curveIndex); } } if (m_Clip.m_ConstantClip != null) { var m_ConstantClip = m_Clip.m_ConstantClip; var denseCount = m_Clip.m_DenseClip.m_CurveCount; var time2 = 0.0f; for (int i = 0; i < 2; i++) { for (int curveIndex = 0; curveIndex < m_ConstantClip.data.Length;) { var index = streamCount + denseCount + curveIndex; ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time2, m_ConstantClip.data, 0, ref curveIndex); } time2 = animationClip.m_MuscleClip.m_StopTime; } } } } }
private void ReadCurveData(ImportedKeyframedAnimation iAnim, AnimationClipBindingConstant m_ClipBindingConstant, int index, float time, float[] data, int offset, ref int curveIndex) { var binding = m_ClipBindingConstant.FindBinding(index); if (binding.typeID == ClassIDType.SkinnedMeshRenderer) //BlendShape { var channelName = GetChannelNameFromHash(binding.attribute); if (string.IsNullOrEmpty(channelName)) { curveIndex++; return; } int dotPos = channelName.IndexOf('.'); if (dotPos >= 0) { channelName = channelName.Substring(dotPos + 1); } var bPath = FixBonePath(GetPathFromHash(binding.path)); if (string.IsNullOrEmpty(bPath)) { bPath = GetPathByChannelName(channelName); } var bTrack = iAnim.FindTrack(bPath); bTrack.BlendShape = new ImportedBlendShape(); bTrack.BlendShape.ChannelName = channelName; bTrack.BlendShape.Keyframes.Add(new ImportedKeyframe <float>(time, data[curveIndex++ + offset])); } else if (binding.typeID == ClassIDType.Transform) { var path = FixBonePath(GetPathFromHash(binding.path)); var track = iAnim.FindTrack(path); switch (binding.attribute) { case 1: track.Translations.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( -data[curveIndex++ + offset], data[curveIndex++ + offset], data[curveIndex++ + offset] ))); break; case 2: var value = Fbx.QuaternionToEuler(new Quaternion ( data[curveIndex++ + offset], -data[curveIndex++ + offset], -data[curveIndex++ + offset], data[curveIndex++ + offset] )); track.Rotations.Add(new ImportedKeyframe <Vector3>(time, value)); break; case 3: track.Scalings.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( data[curveIndex++ + offset], data[curveIndex++ + offset], data[curveIndex++ + offset] ))); break; case 4: track.Rotations.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( data[curveIndex++ + offset], -data[curveIndex++ + offset], -data[curveIndex++ + offset] ))); break; default: curveIndex++; break; } } else { curveIndex++; } }
private void ConvertAnimations() { foreach (var assetPreloadData in animationClipHashSet) { var clip = new AnimationClip(assetPreloadData); if (clip.m_Legacy) { var iAnim = new ImportedKeyframedAnimation(); iAnim.Name = clip.m_Name; AnimationList.Add(iAnim); iAnim.TrackList = new List <ImportedAnimationKeyframedTrack>(); foreach (var m_RotationCurve in clip.m_RotationCurves) { var path = m_RotationCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); if (track == null) { track = new ImportedAnimationKeyframedTrack(); track.Name = boneName; iAnim.TrackList.Add(track); } foreach (var m_Curve in m_RotationCurve.curve.m_Curve) { var value = Fbx.QuaternionToEuler(new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W)); var inSlope = Fbx.QuaternionToEuler(new Quaternion(m_Curve.inSlope.X, -m_Curve.inSlope.Y, -m_Curve.inSlope.Z, m_Curve.inSlope.W)); var outSlope = Fbx.QuaternionToEuler(new Quaternion(m_Curve.outSlope.X, -m_Curve.outSlope.Y, -m_Curve.outSlope.Z, m_Curve.outSlope.W)); track.Rotations.Add(new ImportedKeyframe <Vector3>(m_Curve.time, value, inSlope, outSlope)); } } foreach (var m_PositionCurve in clip.m_PositionCurves) { var path = m_PositionCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); if (track == null) { track = new ImportedAnimationKeyframedTrack(); track.Name = boneName; iAnim.TrackList.Add(track); } foreach (var m_Curve in m_PositionCurve.curve.m_Curve) { track.Translations.Add(new ImportedKeyframe <Vector3>( m_Curve.time, new Vector3(-m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z), new Vector3(-m_Curve.inSlope.X, m_Curve.inSlope.Y, m_Curve.inSlope.Z), new Vector3(-m_Curve.outSlope.X, m_Curve.outSlope.Y, m_Curve.outSlope.Z))); } } foreach (var m_ScaleCurve in clip.m_ScaleCurves) { var path = m_ScaleCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); if (track == null) { track = new ImportedAnimationKeyframedTrack(); track.Name = boneName; iAnim.TrackList.Add(track); } foreach (var m_Curve in m_ScaleCurve.curve.m_Curve) { track.Scalings.Add(new ImportedKeyframe <Vector3>( m_Curve.time, new Vector3(m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z), new Vector3(m_Curve.inSlope.X, m_Curve.inSlope.Y, m_Curve.inSlope.Z), new Vector3(m_Curve.outSlope.X, m_Curve.outSlope.Y, m_Curve.outSlope.Z))); } } if ((bool)Properties.Settings.Default["FixRotation"]) { foreach (var track in iAnim.TrackList) { var prevKey = new Vector3(); foreach (var rotation in track.Rotations) { var value = rotation.value; ReplaceOutOfBound(ref prevKey, ref value); prevKey = value; rotation.value = value; } } } } else { var iAnim = new ImportedSampledAnimation(); iAnim.Name = clip.m_Name; iAnim.SampleRate = clip.m_SampleRate; AnimationList.Add(iAnim); int numTracks = (clip.m_MuscleClip.m_Clip.m_ConstantClip.data.Length + (int)clip.m_MuscleClip.m_Clip.m_DenseClip.m_CurveCount + (int)clip.m_MuscleClip.m_Clip.m_StreamedClip.curveCount + 9) / 10; iAnim.TrackList = new List <ImportedAnimationSampledTrack>(numTracks); var streamedFrames = clip.m_MuscleClip.m_Clip.m_StreamedClip.ReadData(); float[] streamedValues = new float[clip.m_MuscleClip.m_Clip.m_StreamedClip.curveCount]; int numFrames = Math.Max(clip.m_MuscleClip.m_Clip.m_DenseClip.m_FrameCount, streamedFrames.Count - 2); for (int frameIdx = 0; frameIdx < numFrames; frameIdx++) { if (1 + frameIdx < streamedFrames.Count) { for (int i = 0; i < streamedFrames[1 + frameIdx].keyList.Count; i++) { streamedValues[i] = streamedFrames[1 + frameIdx].keyList[i].value; } } int numStreamedCurves = 1 + frameIdx < streamedFrames.Count ? streamedFrames[1 + frameIdx].keyList.Count : 0; int numCurves = numStreamedCurves + (int)clip.m_MuscleClip.m_Clip.m_DenseClip.m_CurveCount + clip.m_MuscleClip.m_Clip.m_ConstantClip.data.Length; int streamOffset = numStreamedCurves - (int)clip.m_MuscleClip.m_Clip.m_StreamedClip.curveCount; for (int curveIdx = 0; curveIdx < numCurves;) { GenericBinding binding; float[] data; int dataOffset; if (1 + frameIdx < streamedFrames.Count && curveIdx < streamedFrames[1 + frameIdx].keyList.Count) { binding = clip.m_ClipBindingConstant.FindBinding(streamedFrames[1 + frameIdx].keyList[curveIdx].index); data = streamedValues; dataOffset = 0; } else if (curveIdx < numStreamedCurves + clip.m_MuscleClip.m_Clip.m_DenseClip.m_CurveCount) { binding = clip.m_ClipBindingConstant.FindBinding(curveIdx - streamOffset); data = clip.m_MuscleClip.m_Clip.m_DenseClip.m_SampleArray; dataOffset = numStreamedCurves - frameIdx * (int)clip.m_MuscleClip.m_Clip.m_DenseClip.m_CurveCount; } else { binding = clip.m_ClipBindingConstant.FindBinding(curveIdx - streamOffset); data = clip.m_MuscleClip.m_Clip.m_ConstantClip.data; dataOffset = numStreamedCurves + (int)clip.m_MuscleClip.m_Clip.m_DenseClip.m_CurveCount; } if (binding.path == 0) { curveIdx++; continue; } string boneName = GetNameFromHashes(binding.path, binding.attribute); ImportedAnimationSampledTrack track = iAnim.FindTrack(boneName); if (track == null) { track = new ImportedAnimationSampledTrack(); track.Name = boneName; iAnim.TrackList.Add(track); } try { switch (binding.attribute) { case 1: if (track.Translations == null) { track.Translations = new Vector3?[numFrames]; } track.Translations[frameIdx] = new Vector3 ( -data[curveIdx++ - dataOffset], data[curveIdx++ - dataOffset], data[curveIdx++ - dataOffset] ); break; case 2: if (track.Rotations == null) { track.Rotations = new Vector3?[numFrames]; } track.Rotations[frameIdx] = Fbx.QuaternionToEuler(new Quaternion ( data[curveIdx++ - dataOffset], -data[curveIdx++ - dataOffset], -data[curveIdx++ - dataOffset], data[curveIdx++ - dataOffset] )); break; case 3: if (track.Scalings == null) { track.Scalings = new Vector3?[numFrames]; } track.Scalings[frameIdx] = new Vector3 ( data[curveIdx++ - dataOffset], data[curveIdx++ - dataOffset], data[curveIdx++ - dataOffset] ); break; case 4: if (track.Rotations == null) { track.Rotations = new Vector3?[numFrames]; } track.Rotations[frameIdx] = new Vector3 ( data[curveIdx++ - dataOffset], -data[curveIdx++ - dataOffset], -data[curveIdx++ - dataOffset] ); break; default: if (track.Curve == null) { track.Curve = new float?[numFrames]; } track.Curve[frameIdx] = data[curveIdx++ - dataOffset]; break; } } catch { //errors.Append(" ").Append(boneName).Append(" a=").Append(binding.attribute).Append(" ci=").Append(curveIdx).Append("/#=").Append(numCurves).Append(" of=").Append(dataOffset).Append(" f=").Append(frameIdx).Append("/#=").Append(numFrames).Append("\n"); //TODO Display error break; } } } if ((bool)Properties.Settings.Default["FixRotation"]) { foreach (var track in iAnim.TrackList) { if (track.Rotations == null) { continue; } var prevKey = new Vector3(); for (var i = 0; i < track.Rotations.Length; i++) { var rotation = track.Rotations[i]; if (rotation == null) { continue; } var value = new Vector3(rotation.Value.X, rotation.Value.Y, rotation.Value.Z); ReplaceOutOfBound(ref prevKey, ref value); prevKey = value; track.Rotations[i] = value; } } } } } }
private void ReadCurveData(ImportedKeyframedAnimation iAnim, AnimationClipBindingConstant m_ClipBindingConstant, int index, float time, float[] data, int offset, ref int curveIndex) { var binding = m_ClipBindingConstant.FindBinding(index); if (binding.path == 0) { curveIndex++; return; } var boneName = GetNameFromHashes(binding.path, binding.attribute); var track = iAnim.FindTrack(boneName); if (track == null) { track = new ImportedAnimationKeyframedTrack { Name = boneName }; iAnim.TrackList.Add(track); } switch (binding.attribute) { case 1: track.Translations.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( -data[curveIndex++ + offset], data[curveIndex++ + offset], data[curveIndex++ + offset] ))); break; case 2: var value = Fbx.QuaternionToEuler(new Quaternion ( data[curveIndex++ + offset], -data[curveIndex++ + offset], -data[curveIndex++ + offset], data[curveIndex++ + offset] )); track.Rotations.Add(new ImportedKeyframe <Vector3>(time, value)); break; case 3: track.Scalings.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( data[curveIndex++ + offset], data[curveIndex++ + offset], data[curveIndex++ + offset] ))); break; case 4: track.Rotations.Add(new ImportedKeyframe <Vector3>(time, new Vector3 ( data[curveIndex++ + offset], -data[curveIndex++ + offset], -data[curveIndex++ + offset] ))); break; default: track.Curve.Add(new ImportedKeyframe <float>(time, data[curveIndex++])); break; } }