Skip to content

Instantly share code, notes, and snippets.

@joncardasis
Last active November 3, 2024 09:41
Show Gist options
  • Save joncardasis/e815ec69f81ed767389aa7a878f3deb6 to your computer and use it in GitHub Desktop.
Save joncardasis/e815ec69f81ed767389aa7a878f3deb6 to your computer and use it in GitHub Desktop.
Convert Collada (.DAE) animation files for Xcode Scenekit Animation use by removing unnecessary data.
#!/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)
@joncardasis
Copy link
Author

For an Automator Finder service workflow: https://drive.google.com/open?id=1iVHuuOMxelljhAViMFewkxKCEWBLWjMg

@Cobertos
Copy link

Cobertos commented Nov 14, 2018

I've noticed that THREE.js will have issues with animation nodes with with no frames (will load with NaNs which fucks up the renderer). If anyone else is having this issue, the js code below might help

//Remove this entire node if there's no animation data
//(as otherwise NaN's will occur). Node is an <animation> node
if(node.querySelector("float_array[count='0']")) {
	node.remove();
	return;
}

@8secz-johndpope
Copy link

8secz-johndpope commented May 29, 2020

Is this to achieve smaller file size?
I arrived here from stack overflow - but I'm wanting to get clarity around if I have the (mixamo samba style) dances in FBX
can I some how import this animation (500kb) and have the usdz or dae model apply the fbx dance?
or do I need to spit out a new dae file with the skeleton /body / mesh ? ~ 20mb or can I import / export it? then apply it? (https://www.reddit.com/r/gamedev/comments/4jb68g/anyone_still_got_the_whole_mixamo_pack/ - talking about the giant animations 4.5gb here)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment