Explorar o código

Merge pull request #6270 from and3rson/dev

Added 'annotate' utility to slice JSON-from-3DS-Max animated model into multiple animations
Ricardo Cabello %!s(int64=10) %!d(string=hai) anos
pai
achega
2af26d6b93
Modificáronse 2 ficheiros con 169 adicións e 0 borrados
  1. 66 0
      utils/exporters/max/annotate/README.md
  2. 103 0
      utils/exporters/max/annotate/annotate.py

+ 66 - 0
utils/exporters/max/annotate/README.md

@@ -0,0 +1,66 @@
+## annotate
+
+Utility to split THREE.js model (exported from 3D Studio Max) with single animation into seperate parts based on frame ranges.
+
+## Description
+
+This utility refactors THREE.js JSON model files that were exported using `ThreeJSAnimationExporter.ms` plugin to maintain few animations instead of a single "Action" animation. List of resulting animation names and frame ranges has to be provided.
+
+## Supported Formats
+
+* THREE.js JSON (.js)
+
+## Usage 
+
+```
+annotate.py [-h] [-u] -i FILE -o FILE range [range ...]
+
+Split THREE.js model animation into seperate parts.
+
+positional arguments:
+  range       range in format "name=frame..frame"
+
+optional arguments:
+  -h, --help  show this help message and exit
+  -u, --gui   run in GUI
+  -i FILE     input file name
+  -o FILE     output file name
+
+example:
+  annotate.py -i model.js -o model.new.js idle=1..10 walk=11..20
+```
+
+## Current Limitations
+
+* No known limitations
+
+## Dependencies
+
+* Python 2.7 or 3.1
+* **Requires THREE.js >= r71 for output animation to work properly.**
+
+## Optional dependencies
+
+* PyQt4
+* argparseui
+
+## Example usage
+
+```
+./annotate.py -i model.js -o model.new.js idle=1..10 walk=1..20
+```
+
+```
+// ...
+this.animation = new THREE.Animation(
+    mesh,
+    geometry.animations.walk,
+    THREE.AnimationHandler.CATMULLROM
+);
+// ...
+```
+
+## Contribution
+
+I highly encourage you to participate in the development of this utility.
+Author: Andrew Dunai

+ 103 - 0
utils/exporters/max/annotate/annotate.py

@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+
+__author__ = 'Andrew Dunai <[email protected]>'
+
+import sys
+import json
+import argparse
+import re
+from collections import namedtuple
+try:
+    from PyQt4 import QtGui
+    import argparseui
+except ImportError:
+    CAN_GUI = False
+else:
+    CAN_GUI = True
+
+range_regexp = re.compile(r'^([\w\d]+)\=([\d]+)\.\.([\d]+)$')
+Range = namedtuple('Range', ('name', 'start', 'end'))
+
+
+def parse_range(value):
+    match = range_regexp.match(value)
+    if not match:
+        raise argparse.ArgumentTypeError('Ranges should be in form "name=frame..frame"')
+    return Range(match.group(1), int(match.group(2)) - 1, int(match.group(3)) - 1)
+
+
+epilog = 'example:\n  %(prog)s -i model.js -o model.new.js idle=1..10 walk=11..20'
+if not CAN_GUI:
+    epilog += '\npro tip:\n  Install PyQt4 and argparseui packages to use GUI ("-u" option).'
+epilog += '\nCreated by {}'.format(__author__)
+
+parser = argparse.ArgumentParser(
+    description='Split THREE.js model animation into seperate parts.',
+    epilog=epilog,
+    formatter_class=argparse.RawDescriptionHelpFormatter
+)
+CAN_GUI and parser.add_argument('-u', '--gui', help='run in GUI', dest='gui', action='store_true')
+parser.add_argument('-i', metavar='FILE', help='input file name', required=True, dest='source', type=argparse.FileType('r'))
+parser.add_argument('-o', metavar='FILE', help='output file name', required=True, dest='destination', type=argparse.FileType('w'))
+parser.add_argument('range', nargs='+', help='range in format "name=frame..frame"', type=parse_range)
+
+
+def process(parser):
+    args = parser.parse_args()
+
+    data = json.loads(args.source.read())
+    animation = data.get('animation')
+
+    fps = float(animation.get('fps'))
+    length = float(animation.get('length'))
+    frame_count = int(length * fps)
+    frame_duration = 1.0 / fps
+
+    all_hierarchy = animation.get('hierarchy')
+
+    animations = {}
+
+    for r in args.range:
+        # Create animation & hierarchy
+        hierarchy = []
+        animation = {
+            'name': r.name,
+            'fps': fps,
+            'length': (r.end - r.start) * frame_duration,
+            'hierarchy': hierarchy
+        }
+
+        # Go through each bone animation
+        for bone in all_hierarchy:
+
+            keys = [key for key in bone['keys'] if (key['time'] >= r.start * frame_duration) and (key['time'] <= r.end * frame_duration)]
+
+            # Patch time
+            time = 0.0
+            for key in keys:
+                key['time'] = round(time, 3)
+                time += frame_duration
+
+            new_bone = {
+                'parent': bone['parent'],
+                'keys': keys
+            }
+            hierarchy.append(new_bone)
+
+        animations[r.name] = animation
+
+    del data['animation']
+    data['animations'] = animations
+
+    args.destination.write(json.dumps(data))
+
+if '-u' in sys.argv and CAN_GUI:
+    app = QtGui.QApplication(sys.argv)
+    a = argparseui.ArgparseUi(parser)
+    a.show()
+    app.exec_()
+    
+    if a.result() == 1:
+        process(a)
+else:
+    process(parser)