• R/O
  • HTTP
  • SSH
  • HTTPS

pymeshio: 提交

pmdとmqoの入出力ライブラリと、それを使ったBlender2.5向けのaddon。


Commit MetaInfo

修訂607dea923e3f6baa83031ae37e22bc2075ee71ff (tree)
時間2011-10-15 16:26:32
作者ousttrue <ousttrue@gmai...>
Commiterousttrue

Log Message

refactoring. separate exporter

Change Summary

差異

--- a/blender26-meshio/export_pmd.py
+++ b/blender26-meshio/export_pmd.py
@@ -35,7 +35,7 @@ This script exports a pmd model.
3535 import io
3636
3737 from . import bl
38-from . import oneskinmesh
38+from . import exporter
3939 from .pymeshio import common
4040 from .pymeshio import pmd
4141 from .pymeshio import englishmap
@@ -52,9 +52,18 @@ def toCP932(s):
5252
5353
5454 def write(self, path):
55- model=pmd.Model(1.0)
56- model.name=self.name.encode('cp932')
57- model.comment=self.comment.encode('cp932')
55+ model=pmd.Model()
56+
57+ o=self.root.o
58+ englishName=o.name
59+ name=o[bl.MMD_MB_NAME] if bl.MMD_MB_NAME in o else 'Blenderエクスポート'
60+ comment=o[bl.MMD_MB_COMMENT] if bl.MMD_MB_COMMENT in o else 'Blnderエクスポート\n'
61+ englishComment=o[bl.MMD_COMMENT] if bl.MMD_COMMENT in o else 'blender export\n'
62+
63+ model.name=name.encode('cp932')
64+ model.english_name=englishName.encode('cp932')
65+ model.comment=comment.encode('cp932')
66+ model.english_comment=englishComment.encode('cp932')
5867
5968 # 頂点
6069 model.vertices=[pmd.Vertex(
@@ -221,10 +230,6 @@ def write(self, path):
221230 continue
222231 model.bone_display_list.append((i, self.skeleton.getBoneGroup(b)))
223232
224- # English
225- model.english_name=self.englishName.encode('cp932')
226- model.english_comment=self.englishComment.encode('cp932')
227-
228233 # toon
229234 toonMeshObject=None
230235 for o in bl.object.each():
@@ -334,9 +339,9 @@ def write(self, path):
334339 )
335340 for obj in self.oneSkinMesh.constraints]
336341
337- # 書き込み
338342 bl.message('write: %s' % path)
339- return writer.write(io.open(path, 'wb'), model)
343+ with io.open(path, 'wb') as f:
344+ return writer.write(f, model)
340345
341346
342347 def _execute(filepath=''):
@@ -345,10 +350,11 @@ def _execute(filepath=''):
345350 print("abort. no active object.")
346351 return
347352
348- exporter=oneskinmesh.Exporter()
349- exporter.setup()
350- print(exporter)
353+ ex=exporter.Exporter()
354+ ex.setup()
355+ print(ex)
351356
352- write(exporter, filepath)
357+ write(ex, filepath)
353358 bl.object.activate(active)
359+ return {'FINISHED'}
354360
--- a/blender26-meshio/export_pmx.py
+++ b/blender26-meshio/export_pmx.py
@@ -1,18 +1,314 @@
11 # coding: utf-8
22
3+import io
34 from . import bl
5+from . import oneskinmesh
6+from .pymeshio import pmx
7+from .pymeshio import common
8+from .pymeshio.pmx import writer
9+
10+
11+def create_pmx(ex):
12+ model=pmx.Model()
13+ model.name=ex.name
14+ model.comment=ex.comment
15+
16+ def get_deform(b0, b1, weight):
17+ print(b0, b1, weight)
18+ if b0==-1:
19+ return pmx.Bdef1(b1, weight)
20+ elif b1==-1:
21+ return pmx.Bdef1(b0, weight)
22+ else:
23+ return pmx.Bdef2(b0, b1, weight)
24+
25+ model.vertices=[pmx.Vertex(
26+ # convert right-handed z-up to left-handed y-up
27+ common.Vector3(pos[0], pos[2], pos[1]),
28+ # convert right-handed z-up to left-handed y-up
29+ common.Vector3(attribute.nx, attribute.nz, attribute.ny),
30+ # reverse vertical
31+ common.Vector2(attribute.u, 1.0-attribute.v),
32+ get_deform(ex.skeleton.indexByName(b0), ex.skeleton.indexByName(b1), weight),
33+ # edge flag, 0: enable edge, 1: not edge
34+ 1.0
35+ )
36+ for pos, attribute, b0, b1, weight in ex.oneSkinMesh.vertexArray.zip()]
37+
38+ # bones
39+ boneNameMap={}
40+ for i, b in enumerate(self.skeleton.bones):
41+
42+ # name
43+ boneNameMap[b.name]=i
44+ v=englishmap.getUnicodeBoneName(b.name)
45+ if not v:
46+ v=[b.name, b.name]
47+ assert(v)
48+ bone=pmx.Bone(v[1])
49+ bone.english_name=b.name
50+
51+ if len(v)>=3:
52+ # has type
53+ if v[2]==5:
54+ b.ik_index=self.skeleton.indexByName('eyes')
55+ bone.type=v[2]
56+ else:
57+ bone.type=b.type
58+
59+ bone.parent_index=b.parent_index
60+ bone.tail_index=b.tail_index
61+ bone.ik_index=b.ik_index
62+
63+ # convert right-handed z-up to left-handed y-up
64+ bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
65+ bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
66+ bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
67+
68+ model.bones.append(bone)
69+ return model
70+
71+ # IK
72+ for ik in self.skeleton.ik_list:
73+ solver=pmd.IK()
74+ solver.index=self.skeleton.getIndex(ik.target)
75+ solver.target=self.skeleton.getIndex(ik.effector)
76+ solver.length=ik.length
77+ b=self.skeleton.bones[ik.effector.parent_index]
78+ for i in range(solver.length):
79+ solver.children.append(self.skeleton.getIndex(b))
80+ b=self.skeleton.bones[b.parent_index]
81+ solver.iterations=ik.iterations
82+ solver.weight=ik.weight
83+ model.ik_list.append(solver)
84+
85+ # 面とマテリアル
86+ vertexCount=self.oneSkinMesh.getVertexCount()
87+ for material_name, indices in self.oneSkinMesh.vertexArray.each():
88+ #print('material:', material_name)
89+ try:
90+ m=bl.material.get(material_name)
91+ except KeyError as e:
92+ m=DefaultMatrial()
93+ def get_texture_name(texture):
94+ pos=texture.replace("\\", "/").rfind("/")
95+ if pos==-1:
96+ return texture
97+ else:
98+ return texture[pos+1:]
99+ textures=[get_texture_name(path)
100+ for path in bl.material.eachEnalbeTexturePath(m)]
101+ print(textures)
102+ # マテリアル
103+ model.materials.append(pmd.Material(
104+ # diffuse_color
105+ common.RGB(m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2]),
106+ m.alpha,
107+ # specular_factor
108+ 0 if m.specular_toon_size<1e-5 else m.specular_hardness*10,
109+ # specular_color
110+ common.RGB(m.specular_color[0], m.specular_color[1], m.specular_color[2]),
111+ # ambient_color
112+ common.RGB(m.mirror_color[0], m.mirror_color[1], m.mirror_color[2]),
113+ # flag
114+ 1 if m.subsurface_scattering.use else 0,
115+ # toon
116+ 0,
117+ # vertex_count
118+ len(indices),
119+ # texture
120+ ('*'.join(textures) if len(textures)>0 else "").encode('cp932')
121+ ))
122+ # 面
123+ for i in indices:
124+ assert(i<vertexCount)
125+ for i in range(0, len(indices), 3):
126+ # reverse triangle
127+ model.indices.append(indices[i])
128+ model.indices.append(indices[i+1])
129+ model.indices.append(indices[i+2])
130+
131+ # 表情
132+ for i, m in enumerate(self.oneSkinMesh.morphList):
133+ v=englishmap.getUnicodeSkinName(m.name)
134+ if not v:
135+ v=[m.name, m.name, 0]
136+ assert(v)
137+ # morph
138+ morph=pmd.Morph(v[1].encode("cp932"))
139+ morph.english_name=m.name.encode("cp932")
140+ m.type=v[2]
141+ morph.type=v[2]
142+ for index, offset in m.offsets:
143+ # convert right-handed z-up to left-handed y-up
144+ morph.append(index, offset[0], offset[2], offset[1])
145+ morph.vertex_count=len(m.offsets)
146+
147+ # 表情枠
148+ # type==0はbase
149+ for i, m in enumerate(self.oneSkinMesh.morphList):
150+ if m.type==3:
151+ model.morph_indices.append(i)
152+ for i, m in enumerate(self.oneSkinMesh.morphList):
153+ if m.type==2:
154+ model.morph_indices.append(i)
155+ for i, m in enumerate(self.oneSkinMesh.morphList):
156+ if m.type==1:
157+ model.morph_indices.append(i)
158+ for i, m in enumerate(self.oneSkinMesh.morphList):
159+ if m.type==4:
160+ model.morph_indices.append(i)
161+
162+ # ボーングループ
163+ for g in self.skeleton.bone_groups:
164+ name=englishmap.getUnicodeBoneGroupName(g[0])
165+ if not name:
166+ name=g[0]
167+ englishName=g[0]
168+
169+ model.bone_group_list.append(pmd.BoneGroup(
170+ (name+'\n').encode('cp932'),
171+ (englishName+'\n').encode('cp932')
172+ ))
173+
174+ # ボーングループメンバー
175+ for i, b in enumerate(self.skeleton.bones):
176+ if i==0:
177+ continue
178+ if b.type in [6, 7]:
179+ continue
180+ model.bone_display_list.append((i, self.skeleton.getBoneGroup(b)))
181+
182+ # English
183+ model.english_name=self.englishName.encode('cp932')
184+ model.english_comment=self.englishComment.encode('cp932')
185+
186+ # toon
187+ toonMeshObject=None
188+ for o in bl.object.each():
189+ try:
190+ if o.name.startswith(bl.TOON_TEXTURE_OBJECT):
191+ toonMeshObject=o
192+ except:
193+ p(o.name)
194+ break
195+ if toonMeshObject:
196+ toonMesh=bl.object.getData(toonMeshObject)
197+ toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
198+ for i in range(10):
199+ t=bl.material.getTexture(toonMaterial, i)
200+ if t:
201+ model.toon_textures[i]=("%s" % t.name).encode('cp932')
202+ else:
203+ model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
204+ else:
205+ for i in range(10):
206+ model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
207+
208+ # rigid body
209+ rigidNameMap={}
210+ for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
211+ name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name
212+ print(name)
213+ rigidNameMap[name]=i
214+ boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]]
215+ if boneIndex==0:
216+ boneIndex=-1
217+ bone=self.skeleton.bones[0]
218+ else:
219+ bone=self.skeleton.bones[boneIndex]
220+ if obj[bl.RIGID_SHAPE_TYPE]==0:
221+ shape_type=pmd.SHAPE_SPHERE
222+ shape_size=common.Vector3(obj.scale[0], 0, 0)
223+ elif obj[bl.RIGID_SHAPE_TYPE]==1:
224+ shape_type=pmd.SHAPE_BOX
225+ shape_size=common.Vector3(obj.scale[0], obj.scale[1], obj.scale[2])
226+ elif obj[bl.RIGID_SHAPE_TYPE]==2:
227+ shape_type=pmd.SHAPE_CAPSULE
228+ shape_size=common.Vector3(obj.scale[0], obj.scale[2], 0)
229+ rigidBody=pmd.RigidBody(
230+ name.encode('cp932'),
231+ collision_group=obj[bl.RIGID_GROUP],
232+ no_collision_group=obj[bl.RIGID_INTERSECTION_GROUP],
233+ bone_index=boneIndex,
234+ shape_position=common.Vector3(
235+ obj.location.x-bone.pos[0],
236+ obj.location.z-bone.pos[2],
237+ obj.location.y-bone.pos[1]),
238+ shape_rotation=common.Vector3(
239+ -obj.rotation_euler[0],
240+ -obj.rotation_euler[2],
241+ -obj.rotation_euler[1]),
242+ shape_type=shape_type,
243+ shape_size=shape_size,
244+ mass=obj[bl.RIGID_WEIGHT],
245+ linear_damping=obj[bl.RIGID_LINEAR_DAMPING],
246+ angular_damping=obj[bl.RIGID_ANGULAR_DAMPING],
247+ restitution=obj[bl.RIGID_RESTITUTION],
248+ friction=obj[bl.RIGID_FRICTION],
249+ mode=obj[bl.RIGID_PROCESS_TYPE]
250+ )
251+ model.rigidbodies.append(rigidBody)
252+
253+ # constraint
254+ model.joints=[pmd.Joint(
255+ name=obj[bl.CONSTRAINT_NAME].encode('cp932'),
256+ rigidbody_index_a=rigidNameMap[obj[bl.CONSTRAINT_A]],
257+ rigidbody_index_b=rigidNameMap[obj[bl.CONSTRAINT_B]],
258+ position=common.Vector3(
259+ obj.location[0],
260+ obj.location[2],
261+ obj.location[1]),
262+ rotation=common.Vector3(
263+ -obj.rotation_euler[0],
264+ -obj.rotation_euler[2],
265+ -obj.rotation_euler[1]),
266+ translation_limit_min=common.Vector3(
267+ obj[bl.CONSTRAINT_POS_MIN][0],
268+ obj[bl.CONSTRAINT_POS_MIN][1],
269+ obj[bl.CONSTRAINT_POS_MIN][2]
270+ ),
271+ translation_limit_max=common.Vector3(
272+ obj[bl.CONSTRAINT_POS_MAX][0],
273+ obj[bl.CONSTRAINT_POS_MAX][1],
274+ obj[bl.CONSTRAINT_POS_MAX][2]
275+ ),
276+ rotation_limit_min=common.Vector3(
277+ obj[bl.CONSTRAINT_ROT_MIN][0],
278+ obj[bl.CONSTRAINT_ROT_MIN][1],
279+ obj[bl.CONSTRAINT_ROT_MIN][2]),
280+ rotation_limit_max=common.Vector3(
281+ obj[bl.CONSTRAINT_ROT_MAX][0],
282+ obj[bl.CONSTRAINT_ROT_MAX][1],
283+ obj[bl.CONSTRAINT_ROT_MAX][2]),
284+ spring_constant_translation=common.Vector3(
285+ obj[bl.CONSTRAINT_SPRING_POS][0],
286+ obj[bl.CONSTRAINT_SPRING_POS][1],
287+ obj[bl.CONSTRAINT_SPRING_POS][2]),
288+ spring_constant_rotation=common.Vector3(
289+ obj[bl.CONSTRAINT_SPRING_ROT][0],
290+ obj[bl.CONSTRAINT_SPRING_ROT][1],
291+ obj[bl.CONSTRAINT_SPRING_ROT][2])
292+ )
293+ for obj in self.oneSkinMesh.constraints]
294+
295+ # 書き込み
296+ bl.message('write: %s' % path)
297+ return writer.write(io.open(path, 'wb'), model)
4298
5299
6300 def _execute(filepath):
7- print(filepath)
8301 active=bl.object.getActive()
9302 if not active:
10303 print("abort. no active object.")
11304 return
12- exporter=PmdExporter()
305+
306+ exporter=oneskinmesh.Exporter()
13307 exporter.setup()
14- print(exporter)
15- exporter.write(filepath)
308+
309+ model=create_pmx(exporter)
16310 bl.object.activate(active)
311+ with io.open(filepath, 'wb') as f:
312+ writer.write(f, model)
17313 return {'FINISHED'}
18314
--- /dev/null
+++ b/blender26-meshio/exporter/__init__.py
@@ -0,0 +1,51 @@
1+# coding: utf-8
2+"""
3+Blenderのメッシュをワンスキンメッシュ化する
4+"""
5+from .. import bl
6+from . import oneskinmesh
7+from . import bonebuilder
8+
9+
10+class ObjectNode(object):
11+ '''
12+ Objectの木構造構築
13+ '''
14+ __slots__=['o', 'children']
15+ def __init__(self, o):
16+ self.o=o
17+ self.children=[]
18+
19+
20+class Exporter(object):
21+ '''
22+ Blenderから情報収集する
23+ '''
24+ __slots__=[
25+ 'oneSkinMesh',
26+ 'skeleton',
27+ 'root',
28+ ]
29+ def setup(self):
30+ # scene内のオブジェクトの木構造を構築する
31+ object_node_map={}
32+ for o in bl.object.each():
33+ object_node_map[o]=ObjectNode(o)
34+ for o in bl.object.each():
35+ node=object_node_map[o]
36+ if node.o.parent:
37+ object_node_map[node.o.parent].children.append(node)
38+ self.root=object_node_map[bl.object.getActive()]
39+
40+ # ワンスキンメッシュを作る
41+ self.oneSkinMesh=oneskinmesh.OneSkinMesh()
42+ self.oneSkinMesh.build(self.root)
43+ bl.message(self.oneSkinMesh)
44+ if len(self.oneSkinMesh.morphList)==0:
45+ # create emtpy skin
46+ self.oneSkinMesh.createEmptyBasicSkin()
47+
48+ # skeleton
49+ self.skeleton=bonebuilder.BoneBuilder()
50+ self.skeleton.build(self.oneSkinMesh.armatureObj)
51+
--- /dev/null
+++ b/blender26-meshio/exporter/bonebuilder.py
@@ -0,0 +1,238 @@
1+# coding: utf-8
2+from .. import bl
3+from ..pymeshio import englishmap
4+
5+
6+class IKSolver(object):
7+ __slots__=['target', 'effector', 'length', 'iterations', 'weight']
8+ def __init__(self, target, effector, length, iterations, weight):
9+ self.target=target
10+ self.effector=effector
11+ self.length=length
12+ self.iterations=iterations
13+ self.weight=weight
14+
15+
16+class Bone(object):
17+ __slots__=['index', 'name', 'ik_index',
18+ 'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
19+ def __init__(self, name, pos, tail, isConnect):
20+ self.index=-1
21+ self.name=name
22+ self.pos=pos
23+ self.tail=tail
24+ self.parent_index=None
25+ self.tail_index=None
26+ self.type=0
27+ self.isConnect=isConnect
28+ self.ik_index=0
29+
30+ def __eq__(self, rhs):
31+ return self.index==rhs.index
32+
33+ def __str__(self):
34+ return "<Bone %s %d>" % (self.name, self.type)
35+
36+
37+class BoneBuilder(object):
38+ __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',]
39+ def __init__(self):
40+ self.bones=[]
41+ self.boneMap={}
42+ self.ik_list=[]
43+ self.bone_groups=[]
44+
45+ def getBoneGroup(self, bone):
46+ for i, g in enumerate(self.bone_groups):
47+ for b in g[1]:
48+ if b==bone.name:
49+ return i+1
50+ print('no gorup', bone)
51+ return 0
52+
53+ def build(self, armatureObj):
54+ if not armatureObj:
55+ return
56+
57+ bl.message("build skeleton")
58+ armature=bl.object.getData(armatureObj)
59+
60+ ####################
61+ # bone group
62+ ####################
63+ for g in bl.object.boneGroups(armatureObj):
64+ self.bone_groups.append((g.name, []))
65+
66+ ####################
67+ # get bones
68+ ####################
69+ for b in armature.bones.values():
70+ if not b.parent:
71+ # root bone
72+ bone=Bone(b.name,
73+ bl.bone.getHeadLocal(b),
74+ bl.bone.getTailLocal(b),
75+ False)
76+ self.__addBone(bone)
77+ self.__getBone(bone, b)
78+
79+ for b in armature.bones.values():
80+ if not b.parent:
81+ self.__checkConnection(b, None)
82+
83+ ####################
84+ # get IK
85+ ####################
86+ pose = bl.object.getPose(armatureObj)
87+ for b in pose.bones.values():
88+ ####################
89+ # assing bone group
90+ ####################
91+ self.__assignBoneGroup(b, b.bone_group)
92+ for c in b.constraints:
93+ if bl.constraint.isIKSolver(c):
94+ ####################
95+ # IK target
96+ ####################
97+ target=self.__boneByName(bl.constraint.ikTarget(c))
98+ target.type=2
99+
100+ ####################
101+ # IK effector
102+ ####################
103+ # IK 接続先
104+ link=self.__boneByName(b.name)
105+ link.type=6
106+
107+ # IK chain
108+ e=b.parent
109+ chainLength=bl.constraint.ikChainLen(c)
110+ for i in range(chainLength):
111+ # IK影響下
112+ chainBone=self.__boneByName(e.name)
113+ chainBone.type=4
114+ chainBone.ik_index=target.index
115+ e=e.parent
116+ self.ik_list.append(
117+ IKSolver(target, link, chainLength,
118+ int(bl.constraint.ikItration(c) * 0.1),
119+ bl.constraint.ikRotationWeight(c)
120+ ))
121+
122+ ####################
123+
124+ # boneのsort
125+ self._sortBy()
126+ self._fix()
127+ # IKのsort
128+ def getIndex(ik):
129+ for i, v in enumerate(englishmap.boneMap):
130+ if v[0]==ik.target.name:
131+ return i
132+ return len(englishmap.boneMap)
133+ self.ik_list.sort(key=getIndex)
134+
135+ def __assignBoneGroup(self, poseBone, boneGroup):
136+ if boneGroup:
137+ for g in self.bone_groups:
138+ if g[0]==boneGroup.name:
139+ g[1].append(poseBone.name)
140+
141+ def __checkConnection(self, b, p):
142+ if bl.bone.isConnected(b):
143+ parent=self.__boneByName(p.name)
144+ parent.isConnect=True
145+
146+ for c in b.children:
147+ self.__checkConnection(c, b)
148+
149+ def _sortBy(self):
150+ """
151+ boneMap順に並べ替える
152+ """
153+ boneMap=englishmap.boneMap
154+ original=self.bones[:]
155+ def getIndex(bone):
156+ for i, k_v in enumerate(boneMap):
157+ if k_v[0]==bone.name:
158+ return i
159+ print(bone)
160+ return len(boneMap)
161+
162+ self.bones.sort(key=getIndex)
163+
164+ sortMap={}
165+ for i, b in enumerate(self.bones):
166+ src=original.index(b)
167+ sortMap[src]=i
168+ for b in self.bones:
169+ b.index=sortMap[b.index]
170+ if b.parent_index:
171+ b.parent_index=sortMap[b.parent_index]
172+ if b.tail_index:
173+ b.tail_index=sortMap[b.tail_index]
174+ if b.ik_index>0:
175+ b.ik_index=sortMap[b.ik_index]
176+
177+ def _fix(self):
178+ """
179+ 調整
180+ """
181+ for b in self.bones:
182+ # parent index
183+ if b.parent_index==None:
184+ b.parent_index=0xFFFF
185+ else:
186+ if b.type==6 or b.type==7:
187+ # fix tail bone
188+ parent=self.bones[b.parent_index]
189+ #print('parnet', parent.name)
190+ parent.tail_index=b.index
191+
192+ for b in self.bones:
193+ if b.tail_index==None:
194+ b.tail_index=0
195+ elif b.type==9:
196+ b.tail_index==0
197+
198+ def getIndex(self, bone):
199+ for i, b in enumerate(self.bones):
200+ if b==bone:
201+ return i
202+ assert(false)
203+
204+ def indexByName(self, name):
205+ if name=='':
206+ return 0
207+ else:
208+ try:
209+ return self.getIndex(self.__boneByName(name))
210+ except:
211+ return 0
212+
213+ def __boneByName(self, name):
214+ return self.boneMap[name]
215+
216+ def __getBone(self, parent, b):
217+ if len(b.children)==0:
218+ parent.type=7
219+ return
220+
221+ for i, c in enumerate(b.children):
222+ bone=Bone(c.name,
223+ bl.bone.getHeadLocal(c),
224+ bl.bone.getTailLocal(c),
225+ bl.bone.isConnected(c))
226+ self.__addBone(bone)
227+ if parent:
228+ bone.parent_index=parent.index
229+ #if i==0:
230+ if bone.isConnect or (not parent.tail_index and parent.tail==bone.pos):
231+ parent.tail_index=bone.index
232+ self.__getBone(bone, c)
233+
234+ def __addBone(self, bone):
235+ bone.index=len(self.bones)
236+ self.bones.append(bone)
237+ self.boneMap[bone.name]=bone
238+
--- /dev/null
+++ b/blender26-meshio/exporter/oneskinmesh.py
@@ -0,0 +1,378 @@
1+# coding: utf-8
2+import bpy
3+from . import vertexarray
4+from .. import bl
5+from ..pymeshio import englishmap
6+
7+
8+class Morph(object):
9+ __slots__=['name', 'type', 'offsets']
10+ def __init__(self, name, type):
11+ self.name=name
12+ self.type=type
13+ self.offsets=[]
14+
15+ def add(self, index, offset):
16+ self.offsets.append((index, offset))
17+
18+ def sort(self):
19+ self.offsets.sort(key=lambda e: e[0])
20+
21+ def __str__(self):
22+ return "<Morph %s>" % self.name
23+
24+
25+class SSS(object):
26+ def __init__(self):
27+ self.use=1
28+
29+
30+class DefaultMatrial(object):
31+ def __init__(self):
32+ self.name='default'
33+ # diffuse
34+ self.diffuse_color=[1, 1, 1]
35+ self.alpha=1
36+ # specular
37+ self.specular_toon_size=0
38+ self.specular_hardness=5
39+ self.specular_color=[1, 1, 1]
40+ # ambient
41+ self.mirror_color=[1, 1, 1]
42+ # flag
43+ self.subsurface_scattering=SSS()
44+ # texture
45+ self.texture_slots=[]
46+
47+
48+class OneSkinMesh(object):
49+ __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', 'armatureObj']
50+ def __init__(self):
51+ self.vertexArray=vertexarray.VertexArray()
52+ self.morphList=[]
53+ self.rigidbodies=[]
54+ self.constraints=[]
55+ self.armatureObj=None
56+
57+ def __str__(self):
58+ return "<OneSkinMesh %s, morph:%d>" % (
59+ self.vertexArray,
60+ len(self.morphList))
61+
62+ def build(self, node):
63+ ############################################################
64+ # search armature modifier
65+ ############################################################
66+ for m in node.o.modifiers:
67+ if bl.modifier.isType(m, 'ARMATURE'):
68+ armatureObj=bl.modifier.getArmatureObject(m)
69+ if not self.armatureObj:
70+ self.armatureObj=armatureObj
71+ elif self.armatureObj!=armatureObj:
72+ print("warning! found multiple armature. ignored.",
73+ armatureObj.name)
74+
75+ if node.o.type.upper()=='MESH':
76+ self.addMesh(node.o)
77+
78+ for child in node.children:
79+ self.build(child)
80+
81+ def addMesh(self, obj):
82+ if not bl.object.isVisible(obj):
83+ return
84+ self.__mesh(obj)
85+ self.__skin(obj)
86+ self.__rigidbody(obj)
87+ self.__constraint(obj)
88+
89+ def __getWeightMap(self, obj, mesh):
90+ # bone weight
91+ weightMap={}
92+ secondWeightMap={}
93+ def setWeight(i, name, w):
94+ if w>0:
95+ if i in weightMap:
96+ if i in secondWeightMap:
97+ # 上位2つのweightを採用する
98+ if w<secondWeightMap[i][1]:
99+ pass
100+ elif w<weightMap[i][1]:
101+ # 2つ目を入れ替え
102+ secondWeightMap[i]=(name, w)
103+ else:
104+ # 1つ目を入れ替え
105+ weightMap[i]=(name, w)
106+ else:
107+ if w>weightMap[i][1]:
108+ # 多い方をweightMapに
109+ secondWeightMap[i]=weightMap[i]
110+ weightMap[i]=(name, w)
111+ else:
112+ secondWeightMap[i]=(name, w)
113+ else:
114+ weightMap[i]=(name, w)
115+
116+ # ToDo bone weightと関係ないvertex groupを除外する
117+ for i, v in enumerate(mesh.vertices):
118+ if len(v.groups)>0:
119+ for g in v.groups:
120+ setWeight(i, obj.vertex_groups[g.group].name, g.weight)
121+ else:
122+ try:
123+ setWeight(i, obj.vertex_groups[0].name, 1)
124+ except:
125+ # no vertex_groups
126+ pass
127+
128+ # 合計値が1になるようにする
129+ for i in range(len(mesh.vertices)):
130+ if i in secondWeightMap:
131+ secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1])
132+ elif i in weightMap:
133+ weightMap[i]=(weightMap[i][0], 1.0)
134+ secondWeightMap[i]=("", 0)
135+ else:
136+ print("no weight vertex")
137+ weightMap[i]=("", 0)
138+ secondWeightMap[i]=("", 0)
139+
140+ return weightMap, secondWeightMap
141+
142+ def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap):
143+ default_material=DefaultMatrial()
144+ # 各面の処理
145+ for i, face in enumerate(mesh.faces):
146+ faceVertexCount=bl.face.getVertexCount(face)
147+ try:
148+ material=mesh.materials[bl.face.getMaterialIndex(face)]
149+ except IndexError as e:
150+ material=default_material
151+ v=[mesh.vertices[index] for index in bl.face.getVertices(face)]
152+ uv=bl.mesh.getFaceUV(
153+ mesh, i, face, bl.face.getVertexCount(face))
154+ # flip triangle
155+ if faceVertexCount==3:
156+ # triangle
157+ self.vertexArray.addTriangle(
158+ obj_name, material.name,
159+ v[2].index,
160+ v[1].index,
161+ v[0].index,
162+ v[2].co,
163+ v[1].co,
164+ v[0].co,
165+ bl.vertex.getNormal(v[2]),
166+ bl.vertex.getNormal(v[1]),
167+ bl.vertex.getNormal(v[0]),
168+ uv[2],
169+ uv[1],
170+ uv[0],
171+ weightMap[v[2].index][0],
172+ weightMap[v[1].index][0],
173+ weightMap[v[0].index][0],
174+ secondWeightMap[v[2].index][0],
175+ secondWeightMap[v[1].index][0],
176+ secondWeightMap[v[0].index][0],
177+ weightMap[v[2].index][1],
178+ weightMap[v[1].index][1],
179+ weightMap[v[0].index][1]
180+ )
181+ elif faceVertexCount==4:
182+ # quadrangle
183+ self.vertexArray.addTriangle(
184+ obj_name, material.name,
185+ v[2].index,
186+ v[1].index,
187+ v[0].index,
188+ v[2].co,
189+ v[1].co,
190+ v[0].co,
191+ bl.vertex.getNormal(v[2]),
192+ bl.vertex.getNormal(v[1]),
193+ bl.vertex.getNormal(v[0]),
194+ uv[2],
195+ uv[1],
196+ uv[0],
197+ weightMap[v[2].index][0],
198+ weightMap[v[1].index][0],
199+ weightMap[v[0].index][0],
200+ secondWeightMap[v[2].index][0],
201+ secondWeightMap[v[1].index][0],
202+ secondWeightMap[v[0].index][0],
203+ weightMap[v[2].index][1],
204+ weightMap[v[1].index][1],
205+ weightMap[v[0].index][1]
206+ )
207+ self.vertexArray.addTriangle(
208+ obj_name, material.name,
209+ v[0].index,
210+ v[3].index,
211+ v[2].index,
212+ v[0].co,
213+ v[3].co,
214+ v[2].co,
215+ bl.vertex.getNormal(v[0]),
216+ bl.vertex.getNormal(v[3]),
217+ bl.vertex.getNormal(v[2]),
218+ uv[0],
219+ uv[3],
220+ uv[2],
221+ weightMap[v[0].index][0],
222+ weightMap[v[3].index][0],
223+ weightMap[v[2].index][0],
224+ secondWeightMap[v[0].index][0],
225+ secondWeightMap[v[3].index][0],
226+ secondWeightMap[v[2].index][0],
227+ weightMap[v[0].index][1],
228+ weightMap[v[3].index][1],
229+ weightMap[v[2].index][1]
230+ )
231+
232+ def __mesh(self, obj):
233+ if bl.RIGID_SHAPE_TYPE in obj:
234+ return
235+ if bl.CONSTRAINT_A in obj:
236+ return
237+
238+ bl.message("export: %s" % obj.name)
239+
240+ # メッシュのコピーを生成してオブジェクトの行列を適用する
241+ copyMesh, copyObj=bl.object.duplicate(obj)
242+ if len(copyMesh.vertices)>0:
243+ # apply transform
244+ """
245+ try:
246+ # svn 36722
247+ copyObj.scale=obj.scale
248+ bpy.ops.object.transform_apply(scale=True)
249+ copyObj.rotation_euler=obj.rotation_euler
250+ bpy.ops.object.transform_apply(rotation=True)
251+ copyObj.location=obj.location
252+ bpy.ops.object.transform_apply(location=True)
253+ except AttributeError as e:
254+ # 2.57b
255+ copyObj.scale=obj.scale
256+ bpy.ops.object.scale_apply()
257+ copyObj.rotation_euler=obj.rotation_euler
258+ bpy.ops.object.rotation_apply()
259+ copyObj.location=obj.location
260+ bpy.ops.object.location_apply()
261+ """
262+ copyMesh.transform(obj.matrix_world)
263+
264+ # apply modifier
265+ for m in [m for m in copyObj.modifiers]:
266+ if m.type=='SOLIDFY':
267+ continue
268+ elif m.type=='ARMATURE':
269+ continue
270+ elif m.type=='MIRROR':
271+ bpy.ops.object.modifier_apply(modifier=m.name)
272+ else:
273+ print(m.type)
274+
275+ weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh)
276+ self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap)
277+ bl.object.delete(copyObj)
278+
279+ def createEmptyBasicSkin(self):
280+ self.__getOrCreateMorph('base', 0)
281+
282+ def __skin(self, obj):
283+ if not bl.object.hasShapeKey(obj):
284+ return
285+
286+ indexRelativeMap={}
287+ blenderMesh=bl.object.getData(obj)
288+ baseMorph=None
289+
290+ # shape keys
291+ vg=bl.object.getVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME)
292+
293+ # base
294+ used=set()
295+ for b in bl.object.getShapeKeys(obj):
296+ if b.name==bl.BASE_SHAPE_NAME:
297+ baseMorph=self.__getOrCreateMorph('base', 0)
298+ basis=b
299+
300+ relativeIndex=0
301+ for index in vg:
302+ v=bl.shapekey.getByIndex(b, index)
303+ pos=[v[0], v[1], v[2]]
304+
305+ indices=self.vertexArray.getMappedIndex(obj.name, index)
306+ for attribute, i in indices.items():
307+ if i in used:
308+ continue
309+ used.add(i)
310+
311+ baseMorph.add(i, pos)
312+ indexRelativeMap[i]=relativeIndex
313+ relativeIndex+=1
314+
315+ break
316+ assert(basis)
317+ #print(basis.name, len(baseMorph.offsets))
318+
319+ if len(baseMorph.offsets)==0:
320+ return
321+
322+ # shape keys
323+ for b in bl.object.getShapeKeys(obj):
324+ if b.name==bl.BASE_SHAPE_NAME:
325+ continue
326+
327+ #print(b.name)
328+ morph=self.__getOrCreateMorph(b.name, 4)
329+ used=set()
330+ for index, src, dst in zip(
331+ range(len(blenderMesh.vertices)),
332+ bl.shapekey.get(basis),
333+ bl.shapekey.get(b)):
334+ offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
335+ if offset[0]==0 and offset[1]==0 and offset[2]==0:
336+ continue
337+ if index in vg:
338+ indices=self.vertexArray.getMappedIndex(obj.name, index)
339+ for attribute, i in indices.items():
340+ if i in used:
341+ continue
342+ used.add(i)
343+ morph.add(indexRelativeMap[i], offset)
344+ assert(len(morph.offsets)<len(baseMorph.offsets))
345+
346+ # sort skinmap
347+ original=self.morphList[:]
348+ def getIndex(morph):
349+ for i, v in enumerate(englishmap.skinMap):
350+ if v[0]==morph.name:
351+ return i
352+ #print(morph)
353+ return len(englishmap.skinMap)
354+ self.morphList.sort(key=getIndex)
355+
356+ def __rigidbody(self, obj):
357+ if not bl.RIGID_SHAPE_TYPE in obj:
358+ return
359+ self.rigidbodies.append(obj)
360+
361+ def __constraint(self, obj):
362+ if not bl.CONSTRAINT_A in obj:
363+ return
364+ self.constraints.append(obj)
365+
366+ def __getOrCreateMorph(self, name, type):
367+ for m in self.morphList:
368+ if m.name==name:
369+ return m
370+ m=Morph(name, type)
371+ self.morphList.append(m)
372+ return m
373+
374+ def getVertexCount(self):
375+ return len(self.vertexArray.positions)
376+
377+
378+
--- /dev/null
+++ b/blender26-meshio/exporter/vertexarray.py
@@ -0,0 +1,142 @@
1+# coding: utf-8
2+
3+
4+class VertexAttribute(object):
5+ __slots__=[
6+ 'nx', 'ny', 'nz', # normal
7+ 'u', 'v', # uv
8+ ]
9+ def __init__(self, nx, ny, nz, u, v):
10+ self.nx=nx
11+ self.ny=ny
12+ self.nz=nz
13+ self.u=u
14+ self.v=v
15+
16+ def __str__(self):
17+ return "<vkey: %f, %f, %f, %f, %f>" % (
18+ self.nx, self.ny, self.nz, self.u, self.v)
19+
20+ def __hash__(self):
21+ return int(100*(self.nx + self.ny + self.nz + self.u + self.v))
22+
23+ def __eq__(self, rhs):
24+ return self.nx==rhs.nx and self.ny==rhs.ny and self.nz==rhs.nz and self.u==rhs.u and self.v==rhs.v
25+
26+
27+class VertexKey(object):
28+ __slots__=[
29+ 'obj_index', 'index',
30+ ]
31+
32+ def __init__(self, obj_index, index):
33+ self.obj_index=obj_index
34+ self.index=index
35+
36+ def __str__(self):
37+ return "<vkey: %d, %d>" % (self.obj_index, self.index)
38+
39+ def __hash__(self):
40+ return self.index*100+self.obj_index
41+
42+ def __eq__(self, rhs):
43+ return self.obj_index==rhs.obj_index and self.index==rhs.index
44+
45+
46+class VertexArray(object):
47+ """
48+ 頂点配列
49+ """
50+ __slots__=[
51+ 'indexArrays',
52+ 'positions',
53+ 'attributes', # normal and uv
54+ 'b0', 'b1', 'weight',
55+ 'vertexMap',
56+ 'objectMap',
57+ ]
58+ def __init__(self):
59+ # indexArrays split with each material
60+ self.indexArrays={}
61+
62+ self.positions=[]
63+ self.attributes=[]
64+ self.b0=[]
65+ self.b1=[]
66+ self.weight=[]
67+
68+ self.vertexMap={}
69+ self.objectMap={}
70+
71+ def __str__(self):
72+ return "<VertexArray %d positions, %d indexArrays>" % (
73+ len(self.positions), len(self.indexArrays))
74+
75+ def zip(self):
76+ return zip(
77+ self.positions, self.attributes,
78+ self.b0, self.b1, self.weight)
79+
80+ def each(self):
81+ keys=[key for key in self.indexArrays.keys()]
82+ keys.sort()
83+ for key in keys:
84+ yield(key, self.indexArrays[key])
85+
86+ def __addOrGetIndex(self, obj_index, base_index, pos, normal, uv, b0, b1, weight0):
87+ key=VertexKey(obj_index, base_index)
88+ attribute=VertexAttribute(
89+ normal[0], normal[1], normal[2],
90+ uv[0], uv[1])
91+ if key in self.vertexMap:
92+ if attribute in self.vertexMap[key]:
93+ return self.vertexMap[key][attribute]
94+ else:
95+ return self.__addVertex(self.vertexMap[key],
96+ pos, attribute, b0, b1, weight0)
97+ else:
98+ vertexMapKey={}
99+ self.vertexMap[key]=vertexMapKey
100+ return self.__addVertex(vertexMapKey,
101+ pos, attribute, b0, b1, weight0)
102+
103+ def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0):
104+ index=len(self.positions)
105+ vertexMapKey[attribute]=index
106+ # position
107+ self.positions.append((pos.x, pos.y, pos.z))
108+ # unique attribute
109+ self.attributes.append(attribute)
110+ # shared attribute
111+ self.b0.append(b0)
112+ self.b1.append(b1)
113+ self.weight.append(weight0)
114+ assert(index<=65535)
115+ return index
116+
117+ def getMappedIndex(self, obj_name, base_index):
118+ return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)]
119+
120+ def addTriangle(self,
121+ object_name, material,
122+ base_index0, base_index1, base_index2,
123+ pos0, pos1, pos2,
124+ n0, n1, n2,
125+ uv0, uv1, uv2,
126+ b0_0, b0_1, b0_2,
127+ b1_0, b1_1, b1_2,
128+ weight0, weight1, weight2
129+ ):
130+ if object_name in self.objectMap:
131+ obj_index=self.objectMap[object_name]
132+ else:
133+ obj_index=len(self.objectMap)
134+ self.objectMap[object_name]=obj_index
135+ index0=self.__addOrGetIndex(obj_index, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
136+ index1=self.__addOrGetIndex(obj_index, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
137+ index2=self.__addOrGetIndex(obj_index, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
138+
139+ if not material in self.indexArrays:
140+ self.indexArrays[material]=[]
141+ self.indexArrays[material]+=[index0, index1, index2]
142+
--- a/blender26-meshio/oneskinmesh.py
+++ /dev/null
@@ -1,801 +0,0 @@
1-# coding: utf-8
2-"""
3-Blenderのメッシュをワンスキンメッシュ化する
4-"""
5-import bpy
6-
7-from . import bl
8-from .pymeshio import englishmap
9-
10-class VertexAttribute(object):
11- __slots__=[
12- 'nx', 'ny', 'nz', # normal
13- 'u', 'v', # uv
14- ]
15- def __init__(self, nx, ny, nz, u, v):
16- self.nx=nx
17- self.ny=ny
18- self.nz=nz
19- self.u=u
20- self.v=v
21-
22- def __str__(self):
23- return "<vkey: %f, %f, %f, %f, %f>" % (
24- self.nx, self.ny, self.nz, self.u, self.v)
25-
26- def __hash__(self):
27- return int(100*(self.nx + self.ny + self.nz + self.u + self.v))
28-
29- def __eq__(self, rhs):
30- return self.nx==rhs.nx and self.ny==rhs.ny and self.nz==rhs.nz and self.u==rhs.u and self.v==rhs.v
31-
32-
33-class VertexKey(object):
34- __slots__=[
35- 'obj_index', 'index',
36- ]
37-
38- def __init__(self, obj_index, index):
39- self.obj_index=obj_index
40- self.index=index
41-
42- def __str__(self):
43- return "<vkey: %d, %d>" % (self.obj_index, self.index)
44-
45- def __hash__(self):
46- return self.index*100+self.obj_index
47-
48- def __eq__(self, rhs):
49- return self.obj_index==rhs.obj_index and self.index==rhs.index
50-
51-
52-class VertexArray(object):
53- """
54- 頂点配列
55- """
56- __slots__=[
57- 'indexArrays',
58- 'positions',
59- 'attributes', # normal and uv
60- 'b0', 'b1', 'weight',
61- 'vertexMap',
62- 'objectMap',
63- ]
64- def __init__(self):
65- # indexArrays split with each material
66- self.indexArrays={}
67-
68- self.positions=[]
69- self.attributes=[]
70- self.b0=[]
71- self.b1=[]
72- self.weight=[]
73-
74- self.vertexMap={}
75- self.objectMap={}
76-
77- def __str__(self):
78- return "<VertexArray %d positions, %d indexArrays>" % (
79- len(self.positions), len(self.indexArrays))
80-
81- def zip(self):
82- return zip(
83- self.positions, self.attributes,
84- self.b0, self.b1, self.weight)
85-
86- def each(self):
87- keys=[key for key in self.indexArrays.keys()]
88- keys.sort()
89- for key in keys:
90- yield(key, self.indexArrays[key])
91-
92- def __addOrGetIndex(self, obj_index, base_index, pos, normal, uv, b0, b1, weight0):
93- key=VertexKey(obj_index, base_index)
94- attribute=VertexAttribute(
95- normal[0], normal[1], normal[2],
96- uv[0], uv[1])
97- if key in self.vertexMap:
98- if attribute in self.vertexMap[key]:
99- return self.vertexMap[key][attribute]
100- else:
101- return self.__addVertex(self.vertexMap[key],
102- pos, attribute, b0, b1, weight0)
103- else:
104- vertexMapKey={}
105- self.vertexMap[key]=vertexMapKey
106- return self.__addVertex(vertexMapKey,
107- pos, attribute, b0, b1, weight0)
108-
109- def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0):
110- index=len(self.positions)
111- vertexMapKey[attribute]=index
112- # position
113- self.positions.append((pos.x, pos.y, pos.z))
114- # unique attribute
115- self.attributes.append(attribute)
116- # shared attribute
117- self.b0.append(b0)
118- self.b1.append(b1)
119- self.weight.append(weight0)
120- assert(index<=65535)
121- return index
122-
123- def getMappedIndex(self, obj_name, base_index):
124- return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)]
125-
126- def addTriangle(self,
127- object_name, material,
128- base_index0, base_index1, base_index2,
129- pos0, pos1, pos2,
130- n0, n1, n2,
131- uv0, uv1, uv2,
132- b0_0, b0_1, b0_2,
133- b1_0, b1_1, b1_2,
134- weight0, weight1, weight2
135- ):
136- if object_name in self.objectMap:
137- obj_index=self.objectMap[object_name]
138- else:
139- obj_index=len(self.objectMap)
140- self.objectMap[object_name]=obj_index
141- index0=self.__addOrGetIndex(obj_index, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
142- index1=self.__addOrGetIndex(obj_index, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
143- index2=self.__addOrGetIndex(obj_index, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
144-
145- if not material in self.indexArrays:
146- self.indexArrays[material]=[]
147- self.indexArrays[material]+=[index0, index1, index2]
148-
149-
150-class Morph(object):
151- __slots__=['name', 'type', 'offsets']
152- def __init__(self, name, type):
153- self.name=name
154- self.type=type
155- self.offsets=[]
156-
157- def add(self, index, offset):
158- self.offsets.append((index, offset))
159-
160- def sort(self):
161- self.offsets.sort(key=lambda e: e[0])
162-
163- def __str__(self):
164- return "<Morph %s>" % self.name
165-
166-class IKSolver(object):
167- __slots__=['target', 'effector', 'length', 'iterations', 'weight']
168- def __init__(self, target, effector, length, iterations, weight):
169- self.target=target
170- self.effector=effector
171- self.length=length
172- self.iterations=iterations
173- self.weight=weight
174-
175-
176-class SSS(object):
177- def __init__(self):
178- self.use=1
179-
180-
181-class DefaultMatrial(object):
182- def __init__(self):
183- self.name='default'
184- # diffuse
185- self.diffuse_color=[1, 1, 1]
186- self.alpha=1
187- # specular
188- self.specular_toon_size=0
189- self.specular_hardness=5
190- self.specular_color=[1, 1, 1]
191- # ambient
192- self.mirror_color=[1, 1, 1]
193- # flag
194- self.subsurface_scattering=SSS()
195- # texture
196- self.texture_slots=[]
197-
198-
199-class OneSkinMesh(object):
200- __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', ]
201- def __init__(self):
202- self.vertexArray=VertexArray()
203- self.morphList=[]
204- self.rigidbodies=[]
205- self.constraints=[]
206-
207- def __str__(self):
208- return "<OneSkinMesh %s, morph:%d>" % (
209- self.vertexArray,
210- len(self.morphList))
211-
212- def addMesh(self, obj):
213- if not bl.object.isVisible(obj):
214- return
215- self.__mesh(obj)
216- self.__skin(obj)
217- self.__rigidbody(obj)
218- self.__constraint(obj)
219-
220- def __getWeightMap(self, obj, mesh):
221- # bone weight
222- weightMap={}
223- secondWeightMap={}
224- def setWeight(i, name, w):
225- if w>0:
226- if i in weightMap:
227- if i in secondWeightMap:
228- # 上位2つのweightを採用する
229- if w<secondWeightMap[i][1]:
230- pass
231- elif w<weightMap[i][1]:
232- # 2つ目を入れ替え
233- secondWeightMap[i]=(name, w)
234- else:
235- # 1つ目を入れ替え
236- weightMap[i]=(name, w)
237- else:
238- if w>weightMap[i][1]:
239- # 多い方をweightMapに
240- secondWeightMap[i]=weightMap[i]
241- weightMap[i]=(name, w)
242- else:
243- secondWeightMap[i]=(name, w)
244- else:
245- weightMap[i]=(name, w)
246-
247- # ToDo bone weightと関係ないvertex groupを除外する
248- for i, v in enumerate(mesh.vertices):
249- if len(v.groups)>0:
250- for g in v.groups:
251- setWeight(i, obj.vertex_groups[g.group].name, g.weight)
252- else:
253- try:
254- setWeight(i, obj.vertex_groups[0].name, 1)
255- except:
256- # no vertex_groups
257- pass
258-
259- # 合計値が1になるようにする
260- for i in range(len(mesh.vertices)):
261- if i in secondWeightMap:
262- secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1])
263- elif i in weightMap:
264- weightMap[i]=(weightMap[i][0], 1.0)
265- secondWeightMap[i]=("", 0)
266- else:
267- print("no weight vertex")
268- weightMap[i]=("", 0)
269- secondWeightMap[i]=("", 0)
270-
271- return weightMap, secondWeightMap
272-
273- def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap):
274- default_material=DefaultMatrial()
275- # 各面の処理
276- for i, face in enumerate(mesh.faces):
277- faceVertexCount=bl.face.getVertexCount(face)
278- try:
279- material=mesh.materials[bl.face.getMaterialIndex(face)]
280- except IndexError as e:
281- material=default_material
282- v=[mesh.vertices[index] for index in bl.face.getVertices(face)]
283- uv=bl.mesh.getFaceUV(
284- mesh, i, face, bl.face.getVertexCount(face))
285- # flip triangle
286- if faceVertexCount==3:
287- # triangle
288- self.vertexArray.addTriangle(
289- obj_name, material.name,
290- v[2].index,
291- v[1].index,
292- v[0].index,
293- v[2].co,
294- v[1].co,
295- v[0].co,
296- bl.vertex.getNormal(v[2]),
297- bl.vertex.getNormal(v[1]),
298- bl.vertex.getNormal(v[0]),
299- uv[2],
300- uv[1],
301- uv[0],
302- weightMap[v[2].index][0],
303- weightMap[v[1].index][0],
304- weightMap[v[0].index][0],
305- secondWeightMap[v[2].index][0],
306- secondWeightMap[v[1].index][0],
307- secondWeightMap[v[0].index][0],
308- weightMap[v[2].index][1],
309- weightMap[v[1].index][1],
310- weightMap[v[0].index][1]
311- )
312- elif faceVertexCount==4:
313- # quadrangle
314- self.vertexArray.addTriangle(
315- obj_name, material.name,
316- v[2].index,
317- v[1].index,
318- v[0].index,
319- v[2].co,
320- v[1].co,
321- v[0].co,
322- bl.vertex.getNormal(v[2]),
323- bl.vertex.getNormal(v[1]),
324- bl.vertex.getNormal(v[0]),
325- uv[2],
326- uv[1],
327- uv[0],
328- weightMap[v[2].index][0],
329- weightMap[v[1].index][0],
330- weightMap[v[0].index][0],
331- secondWeightMap[v[2].index][0],
332- secondWeightMap[v[1].index][0],
333- secondWeightMap[v[0].index][0],
334- weightMap[v[2].index][1],
335- weightMap[v[1].index][1],
336- weightMap[v[0].index][1]
337- )
338- self.vertexArray.addTriangle(
339- obj_name, material.name,
340- v[0].index,
341- v[3].index,
342- v[2].index,
343- v[0].co,
344- v[3].co,
345- v[2].co,
346- bl.vertex.getNormal(v[0]),
347- bl.vertex.getNormal(v[3]),
348- bl.vertex.getNormal(v[2]),
349- uv[0],
350- uv[3],
351- uv[2],
352- weightMap[v[0].index][0],
353- weightMap[v[3].index][0],
354- weightMap[v[2].index][0],
355- secondWeightMap[v[0].index][0],
356- secondWeightMap[v[3].index][0],
357- secondWeightMap[v[2].index][0],
358- weightMap[v[0].index][1],
359- weightMap[v[3].index][1],
360- weightMap[v[2].index][1]
361- )
362-
363- def __mesh(self, obj):
364- if bl.RIGID_SHAPE_TYPE in obj:
365- return
366- if bl.CONSTRAINT_A in obj:
367- return
368-
369- bl.message("export: %s" % obj.name)
370-
371- # メッシュのコピーを生成してオブジェクトの行列を適用する
372- copyMesh, copyObj=bl.object.duplicate(obj)
373- if len(copyMesh.vertices)>0:
374- # apply transform
375- """
376- try:
377- # svn 36722
378- copyObj.scale=obj.scale
379- bpy.ops.object.transform_apply(scale=True)
380- copyObj.rotation_euler=obj.rotation_euler
381- bpy.ops.object.transform_apply(rotation=True)
382- copyObj.location=obj.location
383- bpy.ops.object.transform_apply(location=True)
384- except AttributeError as e:
385- # 2.57b
386- copyObj.scale=obj.scale
387- bpy.ops.object.scale_apply()
388- copyObj.rotation_euler=obj.rotation_euler
389- bpy.ops.object.rotation_apply()
390- copyObj.location=obj.location
391- bpy.ops.object.location_apply()
392- """
393- copyMesh.transform(obj.matrix_world)
394-
395- # apply modifier
396- for m in [m for m in copyObj.modifiers]:
397- if m.type=='SOLIDFY':
398- continue
399- elif m.type=='ARMATURE':
400- continue
401- elif m.type=='MIRROR':
402- bpy.ops.object.modifier_apply(modifier=m.name)
403- else:
404- print(m.type)
405-
406- weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh)
407- self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap)
408- bl.object.delete(copyObj)
409-
410- def createEmptyBasicSkin(self):
411- self.__getOrCreateMorph('base', 0)
412-
413- def __skin(self, obj):
414- if not bl.object.hasShapeKey(obj):
415- return
416-
417- indexRelativeMap={}
418- blenderMesh=bl.object.getData(obj)
419- baseMorph=None
420-
421- # shape keys
422- vg=bl.object.getVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME)
423-
424- # base
425- used=set()
426- for b in bl.object.getShapeKeys(obj):
427- if b.name==bl.BASE_SHAPE_NAME:
428- baseMorph=self.__getOrCreateMorph('base', 0)
429- basis=b
430-
431- relativeIndex=0
432- for index in vg:
433- v=bl.shapekey.getByIndex(b, index)
434- pos=[v[0], v[1], v[2]]
435-
436- indices=self.vertexArray.getMappedIndex(obj.name, index)
437- for attribute, i in indices.items():
438- if i in used:
439- continue
440- used.add(i)
441-
442- baseMorph.add(i, pos)
443- indexRelativeMap[i]=relativeIndex
444- relativeIndex+=1
445-
446- break
447- assert(basis)
448- #print(basis.name, len(baseMorph.offsets))
449-
450- if len(baseMorph.offsets)==0:
451- return
452-
453- # shape keys
454- for b in bl.object.getShapeKeys(obj):
455- if b.name==bl.BASE_SHAPE_NAME:
456- continue
457-
458- #print(b.name)
459- morph=self.__getOrCreateMorph(b.name, 4)
460- used=set()
461- for index, src, dst in zip(
462- range(len(blenderMesh.vertices)),
463- bl.shapekey.get(basis),
464- bl.shapekey.get(b)):
465- offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
466- if offset[0]==0 and offset[1]==0 and offset[2]==0:
467- continue
468- if index in vg:
469- indices=self.vertexArray.getMappedIndex(obj.name, index)
470- for attribute, i in indices.items():
471- if i in used:
472- continue
473- used.add(i)
474- morph.add(indexRelativeMap[i], offset)
475- assert(len(morph.offsets)<len(baseMorph.offsets))
476-
477- # sort skinmap
478- original=self.morphList[:]
479- def getIndex(morph):
480- for i, v in enumerate(englishmap.skinMap):
481- if v[0]==morph.name:
482- return i
483- #print(morph)
484- return len(englishmap.skinMap)
485- self.morphList.sort(key=getIndex)
486-
487- def __rigidbody(self, obj):
488- if not bl.RIGID_SHAPE_TYPE in obj:
489- return
490- self.rigidbodies.append(obj)
491-
492- def __constraint(self, obj):
493- if not bl.CONSTRAINT_A in obj:
494- return
495- self.constraints.append(obj)
496-
497- def __getOrCreateMorph(self, name, type):
498- for m in self.morphList:
499- if m.name==name:
500- return m
501- m=Morph(name, type)
502- self.morphList.append(m)
503- return m
504-
505- def getVertexCount(self):
506- return len(self.vertexArray.positions)
507-
508-
509-class Bone(object):
510- __slots__=['index', 'name', 'ik_index',
511- 'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
512- def __init__(self, name, pos, tail, isConnect):
513- self.index=-1
514- self.name=name
515- self.pos=pos
516- self.tail=tail
517- self.parent_index=None
518- self.tail_index=None
519- self.type=0
520- self.isConnect=isConnect
521- self.ik_index=0
522-
523- def __eq__(self, rhs):
524- return self.index==rhs.index
525-
526- def __str__(self):
527- return "<Bone %s %d>" % (self.name, self.type)
528-
529-class BoneBuilder(object):
530- __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',]
531- def __init__(self):
532- self.bones=[]
533- self.boneMap={}
534- self.ik_list=[]
535- self.bone_groups=[]
536-
537- def getBoneGroup(self, bone):
538- for i, g in enumerate(self.bone_groups):
539- for b in g[1]:
540- if b==bone.name:
541- return i+1
542- print('no gorup', bone)
543- return 0
544-
545- def build(self, armatureObj):
546- if not armatureObj:
547- return
548-
549- bl.message("build skeleton")
550- armature=bl.object.getData(armatureObj)
551-
552- ####################
553- # bone group
554- ####################
555- for g in bl.object.boneGroups(armatureObj):
556- self.bone_groups.append((g.name, []))
557-
558- ####################
559- # get bones
560- ####################
561- for b in armature.bones.values():
562- if not b.parent:
563- # root bone
564- bone=Bone(b.name,
565- bl.bone.getHeadLocal(b),
566- bl.bone.getTailLocal(b),
567- False)
568- self.__addBone(bone)
569- self.__getBone(bone, b)
570-
571- for b in armature.bones.values():
572- if not b.parent:
573- self.__checkConnection(b, None)
574-
575- ####################
576- # get IK
577- ####################
578- pose = bl.object.getPose(armatureObj)
579- for b in pose.bones.values():
580- ####################
581- # assing bone group
582- ####################
583- self.__assignBoneGroup(b, b.bone_group)
584- for c in b.constraints:
585- if bl.constraint.isIKSolver(c):
586- ####################
587- # IK target
588- ####################
589- target=self.__boneByName(bl.constraint.ikTarget(c))
590- target.type=2
591-
592- ####################
593- # IK effector
594- ####################
595- # IK 接続先
596- link=self.__boneByName(b.name)
597- link.type=6
598-
599- # IK chain
600- e=b.parent
601- chainLength=bl.constraint.ikChainLen(c)
602- for i in range(chainLength):
603- # IK影響下
604- chainBone=self.__boneByName(e.name)
605- chainBone.type=4
606- chainBone.ik_index=target.index
607- e=e.parent
608- self.ik_list.append(
609- IKSolver(target, link, chainLength,
610- int(bl.constraint.ikItration(c) * 0.1),
611- bl.constraint.ikRotationWeight(c)
612- ))
613-
614- ####################
615-
616- # boneのsort
617- self._sortBy()
618- self._fix()
619- # IKのsort
620- def getIndex(ik):
621- for i, v in enumerate(englishmap.boneMap):
622- if v[0]==ik.target.name:
623- return i
624- return len(englishmap.boneMap)
625- self.ik_list.sort(key=getIndex)
626-
627- def __assignBoneGroup(self, poseBone, boneGroup):
628- if boneGroup:
629- for g in self.bone_groups:
630- if g[0]==boneGroup.name:
631- g[1].append(poseBone.name)
632-
633- def __checkConnection(self, b, p):
634- if bl.bone.isConnected(b):
635- parent=self.__boneByName(p.name)
636- parent.isConnect=True
637-
638- for c in b.children:
639- self.__checkConnection(c, b)
640-
641- def _sortBy(self):
642- """
643- boneMap順に並べ替える
644- """
645- boneMap=englishmap.boneMap
646- original=self.bones[:]
647- def getIndex(bone):
648- for i, k_v in enumerate(boneMap):
649- if k_v[0]==bone.name:
650- return i
651- print(bone)
652- return len(boneMap)
653-
654- self.bones.sort(key=getIndex)
655-
656- sortMap={}
657- for i, b in enumerate(self.bones):
658- src=original.index(b)
659- sortMap[src]=i
660- for b in self.bones:
661- b.index=sortMap[b.index]
662- if b.parent_index:
663- b.parent_index=sortMap[b.parent_index]
664- if b.tail_index:
665- b.tail_index=sortMap[b.tail_index]
666- if b.ik_index>0:
667- b.ik_index=sortMap[b.ik_index]
668-
669- def _fix(self):
670- """
671- 調整
672- """
673- for b in self.bones:
674- # parent index
675- if b.parent_index==None:
676- b.parent_index=0xFFFF
677- else:
678- if b.type==6 or b.type==7:
679- # fix tail bone
680- parent=self.bones[b.parent_index]
681- #print('parnet', parent.name)
682- parent.tail_index=b.index
683-
684- for b in self.bones:
685- if b.tail_index==None:
686- b.tail_index=0
687- elif b.type==9:
688- b.tail_index==0
689-
690- def getIndex(self, bone):
691- for i, b in enumerate(self.bones):
692- if b==bone:
693- return i
694- assert(false)
695-
696- def indexByName(self, name):
697- if name=='':
698- return 0
699- else:
700- try:
701- return self.getIndex(self.__boneByName(name))
702- except:
703- return 0
704-
705- def __boneByName(self, name):
706- return self.boneMap[name]
707-
708- def __getBone(self, parent, b):
709- if len(b.children)==0:
710- parent.type=7
711- return
712-
713- for i, c in enumerate(b.children):
714- bone=Bone(c.name,
715- bl.bone.getHeadLocal(c),
716- bl.bone.getTailLocal(c),
717- bl.bone.isConnected(c))
718- self.__addBone(bone)
719- if parent:
720- bone.parent_index=parent.index
721- #if i==0:
722- if bone.isConnect or (not parent.tail_index and parent.tail==bone.pos):
723- parent.tail_index=bone.index
724- self.__getBone(bone, c)
725-
726- def __addBone(self, bone):
727- bone.index=len(self.bones)
728- self.bones.append(bone)
729- self.boneMap[bone.name]=bone
730-
731-
732-class Node(object):
733- __slots__=['o', 'children']
734- def __init__(self, o):
735- self.o=o
736- self.children=[]
737-
738-
739-
740-class Exporter(object):
741-
742- __slots__=[
743- 'armatureObj',
744- 'oneSkinMesh',
745- 'englishName',
746- 'englishComment',
747- 'name',
748- 'comment',
749- 'skeleton',
750- ]
751- def setup(self):
752- self.armatureObj=None
753-
754- # 木構造を構築する
755- object_node_map={}
756- for o in bl.object.each():
757- object_node_map[o]=Node(o)
758- for o in bl.object.each():
759- node=object_node_map[o]
760- if node.o.parent:
761- object_node_map[node.o.parent].children.append(node)
762-
763- # ルートを得る
764- root=object_node_map[bl.object.getActive()]
765- o=root.o
766- self.englishName=o.name
767- self.englishComment=o[bl.MMD_COMMENT] if bl.MMD_COMMENT in o else 'blender export\n'
768- self.name=o[bl.MMD_MB_NAME] if bl.MMD_MB_NAME in o else 'Blenderエクスポート'
769- self.comment=o[bl.MMD_MB_COMMENT] if bl.MMD_MB_COMMENT in o else 'Blnderエクスポート\n'
770-
771- # ワンスキンメッシュを作る
772- self.oneSkinMesh=OneSkinMesh()
773- self.__createOneSkinMesh(root)
774- bl.message(self.oneSkinMesh)
775- if len(self.oneSkinMesh.morphList)==0:
776- # create emtpy skin
777- self.oneSkinMesh.createEmptyBasicSkin()
778-
779- # skeleton
780- self.skeleton=BoneBuilder()
781- self.skeleton.build(self.armatureObj)
782-
783- def __createOneSkinMesh(self, node):
784- ############################################################
785- # search armature modifier
786- ############################################################
787- for m in node.o.modifiers:
788- if bl.modifier.isType(m, 'ARMATURE'):
789- armatureObj=bl.modifier.getArmatureObject(m)
790- if not self.armatureObj:
791- self.armatureObj=armatureObj
792- elif self.armatureObj!=armatureObj:
793- print("warning! found multiple armature. ignored.",
794- armatureObj.name)
795-
796- if node.o.type.upper()=='MESH':
797- self.oneSkinMesh.addMesh(node.o)
798-
799- for child in node.children:
800- self.__createOneSkinMesh(child)
801-
Show on old repository browser