[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [2570] trunk/py/scripts/addons: Added the first public version of Acclaim importer addon.

Daniel M. Basso danielmbasso at gmail.com
Thu Nov 3 00:35:43 CET 2011


Revision: 2570
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=2570
Author:   dmbasso
Date:     2011-11-02 23:35:42 +0000 (Wed, 02 Nov 2011)
Log Message:
-----------
Added the first public version of Acclaim importer addon.

Added Paths:
-----------
    trunk/py/scripts/addons/io_anim_acclaim/
    trunk/py/scripts/addons/io_anim_acclaim/__init__.py

Added: trunk/py/scripts/addons/io_anim_acclaim/__init__.py
===================================================================
--- trunk/py/scripts/addons/io_anim_acclaim/__init__.py	                        (rev 0)
+++ trunk/py/scripts/addons/io_anim_acclaim/__init__.py	2011-11-02 23:35:42 UTC (rev 2570)
@@ -0,0 +1,449 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 3
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8-80 compliant>
+
+# This script was developed with financial support from the Foundation for
+# Science and Technology of Portugal, under the grant SFRH/BD/66452/2009.
+
+
+bl_info = {
+    'name': "Acclaim Motion Capture Files (.asf, .amc)",
+    'author': "Daniel Monteiro Basso <daniel at basso.inf.br>",
+    'version': (2011, 11, 2, 1),
+    'blender': (2, 6, 0),
+    'api': 41226,
+    'location': "File > Import",
+    'description': "Imports Acclaim Skeleton and Motion Capture Files",
+    'wiki_url': "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
+                "Scripts/Import-Export/Acclaim_Importer",
+    'tracker_url': "http://projects.blender.org/tracker/index.php?"\
+                   "func=detail&aid=27127&group_id=153&atid=467",
+    'category': 'Import-Export'}
+
+
+import re
+import bpy
+from mathutils import Vector, Matrix
+from math import radians as rad, degrees
+from bpy.props import StringProperty, BoolProperty, FloatProperty, IntProperty
+
+
+class DataStructure:
+    """
+        Parse the Skeleton and Motion Files to an internal data structure.
+    """
+    doc = re.compile(r"(?ms):(\w+)\s+([^:]+)")
+    block = re.compile(r"(?ms)begin\s+(.*?)\s+end")
+    bonedata = re.compile(r"(?ms)(name|direction|length|axis|dof)\s+(.*?)\s*$"
+                          "|limits(\s.*)")
+
+    def __init__(self, file_path, scale=1.):
+        self.scale = scale
+        source = open(file_path).read()
+        sections = dict(DataStructure.doc.findall(source))
+        if not sections:
+            raise ValueError("Wrong file structure.")
+
+        if 'units' in sections:
+            units = dict(u.strip().split()
+                for u in sections['units'].splitlines()
+                if u.strip())
+            if 'length' in units:
+                self.scale /= float(units['length'])
+
+        if 'bonedata' not in sections:
+            raise ValueError("Bone data section not found.")
+        bm = DataStructure.block.findall(sections['bonedata'])
+        if not bm:
+            raise ValueError("Bone data section malformed.")
+        self.bones = {'root': {
+            'dof': ['X', 'Y', 'Z'],
+            'direction': Vector(),  # should be orientation of root sector
+            'length': 1,
+            'axis': Matrix(),
+            'axis_inv': Matrix(),
+            }}
+        for b in bm:
+            bd = dict((i[0] or 'limits', i[0] and i[1] or i[2])
+                        for i in DataStructure.bonedata.findall(b))
+            for k in bd:
+                s = [t for t in re.split(r"[^a-zA-Z0-9-+.]", bd[k]) if t]
+                if k == 'axis':
+                    rot = Matrix()
+                    for ang, basis in zip(s[:3], s[3].upper()):
+                        rot = Matrix.Rotation(rad(float(ang)), 4, basis) * rot
+                    bd['axis'] = rot
+                elif k == 'direction':
+                    bd[k] = Vector([float(n) for n in s])
+                elif k == 'length':
+                    bd[k] = float(s[0]) * self.scale
+                elif k == 'dof':
+                    bd[k] = [a[1].upper() for a in s]  # only rotations
+                elif k == 'limits':
+                    bd[k] = s
+            if 'axis' in bd:
+                bd['axis_inv'] = bd['axis'].inverted()
+            self.bones[bd['name']] = bd
+
+        if 'hierarchy' not in sections:
+            raise ValueError("Hierarchy section not found.")
+        hm = DataStructure.block.search(sections['hierarchy'])
+        if not hm:
+            raise ValueError("Hierarchy section malformed.")
+        self.hierarchy = {}
+        for l in hm.group(1).splitlines():
+            t = l.strip().split()
+            self.hierarchy[t[0]] = t[1:]
+
+    def scan_motion_capture(self, filename, skip=5):
+        """
+            Parse an Acclaim Motion Capture file and iterates over the data
+        """
+        amc = open(filename)
+        l = ' '
+        while l and not l[0].isdigit():
+            l = amc.readline().strip()
+        while l:
+            frame = int(l)
+            bdefs = []
+            while True:
+                l = amc.readline().strip()
+                if not l or l[0].isdigit():
+                    break
+                bdefs.append(l.split())
+            if (frame - 1) % skip != 0:
+                continue
+            self.pose_def = {}
+            for b in bdefs:
+                vs = [float(v) for v in b[1:]]
+                if b[0] == 'root':
+                    loc = Vector(vs[:3]) * self.scale
+                    vs = vs[3:]
+                rot = Matrix()
+                for dof, ang in zip(self.bones[b[0]]['dof'], vs):
+                    rot = Matrix.Rotation(rad(ang), 4, dof) * rot
+                self.pose_def[b[0]] = rot
+            pose = self.calculate_pose(Matrix.Translation(loc))
+            yield(frame / skip + 1, pose)
+
+    def calculate_pose(self, parent, bone='root'):
+        """
+            Calculate each bone transform iteratively
+        """
+        bd = self.bones[bone]
+        tail = Matrix.Translation(bd['direction'] * bd['length'])
+        if bone in self.pose_def:
+            tail = bd['axis'] * self.pose_def[bone] * bd['axis_inv'] * tail
+        world = parent * tail
+        local = parent.inverted() * world
+        yield(bone, world, local)
+        if bone in self.hierarchy:
+            for child in self.hierarchy[bone]:
+                for b, w, l in self.calculate_pose(world, child):
+                    yield(b, w, l)
+
+
+class StructureBuilder(DataStructure):
+    def __init__(self, file_path, name="Skel", scale=1.):
+        """
+            Setup instance data and load the skeleton
+        """
+        self.file_path = file_path
+        self.name = name
+        self.user_def_scale = scale
+        DataStructure.__init__(self, file_path, scale)
+
+    def create_armature(self):
+        """
+            Create the armature and leave it in edit mode
+        """
+        bpy.context.scene.objects.active = None
+        bpy.ops.object.add(type='ARMATURE', enter_editmode=True)
+        self.object = bpy.context.scene.objects.active
+        self.armature = self.object.data
+        self.object.name = self.name
+        self.armature.name = self.name
+        self.armature.draw_type = 'STICK'
+        self.object['source_file_path'] = self.file_path
+        self.object['source_scale'] = self.user_def_scale
+        self.object['MhxArmature'] = 'Daz'
+
+    def load_armature(self, obj):
+        """
+            Assign the armature object to be used for loading motion
+        """
+        self.object = obj
+
+    def build_structure(self, use_limits=False):
+        """
+            Create the root bone and start the recursion, exit edit mode
+        """
+        self.use_limits = use_limits
+        bpy.ops.armature.bone_primitive_add(name='root')
+        root_dir = Vector((0, 0.1 * self.scale, 0))
+        bpy.ops.transform.translate(value=root_dir + Vector((.0, .0, -1.0)))
+        self.recursive_add_bones()
+        bpy.ops.armature.select_all(action='DESELECT')
+        bpy.ops.object.mode_set(mode='OBJECT')
+
+    def recursive_add_bones(self, parent_name='root'):
+        """
+            Traverse the hierarchy creating bones and constraints
+        """
+        if  parent_name not in self.hierarchy:
+            return
+        for name in self.hierarchy[parent_name]:
+            self.add_bone(name, parent_name)
+            if self.use_limits:
+                self.add_limit_constraint(name)
+            self.recursive_add_bones(name)
+
+    def add_bone(self, name, parent_name):
+        """
+            Extrude a bone from the specified parent, and configure it
+        """
+        bone_def = self.bones[name]
+        bpy.ops.armature.select_all(action='DESELECT')
+        # select tail of parent bone
+        self.armature.edit_bones[parent_name].select_tail = True
+        # extrude and name the new bone
+        bpy.ops.armature.extrude()
+        self.armature.edit_bones[-1].name = name
+        # translate the tail of the new bone
+        tail = bone_def['direction'] * bone_def['length']
+        bpy.ops.transform.translate(value=tail)
+        # align the bone to the rotation axis
+        axis = bone_def['axis'].to_3x3()
+        vec = axis * Vector((.0, .0, -1.0))
+        self.armature.edit_bones[-1].align_roll(vector=vec)
+
+    def add_limit_constraint(self, name):
+        """
+            Create the limit rotation constraint of the specified bone
+        """
+        bpy.ops.object.mode_set(mode='POSE')
+        bone_def = self.bones[name]
+        dof = bone_def['dof'] if 'dof' in bone_def else ''
+        pb = self.object.pose.bones[name]
+        self.armature.bones.active = self.armature.bones[name]
+        bpy.ops.pose.constraint_add(type='LIMIT_ROTATION')
+        constr = pb.constraints[-1]
+        constr.owner_space = 'LOCAL'
+        constr.use_limit_x = True
+        constr.use_limit_y = True
+        constr.use_limit_z = True
+        if dof:
+            limits = (rad(float(v)) for v in bone_def['limits'])
+            if 'X' in dof:
+                constr.min_x = next(limits)
+                constr.max_x = next(limits)
+            if 'Y' in dof:
+                constr.max_z = -next(limits)

@@ Diff output truncated at 10240 characters. @@


More information about the Bf-extensions-cvs mailing list