[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