├── spine_sequence_tutorial_files └── images │ ├── fancy │ ├── 000.png │ ├── 001.png │ ├── 002.png │ ├── 003.png │ ├── 004.png │ ├── 005.png │ ├── 006.png │ ├── 007.png │ ├── 008.png │ ├── 009.png │ ├── 010.png │ ├── 011.png │ ├── 012.png │ ├── 013.png │ ├── 014.png │ ├── 015.png │ ├── 016.png │ ├── 017.png │ ├── 018.png │ ├── 019.png │ ├── 020.png │ ├── 021.png │ ├── 022.png │ ├── 023.png │ ├── 024.png │ ├── 025.png │ ├── 026.png │ ├── 027.png │ ├── 028.png │ └── 029.png │ └── spinny │ ├── 000.png │ ├── 001.png │ ├── 002.png │ ├── 003.png │ ├── 004.png │ ├── 005.png │ ├── 006.png │ ├── 007.png │ ├── 008.png │ ├── 009.png │ ├── 010.png │ ├── 011.png │ ├── 012.png │ ├── 013.png │ ├── 014.png │ ├── 015.png │ ├── 016.png │ ├── 017.png │ ├── 018.png │ ├── 019.png │ ├── 020.png │ ├── 021.png │ ├── 022.png │ ├── 023.png │ ├── 024.png │ ├── 025.png │ ├── 026.png │ ├── 027.png │ ├── 028.png │ └── 029.png ├── LICENSE ├── .gitattributes ├── README.md ├── .gitignore └── spine_sequence.py /spine_sequence_tutorial_files/images/fancy/000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/000.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/001.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/002.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/003.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/004.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/005.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/006.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/007.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/008.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/009.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/010.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/011.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/012.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/013.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/014.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/015.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/016.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/017.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/018.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/019.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/020.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/021.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/022.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/023.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/024.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/025.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/026.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/027.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/028.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/fancy/029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/fancy/029.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/000.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/001.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/002.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/003.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/004.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/005.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/006.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/007.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/008.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/009.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/010.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/011.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/012.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/013.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/014.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/015.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/016.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/017.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/018.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/019.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/020.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/021.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/022.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/023.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/024.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/025.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/026.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/027.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/028.png -------------------------------------------------------------------------------- /spine_sequence_tutorial_files/images/spinny/029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattOstgard/spine_sequence/HEAD/spine_sequence_tutorial_files/images/spinny/029.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 MattOstgard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | 19 | # These files are binary and should be left untouched 20 | # (binary is a macro for -text -diff) 21 | *.png binary 22 | *.jpg binary 23 | *.jpeg binary 24 | *.gif binary 25 | *.ico binary 26 | *.mov binary 27 | *.mp4 binary 28 | *.mp3 binary 29 | *.flv binary 30 | *.fla binary 31 | *.swf binary 32 | *.gz binary 33 | *.zip binary 34 | *.7z binary 35 | *.ttf binary 36 | *.webm binary 37 | *.webp binary 38 | 39 | 40 | ############# Basic .gitattributes for a python repo. ############# 41 | 42 | # Source files 43 | # ============ 44 | *.pxd text 45 | *.py text 46 | *.py3 text 47 | *.pyw text 48 | *.pyx text 49 | 50 | # Binary files 51 | # ============ 52 | *.db binary 53 | *.p binary 54 | *.pkl binary 55 | *.pyc binary 56 | *.pyd binary 57 | *.pyo binary 58 | 59 | # Note: .db, .p, and .pkl files are associated 60 | # with the python modules ``pickle``, ``dbm.*``, 61 | # ``shelve``, ``marshal``, ``anydbm``, & ``bsddb`` 62 | # (among others). 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spine_sequence.py 2 | Add an image sequence to an Esoteric Spine skeleton. 3 | 4 | [Official thread on Esoteric's forums.](http://esotericsoftware.com/forum/spine-sequence-py-Add-image-sequences-to-a-Spine-skeleton-5868) 5 | 6 | #Installation and Usage 7 | [![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/c9-UdM7oHKg/0.jpg)](http://www.youtube.com/watch?v=c9-UdM7oHKg) 8 | - Requires Python 2.7 or above. Mac users have this installed by default. Windows users will have to download: https://www.python.org/downloads/ 9 | - Usage (same as --help argument): 10 | ``` 11 | spine_sequence.py [-h] --output JSON_FILE --images WILDCARD_PATH 12 | [--images_root FOLDER_PATH] [--merge SPINE_JSON_FILE] 13 | [--bone NAME] [--framerate NUMBER] 14 | 15 | Creates a spine .json file from an image sequence. 16 | 17 | optional arguments: 18 | -h, --help show this help message and exit 19 | --output JSON_FILE Output .json file: Example: "my_image_sequence.json" 20 | --images WILDCARD_PATH 21 | Wildcard path to image sequence. This is relative to 22 | --images_root_path. Example: "my_images/*.png" 23 | --images_root FOLDER_PATH 24 | (Optional, default is current folder)The root path 25 | used in spine for images. Example: "assets/images/" 26 | --merge SPINE_JSON_FILE 27 | (Optional) Spine skeleton to add image sequence to. If 28 | not supplied it will be added to an empty spine 29 | skeleton. Example: "my_existing_skeleton.json" 30 | --bone NAME (Optional, default is "root") Bone name to attach to. 31 | Example: "my_bone" 32 | --framerate NUMBER (Optional, default is 30) Framerate to animate at. 33 | ``` 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# Python project .gitignore ################# 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | # PyBuilder 60 | target/ 61 | 62 | #Ipython Notebook 63 | .ipynb_checkpoints 64 | 65 | ################# Node project .gitignore ################# 66 | # Logs 67 | logs 68 | *.log 69 | npm-debug.log* 70 | 71 | # Runtime data 72 | pids 73 | *.pid 74 | *.seed 75 | 76 | # Directory for instrumented libs generated by jscoverage/JSCover 77 | lib-cov 78 | 79 | # Coverage directory used by tools like istanbul 80 | coverage 81 | 82 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 83 | .grunt 84 | 85 | # node-waf configuration 86 | .lock-wscript 87 | 88 | # Compiled binary addons (http://nodejs.org/api/addons.html) 89 | build/Release 90 | 91 | # Dependency directory 92 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 93 | node_modules 94 | 95 | # ========================= 96 | # Operating System Files 97 | # ========================= 98 | 99 | # OSX 100 | # ========================= 101 | 102 | .DS_Store 103 | .AppleDouble 104 | .LSOverride 105 | 106 | # Thumbnails 107 | ._* 108 | 109 | # Files that might appear in the root of a volume 110 | .DocumentRevisions-V100 111 | .fseventsd 112 | .Spotlight-V100 113 | .TemporaryItems 114 | .Trashes 115 | .VolumeIcon.icns 116 | 117 | # Directories potentially created on remote AFP share 118 | .AppleDB 119 | .AppleDesktop 120 | Network Trash Folder 121 | Temporary Items 122 | .apdisk 123 | 124 | # Windows 125 | # ========================= 126 | 127 | # Windows image file caches 128 | Thumbs.db 129 | ehthumbs.db 130 | 131 | # Folder config file 132 | Desktop.ini 133 | 134 | # Recycle Bin used on file shares 135 | $RECYCLE.BIN/ 136 | 137 | # Windows Installer files 138 | *.cab 139 | *.msi 140 | *.msm 141 | *.msp 142 | 143 | # Windows shortcuts 144 | *.lnk 145 | -------------------------------------------------------------------------------- /spine_sequence.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import json 5 | from collections import OrderedDict 6 | from glob import glob 7 | 8 | 9 | def main(): 10 | # Import extra modules 11 | import argparse 12 | import struct 13 | import imghdr 14 | 15 | 16 | # Extra function to get image sizes 17 | def get_image_size(fname): 18 | '''Determine the image type of fhandle and return its size. 19 | from draco''' 20 | with open(fname, 'rb') as fhandle: 21 | head = fhandle.read(24) 22 | if len(head) != 24: 23 | return 24 | if imghdr.what(fname) == 'png': 25 | check = struct.unpack('>i', head[4:8])[0] 26 | if check != 0x0d0a1a0a: 27 | return 28 | width, height = struct.unpack('>ii', head[16:24]) 29 | elif imghdr.what(fname) == 'gif': 30 | width, height = struct.unpack('H', fhandle.read(2))[0] - 2 43 | # We are at a SOFn block 44 | fhandle.seek(1, 1) # Skip `precision' byte. 45 | height, width = struct.unpack('>HH', fhandle.read(4)) 46 | except Exception: #IGNORE:W0703 47 | return 48 | else: 49 | return 50 | return width, height 51 | 52 | 53 | # Process arguments 54 | parser = argparse.ArgumentParser(description='Creates a spine .json file from an image sequence.') 55 | 56 | parser.add_argument('--output', metavar='JSON_FILE', required=True, 57 | help='Output .json file: Example: "my_image_sequence.json"' 58 | ) 59 | 60 | parser.add_argument('--images', metavar='WILDCARD_PATH', required=True, nargs='+', 61 | help='Wildcard path to image sequence. This is relative to --images_root_path. Example: "my_images/*.png"' 62 | ) 63 | 64 | parser.add_argument('--images_root', metavar='FOLDER_PATH', default='', 65 | help='(Optional, default is current folder)The root path used in spine for images. Example: "assets/images/"' 66 | ) 67 | 68 | parser.add_argument('--merge', metavar='SPINE_JSON_FILE', 69 | help='(Optional) Spine skeleton to add image sequence to. If not supplied it will be added to an empty spine ' 70 | 'skeleton. Example: "my_existing_skeleton.json"' 71 | ) 72 | 73 | parser.add_argument('--bone', metavar='NAME', default='root', 74 | help='(Optional, default is "root") Bone name to attach to. Example: "my_bone"' 75 | ) 76 | 77 | parser.add_argument('--framerate', metavar='NUMBER', default=30, type=float, 78 | help='(Optional, default is 30) Framerate to animate at.' 79 | ) 80 | 81 | args = parser.parse_args() 82 | 83 | 84 | # Print a new line for sanity 85 | print() 86 | 87 | # Get images from wildcard argument 88 | globbed_paths = [] 89 | for glob_pattern in args.images: 90 | glob_pattern = os.path.join(args.images_root, glob_pattern) 91 | globbed_paths += glob(glob_pattern) 92 | 93 | # Make image paths relative to image root 94 | image_paths = [] 95 | for path in globbed_paths: 96 | image_paths.append(os.path.relpath(path, args.images_root)) 97 | 98 | if len(image_paths) == 0: 99 | print(' - No images to process!') 100 | print('No images found in supplied path.') 101 | print('Please verify --images and --images_root arguments are correct.\n') 102 | return False 103 | 104 | # Get first image's width and height 105 | width, height = get_image_size(globbed_paths[0]) 106 | 107 | # Full images path 108 | print(' - {} images found.'.format(len(image_paths))) 109 | 110 | # Do the actual work 111 | spineSeq = SpineSequence(image_paths, width, height, args.merge, args.bone, args.framerate) 112 | 113 | # Save the new json file 114 | with open(args.output, 'w') as outfile: 115 | json.dump(spineSeq.skel, outfile, indent=4) 116 | 117 | # Goodbye! 118 | print(' - Saved new json file: {}\n'.format(args.output)) 119 | 120 | 121 | 122 | class SpineSequence: 123 | json_root_sort_order = ['skeleton', 'bones', 'slots', 'skins', 'events', 'animations'] 124 | empty_spine_skeleton_string = ( 125 | '{' 126 | '"skeleton": { "hash": "AnNGCgm1KE26nDxUu2R4xqNyrKs", "spine": "3.0.10", "width": 0, "height": 0 },' 127 | '"bones": [' 128 | ' { "name": "root" }' 129 | '],' 130 | '"animations": {' 131 | ' "animation": {}' 132 | '}' 133 | '}' 134 | ) 135 | 136 | def __init__(self, image_paths, width, height, skeleton_path=None, bone_name='root', framerate=30, 137 | anim_name='animation'): 138 | # type: (image_paths:str, width:int, height:int, skeleton_path:str, bone_name:str, framerate:int, anim_name:str) -> None 139 | 140 | # Get skeleton dict from json file 141 | if skeleton_path: 142 | with open(skeleton_path) as f: 143 | self.skel = json.load(f, object_pairs_hook=OrderedDict) 144 | else: 145 | self.skel = json.loads(self.empty_spine_skeleton_string) 146 | 147 | 148 | # Verify there is a bone to attach to 149 | if not self.has_bone(bone_name): 150 | raise LookupError('Cannot find bone_name "{}" in skeleton "{}".\n'.format(bone_name, skeleton_path)) 151 | 152 | 153 | # Set width and height of document 154 | if width > self.skel['skeleton']['width']: 155 | self.skel['skeleton']['width'] = width 156 | 157 | if height > self.skel['skeleton']['height']: 158 | self.skel['skeleton']['height'] = height 159 | 160 | 161 | # Ensure we have a slots array 162 | if not 'slots' in self.skel: 163 | self.skel['slots'] = [] 164 | 165 | 166 | # Ensure we have a skins dict 167 | if not 'skins' in self.skel: 168 | self.skel['skins'] = OrderedDict() 169 | 170 | if not 'default' in self.skel['skins']: 171 | self.skel['skins']['default'] = OrderedDict() 172 | 173 | 174 | # Ensure we have a slots animation dict 175 | if not 'animations' in self.skel: 176 | self.skel['animations'] = OrderedDict() 177 | 178 | if not anim_name in self.skel['animations']: 179 | self.skel['animations'][anim_name] = OrderedDict() 180 | 181 | if not 'slots' in self.skel['animations'][anim_name]: 182 | self.skel['animations'][anim_name]['slots'] = OrderedDict() 183 | 184 | 185 | # Make a unique slot name 186 | existing_slot_names = [] 187 | for slot in self.skel['slots']: 188 | existing_slot_names.append(slot['name']) 189 | 190 | slot_name = os.path.splitext(image_paths[0])[0].replace('\\', '/') 191 | unique_slot_name = slot_name 192 | i = 0 193 | while unique_slot_name in existing_slot_names: 194 | i += 1 195 | unique_slot_name = '{} ({})'.format(slot_name, i) 196 | slot_name = unique_slot_name 197 | 198 | 199 | # Add slot 200 | # { "name": "fancy_slot", "bone": "fancy", "attachment": "fancy_000" } 201 | slot = OrderedDict() 202 | slot['name'] = slot_name 203 | slot['bone'] = bone_name 204 | slot['attachment'] = os.path.splitext(image_paths[0])[0].replace('\\', '/') 205 | self.skel['slots'].append(slot) 206 | 207 | 208 | # Add attachments to slot 209 | # "fancy_000": { "width": 512, "height": 256 }, 210 | attachments = OrderedDict() 211 | for image_path in image_paths: 212 | name = os.path.splitext(image_path)[0].replace('\\', '/') 213 | attachments[name] = OrderedDict() 214 | attachments[name]['width'] = width 215 | attachments[name]['height'] = height 216 | 217 | self.skel['skins']['default'][slot_name] = attachments 218 | 219 | 220 | # Add frames to animation 221 | # { "time": 0.0333, "name": "fancy_000" }, 222 | keyframes = [] 223 | count = 0 224 | for image_path in image_paths: 225 | keyframe = OrderedDict() 226 | keyframe['time'] = self.round_time_like_spine((count / framerate)) 227 | keyframe['name'] = os.path.splitext(image_path)[0].replace('\\', '/') 228 | keyframes.append(keyframe) 229 | count += 1 230 | 231 | self.skel['animations'][anim_name]['slots'][slot_name] = OrderedDict() 232 | self.skel['animations'][anim_name]['slots'][slot_name]['attachment'] = keyframes 233 | 234 | # Reorder dict so it can be easily diffed against original 235 | ordered = OrderedDict() 236 | for key in self.json_root_sort_order: 237 | if key in self.skel: 238 | ordered[key] = self.skel[key] 239 | self.skel = ordered 240 | 241 | 242 | def has_bone(self, bone_name): 243 | for bone in self.skel['bones']: 244 | if bone_name == bone['name']: 245 | return True 246 | return False 247 | 248 | 249 | def round_time_like_spine(self, time): 250 | # Floor to 4 decimal points 251 | time = int(time * 10000) / 10000.0 252 | 253 | # Remove decimals for whole numbers 254 | if time == int(time): 255 | time = int(time) 256 | 257 | # 258 | return time 259 | 260 | 261 | if __name__ == '__main__': 262 | import time 263 | start_time = time.time() 264 | main() 265 | print('--- {} seconds ---'.format(round(time.time() - start_time, 5))) --------------------------------------------------------------------------------