From 802733e2de82efd381b02f51c7c60cff56a6330b Mon Sep 17 00:00:00 2001 From: ousttrue Date: Wed, 18 May 2011 06:08:55 +0900 Subject: [PATCH] add setup.py --- .gitignore | 4 + README.txt | 9 +- {meshio => blender25-meshio}/__init__.py | 0 blender25-meshio/__init__.pyc | Bin 0 -> 6922 bytes {meshio => blender25-meshio}/bl25.py | 0 {meshio => blender25-meshio}/export_mqo.py | 0 {meshio => blender25-meshio}/export_pmd.py | 0 {meshio => blender25-meshio}/import_mqo.py | 0 {meshio => blender25-meshio}/import_pmd.py | 0 {meshio => blender25-meshio}/pymeshio/__init__.py | 0 .../pymeshio/englishmap.py | 0 blender25-meshio/pymeshio/mmd.py | 333 +++++++++++ {meshio => blender25-meshio}/pymeshio/mqo.py | 6 +- .../mmd.py => blender25-meshio/pymeshio/pmd.py | 643 +-------------------- blender25-meshio/pymeshio/vmd.py | 223 +++++++ blender25-meshio/pymeshio/vpd.py | 86 +++ setup.cfg | 5 + setup.py | 18 + test/mqo_test.py | 9 + test/mqo_test.pyc | Bin 0 -> 489 bytes test/pmd_test.py | 9 + test/pmd_test.pyc | Bin 0 -> 502 bytes 22 files changed, 702 insertions(+), 643 deletions(-) create mode 100644 .gitignore rename {meshio => blender25-meshio}/__init__.py (100%) mode change 100755 => 100644 create mode 100755 blender25-meshio/__init__.pyc rename {meshio => blender25-meshio}/bl25.py (100%) mode change 100755 => 100644 rename {meshio => blender25-meshio}/export_mqo.py (100%) mode change 100755 => 100644 rename {meshio => blender25-meshio}/export_pmd.py (100%) mode change 100755 => 100644 rename {meshio => blender25-meshio}/import_mqo.py (100%) mode change 100755 => 100644 rename {meshio => blender25-meshio}/import_pmd.py (100%) mode change 100755 => 100644 rename {meshio => blender25-meshio}/pymeshio/__init__.py (100%) mode change 100755 => 100644 rename {meshio => blender25-meshio}/pymeshio/englishmap.py (100%) mode change 100755 => 100644 create mode 100644 blender25-meshio/pymeshio/mmd.py rename {meshio => blender25-meshio}/pymeshio/mqo.py (95%) mode change 100755 => 100644 rename meshio/pymeshio/mmd.py => blender25-meshio/pymeshio/pmd.py (57%) mode change 100755 => 100644 create mode 100644 blender25-meshio/pymeshio/vmd.py create mode 100644 blender25-meshio/pymeshio/vpd.py create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test/mqo_test.py create mode 100755 test/mqo_test.pyc create mode 100644 test/pmd_test.py create mode 100755 test/pmd_test.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfe6115 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Makefile +build +dist +pymeshio.egg-info diff --git a/README.txt b/README.txt index afded69..3e5b8c2 100644 --- a/README.txt +++ b/README.txt @@ -19,9 +19,10 @@ Import-Exportの中から"meshio. (.pmd)(.mqo)"を探して右のチェックボ 更新履歴 ======== -20101229 1.05 +20110518 1.10 ------------- -blender2.55向け修正。 +setup.pyとtest導入。 +python2と3両用に改造。 20110429 1.06 ------------- @@ -29,3 +30,7 @@ MeshIOからリポジトリを分離。 blender2.57b向け修正。 blender2.49サポートを終了。 +20101229 1.05 +------------- +blender2.55向け修正。 + diff --git a/meshio/__init__.py b/blender25-meshio/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/__init__.py rename to blender25-meshio/__init__.py diff --git a/blender25-meshio/__init__.pyc b/blender25-meshio/__init__.pyc new file mode 100755 index 0000000000000000000000000000000000000000..24c46909b3d74415a8d2e32f261a1813ea843969 GIT binary patch literal 6922 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 literal 0 HcmV?d00001 diff --git a/meshio/bl25.py b/blender25-meshio/bl25.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/bl25.py rename to blender25-meshio/bl25.py diff --git a/meshio/export_mqo.py b/blender25-meshio/export_mqo.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/export_mqo.py rename to blender25-meshio/export_mqo.py diff --git a/meshio/export_pmd.py b/blender25-meshio/export_pmd.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/export_pmd.py rename to blender25-meshio/export_pmd.py diff --git a/meshio/import_mqo.py b/blender25-meshio/import_mqo.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/import_mqo.py rename to blender25-meshio/import_mqo.py diff --git a/meshio/import_pmd.py b/blender25-meshio/import_pmd.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/import_pmd.py rename to blender25-meshio/import_pmd.py diff --git a/meshio/pymeshio/__init__.py b/blender25-meshio/pymeshio/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/pymeshio/__init__.py rename to blender25-meshio/pymeshio/__init__.py diff --git a/meshio/pymeshio/englishmap.py b/blender25-meshio/pymeshio/englishmap.py old mode 100755 new mode 100644 similarity index 100% rename from meshio/pymeshio/englishmap.py rename to blender25-meshio/pymeshio/englishmap.py diff --git a/blender25-meshio/pymeshio/mmd.py b/blender25-meshio/pymeshio/mmd.py new file mode 100644 index 0000000..5a1614a --- /dev/null +++ b/blender25-meshio/pymeshio/mmd.py @@ -0,0 +1,333 @@ +#!/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() + diff --git a/meshio/pymeshio/mqo.py b/blender25-meshio/pymeshio/mqo.py old mode 100755 new mode 100644 similarity index 95% rename from meshio/pymeshio/mqo.py rename to blender25-meshio/pymeshio/mqo.py index e9eee56..f8b825c --- a/meshio/pymeshio/mqo.py +++ b/blender25-meshio/pymeshio/mqo.py @@ -258,7 +258,11 @@ class Face(object): def withio(method): def new(self, path): - self.io=open(path, encoding='cp932') + print sys.version_info[0] + if sys.version_info[0]<3: + self.io=open(path) + else: + self.io=open(path, encoding='cp932') result=method(self) self.io=None return result diff --git a/meshio/pymeshio/mmd.py b/blender25-meshio/pymeshio/pmd.py old mode 100755 new mode 100644 similarity index 57% rename from meshio/pymeshio/mmd.py rename to blender25-meshio/pymeshio/pmd.py index 00d9ecf..caf4639 --- a/meshio/pymeshio/mmd.py +++ b/blender25-meshio/pymeshio/pmd.py @@ -1,447 +1,7 @@ -#!/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 '' % (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 '' % ( - 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.nameself.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 @@ -705,7 +265,7 @@ class BoneGroup(object): 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', @@ -1142,200 +702,3 @@ class PMDLoader(object): 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 "" % 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() - diff --git a/blender25-meshio/pymeshio/vmd.py b/blender25-meshio/pymeshio/vmd.py new file mode 100644 index 0000000..81596a2 --- /dev/null +++ b/blender25-meshio/pymeshio/vmd.py @@ -0,0 +1,223 @@ +############################################################################### +# 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 '' % (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 '' % ( + 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.nameself.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) + diff --git a/blender25-meshio/pymeshio/vpd.py b/blender25-meshio/pymeshio/vpd.py new file mode 100644 index 0000000..ec3f124 --- /dev/null +++ b/blender25-meshio/pymeshio/vpd.py @@ -0,0 +1,86 @@ +############################################################################### +# 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 "" % 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()=="}" + + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..ba0d4d6 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[nosetests] +verbosity = 3 +detailed-errors = 1 +exclude-dir=blender25-meshio +#color=1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4403a55 --- /dev/null +++ b/setup.py @@ -0,0 +1,18 @@ +import setuptools + +setuptools.setup( + name='pymeshio', + version='1.1.0', + description='pure python 3d model io library', + keywords=[], + author='ousttrue', + author_email='ousttrue@gmail.com', + url='http://meshio.sourceforge.jp/', + license='zlib', + package_dir={ + 'pymeshio': 'blender25-meshio/pymeshio' + }, + packages=['pymeshio'], + test_suite='nose.collector' + ) + diff --git a/test/mqo_test.py b/test/mqo_test.py new file mode 100644 index 0000000..12daf50 --- /dev/null +++ b/test/mqo_test.py @@ -0,0 +1,9 @@ +import pymeshio.mqo +import sys + +MQO_FILE="K:/model/cube.mqo" + +def test_mqo_load(): + io=pymeshio.mqo.IO() + assert io.read(MQO_FILE) + diff --git a/test/mqo_test.pyc b/test/mqo_test.pyc new file mode 100755 index 0000000000000000000000000000000000000000..ce866e49ba58f87345b215567367bb6a7330eb3d GIT binary patch literal 489 zcmbVIQAz_b5S{F{wa8ZS4t^|%?SBzbN`<0rvBjUVu+-g%+ip$Gq)0)3tT*x|p1?bJ z0DTiI{u`2)N#@O)$An*v-S^Lren!75zD6~0$5J_nYf@4NEY}Oo_l0pG9#D6p+8d&h$m(hCN&V|#deOV;x;^}!Gi&P#^U$@{ zSg+@c3L`$q10N&mw38c9Ik_|0TqjS)&XaAs#A$X3`lS*ymuuHRg~X|RmS&oZMHU;R v_ElV!bOhKc$S6jV_0DQfmQilh8l=@f@Z=#NHuoZ;v=xe`N{g2gC?UJWZ4x(Rwn(5}tY7FS_zQl3 z&MX%1F1v5v?!1|KjQ`a;3_d^N41SkK_Z1yHBapNO6aWjc=N>FPkc2)+AKod{V$D2k zkzRk(n&Ju3+d&k^qo_1e6_Hu3bf(iHDs{f;SEY3A1j)GFkPe0f7GN!4?ZFP<6<|wG zA8ZSD0`L@U8{V9adB~a5u)`?g1j>0cw>>CwhoS%bUsF*dEdE zHKMAmHqYa_HnpqglC)tT-~i8w4F}01NikWOELX{ksqGKL7v# literal 0 HcmV?d00001 -- 2.11.0