如何从SceneKit中的Collada文件中分割出多个动画

我将第三方.dae Collada文件作为场景加载到SceneKit项目中。

.dae文件中有许多不同的动画,设置在不同的时间/帧。 我试图弄清楚如何将它们分开并通过引用名称引用每个单独的动画。 dae文件中没有可理解的引用名称 – 动画都设置为单个动画。

我能够将动画解析为CAAnimation对象,并使用以下代码validation我是否成功完成了此操作:

SCNScene *scene = [SCNScene sceneNamed:@"art.scnassets/man.dae"]; SCNNode *man = [scene.rootNode childNodeWithName:@"Bip01" recursively:YES]; CAAnimation *animation = [man animationForKey:@"test_Collada_DAE-1"]; [man removeAllAnimations]; [man addAnimation:animation forKey:@"animation"]; 

有没有办法为我的CAAnimation对象设置开始和结束帧或时间? 解析各种动画的最佳方法是什么? 我希望我不必手动将dae文件拆分成多个并单独加载每个文件。

3d工具通常将多个动画导出为具有子动画的单个动画。 在这种情况下,SceneKit会将这些动画加载为具有子动画的CAAnimationGroup。 因此,一个选项是“解析”动画组的子动画并检索所需的动画。 另一种选择是使用SCNSceneSource按名称检索(子)动画(但这仅在您的3d工具导出DAE时导出名称时才有效)。

如果您需要“裁剪”动画(即从较长的动画中提取以t0开头且持续时间为D的动画),CoreAnimation有一个API:

  • 创建一个动画组以“裁剪”持续时间D.

  • 将要裁剪的动画添加为子动画,并将其timeOffset设置为t0。

正如Toyos在他的回答中提到的,使用SCNSceneSource枚举CAAnimationGroup并检索CAAnimation对象,如下所示:

 NSURL *daeURL = [[NSBundle mainBundle] URLForResource:@"exportedFilename" withExtension:@"dae"]; SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:daeURL options:nil]; NSMutableArray *myAnimations = [@[] mutableCopy]; for (NSString *singleAnimationName in [sceneSource identifiersOfEntriesWithClass:[CAAnimation class]]) { CAAnimation *thisAnimation = [sceneSource entryWithIdentifier:singleAnimationName withClass:[CAAnimation class]]; [myAnimations addObject:thisAnimation]; } 

下面是将帧数转换为时间的代码,然后通过使用CAAnimationGroup作为@Toyos描述,仅播放动画的那部分。 此示例代码通过重复fullAnimation 10帧到第160帧来播放“空闲”动画:

 func playIdleAnimation() { let animation = subAnimation(of:fullAnimation, startFrame: 10, endFrame: 160) animation.repeatCount = .greatestFiniteMagnitude addAnimation(animation, forKey: "animation") } func subAnimation(of fullAnimation:CAAnimation, startFrame:Int, endFrame:Int) -> CAAnimation { let (startTime, duration) = timeRange(startFrame:startFrame, endFrame:endFrame) let animation = subAnimation(of: fullAnimation, offset: startTime, duration: duration) return animation } func subAnimation(of fullAnimation:CAAnimation, offset timeOffset:CFTimeInterval, duration:CFTimeInterval) -> CAAnimation { fullAnimation.timeOffset = timeOffset let container = CAAnimationGroup() container.animations = [fullAnimation] container.duration = duration return container } func timeRange(startFrame:Int, endFrame:Int) -> (startTime:CFTimeInterval, duration:CFTimeInterval) { let startTime = timeOf(frame:startFrame) let endTime = timeOf(frame:endFrame) let duration = endTime - startTime return (startTime, duration) } func timeOf(frame:Int) -> CFTimeInterval { return CFTimeInterval(frame) / framesPerSecond() } func framesPerSecond() -> CFTimeInterval { // number of frames per second the model was designed with return 30.0 }