Подтвердить что ты не робот

Как я могу экспортировать файлы DAE для использования в наборе сцен, не видя "untitled-animations"?

Я пытаюсь загрузить анимации, созданные в Cheetah 3D и Blender 3D, в Scene Kit, но все, что я получаю, это куча "untitled-animations", каждая из которых является одной и той же анимацией.

Кто-нибудь знает, как правильно экспортировать их из Blender или Cheetah 3D, чтобы Scene Kit мог их использовать?

4b9b3361

Ответ 1

Я копался в этом, потому что это меня тоже раздражало. Все "анимации без названия" являются индивидуальными анимациями для каждой кости. Вы можете получить идентификатор из инспектора атрибутов на панели справа от xcode. Используя swift, вы можете получить анимацию.

let urlOfScene = Bundle.main.url(forResources: "your url", withExtension: "dae")
let source = SCNSceneSource(url: urlOfScene, options: nil)
let armature = source.entryWithIdentifier("Armature", withClass: SCNNode.self) as SCNNode

let animation = armature.entryWithIdentifier("your bone id", withClass: CAAnimation.self) as CAAnimation

Это должно быть сделано для всех костей в арматуре. ** Досадно !!! *

Apple использовала 3dmax для всех своих примеров проектов, которые показывают только одну анимацию для каждого файла collada. Это связано с тем, что 3dmax экспортирует все кости под одну анимацию, а блендер разделяет каждую кость.

Временная работа Используйте TextEdit или добавьте расширение .xml в конец вашего файла .dae и откройте его с помощью редактора XML (многие из них бесплатны онлайн). с XML-редакторами работать немного проще. Прокрутите вниз до начального блока анимации. Это будет выглядеть так...

<library_animations>
<animation id= "first_bone"> 
<source id= "first_bone-input"> 

Измените это на...

<library_animations>
<animation>                      ---this is the only modified line
<source id="first_bone-input"> 

В конце каждой анимации будет конец блока, как...

</animtion>                      ---delete this line as long as its not your last bone    
<animation id="second_bone">     ---delete this line too
<source id="second_bone-input">  

Конечно, в конце вашей последней кости оставьте блок конца анимации примерно так...

</animation>
</library_animations>

Это даст вам одну анимацию в вашем файле .dae, имя которого совпадает с именем вашего файла, с добавлением -1 в конце!

РЕДАКТИРОВАТЬ - Вот ссылка на сервис Automator, который преобразует вышеуказанный код для вас!

Automater Collada Converter скачать

Разархивируйте и поместите файл в папку ~/Library/services. Оттуда вы можете просто щелкнуть правой кнопкой мыши на вашем файле collada и прокрутить вниз до ConvertToXcodeCollada и presto! По завершении откроется окно (около полсекунды).

Ответ 2

Это потому, что каждая кость в вашем файле .dae имеет свой собственный тег <animation>.

FlippinFun правильно заявляет, что удаление всех открывающих и закрывающих тегов <animation>, за исключением самого первого и последнего, будет группировать анимацию вместе, делая ее доступной в Xcode идентификатором FileName-1.

Я использую рабочий процесс MayaLT > .FBX > .DAE и обнаружил, что связанная с ним служба не работает для меня. Это связано с тем, что файл .dae был плохо отформатирован с помощью тегов <source> в той же строке, что и двойные вложенные теги <animation>. В результате вся строка была удалена, повредив файл .dae.

Для всех, кто использует этот рабочий процесс, вот команда sed, которую я запускаю, чтобы очистить, надеюсь, что это будет полезно кому-то!

sed -i .bak -e 's/\(.*\)<animation id.*><animation>\(.*\)/\1\2/g; s/\(.*\)<\/animation><\/animation>\(.*\)/\1\2/g; s/\(.*\)<library_animations>\(.*\)/\1<library_animations><animation>\2/g; s/\(.*\)<\/library_animations>\(.*\)/\1<\/animation><\/library_animations>\2/g' Walk.dae

Ответ 3

Я работаю над той же проблемой, но я использую Maya, если вы загружаете слайды SceneKit для WWDC 2014

В файле AAPLSlideAnimationEvents.m есть несколько примеров импорта файлов DAE с несколькими "untitled-animations", которые вы описываете, надеясь, что это поможет

Ответ 4

В случае, если кто-то еще считает это полезным, я написал скрипт на python, который делает именно это. Предоставляя массив путей к файлам, сценарий объединит анимацию в одну анимацию, удалит геометрию и удалит материалы.

Этот новый, более тонкий dae можно использовать в качестве анимации сцены, если кости модели, к которой вы применяете анимацию, названы и точно совпадают (как и должно быть).

#!/usr/local/bin/python
# Jonathan Cardasis, 2018
#
# Cleans up a collada 'dae' file removing all unnessasary data
# only leaving animations and bone structures behind.
# Combines multiple animation sequences into a single animation
# sequence for Xcode to use.
import sys
import os
import re
import subprocess

def print_usage(app_name):
  print 'Usage:'
  print '  {} [path(s) to collada file(s)...]'.format(app_name)
  print ''

def xml_is_collada(xml_string):
    return bool(re.search('(<COLLADA).*(>)', xml_string))

################
##    MAIN    ##
################
DAE_TAGS_TO_STRIP = ['library_geometries', 'library_materials', 'library_images']

if len(sys.argv) < 2:
    app_name = os.path.basename(sys.argv[0])
    print_usage(app_name)
    sys.exit(1)

print 'Stripping collada files of non-animation essential features...'
failed_file_conversions = 0

for file_path in sys.argv[1:]:
    try:
        print 'Stripping {} ...'.format(file_path)
        dae_filename = os.path.basename(file_path)
        renamed_dae_path = file_path + '.old'

        dae = open(file_path, 'r')
        xml_string = dae.read().strip()
        dae.close()

        # Ensure is a collada file
        if not xml_is_collada(xml_string):
            raise Exception('Not a proper Collada file.')

        # Strip tags
        for tag in DAE_TAGS_TO_STRIP:
            xml_string = re.sub('(?:<{tag}>)([\s\S]+?)(?:</{tag}>)'.format(tag=tag), '', xml_string)

        # Combine animation keys into single key:
        #  1. Remove all <animation> tags.
        #  2. Add leading and trailing <library_animation> tags with single <animation> tag between.
        xml_string = re.sub(r'\s*(<animation[^>]*>)\s*', '\n', xml_string)
        xml_string = re.sub(r'\s*(<\/animation\s*>.*)\s*', '', xml_string)

        xml_string = re.sub(r'\s*(<library_animations>)\s*', '<library_animations>\n<animation>\n', xml_string)
        xml_string = re.sub(r'\s*(<\/library_animations>)\s*', '\n</animation>\n</library_animations>', xml_string)

        # Rename original and dump xml to previous file location
        os.rename(file_path, renamed_dae_path)
        with open(file_path, 'w') as new_dae:
            new_dae.write(xml_string)
            print 'Finished processing {}. Old file can be found at {}.\n'.format(file_path, renamed_dae_path)
    except Exception as e:
        print '[!] Failed to correctly parse {}: {}'.format(file_path, e)
        failed_file_conversions += 1

if failed_file_conversions > 0:
    print '\nFailed {} conversion(s).'.format(failed_file_conversions)
    sys.exit(1)

Использование: python cleanupForXcodeColladaAnimation.py dancing_anim.dae

https://gist.github.com/joncardasis/e815ec69f81ed767389aa7a878f3deb6

Ответ 5

Удалите теги анимаций (см. решение FlippinFun) и прочитайте приведенную ниже ссылку о том, как подготовить анимацию Blender для воспроизведения с помощью SceneKit (инструкции не для SceneKit, но они работают очень хорошо).

http://trac.wildfiregames.com/wiki/AnimationExportTutorial

Ответ 6

Вот как вы можете удалить ненужные узлы xml:

let currentDirectory = NSFileManager.defaultManager().currentDirectoryPath
let files = (try! NSFileManager.defaultManager().contentsOfDirectoryAtPath(currentDirectory)).filter { (fname:String) -> Bool in
    return NSString(string: fname).pathExtension.lowercaseString == "dae"
    }.map { (fname: String) -> String in
    return "\(currentDirectory)/\(fname)"
}
//print(files)

for file in files {
    print(file)
    var fileContent = try! NSString(contentsOfFile: file, encoding: NSUTF8StringEncoding)

    // remove all but not last </animation>
    let closing_animation = "</animation>"
    var closing_animation_last_range = fileContent.rangeOfString(closing_animation, options: NSStringCompareOptions.CaseInsensitiveSearch.union(NSStringCompareOptions.BackwardsSearch))
    var closing_animation_current_range = fileContent.rangeOfString(closing_animation, options: NSStringCompareOptions.CaseInsensitiveSearch)
    while closing_animation_current_range.location != closing_animation_last_range.location {
        fileContent = fileContent.stringByReplacingCharactersInRange(closing_animation_current_range, withString: "")
        closing_animation_current_range = fileContent.rangeOfString(closing_animation, options: NSStringCompareOptions.CaseInsensitiveSearch)
        closing_animation_last_range = fileContent.rangeOfString(closing_animation, options: NSStringCompareOptions.CaseInsensitiveSearch.union(NSStringCompareOptions.BackwardsSearch))
    }

    // remove all but not first <animation .. >
    let openning_animation_begin = "<animation "
    let openning_animation_end = ">"

    let openning_animation_begin_range = fileContent.rangeOfString(openning_animation_begin, options:NSStringCompareOptions.CaseInsensitiveSearch)
    var openning_animation_end_range = fileContent.rangeOfString(openning_animation_begin, options: NSStringCompareOptions.CaseInsensitiveSearch.union(NSStringCompareOptions.BackwardsSearch))

    while openning_animation_begin_range.location != openning_animation_end_range.location {
        let openning_animation_end_location = fileContent.rangeOfString(openning_animation_end, options: .CaseInsensitiveSearch, range: NSRange.init(location: openning_animation_end_range.location, length: openning_animation_end.characters.count))
        let lengthToRemove = NSString(string: fileContent.substringFromIndex(openning_animation_end_range.location)).rangeOfString(openning_animation_end, options:NSStringCompareOptions.CaseInsensitiveSearch).location + openning_animation_end.characters.count
        let range = NSRange.init(location: openning_animation_end_range.location, length: lengthToRemove)
        fileContent = fileContent.stringByReplacingCharactersInRange(range, withString: "")
        openning_animation_end_range = fileContent.rangeOfString(openning_animation_begin, options: NSStringCompareOptions.CaseInsensitiveSearch.union(NSStringCompareOptions.BackwardsSearch))
    }

    // save
    try! fileContent.writeToFile(file, atomically: true, encoding: NSUTF8StringEncoding)
}

Ответ 7

Вот моя модификация n33kos script для работы с ресурсами Mixamo.

sed -i .bak -e 's/\(.*\)<animation id.*<source\(.*\)/\1<source\2/g; s/\(.*\)<\/animation>\(.*\)/\1\2/g; s/\(.*\)<library_animations>\(.*\)/\1<library_animations><animation>\2/g; s/\(.*\)<\/library_animations>\(.*\)/\1<\/animation><\/library_animations>\2/g' Standing_Idle.dae

Ответ 8

Может быть, кому-то пригодится следующее наблюдение: я напрямую импортировал нетронутый mixamo.dae (с анимацией) в xcode 10.2.1 В моем случае персонаж "Большой Вегас" (с танцем самбы). следующий код дает идентификатор списка анимаций:

var sceneUrl = Bundle.main.url(forResource: "Art.scnassets/Elvis/SambaDancingFixed", withExtension: "dae")!

    if let sceneSource = SCNSceneSource(url: sceneUrl, options: nil){

        let caAnimationIDs = sceneSource.identifiersOfEntries(withClass: CAAnimation.self)

        caAnimationIDs.forEach({id in
            let anAnimation = sceneSource.entryWithIdentifier(id, withClass: CAAnimation.self)
            print(id,anAnimation)
        })    
    }

ВЫХОД:

animation/1 Optional(<CAAnimationGroup:0x283c05fe0; animations = (
"SCN_CAKeyframeAnimation 0x28324f5a0 (duration=23.833332, keyPath:/newVegas_Hips.transform)",
"SCN_CAKeyframeAnimation 0x28324f600 (duration=23.833332, keyPath:/newVegas_Pelvis.transform)",
"SCN_CAKeyframeAnimation 0x28324f690 (duration=23.833332, keyPath:/newVegas_LeftUpLeg.transform)",
"SCN_CAKeyframeAnimation 0x28324f750 (duration=23.833332, keyPath:/newVegas_LeftLeg.transform)",
"SCN_CAKeyframeAnimation 0x28324f810 (duration=23.833332, keyPath:/newVegas_LeftFoot.transform)",
"SCN_CAKeyframeAnimation 0x28324f8d0 (duration=23.833332, keyPath:/newVegas_RightUpLeg.transform)",
... and so on ...

Как вы можете заметить, "animation/1" - это группа анимации, доступ к которой можно получить с помощью:

let sambaAnimation = sceneSource.entryWithIdentifier("animation/1", withClass: CAAnimation.self)

"sambaAnimation" может применяться к родительскому узлу "Большого Вегаса":

self.addAnimation(sambaAnimation, forKey: "Dance")

в случае, если вы загружаете того же персонажа с другими анимациями, вы можете извлечь анимацию, как описано:

let animation = sceneSource.entryWithIdentifier("animation/1", withClass: CAAnimation.self)

и применить его к своему персонажу.