OSDN Git Service

refactoring pmd.writer
authorousttrue <ousttrue@gmail.com>
Sun, 2 Oct 2011 13:37:47 +0000 (22:37 +0900)
committerousttrue <ousttrue@gmail.com>
Sun, 2 Oct 2011 13:37:47 +0000 (22:37 +0900)
12 files changed:
pymeshio/common.py
pymeshio/mqo/__init__.py
pymeshio/mqo/loader.py
pymeshio/pmd/__init__.py
pymeshio/pmd/loader.py
pymeshio/pmd/writer.py [new file with mode: 0644]
pymeshio/pmx/loader.py
test/mqo_test.py
test/mqo_test.pyc
test/pmd_test.py
test/pmd_test.pyc
test/pmx_test.py

index dca807d..c8c2d75 100644 (file)
@@ -21,6 +21,9 @@ class Vector2(object):
     def __str__(self):\r
         return "<%f %f>" % (self.x, self.y)\r
 \r
+    def __eq__(self, rhs):\r
+        return self.x==rhs.x and self.y==rhs.y\r
+\r
     def __getitem__(self, key):\r
         if key==0:\r
             return self.x\r
@@ -50,6 +53,9 @@ class Vector3(object):
     def __str__(self):\r
         return "<%f %f %f>" % (self.x, self.y, self.z)\r
 \r
+    def __eq__(self, rhs):\r
+        return self.x==rhs.x and self.y==rhs.y and self.z==rhs.z\r
+\r
     def __getitem__(self, key):\r
         if key==0:\r
             return self.x\r
@@ -212,6 +218,9 @@ class RGB(object):
         self.g=g\r
         self.b=b\r
 \r
+    def __eq__(self, rhs):\r
+        return self.r==rhs.r and self.g==rhs.g and self.b==rhs.b\r
+\r
     def __getitem__(self, key):\r
         if key==0:\r
             return self.r\r
@@ -270,14 +279,14 @@ def readall(path):
 class BinaryLoader(object):\r
     """general BinaryLoader\r
     """\r
-    def __init__(self, io):\r
-        self.io=io\r
+    def __init__(self, ios):\r
+        self.ios=ios\r
 \r
     def is_end(self):\r
-        return not self.io.readable()\r
+        return not self.ios.readable()\r
 \r
     def unpack(self, fmt, size):\r
-        result=struct.unpack(fmt, self.io.read(size))\r
+        result=struct.unpack(fmt, self.ios.read(size))\r
         return result[0]\r
 \r
     def read_uint(self, size):\r
@@ -321,3 +330,37 @@ class BinaryLoader(object):
                 self.read_float()\r
                 )\r
 \r
+\r
+class BinaryWriter(object):\r
+    def __init__(self, ios):\r
+        self.ios=ios\r
+\r
+    def write_text(self, v, size=None):\r
+        if size:\r
+            self.ios.write(struct.pack("={0}s".format(size), v))\r
+        else:\r
+            self.ios.write(v)\r
+\r
+    def write_float(self, v):\r
+        self.ios.write(struct.pack("f", v))\r
+\r
+    def write_uint(self, v, size):\r
+        if size==1:\r
+            self.ios.write(struct.pack("B", v))\r
+        elif size==2:\r
+            self.ios.write(struct.pack("H", v))\r
+        elif size==4:\r
+            self.ios.write(struct.pack("I", v))\r
+        else:\r
+            raise WriteError("invalid int uint size")\r
+\r
+    def write_vector2(self, v):\r
+        self.ios.write(struct.pack("=2f", v.x, v.y))\r
+\r
+    def write_vector3(self, v):\r
+        self.ios.write(struct.pack("=3f", v.x, v.y, v.z))\r
+\r
+    def write_rgb(self, v):\r
+        self.ios.write(struct.pack("=3f", v.r, v.g, v.b))\r
+\r
+\r
index e00d9ae..45aca2e 100644 (file)
@@ -233,7 +233,7 @@ class IO(object):
 \r
     def read(self, path):\r
         warnings.warn("'pymeshio.mqo.IO.read' will be replaced by 'pymeshio.mqo.loader.load'")\r
-        model=pymeshio.mqo.loader.load(path)\r
+        model=pymeshio.mqo.loader.load_from_file(path)\r
         if model:\r
             self.has_mikoto=model.has_mikoto\r
             self.materials=model.materials\r
index 561dd67..60f7318 100644 (file)
@@ -23,8 +23,8 @@ class Loader(object):
             "eof", "io", "lines",\r
             "materials", "objects",\r
             ]\r
-    def __init__(self, io):\r
-        self.io=io\r
+    def __init__(self, ios):\r
+        self.ios=ios\r
         self.eof=False\r
         self.lines=0\r
 \r
@@ -33,7 +33,7 @@ class Loader(object):
                 self.lines, len(self.materials), len(self.objects))\r
 \r
     def getline(self):\r
-        line=self.io.readline()\r
+        line=self.ios.readline()\r
         self.lines+=1\r
         if line=="":\r
             self.eof=True\r
@@ -168,59 +168,64 @@ class Loader(object):
         return False\r
 \r
 \r
-def load(path):\r
-    with open(path, 'rb') as io:\r
-        loader=Loader(io)\r
-        model=pymeshio.mqo.Model()\r
+def load_from_file(path):\r
+    with open(path, 'rb') as ios:\r
+        load(ios)\r
 \r
-        line=loader.getline()\r
-        if line!="Metasequoia Document":\r
-            print("invalid signature")\r
-            return False\r
 \r
-        line=loader.getline()\r
-        if line!="Format Text Ver 1.0":\r
-            print("unknown version: %s" % line)\r
+def load(ios):\r
+    assert(isinstance(ios, io.IOBase))\r
+    loader=Loader(ios)\r
+    model=pymeshio.mqo.Model()\r
 \r
-        while True:\r
-            line=loader.getline()\r
-            if line==None:\r
-                # eof\r
-                break;\r
-            if line=="":\r
-                # empty line\r
-                continue\r
+    line=loader.getline()\r
+    if line!="Metasequoia Document":\r
+        print("invalid signature")\r
+        return False\r
 \r
-            tokens=line.split()\r
-            key=tokens[0]\r
-            if key=="Eof":\r
-                return model\r
-            elif key=="Scene":\r
-                if not loader.readChunk():\r
-                    return\r
-            elif key=="Material":\r
-                materials=loader.readMaterial()\r
-                if not materials:\r
-                    return\r
-                model.materials=materials\r
-            elif key=="Object":\r
-                firstQuote=line.find('"')\r
-                secondQuote=line.find('"', firstQuote+1)\r
-                obj=loader.readObject(line[firstQuote+1:secondQuote])\r
-                if not obj:\r
-                    return\r
-                model.objects.append(obj)\r
-            elif key=="BackImage":\r
-                if not loader.readChunk():\r
-                    return\r
-            elif key=="IncludeXml":\r
-                firstQuote=line.find('"')\r
-                secondQuote=line.find('"', firstQuote+1)\r
-                print("IncludeXml", line[firstQuote+1:secondQuote])\r
-            else:\r
-                print("unknown key: %s" % key)\r
-                if not loader.readChunk():\r
-                    return\r
-        # error not reach here\r
-        raise ParseException("invalid eof")\r
+    line=loader.getline()\r
+    if line!="Format Text Ver 1.0":\r
+        print("unknown version: %s" % line)\r
+\r
+    while True:\r
+        line=loader.getline()\r
+        if line==None:\r
+            # eof\r
+            break;\r
+        if line=="":\r
+            # empty line\r
+            continue\r
+\r
+        tokens=line.split()\r
+        key=tokens[0]\r
+        if key=="Eof":\r
+            return model\r
+        elif key=="Scene":\r
+            if not loader.readChunk():\r
+                return\r
+        elif key=="Material":\r
+            materials=loader.readMaterial()\r
+            if not materials:\r
+                return\r
+            model.materials=materials\r
+        elif key=="Object":\r
+            firstQuote=line.find('"')\r
+            secondQuote=line.find('"', firstQuote+1)\r
+            obj=loader.readObject(line[firstQuote+1:secondQuote])\r
+            if not obj:\r
+                return\r
+            model.objects.append(obj)\r
+        elif key=="BackImage":\r
+            if not loader.readChunk():\r
+                return\r
+        elif key=="IncludeXml":\r
+            firstQuote=line.find('"')\r
+            secondQuote=line.find('"', firstQuote+1)\r
+            print("IncludeXml", line[firstQuote+1:secondQuote])\r
+        else:\r
+            print("unknown key: %s" % key)\r
+            if not loader.readChunk():\r
+                return\r
+    # error not reach here\r
+    raise ParseException("invalid eof")\r
 \r
index c365c07..e8c9a44 100644 (file)
@@ -40,6 +40,17 @@ class Vertex(object):
                 str(self.uv), 
                 self.bone0, self.bone1, self.weight0)
 
+    def __eq__(self, rhs):
+        return (
+                self.pos==rhs.pos
+                and self.normal==rhs.normal
+                and self.uv==rhs.uv
+                and self.bone0==rhs.bone0
+                and self.bone1==rhs.bone1
+                and self.weight0==rhs.weight0
+                and self.edge_flag==rhs.edge_flag
+                )
+
     def __getitem__(self, key):
         if key==0:
             return self.pos.x
@@ -90,6 +101,19 @@ class Material(object):
                 self.diffuse[2], self.diffuse[3],
                 )
 
+    def __eq__(self, rhs):
+        return (
+                self.diffuse_color==rhs.diffuse_color
+                and self.alpha==rhs.alpha
+                and self.specular_factor==rhs.specular_factor
+                and self.specular_color==rhs.specular_color
+                and self.ambient_color==rhs.ambient_color
+                and self.toon_index==rhs.toon_index
+                and self.edge_flag==rhs.edge_flag
+                and self.vertex_count==rhs.vertex_count
+                and self.texture_file==rhs.texture_file
+                )
+
 
 class Bone(object):
     """pmd material struct.
@@ -137,6 +161,20 @@ class Bone(object):
         self.children=[]
         self.english_name=''
 
+    def __eq__(self, rhs):
+        return (
+                self.name==rhs.name
+                and self.index==rhs.index
+                and self.type==rhs.type
+                and self.parent_index==rhs.parent_index
+                and self.tail_index==rhs.tail_index
+                and self.tail==rhs.tail
+                and self.ik_index==rhs.ik_index
+                and self.pos==rhs.pos
+                and self.children==rhs.children
+                and self.english_name==rhs.english_name
+                )
+
     def hasParent(self):
         return self.parent_index!=0xFFFF
 
@@ -268,11 +306,20 @@ class IK(object):
     def __str__(self):
         return "<IK index: %d, target: %d, iterations: %d, weight: %f, children: %s(%d)>" %(self.index, self.target, self.iterations, self.weight, '-'.join([str(i) for i in self.children]), len(self.children))
 
+    def __eq__(self, rhs):
+        return (
+                self.index==rhs.index
+                and self.target==rhs.target
+                and self.iterations==rhs.iterations
+                and self.weight==rhs.weight
+                and self.children==rhs.children
+                )
+
 
-class Skin(object):
+class Morph(object):
     __slots__=['name', 'type', 'indices', 'pos_list', 'english_name',
             'vertex_count']
-    def __init__(self, name='skin'):
+    def __init__(self, name):
         self.name=name
         self.type=None
         self.indices=[]
@@ -288,6 +335,16 @@ class Skin(object):
         return '<Skin name: "%s", type: %d, vertex: %d>' % (
             self.name, self.type, len(self.indices))
 
+    def __eq__(self, rhs):
+        return (
+                self.name==rhs.name
+                and self.type==rhs.type
+                and self.indices==rhs.indices
+                and self.pos_list==rhs.pos_list
+                and self.english_name==rhs.english_name
+                and self.vertex_count==rhs.vertex_count
+                )
+
 
 class BoneGroup(object):
     __slots__=['_name', '_english_name']
@@ -349,6 +406,24 @@ class RigidBody(object):
         self.friction=friction
         self.mode=mode
 
+    def __eq__(self, rhs):
+        return (
+                self.name==rhs.name
+                and self.bone_index==rhs.bone_index
+                and self.collision_group==rhs.collision_group
+                and self.no_collision_group==rhs.no_collision_group
+                and self.shape_type==rhs.shape_type
+                and self.shape_size==rhs.shape_size
+                and self.shape_position==rhs.shape_position
+                and self.shape_rotation==rhs.shape_rotation
+                and self.mass==rhs.mass
+                and self.linear_damping==rhs.linear_damping
+                and self.angular_damping==rhs.angular_damping
+                and self.restitution==rhs.restitution
+                and self.friction==rhs.friction
+                and self.mode==rhs.mode
+                )
+
 
 class Joint(object):
     __slots__=[ 'name', 'rigidbody_index_a', 'rigidbody_index_b', 
@@ -376,6 +451,21 @@ class Joint(object):
         self.spring_constant_translation=spring_constant_translation
         self.spring_constant_rotation=spring_constant_rotation
 
+    def __eq__(self, rhs):
+        return (
+                self.name==rhs.name
+                and self.rigidbody_index_a==rhs.rigidbody_index_a
+                and self.rigidbody_index_b==rhs.rigidbody_index_b
+                and self.position==rhs.position
+                and self.rotation==rhs.rotation
+                and self.translation_limit_max==rhs.translation_limit_max
+                and self.translation_limit_min==rhs.translation_limit_min
+                and self.rotation_limit_max==rhs.rotation_limit_max
+                and self.rotation_limit_min==rhs.rotation_limit_min
+                and self.spring_constant_translation==rhs.spring_constant_translation
+                and self.spring_constant_rotation==rhs.spring_constant_rotation
+                )
+
 
 class ToonTextures(object):
     __slots__=['_toon_textures']
@@ -412,9 +502,11 @@ class Model(object):
             'vertices', 'indices', 'materials', 'bones', 
             'ik_list', 'morphs',
             'morph_indices', 'bone_group_list', 'bone_display_list',
+            'bone_group_english_list',
             'toon_textures',
-            'no_parent_bones',
             'rigidbodies', 'joints',
+
+            'no_parent_bones',
             ]
     def __init__(self, version):
         self.version=version
@@ -432,6 +524,7 @@ class Model(object):
         self.bone_group_list=[]
         self.bone_display_list=[]
         # extend
+        self.bone_group_english_list=[]
         self.toon_textures=ToonTextures()
         self.rigidbodies=[]
         self.joints=[]
@@ -446,6 +539,27 @@ class Model(object):
             self.version, self.name, len(self.vertices), len(self.indices),
             len(self.materials), len(self.bones), len(self.ik_list), len(self.morph_list))
 
+    def __eq__(self, rhs):
+        return (
+                self.name==rhs.name
+                and self.comment==rhs.comment
+                and self.english_name==rhs.english_name
+                and self.english_comment==rhs.english_comment
+                and self.vertices==rhs.vertices
+                and self.indices==rhs.indices
+                and self.materials==rhs.materials
+                and self.bones==rhs.bones
+                and self.ik_list==rhs.ik_list
+                and self.morphs==rhs.morphs
+                and self.morph_indices==rhs.morph_indices
+                and self.bone_group_list==rhs.bone_group_list
+                and self.bone_display_list==rhs.bone_display_list
+                and self.bone_group_english_list==rhs.bone_group_english_list
+                and self.toon_textures==rhs.toon_textures
+                and self.rigidbodies==rhs.rigidbodies
+                and self.joints==rhs.joints
+                )
+
 
 class IO(object):
     def __init__(self):
@@ -453,7 +567,7 @@ class IO(object):
 
     def read(self, path):
         warnings.warn("'pymeshio.mqo.IO.read' will be replaced by 'pymeshio.mqo.loader.load'")
-        model=pymeshio.pmd.loader.load(path)
+        model=pymeshio.pmd.loader.load_from_file(path)
         if model:
             return True
 
index c897f22..3f16099 100644 (file)
@@ -7,8 +7,8 @@ import pymeshio.pmd
 class Loader(pymeshio.common.BinaryLoader):\r
     """pmx loader\r
     """\r
-    def __init__(self, io, version):\r
-        super(Loader, self).__init__(io)\r
+    def __init__(self, ios, version):\r
+        super(Loader, self).__init__(ios)\r
         self.version=version\r
 \r
     def read_text(self, size):\r
@@ -65,13 +65,13 @@ class Loader(pymeshio.common.BinaryLoader):
         return ik\r
 \r
     def read_morph(self):\r
-        skin=pymeshio.pmd.Skin(self.read_text(20))\r
-        skin_size = self.read_uint(4)\r
-        skin.type = self.read_uint(1)\r
-        for j in range(skin_size):\r
-            skin.indices.append(self.read_uint(4))\r
-            skin.pos_list.append(self.read_vector3())\r
-        return skin\r
+        morph=pymeshio.pmd.Morph(self.read_text(20))\r
+        morph_size = self.read_uint(4)\r
+        morph.type = self.read_uint(1)\r
+        for j in range(morph_size):\r
+            morph.indices.append(self.read_uint(4))\r
+            morph.pos_list.append(self.read_vector3())\r
+        return morph\r
 \r
     def read_rigidbody(self):\r
         return pymeshio.pmd.RigidBody(\r
@@ -150,8 +150,8 @@ def __load(loader, model):
         if morph.name==b'base':\r
             continue\r
         morph.english_name=loader.read_text(20)\r
-    for bone_group in model.bone_group_list:\r
-        bone_group=loader.read_text(50)\r
+    model.bone_group_english_list=[loader.read_text(50)\r
+            for _ in model.bone_group_list]\r
 \r
     ############################################################\r
     # extend2: toon_textures\r
@@ -176,10 +176,13 @@ def __load(loader, model):
     return True\r
 \r
 \r
-def load(path):\r
-    # general binary loader\r
-    #loader=pymeshio.common.BinaryLoader(open(path, 'rb'))\r
-    loader=pymeshio.common.BinaryLoader(io.BytesIO(pymeshio.common.readall(path)))\r
+def load_from_file(path):\r
+    return load(io.BytesIO(pymeshio.common.readall(path)))\r
+\r
+\r
+def load(ios):\r
+    assert(isinstance(ios, io.IOBase))\r
+    loader=pymeshio.common.BinaryLoader(ios)\r
 \r
     # header\r
     signature=loader.unpack("3s", 3)\r
@@ -189,11 +192,12 @@ def load(path):
     version=loader.read_float()\r
 \r
     model=pymeshio.pmd.Model(version)\r
-    loader=Loader(loader.io, version)\r
+    loader=Loader(loader.ios, version)\r
     if(__load(loader, model)):\r
         # check eof\r
         if not loader.is_end():\r
-            print("can not reach eof.")\r
+            #print("can not reach eof.")\r
+            pass\r
 \r
         # build bone tree\r
         for i, child in enumerate(model.bones):\r
@@ -206,143 +210,11 @@ def load(path):
                 parent=model.bones[child.parent_index]\r
                 child.parent=parent\r
                 parent.children.append(child)\r
-            # \8cã\88Ê\92u\r
+            # 後位置\r
             if child.hasChild():\r
                 child.tail=model.bones[child.tail_index].pos\r
 \r
         return model\r
 \r
 \r
-def save(self, path):\r
-    io=open(path, 'wb')\r
-    if not io:\r
-        return False\r
-    # Header\r
-    io.write(b"Pmd")\r
-    io.write(struct.pack("f", self.version))\r
-    io.write(struct.pack("20s", self.name))\r
-    io.write(struct.pack("256s", self.comment))\r
-\r
-    # Vertices\r
-    io.write(struct.pack("I", len(self.vertices)))\r
-    sVertex=struct.Struct("=8f2H2B") # 38byte\r
-    assert(sVertex.size==38)\r
-    for v in self.vertices:\r
-        data=sVertex.pack( \r
-            v.pos[0], v.pos[1], v.pos[2],\r
-            v.normal[0], v.normal[1], v.normal[2],\r
-            v.uv[0], v.uv[1],\r
-            v.bone0, v.bone1, v.weight0, v.edge_flag)\r
-        io.write(data)\r
-\r
-    # Faces\r
-    io.write(struct.pack("I", len(self.indices)))\r
-    io.write(struct.pack("=%dH" % len(self.indices), *self.indices))\r
-\r
-    # material\r
-    io.write(struct.pack("I", len(self.materials)))\r
-    sMaterial=struct.Struct("=3fff3f3fBBI20s") # 70byte\r
-    assert(sMaterial.size==70)\r
-    for m in self.materials:\r
-        io.write(sMaterial.pack(\r
-            m.diffuse[0], m.diffuse[1], m.diffuse[2], m.diffuse[3],\r
-            m.shinness, \r
-            m.specular[0], m.specular[1], m.specular[2],\r
-            m.ambient[0], m.ambient[1], m.ambient[2],\r
-            m.toon_index, m.flag,\r
-            m.vertex_count,\r
-            m.texture\r
-            ))\r
-\r
-    # bone\r
-    io.write(struct.pack("H", len(self.bones)))\r
-    sBone=struct.Struct("=20sHHBH3f")\r
-    assert(sBone.size==39)\r
-    for b in self.bones:\r
-        io.write(sBone.pack(\r
-            b.name,\r
-            b.parent_index, b.tail_index, b.type, b.ik_index,\r
-            b.pos[0], b.pos[1], b.pos[2]))\r
-\r
-    # IK\r
-    io.write(struct.pack("H", len(self.ik_list)))\r
-    for ik in self.ik_list:\r
-        io.write(struct.pack("=2HBHf", \r
-            ik.index, ik.target, ik.length, ik.iterations, ik.weight\r
-            ))\r
-        for c in ik.children:\r
-            io.write(struct.pack("H", c))\r
-\r
-    # skin\r
-    io.write(struct.pack("H", len(self.morph_list)))\r
-    for s in self.morph_list:\r
-        io.write(struct.pack("20sIB", \r
-            s.name, len(s.indices), s.type))\r
-        for i, v in zip(s.indices, s.pos_list):\r
-            io.write(struct.pack("I3f", i, v[0], v[1], v[2]))\r
-\r
-    # skin disp list\r
-    io.write(struct.pack("B", len(self.face_list)))\r
-    for i in self.face_list:\r
-        io.write(struct.pack("H", i))\r
-\r
-    # bone disp list\r
-    io.write(struct.pack("B", len(self.bone_group_list)))\r
-    for g in self.bone_group_list:\r
-        io.write(struct.pack("50s", g.name))\r
-\r
-    io.write(struct.pack("I", len(self.bone_display_list)))\r
-    for l in self.bone_display_list:\r
-        io.write(struct.pack("=HB", *l))\r
-\r
-    ############################################################\r
-    # extend data\r
-    ############################################################\r
-    io.write(struct.pack("B", 1))\r
-    # english name\r
-    io.write(struct.pack("=20s", self.english_name))\r
-    io.write(struct.pack("=256s", self.english_comment))\r
-    # english bone name\r
-    for bone in self.bones:\r
-        io.write(struct.pack("=20s", bone.english_name))\r
-    # english skin list\r
-    for skin in self.morph_list:\r
-        #print(skin.name)\r
-        if skin.name==b'base':\r
-            continue\r
-        io.write(struct.pack("=20s", skin.english_name))\r
-    # english bone list\r
-    for bone_group in self.bone_group_list:\r
-        io.write(struct.pack("50s", bone_group.english_name))\r
-    # toon texture\r
-    for toon_texture in self.toon_textures:\r
-        io.write(struct.pack("=100s", toon_texture))\r
-    # rigid\r
-    io.write(struct.pack("I", len(self.rigidbodies)))\r
-    for r in self.rigidbodies:\r
-        io.write(struct.pack("=20sHBHB14fB",\r
-            r.name, r.boneIndex, r.group, r.target, r.shapeType,\r
-            r.w, r.h, r.d, \r
-            r.position.x, r.position.y, r.position.z, \r
-            r.rotation.x, r.rotation.y, r.rotation.z, \r
-            r.weight,\r
-            r.linearDamping, r.angularDamping, r.restitution,\r
-            r.friction, r.processType))\r
-\r
-    # constraint\r
-    io.write(struct.pack("I", len(self.constraints)))\r
-    for c in self.constraints:\r
-        io.write(struct.pack("=20sII24f",\r
-            c.name, c.rigidA, c.rigidB,\r
-            c.pos.x, c.pos.y, c.pos.z,\r
-            c.rot.x, c.rot.y, c.rot.z,\r
-            c.constraintPosMin.x, c.constraintPosMin.y, c.constraintPosMin.z,\r
-            c.constraintPosMax.x, c.constraintPosMax.y, c.constraintPosMax.z,\r
-            c.constraintRotMin.x, c.constraintRotMin.y, c.constraintRotMin.z,\r
-            c.constraintRotMax.x, c.constraintRotMax.y, c.constraintRotMax.z,\r
-            c.springPos.x, c.springPos.y, c.springPos.z,\r
-            c.springRot.x, c.springRot.y, c.springRot.z\r
-            ))\r
-\r
-    return True\r
 \r
diff --git a/pymeshio/pmd/writer.py b/pymeshio/pmd/writer.py
new file mode 100644 (file)
index 0000000..b1340c4
--- /dev/null
@@ -0,0 +1,152 @@
+# coding: utf-8\r
+import io\r
+import struct\r
+import pymeshio.common\r
+import pymeshio.pmd\r
+\r
+\r
+class Writer(pymeshio.common.BinaryWriter):\r
+    def write_veritices(self, vertices):\r
+        self.write_uint(len(vertices), 4)\r
+        for v in vertices:\r
+            self.write_vector3(v.pos)\r
+            self.write_vector3(v.normal)\r
+            self.write_vector2(v.uv)\r
+            self.write_uint(v.bone0, 2)\r
+            self.write_uint(v.bone1, 2)\r
+            self.write_uint(v.weight0, 1)\r
+            self.write_uint(v.edge_flag, 1)\r
+\r
+    def write_indices(self, indices):\r
+        self.write_uint(len(indices), 4)\r
+        self.ios.write(struct.pack("=%dH" % len(indices), *indices))\r
+\r
+    def write_materials(self, materials):\r
+        self.write_uint(len(materials), 4)\r
+        for m in materials:\r
+            self.write_rgb(m.diffuse_color)\r
+            self.write_float(m.alpha)\r
+            self.write_float(m.specular_factor)\r
+            self.write_rgb(m.specular_color)\r
+            self.write_rgb(m.ambient_color)\r
+            self.write_uint(m.toon_index, 1)\r
+            self.write_uint(m.edge_flag, 1)\r
+            self.write_uint(m.vertex_count, 4)\r
+            self.write_text(m.texture_file, 20)\r
+\r
+    def write_bones(self, bones):\r
+        self.write_uint(len(bones), 2)\r
+        sBone=struct.Struct("=20sHHBH3f")\r
+        assert(sBone.size==39)\r
+        for b in bones:\r
+            self.write_text(b.name, 20)\r
+            self.write_uint(b.parent_index, 2)\r
+            self.write_uint(b.tail_index, 2)\r
+            self.write_uint(b.type, 1)\r
+            self.write_uint(b.ik_index, 2)\r
+            self.write_vector3(b.pos)\r
+\r
+    def write_ik_list(self, ik_list):\r
+        self.write_uint(len(ik_list), 2)\r
+        for ik in ik_list:\r
+            self.write_uint(ik.index, 2)\r
+            self.write_uint(ik.target, 2)\r
+            self.write_uint(len(ik.children), 1)\r
+            self.write_uint(ik.iterations, 2)\r
+            self.write_float(ik.weight)\r
+            self.ios.write(struct.pack("=%dH" % len(ik.children), *ik.children))\r
+\r
+    def write_morphs(self, morphs):\r
+        self.write_uint(len(morphs), 2)\r
+        for morph in morphs:\r
+            self.write_text(morph.name, 20)\r
+            self.write_uint(len(morph.indices), 4)\r
+            self.write_uint(morph.type, 1)\r
+            for i, v in zip(morph.indices, morph.pos_list):\r
+                self.write_uint(i, 4)\r
+                self.write_vector3(v)\r
+\r
+    def write_morph_indices(self, morph_indices):\r
+        self.write_uint(len(morph_indices), 1)\r
+        self.ios.write(struct.pack("=%dH" % len(morph_indices), *morph_indices))\r
+\r
+    def write_bone_group_list(self, bone_group_list):\r
+        self.write_uint(len(bone_group_list), 1)\r
+        for g in bone_group_list:\r
+            self.write_text(g, 50)\r
+\r
+    def write_bone_display_list(self, bone_display_list):\r
+        self.write_uint(len(bone_display_list), 4)\r
+        for l in bone_display_list:\r
+            self.write_uint(l[0], 2)\r
+            self.write_uint(l[1], 1)\r
+\r
+    def write_rigidbodies(self, rigidbodies):\r
+        self.write_uint(len(rigidbodies), 4)\r
+        for r in rigidbodies:\r
+            self.write_text(r.name, 20)\r
+            self.write_uint(r.bone_index, 2)\r
+            self.write_uint(r.collision_group, 1)\r
+            self.write_uint(r.no_collision_group, 2)\r
+            self.write_uint(r.shape_type, 1)\r
+            self.write_vector3(r.shape_size)\r
+            self.write_vector3(r.shape_position)\r
+            self.write_vector3(r.shape_rotation)\r
+            self.write_float(r.mass)\r
+            self.write_float(r.linear_damping)\r
+            self.write_float(r.angular_damping)\r
+            self.write_float(r.restitution)\r
+            self.write_float(r.friction)\r
+            self.write_uint(r.mode, 1)\r
+\r
+    def write_joints(self, joints):\r
+        self.write_uint(len(joints), 4)\r
+        for j in joints:\r
+            self.write_text(j.name, 20)\r
+            self.write_uint(j.rigidbody_index_a, 4)\r
+            self.write_uint(j.rigidbody_index_b, 4)\r
+            self.write_vector3(j.position)\r
+            self.write_vector3(j.rotation)\r
+            self.write_vector3(j.translation_limit_min)\r
+            self.write_vector3(j.translation_limit_max)\r
+            self.write_vector3(j.rotation_limit_min)\r
+            self.write_vector3(j.rotation_limit_max)\r
+            self.write_vector3(j.spring_constant_translation)\r
+            self.write_vector3(j.spring_constant_rotation)\r
+\r
+\r
+def write(ios, model):\r
+    assert(isinstance(ios, io.IOBase))\r
+    assert(isinstance(model, pymeshio.pmd.Model))\r
+    writer=Writer(ios)\r
+    writer.write_text(b"Pmd")\r
+    writer.write_float(model.version)\r
+    writer.write_text(model.name, 20)\r
+    writer.write_text(model.comment, 256)\r
+    writer.write_veritices(model.vertices)\r
+    writer.write_indices(model.indices)\r
+    writer.write_materials(model.materials)\r
+    writer.write_bones(model.bones)\r
+    writer.write_ik_list(model.ik_list)\r
+    writer.write_morphs(model.morphs)\r
+    writer.write_morph_indices(model.morph_indices)\r
+    writer.write_bone_group_list(model.bone_group_list)\r
+    writer.write_bone_display_list(model.bone_display_list)\r
+    # extend data\r
+    writer.write_uint(1, 1)\r
+    writer.write_text(model.english_name, 20)\r
+    writer.write_text(model.english_comment, 256)\r
+    for bone in model.bones:\r
+        writer.write_text(bone.english_name, 20)\r
+    for skin in model.morphs:\r
+        if skin.name==b'base':\r
+            continue\r
+        writer.write_text(skin.english_name, 20)\r
+    for english in model.bone_group_english_list:\r
+        writer.write_text(english, 50)\r
+    for toon_texture in model.toon_textures:\r
+        writer.write_text(toon_texture, 100)\r
+    writer.write_rigidbodies(model.rigidbodies)\r
+    writer.write_joints(model.joints)\r
+    return True\r
+\r
index 2802b43..b78ed96 100644 (file)
@@ -7,7 +7,7 @@ import pymeshio.pmx
 class Loader(pymeshio.common.BinaryLoader):\r
     """pmx loader\r
     """\r
-    def __init__(self, io,\r
+    def __init__(self, ios,\r
             text_encoding,\r
             extended_uv,\r
             vertex_index_size,\r
@@ -17,7 +17,7 @@ class Loader(pymeshio.common.BinaryLoader):
             morph_index_size,\r
             rigidbody_index_size\r
             ):\r
-        super(Loader, self).__init__(io)\r
+        super(Loader, self).__init__(ios)\r
         self.read_text=self.get_read_text(text_encoding)\r
         if extended_uv>0:\r
             raise pymeshio.common.ParseException(\r
@@ -267,14 +267,14 @@ class Loader(pymeshio.common.BinaryLoader):
                 spring_constant_rotation=self.read_vector3())\r
 \r
 \r
-def load(path):\r
-    # general binary loader\r
-    loader=pymeshio.common.BinaryLoader(\r
-            io.BytesIO(\r
-                pymeshio.common.readall(path)))\r
-    #loader=pymeshio.common.BinaryLoader(open(path, 'rb'))\r
+def load_from_file(path):\r
+    return load(io.BytesIO(pymeshio.common.readall(path)))\r
 \r
 \r
+def load(ios):\r
+    assert(isinstance(ios, io.IOBase))\r
+    loader=pymeshio.common.BinaryLoader(ios)\r
+\r
     # header\r
     signature=loader.unpack("4s", 4)\r
     if signature!=b"PMX ":\r
index 5b054c8..1be8cae 100644 (file)
@@ -8,7 +8,7 @@ def test_old_mqo_load():
     assert io.read(MQO_FILE)
 
 def test_mqo_load():
-    model=pymeshio.mqo.loader.load(MQO_FILE)
+    model=pymeshio.mqo.loader.load_from_file(MQO_FILE)
     print(model.materials)
     assert pymeshio.mqo.Model==model.__class__
     assert 6==len(model.materials)
index 3f8c2e3..c489dfc 100644 (file)
Binary files a/test/mqo_test.pyc and b/test/mqo_test.pyc differ
index 606ca10..a4dc095 100644 (file)
@@ -1,16 +1,18 @@
 # coding: utf-8
-import pymeshio.pmd
-import pymeshio.pmd.loader
 import sys
+import io
 import unittest
+import pymeshio.pmd
+import pymeshio.pmd.loader
+import pymeshio.pmd.writer
 
 
 PMD_FILE=u'resources/初音ミクVer2.pmd'
 
 
 def test_old_pmd_load():
-    io=pymeshio.pmd.IO()
-    assert io.read(PMD_FILE)
+    loader=pymeshio.pmd.IO()
+    assert loader.read(PMD_FILE)
 
 
 class TestPmd(unittest.TestCase):
@@ -18,8 +20,8 @@ class TestPmd(unittest.TestCase):
     def setUp(self):
         pass
 
-    def test_read(self):
-        model=pymeshio.pmd.loader.load(PMD_FILE)
+    def test_load(self):
+        model=pymeshio.pmd.loader.load_from_file(PMD_FILE)
         self.assertEqual(pymeshio.pmd.Model,  model.__class__)
         self.assertEqual(u'初音ミク'.encode('cp932'),  model.name)
         self.assertEqual(u'Miku Hatsune'.encode('cp932'),  model.english_name)
@@ -48,3 +50,14 @@ class TestPmd(unittest.TestCase):
         self.assertEqual(45,  len(model.rigidbodies))
         self.assertEqual(27,  len(model.joints))
 
+    def test_write(self):
+        # read source file
+        buf=pymeshio.common.readall(PMD_FILE)
+        # load and write to out
+        model=pymeshio.pmd.loader.load(io.BytesIO(buf))
+        out=io.BytesIO()
+        pymeshio.pmd.writer.write(out, model)
+        # read out buffer again
+        model2=pymeshio.pmd.loader.load(io.BytesIO(out.getvalue()))
+        self.assertEqual(model, model2)
+
index cc09a74..2a940e5 100644 (file)
Binary files a/test/pmd_test.pyc and b/test/pmd_test.pyc differ
index c8e060a..0a040b5 100644 (file)
@@ -12,7 +12,7 @@ class TestPmx(unittest.TestCase):
         pass\r
 \r
     def test_read(self):\r
-        model=pymeshio.pmx.loader.load(PMX_FILE)\r
+        model=pymeshio.pmx.loader.load_from_file(PMX_FILE)\r
         self.assertEqual(pymeshio.pmx.Model,  model.__class__)\r
         self.assertEqual(u'初音ミク',  model.name)\r
         self.assertEqual(u'Miku Hatsune',  model.english_name)\r