--- /dev/null
+#!/usr/bin/python
+# coding: utf-8
+"""
+20091202: VPD読み込みを追加
+20100318: PMD書き込みを追加
+20100731: meshioと互換になるように改造
+
+VMDの読み込み
+http://yumin3123.at.webry.info/200810/article_4.html
+http://atupdate.web.fc2.com/vmd_format.htm
+
+PMDの読み込み
+http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
+
+VPDの読み込み
+
+ToDo:
+ rigdid bodies
+ constraints
+"""
+import sys
+import codecs
+import os.path
+import struct
+import math
+import re
+#import numpy
+from decimal import *
+
+ENCODING='cp932'
+
+if sys.version_info[0]>=3:
+ xrange=range
+
+###############################################################################
+# utility
+###############################################################################
+def truncate_zero(src):
+ """
+ 0x00以降を捨てる
+ """
+ pos = src.find(b"\x00")
+ assert(type(src)==bytes)
+ if pos >= 0:
+ return src[:pos]
+ else:
+ return src
+
+def radian_to_degree(x):
+ return x/math.pi * 180.0
+
+
+###############################################################################
+# geometry
+###############################################################################
+class Vector2(object):
+ __slots__=['x', 'y']
+ def __init__(self, x=0, y=0):
+ self.x=x
+ self.y=y
+
+ def __str__(self):
+ return "<%f %f>" % (self.x, self.y)
+
+ def __getitem__(self, key):
+ if key==0:
+ return self.x
+ elif key==1:
+ return self.y
+ else:
+ assert(False)
+
+ def to_tuple(self):
+ return (self.x, self.y)
+
+
+class Vector3(object):
+ __slots__=['x', 'y', 'z']
+ def __init__(self, x=0, y=0, z=0):
+ self.x=x
+ self.y=y
+ self.z=z
+
+ def __str__(self):
+ return "<%f %f %f>" % (self.x, self.y, self.z)
+
+ def __getitem__(self, key):
+ if key==0:
+ return self.x
+ elif key==1:
+ return self.y
+ elif key==2:
+ return self.z
+ else:
+ assert(False)
+
+ def to_tuple(self):
+ return (self.x, self.y, self.z)
+
+class Quaternion(object):
+ __slots__=['x', 'y', 'z', 'w']
+ def __init__(self, x=0, y=0, z=0, w=1):
+ self.x=x
+ self.y=y
+ self.z=z
+ self.w=w
+
+ def __str__(self):
+ return "<%f %f %f %f>" % (self.x, self.y, self.z, self.w)
+
+ def __mul__(self, rhs):
+ u=numpy.array([self.x, self.y, self.z], 'f')
+ v=numpy.array([rhs.x, rhs.y, rhs.z], 'f')
+ xyz=self.w*v+rhs.w*u+numpy.cross(u, v)
+ q=Quaternion(xyz[0], xyz[1], xyz[2], self.w*rhs.w-numpy.dot(u, v))
+ return q
+
+ def dot(self, rhs):
+ return self.x*rhs.x+self.y*rhs.y+self.z*rhs.z+self.w*rhs.w
+
+ def getMatrix(self):
+ sqX=self.x*self.x
+ sqY=self.y*self.y
+ sqZ=self.z*self.z
+ xy=self.x*self.y
+ xz=self.x*self.z
+ yz=self.y*self.z
+ wx=self.w*self.x
+ wy=self.w*self.y
+ wz=self.w*self.z
+ return numpy.array([
+ # 1
+ [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
+ # 2
+ [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
+ # 3
+ [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
+ # 4
+ [0, 0, 0, 1]],
+ 'f')
+
+ def getRHMatrix(self):
+ x=-self.x
+ y=-self.y
+ z=self.z
+ w=self.w
+ sqX=x*x
+ sqY=y*y
+ sqZ=z*z
+ xy=x*y
+ xz=x*z
+ yz=y*z
+ wx=w*x
+ wy=w*y
+ wz=w*z
+ return numpy.array([
+ # 1
+ [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
+ # 2
+ [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
+ # 3
+ [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
+ # 4
+ [0, 0, 0, 1]],
+ 'f')
+
+ def getRollPitchYaw(self):
+ m=self.getMatrix()
+
+ roll = math.atan2(m[0, 1], m[1, 1])
+ pitch = math.asin(-m[2, 1])
+ yaw = math.atan2(m[2, 0], m[2, 2])
+
+ if math.fabs(math.cos(pitch)) < 1.0e-6:
+ roll += m[0, 1] > math.pi if 0.0 else -math.pi
+ yaw += m[2, 0] > math.pi if 0.0 else -math.pi
+
+ return roll, pitch, yaw
+
+ def getSqNorm(self):
+ return self.x*self.x+self.y*self.y+self.z*self.z+self.w*self.w
+
+ def getNormalized(self):
+ f=1.0/self.getSqNorm()
+ q=Quaternion(self.x*f, self.y*f, self.z*f, self.w*f)
+ return q
+
+ def getRightHanded(self):
+ "swap y and z axis"
+ return Quaternion(-self.x, -self.z, -self.y, self.w)
+
+ @staticmethod
+ def createFromAxisAngle(axis, rad):
+ q=Quaternion()
+ half_rad=rad/2.0
+ c=math.cos(half_rad)
+ s=math.sin(half_rad)
+ return Quaternion(axis[0]*s, axis[1]*s, axis[2]*s, c)
+
+
+class RGBA(object):
+ __slots__=['r', 'g', 'b', 'a']
+ def __init__(self, r=0, g=0, b=0, a=1):
+ self.r=r
+ self.g=g
+ self.b=b
+ self.a=a
+
+ def __getitem__(self, key):
+ if key==0:
+ return self.r
+ elif key==1:
+ return self.g
+ elif key==2:
+ return self.b
+ elif key==3:
+ return self.a
+ else:
+ assert(False)
+
+
+
+
+###############################################################################
+# interface
+###############################################################################
+def load_pmd(path):
+ size=os.path.getsize(path)
+ f=open(path, "rb")
+ l=PMDLoader()
+ if l.load(path, f, size):
+ return l
+
+def load_vmd(path):
+ size=os.path.getsize(path)
+ f=open(path, "rb")
+ l=VMDLoader()
+ if l.load(path, f, size):
+ return l
+
+def load_vpd(path):
+ f=open(path, 'rb')
+ if not f:
+ return;
+ size=os.path.getsize(path)
+ l=VPDLoader()
+ if l.load(path, f, size):
+ return l
+
+
+###############################################################################
+# debug
+###############################################################################
+def debug_pmd(path):
+ l=load_pmd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+
+ print(unicode(l).encode(ENCODING))
+ print(l.comment.encode(ENCODING))
+ print("<ボーン>".decode('utf-8').encode(ENCODING))
+ for bone in l.no_parent_bones:
+ print(bone.name.encode(ENCODING))
+ bone.display()
+ #for bone in l.bones:
+ # uni="%s:%s" % (bone.english_name, bone.name)
+ # print uni.encode(ENCODING)
+ #for skin in l.morph_list:
+ # uni="%s:%s" % (skin.english_name, skin.name)
+ # print uni.encode(ENCODING)
+ #for i, v in enumerate(l.vertices):
+ # print i, v
+ #for i, f in enumerate(l.indices):
+ # print i, f
+ for m in l.materials:
+ print(m)
+
+def debug_pmd_write(path, out):
+ l=load_pmd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+
+ if not l.write(out):
+ print("fail to write")
+ sys.exit()
+
+def debug_vmd(path):
+ l=load_vmd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+ print(unicode(l).encode(ENCODING))
+
+ #for m in l.motions[u'センター']:
+ # print m.frame, m.pos
+ for n, m in l.shapes.items():
+ print(unicode(n).encode(ENCODING), getEnglishSkinName(n))
+
+def debug_vpd(path):
+ l=load_vpd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+ for bone in l.pose:
+ print(unicode(bone).encode(ENCODING))
+
+if __name__=="__main__":
+ if len(sys.argv)<2:
+ print("usage: %s {pmd file}" % sys.argv[0])
+ print("usage: %s {vmd file}" % sys.argv[0])
+ print("usage: %s {vpd file}" % sys.argv[0])
+ print("usage: %s {pmd file} {export pmdfile}" % sys.argv[0])
+ sys.exit()
+
+ path=sys.argv[1]
+ if not os.path.exists(path):
+ print("no such file: %s" % path)
+
+ if path.lower().endswith('.pmd'):
+ if len(sys.argv)==2:
+ debug_pmd(path)
+ else:
+ debug_pmd_write(path, sys.argv[2])
+ elif path.lower().endswith('.vmd'):
+ debug_vmd(path)
+ elif path.lower().endswith('.vpd'):
+ debug_vpd(path)
+ else:
+ print("unknown file type: %s" % path)
+ sys.exit()
+
-#!/usr/bin/python
# coding: utf-8
-"""
-20091202: VPD読み込みを追加
-20100318: PMD書き込みを追加
-20100731: meshioと互換になるように改造
-
-VMDの読み込み
-http://yumin3123.at.webry.info/200810/article_4.html
-http://atupdate.web.fc2.com/vmd_format.htm
-
-PMDの読み込み
-http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
-
-VPDの読み込み
-
-ToDo:
- rigdid bodies
- constraints
-"""
-import sys
-import codecs
-import os.path
+import os
import struct
-import math
-import re
-#import numpy
-from decimal import *
-
-ENCODING='cp932'
-
-if sys.version_info[0]>=3:
- xrange=range
-
-###############################################################################
-# utility
-###############################################################################
-def truncate_zero(src):
- """
- 0x00以降を捨てる
- """
- pos = src.find(b"\x00")
- assert(type(src)==bytes)
- if pos >= 0:
- return src[:pos]
- else:
- return src
-
-def radian_to_degree(x):
- return x/math.pi * 180.0
-
-
-###############################################################################
-# geometry
-###############################################################################
-class Vector2(object):
- __slots__=['x', 'y']
- def __init__(self, x=0, y=0):
- self.x=x
- self.y=y
-
- def __str__(self):
- return "<%f %f>" % (self.x, self.y)
-
- def __getitem__(self, key):
- if key==0:
- return self.x
- elif key==1:
- return self.y
- else:
- assert(False)
-
- def to_tuple(self):
- return (self.x, self.y)
-
-
-class Vector3(object):
- __slots__=['x', 'y', 'z']
- def __init__(self, x=0, y=0, z=0):
- self.x=x
- self.y=y
- self.z=z
-
- def __str__(self):
- return "<%f %f %f>" % (self.x, self.y, self.z)
-
- def __getitem__(self, key):
- if key==0:
- return self.x
- elif key==1:
- return self.y
- elif key==2:
- return self.z
- else:
- assert(False)
-
- def to_tuple(self):
- return (self.x, self.y, self.z)
-
-class Quaternion(object):
- __slots__=['x', 'y', 'z', 'w']
- def __init__(self, x=0, y=0, z=0, w=1):
- self.x=x
- self.y=y
- self.z=z
- self.w=w
-
- def __str__(self):
- return "<%f %f %f %f>" % (self.x, self.y, self.z, self.w)
-
- def __mul__(self, rhs):
- u=numpy.array([self.x, self.y, self.z], 'f')
- v=numpy.array([rhs.x, rhs.y, rhs.z], 'f')
- xyz=self.w*v+rhs.w*u+numpy.cross(u, v)
- q=Quaternion(xyz[0], xyz[1], xyz[2], self.w*rhs.w-numpy.dot(u, v))
- return q
-
- def dot(self, rhs):
- return self.x*rhs.x+self.y*rhs.y+self.z*rhs.z+self.w*rhs.w
-
- def getMatrix(self):
- sqX=self.x*self.x
- sqY=self.y*self.y
- sqZ=self.z*self.z
- xy=self.x*self.y
- xz=self.x*self.z
- yz=self.y*self.z
- wx=self.w*self.x
- wy=self.w*self.y
- wz=self.w*self.z
- return numpy.array([
- # 1
- [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
- # 2
- [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
- # 3
- [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
- # 4
- [0, 0, 0, 1]],
- 'f')
-
- def getRHMatrix(self):
- x=-self.x
- y=-self.y
- z=self.z
- w=self.w
- sqX=x*x
- sqY=y*y
- sqZ=z*z
- xy=x*y
- xz=x*z
- yz=y*z
- wx=w*x
- wy=w*y
- wz=w*z
- return numpy.array([
- # 1
- [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
- # 2
- [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
- # 3
- [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
- # 4
- [0, 0, 0, 1]],
- 'f')
-
- def getRollPitchYaw(self):
- m=self.getMatrix()
-
- roll = math.atan2(m[0, 1], m[1, 1])
- pitch = math.asin(-m[2, 1])
- yaw = math.atan2(m[2, 0], m[2, 2])
-
- if math.fabs(math.cos(pitch)) < 1.0e-6:
- roll += m[0, 1] > math.pi if 0.0 else -math.pi
- yaw += m[2, 0] > math.pi if 0.0 else -math.pi
-
- return roll, pitch, yaw
-
- def getSqNorm(self):
- return self.x*self.x+self.y*self.y+self.z*self.z+self.w*self.w
-
- def getNormalized(self):
- f=1.0/self.getSqNorm()
- q=Quaternion(self.x*f, self.y*f, self.z*f, self.w*f)
- return q
-
- def getRightHanded(self):
- "swap y and z axis"
- return Quaternion(-self.x, -self.z, -self.y, self.w)
-
- @staticmethod
- def createFromAxisAngle(axis, rad):
- q=Quaternion()
- half_rad=rad/2.0
- c=math.cos(half_rad)
- s=math.sin(half_rad)
- return Quaternion(axis[0]*s, axis[1]*s, axis[2]*s, c)
-
-
-class RGBA(object):
- __slots__=['r', 'g', 'b', 'a']
- def __init__(self, r=0, g=0, b=0, a=1):
- self.r=r
- self.g=g
- self.b=b
- self.a=a
-
- def __getitem__(self, key):
- if key==0:
- return self.r
- elif key==1:
- return self.g
- elif key==2:
- return self.b
- elif key==3:
- return self.a
- else:
- assert(False)
-
-
-###############################################################################
-# VMD
-###############################################################################
-class ShapeData(object):
- __slots__=['name', 'frame', 'ratio']
- def __init__(self, name):
- self.name=name
- self.frame=-1
- self.ratio=0
-
- def __cmp__(self, other):
- return cmp(self.frame, other.frame)
-
-class MotionData(object):
- __slots__=['name', 'frame', 'pos', 'q', 'complement']
- def __init__(self, name):
- self.name=name
- self.frame=-1
- self.pos=Vector3()
- self.q=Quaternion()
-
- def __cmp__(self, other):
- return cmp(self.frame, other.frame)
-
- def __str__(self):
- return '<MotionData "%s" %d %s%s>' % (self.name, self.frame, self.pos, self.q)
-
-class VMDLoader(object):
- __slots__=['io', 'end', 'signature',
- 'model_name', 'last_frame',
- 'motions', 'shapes', 'cameras', 'lights',
- ]
- def __init__(self):
- self.model_name=''
- self.motions=[]
- self.shapes=[]
- self.cameras=[]
- self.lights=[]
- self.last_frame=0
-
- def __str__(self):
- return '<VMDLoader model: "%s", motion: %d, shape: %d, camera: %d, light: %d>' % (
- self.model_name, len(self.motions), len(self.shapes),
- len(self.cameras), len(self.lights))
-
- def load(self, path, io, end):
- self.io=io
- self.end=end
-
- # signature
- self.signature=truncate_zero(self.io.read(30))
- version=self.validate_signature(self.signature)
- if not version:
- print("invalid signature", self.signature)
- return False
-
- if version==1:
- if not self.load_verstion_1():
- return False
- elif version==2:
- if not self.load_verstion_2():
- return False
- else:
- raise Exception("unknown version")
-
- # post process
- motions=self.motions
- self.motions={}
- for m in motions:
- if not m.name in self.motions:
- self.motions[m.name]=[]
- self.motions[m.name].append(m)
- for name in self.motions.keys():
- self.motions[name].sort()
-
- shapes=self.shapes
- self.shapes={}
- for s in shapes:
- if not s.name in self.shapes:
- self.shapes[s.name]=[]
- self.shapes[s.name].append(s)
- for name in self.shapes.keys():
- self.shapes[name].sort()
-
- return True
-
- def getMotionCount(self):
- count=0
- for v in self.motions.values():
- count+=len(v)
- return count
-
- def getShapeCount(self):
- count=0
- for v in self.shapes.values():
- count+=len(v)
- return count
-
- def load_verstion_1(self):
- # model name
- self.model_name=truncate_zero(self.io.read(10))
- if not self.loadMotion_1():
- return False
- return True
-
- def loadMotion_1(self):
- count=struct.unpack('H', self.io.read(2))[0]
- self.io.read(2)
- for i in xrange(0, count):
- self.loadFrameData()
- return True
-
- ############################################################
- def load_verstion_2(self):
- # model name
- self.model_name=truncate_zero(self.io.read(20))
-
- if not self.loadMotion():
- return False
- if not self.loadShape():
- return False
- if not self.loadCamera():
- return False
- if not self.loadLight():
- return False
- #assert(self.io.tell()==self.end)
- #self.motions.sort(lambda l, r: l.name<r.name)
-
- return True
-
- def validate_signature(self, signature):
- if self.signature == "Vocaloid Motion Data 0002":
- return 2
- if self.signature == "Vocaloid Motion Data file":
- return 1
- else:
- return None
-
- def loadMotion(self):
- count=struct.unpack('I', self.io.read(4))[0]
- for i in xrange(0, count):
- self.loadFrameData()
- return True
-
- def loadShape(self):
- count=struct.unpack('I', self.io.read(4))[0]
- for i in xrange(0, count):
- self.loadShapeData()
- return True
-
- def loadCamera(self):
- count=struct.unpack('I', self.io.read(4))[0]
- for i in xrange(0, count):
- # not implemented
- assert(False)
- pass
- return True
-
- def loadLight(self):
- count=struct.unpack('I', self.io.read(4))[0]
- for i in xrange(0, count):
- # not implemented
- assert(False)
- pass
- return True
-
- def loadFrameData(self):
- """
- フレームひとつ分を読み込む
- """
- data=MotionData(truncate_zero(self.io.read(15)))
- (data.frame, data.pos.x, data.pos.y, data.pos.z,
- data.q.x, data.q.y, data.q.z, data.q.w) = struct.unpack(
- 'I7f', self.io.read(32))
- # complement data
- data.complement=''.join(
- ['%x' % x for x in struct.unpack('64B', self.io.read(64))])
- self.motions.append(data)
- if data.frame>self.last_frame:
- self.last_frame=data.frame
-
- def loadShapeData(self):
- """
- モーフデータひとつ分を読み込む
- """
- data=ShapeData(truncate_zero(self.io.read(15)))
- (data.frame, data.ratio)=struct.unpack('If', self.io.read(8))
- self.shapes.append(data)
- if data.frame>self.last_frame:
- self.last_frame=data.frame
-
- # vmd -> csv
- ############################################################
- def create_csv_line(m):
- # quaternion -> euler angle
- (roll, pitch, yaw)=m.q.getRollPitchYaw()
- return '%s,%d,%g,%g,%g,%g,%g,%g,0x%s\n' % (
- m.name, m.frame, m.pos.x, m.pos.y, m.pos.z,
- to_degree(pitch), to_degree(yaw), to_degree(roll), m.complement
- )
-
- def write_csv(l, path):
- sys.setdefaultencoding('cp932')
- csv=open(path, "w")
- csv.write('%s,0\n' % l.signature)
- csv.write('%s\n' % l.model_name)
- # motion
- csv.write('%d\n' % len(l.motions))
- for m in l.motions:
- csv.write(create_csv_line(m))
- # shape
- csv.write('%d\n' % len(l.shapes))
- for s in l.shapes:
- csv.write('%s,%d,%f\n' % ( s.name, s.frame, s.ratio))
- # camera
- csv.write('%d\n' % len(l.cameras))
- for camera in l.cameras:
- assert(False)
- # light
- csv.write('%d\n' % len(l.lights))
- for light in l.lights:
- assert(False)
-
+from mmd import *
###############################################################################
# PMD
def getEnglishName(self): return self.english_name.decode('cp932')
def setEnglishName(self, u): self.english_name=u
-class PMDLoader(object):
+class IO(object):
__slots__=['io', 'end', 'pos',
'version', 'model_name', 'comment',
'english_model_name', 'english_comment',
struct.unpack("124s", self.io.read(124))[0]
return True
-
-###############################################################################
-# VPD
-###############################################################################
-class LineLoader(object):
- """
- 行指向の汎用ローダ
- """
- __slots__=['path', 'io', 'end']
- def __str__(self):
- return "<%s current:%d, end:%d>" % (
- self.__class__, self.getPos(), self.getEnd())
-
- def getPos(self):
- return self.io.tell()
-
- def getEnd(self):
- return self.end
-
- def readline(self):
- return (self.io.readline()).strip()
-
- def isEnd(self):
- return self.io.tell()>=self.end
-
- def load(self, path, io, end):
- self.path=path
- self.io=io
- self.end=end
- return self.process()
-
- def process(self):
- """
- dummy. read to end.
- """
- while not self.isEnd():
- self.io.readline()
- return True
-
-
-class VPDLoader(LineLoader):
- __slots__=['pose']
- def __init__(self):
- super(VPDLoader, self).__init__()
- self.pose=[]
-
- def __str__(self):
- return "<VPD poses:%d>" % len(self.pose)
-
- def process(self):
- if self.readline()!="Vocaloid Pose Data file":
- return
-
- RE_OPEN=re.compile('^(\w+){(.*)')
- RE_OSM=re.compile('^\w+\.osm;')
- RE_COUNT=re.compile('^(\d+);')
-
- bone_count=-1
- while not self.isEnd():
- line=self.readline()
- if line=='':
- continue
- m=RE_OPEN.match(line)
- if m:
- if not self.parseBone(m.group(2)):
- raise Exception("invalid bone")
- continue
-
- m=RE_OSM.match(line)
- if m:
- continue
-
- m=RE_COUNT.match(line)
- if m:
- bone_count=int(m.group(1))
- continue
-
- return len(self.pose)==bone_count
-
- def parseBone(self, name):
- bone=MotionData(name)
- self.pose.append(bone)
- bone.pos=Vector3(*[float(token) for token in self.readline().split(';')[0].split(',')])
- bone.q=Quaternion(*[float(token) for token in self.readline().split(';')[0].split(',')])
- return self.readline()=="}"
-
-
-###############################################################################
-# interface
-###############################################################################
-def load_pmd(path):
- size=os.path.getsize(path)
- f=open(path, "rb")
- l=PMDLoader()
- if l.load(path, f, size):
- return l
-
-def load_vmd(path):
- size=os.path.getsize(path)
- f=open(path, "rb")
- l=VMDLoader()
- if l.load(path, f, size):
- return l
-
-def load_vpd(path):
- f=open(path, 'rb')
- if not f:
- return;
- size=os.path.getsize(path)
- l=VPDLoader()
- if l.load(path, f, size):
- return l
-
-
-###############################################################################
-# debug
-###############################################################################
-def debug_pmd(path):
- l=load_pmd(path)
- if not l:
- print("fail to load")
- sys.exit()
-
- print(unicode(l).encode(ENCODING))
- print(l.comment.encode(ENCODING))
- print("<ボーン>".decode('utf-8').encode(ENCODING))
- for bone in l.no_parent_bones:
- print(bone.name.encode(ENCODING))
- bone.display()
- #for bone in l.bones:
- # uni="%s:%s" % (bone.english_name, bone.name)
- # print uni.encode(ENCODING)
- #for skin in l.morph_list:
- # uni="%s:%s" % (skin.english_name, skin.name)
- # print uni.encode(ENCODING)
- #for i, v in enumerate(l.vertices):
- # print i, v
- #for i, f in enumerate(l.indices):
- # print i, f
- for m in l.materials:
- print(m)
-
-def debug_pmd_write(path, out):
- l=load_pmd(path)
- if not l:
- print("fail to load")
- sys.exit()
-
- if not l.write(out):
- print("fail to write")
- sys.exit()
-
-def debug_vmd(path):
- l=load_vmd(path)
- if not l:
- print("fail to load")
- sys.exit()
- print(unicode(l).encode(ENCODING))
-
- #for m in l.motions[u'センター']:
- # print m.frame, m.pos
- for n, m in l.shapes.items():
- print(unicode(n).encode(ENCODING), getEnglishSkinName(n))
-
-def debug_vpd(path):
- l=load_vpd(path)
- if not l:
- print("fail to load")
- sys.exit()
- for bone in l.pose:
- print(unicode(bone).encode(ENCODING))
-
-if __name__=="__main__":
- if len(sys.argv)<2:
- print("usage: %s {pmd file}" % sys.argv[0])
- print("usage: %s {vmd file}" % sys.argv[0])
- print("usage: %s {vpd file}" % sys.argv[0])
- print("usage: %s {pmd file} {export pmdfile}" % sys.argv[0])
- sys.exit()
-
- path=sys.argv[1]
- if not os.path.exists(path):
- print("no such file: %s" % path)
-
- if path.lower().endswith('.pmd'):
- if len(sys.argv)==2:
- debug_pmd(path)
- else:
- debug_pmd_write(path, sys.argv[2])
- elif path.lower().endswith('.vmd'):
- debug_vmd(path)
- elif path.lower().endswith('.vpd'):
- debug_vpd(path)
- else:
- print("unknown file type: %s" % path)
- sys.exit()
-
--- /dev/null
+###############################################################################
+# VMD
+###############################################################################
+class ShapeData(object):
+ __slots__=['name', 'frame', 'ratio']
+ def __init__(self, name):
+ self.name=name
+ self.frame=-1
+ self.ratio=0
+
+ def __cmp__(self, other):
+ return cmp(self.frame, other.frame)
+
+class MotionData(object):
+ __slots__=['name', 'frame', 'pos', 'q', 'complement']
+ def __init__(self, name):
+ self.name=name
+ self.frame=-1
+ self.pos=Vector3()
+ self.q=Quaternion()
+
+ def __cmp__(self, other):
+ return cmp(self.frame, other.frame)
+
+ def __str__(self):
+ return '<MotionData "%s" %d %s%s>' % (self.name, self.frame, self.pos, self.q)
+
+class VMDLoader(object):
+ __slots__=['io', 'end', 'signature',
+ 'model_name', 'last_frame',
+ 'motions', 'shapes', 'cameras', 'lights',
+ ]
+ def __init__(self):
+ self.model_name=''
+ self.motions=[]
+ self.shapes=[]
+ self.cameras=[]
+ self.lights=[]
+ self.last_frame=0
+
+ def __str__(self):
+ return '<VMDLoader model: "%s", motion: %d, shape: %d, camera: %d, light: %d>' % (
+ self.model_name, len(self.motions), len(self.shapes),
+ len(self.cameras), len(self.lights))
+
+ def load(self, path, io, end):
+ self.io=io
+ self.end=end
+
+ # signature
+ self.signature=truncate_zero(self.io.read(30))
+ version=self.validate_signature(self.signature)
+ if not version:
+ print("invalid signature", self.signature)
+ return False
+
+ if version==1:
+ if not self.load_verstion_1():
+ return False
+ elif version==2:
+ if not self.load_verstion_2():
+ return False
+ else:
+ raise Exception("unknown version")
+
+ # post process
+ motions=self.motions
+ self.motions={}
+ for m in motions:
+ if not m.name in self.motions:
+ self.motions[m.name]=[]
+ self.motions[m.name].append(m)
+ for name in self.motions.keys():
+ self.motions[name].sort()
+
+ shapes=self.shapes
+ self.shapes={}
+ for s in shapes:
+ if not s.name in self.shapes:
+ self.shapes[s.name]=[]
+ self.shapes[s.name].append(s)
+ for name in self.shapes.keys():
+ self.shapes[name].sort()
+
+ return True
+
+ def getMotionCount(self):
+ count=0
+ for v in self.motions.values():
+ count+=len(v)
+ return count
+
+ def getShapeCount(self):
+ count=0
+ for v in self.shapes.values():
+ count+=len(v)
+ return count
+
+ def load_verstion_1(self):
+ # model name
+ self.model_name=truncate_zero(self.io.read(10))
+ if not self.loadMotion_1():
+ return False
+ return True
+
+ def loadMotion_1(self):
+ count=struct.unpack('H', self.io.read(2))[0]
+ self.io.read(2)
+ for i in xrange(0, count):
+ self.loadFrameData()
+ return True
+
+ ############################################################
+ def load_verstion_2(self):
+ # model name
+ self.model_name=truncate_zero(self.io.read(20))
+
+ if not self.loadMotion():
+ return False
+ if not self.loadShape():
+ return False
+ if not self.loadCamera():
+ return False
+ if not self.loadLight():
+ return False
+ #assert(self.io.tell()==self.end)
+ #self.motions.sort(lambda l, r: l.name<r.name)
+
+ return True
+
+ def validate_signature(self, signature):
+ if self.signature == "Vocaloid Motion Data 0002":
+ return 2
+ if self.signature == "Vocaloid Motion Data file":
+ return 1
+ else:
+ return None
+
+ def loadMotion(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ self.loadFrameData()
+ return True
+
+ def loadShape(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ self.loadShapeData()
+ return True
+
+ def loadCamera(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ # not implemented
+ assert(False)
+ pass
+ return True
+
+ def loadLight(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ # not implemented
+ assert(False)
+ pass
+ return True
+
+ def loadFrameData(self):
+ """
+ フレームひとつ分を読み込む
+ """
+ data=MotionData(truncate_zero(self.io.read(15)))
+ (data.frame, data.pos.x, data.pos.y, data.pos.z,
+ data.q.x, data.q.y, data.q.z, data.q.w) = struct.unpack(
+ 'I7f', self.io.read(32))
+ # complement data
+ data.complement=''.join(
+ ['%x' % x for x in struct.unpack('64B', self.io.read(64))])
+ self.motions.append(data)
+ if data.frame>self.last_frame:
+ self.last_frame=data.frame
+
+ def loadShapeData(self):
+ """
+ モーフデータひとつ分を読み込む
+ """
+ data=ShapeData(truncate_zero(self.io.read(15)))
+ (data.frame, data.ratio)=struct.unpack('If', self.io.read(8))
+ self.shapes.append(data)
+ if data.frame>self.last_frame:
+ self.last_frame=data.frame
+
+ # vmd -> csv
+ ############################################################
+ def create_csv_line(m):
+ # quaternion -> euler angle
+ (roll, pitch, yaw)=m.q.getRollPitchYaw()
+ return '%s,%d,%g,%g,%g,%g,%g,%g,0x%s\n' % (
+ m.name, m.frame, m.pos.x, m.pos.y, m.pos.z,
+ to_degree(pitch), to_degree(yaw), to_degree(roll), m.complement
+ )
+
+ def write_csv(l, path):
+ sys.setdefaultencoding('cp932')
+ csv=open(path, "w")
+ csv.write('%s,0\n' % l.signature)
+ csv.write('%s\n' % l.model_name)
+ # motion
+ csv.write('%d\n' % len(l.motions))
+ for m in l.motions:
+ csv.write(create_csv_line(m))
+ # shape
+ csv.write('%d\n' % len(l.shapes))
+ for s in l.shapes:
+ csv.write('%s,%d,%f\n' % ( s.name, s.frame, s.ratio))
+ # camera
+ csv.write('%d\n' % len(l.cameras))
+ for camera in l.cameras:
+ assert(False)
+ # light
+ csv.write('%d\n' % len(l.lights))
+ for light in l.lights:
+ assert(False)
+