• R/O
  • HTTP
  • SSH
  • HTTPS

pymeshio: コミット

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


コミットメタ情報

リビジョン924bae1667aea0f57dd3de11b1248c12bc9a95d1 (tree)
日時2011-10-15 10:18:33
作者ousttrue <ousttrue@gmai...>
コミッターousttrue

ログメッセージ

refactoring export_pmd

変更サマリ

差分

--- a/blender26-meshio/bl.py
+++ b/blender26-meshio/bl.py
@@ -315,6 +315,7 @@ class texture:
315315 try:
316316 image=bpy.data.images.load(path)
317317 except RuntimeError:
318+ print('fail to create:', path)
318319 image=bpy.data.images.new('Image', width=16, height=16)
319320 texture.image=image
320321 return texture, image
--- a/blender26-meshio/export_pmd.py
+++ b/blender26-meshio/export_pmd.py
@@ -32,1130 +32,311 @@ This script exports a pmd model.
3232 20110522: implement RigidBody and Constraint.
3333 20111002: update for pymeshio-2.1.0
3434 """
35-
36-bl_addon_info = {
37- 'category': 'Import/Export',
38- 'name': 'Export: MikuMikuDance Model Format (.pmd)',
39- 'author': 'ousttrue',
40- 'version': (2, 2),
41- 'blender': (2, 5, 3),
42- 'location': 'File > Export',
43- 'description': 'Export to the MikuMikuDance Model Format (.pmd)',
44- 'warning': '', # used for warning icon and text in addons panel
45- 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
46- 'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
47- }
48-
49-
50-###############################################################################
51-# import
52-###############################################################################
53-import os
54-import sys
5535 import io
5636
57-
58-from .pymeshio import englishmap
37+from . import bl
38+from . import oneskinmesh
5939 from .pymeshio import common
6040 from .pymeshio import pmd
41+from .pymeshio import englishmap
6142 from .pymeshio.pmd import writer
6243
6344
64-# for 2.5
65-import bpy
66-import mathutils
67-
68-# wrapper
69-from . import bl
70-
71-xrange=range
72-
73-def toCP932(s):
74- return s.encode('cp932')
75-
76-
77-class Node(object):
78- __slots__=['o', 'children']
79- def __init__(self, o):
80- self.o=o
81- self.children=[]
82-
83-
84-###############################################################################
85-# Blenderのメッシュをワンスキンメッシュ化する
86-###############################################################################
8745 def near(x, y, EPSILON=1e-5):
8846 d=x-y
8947 return d>=-EPSILON and d<=EPSILON
9048
9149
92-class VertexAttribute(object):
93- __slots__=[
94- 'nx', 'ny', 'nz', # normal
95- 'u', 'v', # uv
96- ]
97- def __init__(self, nx, ny, nz, u, v):
98- self.nx=nx
99- self.ny=ny
100- self.nz=nz
101- self.u=u
102- self.v=v
103-
104- def __str__(self):
105- return "<vkey: %f, %f, %f, %f, %f>" % (
106- self.nx, self.ny, self.nz, self.u, self.v)
107-
108- def __hash__(self):
109- return int(100*(self.nx + self.ny + self.nz + self.u + self.v))
110-
111- def __eq__(self, rhs):
112- 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
113-
114-
115-class VertexKey(object):
116- __slots__=[
117- 'obj_index', 'index',
118- ]
119-
120- def __init__(self, obj_index, index):
121- self.obj_index=obj_index
122- self.index=index
123-
124- def __str__(self):
125- return "<vkey: %d, %d>" % (self.obj_index, self.index)
126-
127- def __hash__(self):
128- return self.index*100+self.obj_index
129-
130- def __eq__(self, rhs):
131- return self.obj_index==rhs.obj_index and self.index==rhs.index
132-
133-
134-class VertexArray(object):
135- """
136- 頂点配列
137- """
138- __slots__=[
139- 'indexArrays',
140- 'positions',
141- 'attributes', # normal and uv
142- 'b0', 'b1', 'weight',
143- 'vertexMap',
144- 'objectMap',
145- ]
146- def __init__(self):
147- # indexArrays split with each material
148- self.indexArrays={}
149-
150- self.positions=[]
151- self.attributes=[]
152- self.b0=[]
153- self.b1=[]
154- self.weight=[]
155-
156- self.vertexMap={}
157- self.objectMap={}
158-
159- def __str__(self):
160- return "<VertexArray %d positions, %d indexArrays>" % (
161- len(self.positions), len(self.indexArrays))
162-
163- def zip(self):
164- return zip(
165- self.positions, self.attributes,
166- self.b0, self.b1, self.weight)
167-
168- def each(self):
169- keys=[key for key in self.indexArrays.keys()]
170- keys.sort()
171- for key in keys:
172- yield(key, self.indexArrays[key])
173-
174- def __addOrGetIndex(self, obj_index, base_index, pos, normal, uv, b0, b1, weight0):
175- key=VertexKey(obj_index, base_index)
176- attribute=VertexAttribute(
177- normal[0], normal[1], normal[2],
178- uv[0], uv[1])
179- if key in self.vertexMap:
180- if attribute in self.vertexMap[key]:
181- return self.vertexMap[key][attribute]
182- else:
183- return self.__addVertex(self.vertexMap[key],
184- pos, attribute, b0, b1, weight0)
185- else:
186- vertexMapKey={}
187- self.vertexMap[key]=vertexMapKey
188- return self.__addVertex(vertexMapKey,
189- pos, attribute, b0, b1, weight0)
190-
191- def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0):
192- index=len(self.positions)
193- vertexMapKey[attribute]=index
194- # position
195- self.positions.append((pos.x, pos.y, pos.z))
196- # unique attribute
197- self.attributes.append(attribute)
198- # shared attribute
199- self.b0.append(b0)
200- self.b1.append(b1)
201- self.weight.append(weight0)
202- assert(index<=65535)
203- return index
204-
205- def getMappedIndex(self, obj_name, base_index):
206- return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)]
207-
208- def addTriangle(self,
209- object_name, material,
210- base_index0, base_index1, base_index2,
211- pos0, pos1, pos2,
212- n0, n1, n2,
213- uv0, uv1, uv2,
214- b0_0, b0_1, b0_2,
215- b1_0, b1_1, b1_2,
216- weight0, weight1, weight2
217- ):
218- if object_name in self.objectMap:
219- obj_index=self.objectMap[object_name]
220- else:
221- obj_index=len(self.objectMap)
222- self.objectMap[object_name]=obj_index
223- index0=self.__addOrGetIndex(obj_index, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
224- index1=self.__addOrGetIndex(obj_index, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
225- index2=self.__addOrGetIndex(obj_index, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
226-
227- if not material in self.indexArrays:
228- self.indexArrays[material]=[]
229- self.indexArrays[material]+=[index0, index1, index2]
230-
231-
232-class Morph(object):
233- __slots__=['name', 'type', 'offsets']
234- def __init__(self, name, type):
235- self.name=name
236- self.type=type
237- self.offsets=[]
238-
239- def add(self, index, offset):
240- self.offsets.append((index, offset))
241-
242- def sort(self):
243- self.offsets.sort(key=lambda e: e[0])
244-
245- def __str__(self):
246- return "<Morph %s>" % self.name
247-
248-class IKSolver(object):
249- __slots__=['target', 'effector', 'length', 'iterations', 'weight']
250- def __init__(self, target, effector, length, iterations, weight):
251- self.target=target
252- self.effector=effector
253- self.length=length
254- self.iterations=iterations
255- self.weight=weight
256-
257-
258-class SSS(object):
259- def __init__(self):
260- self.use=1
261-
262-
263-class DefaultMatrial(object):
264- def __init__(self):
265- self.name='default'
266- # diffuse
267- self.diffuse_color=[1, 1, 1]
268- self.alpha=1
269- # specular
270- self.specular_toon_size=0
271- self.specular_hardness=5
272- self.specular_color=[1, 1, 1]
273- # ambient
274- self.mirror_color=[1, 1, 1]
275- # flag
276- self.subsurface_scattering=SSS()
277- # texture
278- self.texture_slots=[]
279-
280-
281-class OneSkinMesh(object):
282- __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', ]
283- def __init__(self):
284- self.vertexArray=VertexArray()
285- self.morphList=[]
286- self.rigidbodies=[]
287- self.constraints=[]
288-
289- def __str__(self):
290- return "<OneSkinMesh %s, morph:%d>" % (
291- self.vertexArray,
292- len(self.morphList))
293-
294- def addMesh(self, obj):
295- if not bl.object.isVisible(obj):
296- return
297- self.__mesh(obj)
298- self.__skin(obj)
299- self.__rigidbody(obj)
300- self.__constraint(obj)
301-
302- def __getWeightMap(self, obj, mesh):
303- # bone weight
304- weightMap={}
305- secondWeightMap={}
306- def setWeight(i, name, w):
307- if w>0:
308- if i in weightMap:
309- if i in secondWeightMap:
310- # 上位2つのweightを採用する
311- if w<secondWeightMap[i][1]:
312- pass
313- elif w<weightMap[i][1]:
314- # 2つ目を入れ替え
315- secondWeightMap[i]=(name, w)
316- else:
317- # 1つ目を入れ替え
318- weightMap[i]=(name, w)
319- else:
320- if w>weightMap[i][1]:
321- # 多い方をweightMapに
322- secondWeightMap[i]=weightMap[i]
323- weightMap[i]=(name, w)
324- else:
325- secondWeightMap[i]=(name, w)
326- else:
327- weightMap[i]=(name, w)
328-
329- # ToDo bone weightと関係ないvertex groupを除外する
330- for i, v in enumerate(mesh.vertices):
331- if len(v.groups)>0:
332- for g in v.groups:
333- setWeight(i, obj.vertex_groups[g.group].name, g.weight)
334- else:
335- try:
336- setWeight(i, obj.vertex_groups[0].name, 1)
337- except:
338- # no vertex_groups
339- pass
340-
341- # 合計値が1になるようにする
342- for i in xrange(len(mesh.vertices)):
343- if i in secondWeightMap:
344- secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1])
345- elif i in weightMap:
346- weightMap[i]=(weightMap[i][0], 1.0)
347- secondWeightMap[i]=("", 0)
348- else:
349- print("no weight vertex")
350- weightMap[i]=("", 0)
351- secondWeightMap[i]=("", 0)
352-
353- return weightMap, secondWeightMap
354-
355- def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap):
356- default_material=DefaultMatrial()
357- # 各面の処理
358- for i, face in enumerate(mesh.faces):
359- faceVertexCount=bl.face.getVertexCount(face)
360- try:
361- material=mesh.materials[bl.face.getMaterialIndex(face)]
362- except IndexError as e:
363- material=default_material
364- v=[mesh.vertices[index] for index in bl.face.getVertices(face)]
365- uv=bl.mesh.getFaceUV(
366- mesh, i, face, bl.face.getVertexCount(face))
367- # flip triangle
368- if faceVertexCount==3:
369- # triangle
370- self.vertexArray.addTriangle(
371- obj_name, material.name,
372- v[2].index,
373- v[1].index,
374- v[0].index,
375- v[2].co,
376- v[1].co,
377- v[0].co,
378- bl.vertex.getNormal(v[2]),
379- bl.vertex.getNormal(v[1]),
380- bl.vertex.getNormal(v[0]),
381- uv[2],
382- uv[1],
383- uv[0],
384- weightMap[v[2].index][0],
385- weightMap[v[1].index][0],
386- weightMap[v[0].index][0],
387- secondWeightMap[v[2].index][0],
388- secondWeightMap[v[1].index][0],
389- secondWeightMap[v[0].index][0],
390- weightMap[v[2].index][1],
391- weightMap[v[1].index][1],
392- weightMap[v[0].index][1]
393- )
394- elif faceVertexCount==4:
395- # quadrangle
396- self.vertexArray.addTriangle(
397- obj_name, material.name,
398- v[2].index,
399- v[1].index,
400- v[0].index,
401- v[2].co,
402- v[1].co,
403- v[0].co,
404- bl.vertex.getNormal(v[2]),
405- bl.vertex.getNormal(v[1]),
406- bl.vertex.getNormal(v[0]),
407- uv[2],
408- uv[1],
409- uv[0],
410- weightMap[v[2].index][0],
411- weightMap[v[1].index][0],
412- weightMap[v[0].index][0],
413- secondWeightMap[v[2].index][0],
414- secondWeightMap[v[1].index][0],
415- secondWeightMap[v[0].index][0],
416- weightMap[v[2].index][1],
417- weightMap[v[1].index][1],
418- weightMap[v[0].index][1]
419- )
420- self.vertexArray.addTriangle(
421- obj_name, material.name,
422- v[0].index,
423- v[3].index,
424- v[2].index,
425- v[0].co,
426- v[3].co,
427- v[2].co,
428- bl.vertex.getNormal(v[0]),
429- bl.vertex.getNormal(v[3]),
430- bl.vertex.getNormal(v[2]),
431- uv[0],
432- uv[3],
433- uv[2],
434- weightMap[v[0].index][0],
435- weightMap[v[3].index][0],
436- weightMap[v[2].index][0],
437- secondWeightMap[v[0].index][0],
438- secondWeightMap[v[3].index][0],
439- secondWeightMap[v[2].index][0],
440- weightMap[v[0].index][1],
441- weightMap[v[3].index][1],
442- weightMap[v[2].index][1]
443- )
444-
445- def __mesh(self, obj):
446- if bl.RIGID_SHAPE_TYPE in obj:
447- return
448- if bl.CONSTRAINT_A in obj:
449- return
450-
451- bl.message("export: %s" % obj.name)
452-
453- # メッシュのコピーを生成してオブジェクトの行列を適用する
454- copyMesh, copyObj=bl.object.duplicate(obj)
455- if len(copyMesh.vertices)>0:
456- # apply transform
457- """
458- try:
459- # svn 36722
460- copyObj.scale=obj.scale
461- bpy.ops.object.transform_apply(scale=True)
462- copyObj.rotation_euler=obj.rotation_euler
463- bpy.ops.object.transform_apply(rotation=True)
464- copyObj.location=obj.location
465- bpy.ops.object.transform_apply(location=True)
466- except AttributeError as e:
467- # 2.57b
468- copyObj.scale=obj.scale
469- bpy.ops.object.scale_apply()
470- copyObj.rotation_euler=obj.rotation_euler
471- bpy.ops.object.rotation_apply()
472- copyObj.location=obj.location
473- bpy.ops.object.location_apply()
474- """
475- copyMesh.transform(obj.matrix_world)
476-
477- # apply modifier
478- for m in [m for m in copyObj.modifiers]:
479- if m.type=='SOLIDFY':
480- continue
481- elif m.type=='ARMATURE':
482- continue
483- elif m.type=='MIRROR':
484- bpy.ops.object.modifier_apply(modifier=m.name)
485- else:
486- print(m.type)
487-
488- weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh)
489- self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap)
490- bl.object.delete(copyObj)
491-
492- def createEmptyBasicSkin(self):
493- self.__getOrCreateMorph('base', 0)
494-
495- def __skin(self, obj):
496- if not bl.object.hasShapeKey(obj):
497- return
498-
499- indexRelativeMap={}
500- blenderMesh=bl.object.getData(obj)
501- baseMorph=None
502-
503- # shape keys
504- vg=bl.object.getVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME)
505-
506- # base
507- used=set()
508- for b in bl.object.getShapeKeys(obj):
509- if b.name==bl.BASE_SHAPE_NAME:
510- baseMorph=self.__getOrCreateMorph('base', 0)
511- basis=b
512-
513- relativeIndex=0
514- for index in vg:
515- v=bl.shapekey.getByIndex(b, index)
516- pos=[v[0], v[1], v[2]]
517-
518- indices=self.vertexArray.getMappedIndex(obj.name, index)
519- for attribute, i in indices.items():
520- if i in used:
521- continue
522- used.add(i)
523-
524- baseMorph.add(i, pos)
525- indexRelativeMap[i]=relativeIndex
526- relativeIndex+=1
527-
528- break
529- assert(basis)
530- #print(basis.name, len(baseMorph.offsets))
531-
532- if len(baseMorph.offsets)==0:
533- return
534-
535- # shape keys
536- for b in bl.object.getShapeKeys(obj):
537- if b.name==bl.BASE_SHAPE_NAME:
538- continue
539-
540- #print(b.name)
541- morph=self.__getOrCreateMorph(b.name, 4)
542- used=set()
543- for index, src, dst in zip(
544- xrange(len(blenderMesh.vertices)),
545- bl.shapekey.get(basis),
546- bl.shapekey.get(b)):
547- offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
548- if offset[0]==0 and offset[1]==0 and offset[2]==0:
549- continue
550- if index in vg:
551- indices=self.vertexArray.getMappedIndex(obj.name, index)
552- for attribute, i in indices.items():
553- if i in used:
554- continue
555- used.add(i)
556- morph.add(indexRelativeMap[i], offset)
557- assert(len(morph.offsets)<len(baseMorph.offsets))
558-
559- # sort skinmap
560- original=self.morphList[:]
561- def getIndex(morph):
562- for i, v in enumerate(englishmap.skinMap):
563- if v[0]==morph.name:
564- return i
565- #print(morph)
566- return len(englishmap.skinMap)
567- self.morphList.sort(key=getIndex)
568-
569- def __rigidbody(self, obj):
570- if not bl.RIGID_SHAPE_TYPE in obj:
571- return
572- self.rigidbodies.append(obj)
573-
574- def __constraint(self, obj):
575- if not bl.CONSTRAINT_A in obj:
576- return
577- self.constraints.append(obj)
578-
579- def __getOrCreateMorph(self, name, type):
580- for m in self.morphList:
581- if m.name==name:
582- return m
583- m=Morph(name, type)
584- self.morphList.append(m)
585- return m
586-
587- def getVertexCount(self):
588- return len(self.vertexArray.positions)
589-
590-
591-class Bone(object):
592- __slots__=['index', 'name', 'ik_index',
593- 'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
594- def __init__(self, name, pos, tail, isConnect):
595- self.index=-1
596- self.name=name
597- self.pos=pos
598- self.tail=tail
599- self.parent_index=None
600- self.tail_index=None
601- self.type=0
602- self.isConnect=isConnect
603- self.ik_index=0
604-
605- def __eq__(self, rhs):
606- return self.index==rhs.index
607-
608- def __str__(self):
609- return "<Bone %s %d>" % (self.name, self.type)
610-
611-class BoneBuilder(object):
612- __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',]
613- def __init__(self):
614- self.bones=[]
615- self.boneMap={}
616- self.ik_list=[]
617- self.bone_groups=[]
618-
619- def getBoneGroup(self, bone):
620- for i, g in enumerate(self.bone_groups):
621- for b in g[1]:
622- if b==bone.name:
623- return i+1
624- print('no gorup', bone)
625- return 0
626-
627- def build(self, armatureObj):
628- if not armatureObj:
629- return
630-
631- bl.message("build skeleton")
632- armature=bl.object.getData(armatureObj)
633-
634- ####################
635- # bone group
636- ####################
637- for g in bl.object.boneGroups(armatureObj):
638- self.bone_groups.append((g.name, []))
639-
640- ####################
641- # get bones
642- ####################
643- for b in armature.bones.values():
644- if not b.parent:
645- # root bone
646- bone=Bone(b.name,
647- bl.bone.getHeadLocal(b),
648- bl.bone.getTailLocal(b),
649- False)
650- self.__addBone(bone)
651- self.__getBone(bone, b)
652-
653- for b in armature.bones.values():
654- if not b.parent:
655- self.__checkConnection(b, None)
656-
657- ####################
658- # get IK
659- ####################
660- pose = bl.object.getPose(armatureObj)
661- for b in pose.bones.values():
662- ####################
663- # assing bone group
664- ####################
665- self.__assignBoneGroup(b, b.bone_group)
666- for c in b.constraints:
667- if bl.constraint.isIKSolver(c):
668- ####################
669- # IK target
670- ####################
671- target=self.__boneByName(bl.constraint.ikTarget(c))
672- target.type=2
673-
674- ####################
675- # IK effector
676- ####################
677- # IK 接続先
678- link=self.__boneByName(b.name)
679- link.type=6
680-
681- # IK chain
682- e=b.parent
683- chainLength=bl.constraint.ikChainLen(c)
684- for i in range(chainLength):
685- # IK影響下
686- chainBone=self.__boneByName(e.name)
687- chainBone.type=4
688- chainBone.ik_index=target.index
689- e=e.parent
690- self.ik_list.append(
691- IKSolver(target, link, chainLength,
692- int(bl.constraint.ikItration(c) * 0.1),
693- bl.constraint.ikRotationWeight(c)
694- ))
695-
696- ####################
697-
698- # boneのsort
699- self._sortBy()
700- self._fix()
701- # IKのsort
702- def getIndex(ik):
703- for i, v in enumerate(englishmap.boneMap):
704- if v[0]==ik.target.name:
705- return i
706- return len(englishmap.boneMap)
707- self.ik_list.sort(key=getIndex)
708-
709- def __assignBoneGroup(self, poseBone, boneGroup):
710- if boneGroup:
711- for g in self.bone_groups:
712- if g[0]==boneGroup.name:
713- g[1].append(poseBone.name)
714-
715- def __checkConnection(self, b, p):
716- if bl.bone.isConnected(b):
717- parent=self.__boneByName(p.name)
718- parent.isConnect=True
719-
720- for c in b.children:
721- self.__checkConnection(c, b)
722-
723- def _sortBy(self):
724- """
725- boneMap順に並べ替える
726- """
727- boneMap=englishmap.boneMap
728- original=self.bones[:]
729- def getIndex(bone):
730- for i, k_v in enumerate(boneMap):
731- if k_v[0]==bone.name:
732- return i
733- print(bone)
734- return len(boneMap)
735-
736- self.bones.sort(key=getIndex)
50+def toCP932(s):
51+ return s.encode('cp932')
73752
738- sortMap={}
739- for i, b in enumerate(self.bones):
740- src=original.index(b)
741- sortMap[src]=i
742- for b in self.bones:
743- b.index=sortMap[b.index]
744- if b.parent_index:
745- b.parent_index=sortMap[b.parent_index]
746- if b.tail_index:
747- b.tail_index=sortMap[b.tail_index]
748- if b.ik_index>0:
749- b.ik_index=sortMap[b.ik_index]
75053
751- def _fix(self):
752- """
753- 調整
754- """
755- for b in self.bones:
756- # parent index
757- if b.parent_index==None:
758- b.parent_index=0xFFFF
54+def write(self, path):
55+ model=pmd.Model(1.0)
56+ model.name=self.name.encode('cp932')
57+ model.comment=self.comment.encode('cp932')
58+
59+ # 頂点
60+ model.vertices=[pmd.Vertex(
61+ # convert right-handed z-up to left-handed y-up
62+ common.Vector3(pos[0], pos[2], pos[1]),
63+ # convert right-handed z-up to left-handed y-up
64+ common.Vector3(attribute.nx, attribute.nz, attribute.ny),
65+ # reverse vertical
66+ common.Vector2(attribute.u, 1.0-attribute.v),
67+ self.skeleton.indexByName(b0),
68+ self.skeleton.indexByName(b1),
69+ int(100*weight),
70+ # edge flag, 0: enable edge, 1: not edge
71+ 0
72+ )
73+ for pos, attribute, b0, b1, weight in self.oneSkinMesh.vertexArray.zip()]
74+
75+ # 面とマテリアル
76+ vertexCount=self.oneSkinMesh.getVertexCount()
77+ for material_name, indices in self.oneSkinMesh.vertexArray.each():
78+ #print('material:', material_name)
79+ try:
80+ m=bl.material.get(material_name)
81+ except KeyError as e:
82+ m=DefaultMatrial()
83+ def get_texture_name(texture):
84+ pos=texture.replace("\\", "/").rfind("/")
85+ if pos==-1:
86+ return texture
75987 else:
760- if b.type==6 or b.type==7:
761- # fix tail bone
762- parent=self.bones[b.parent_index]
763- #print('parnet', parent.name)
764- parent.tail_index=b.index
765-
766- for b in self.bones:
767- if b.tail_index==None:
768- b.tail_index=0
769- elif b.type==9:
770- b.tail_index==0
771-
772- def getIndex(self, bone):
773- for i, b in enumerate(self.bones):
774- if b==bone:
775- return i
776- assert(false)
777-
778- def indexByName(self, name):
779- if name=='':
780- return 0
88+ return texture[pos+1:]
89+ textures=[get_texture_name(path)
90+ for path in bl.material.eachEnalbeTexturePath(m)]
91+ print(textures)
92+ # マテリアル
93+ model.materials.append(pmd.Material(
94+ # diffuse_color
95+ common.RGB(m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2]),
96+ m.alpha,
97+ # specular_factor
98+ 0 if m.specular_toon_size<1e-5 else m.specular_hardness*10,
99+ # specular_color
100+ common.RGB(m.specular_color[0], m.specular_color[1], m.specular_color[2]),
101+ # ambient_color
102+ common.RGB(m.mirror_color[0], m.mirror_color[1], m.mirror_color[2]),
103+ # flag
104+ 1 if m.subsurface_scattering.use else 0,
105+ # toon
106+ 0,
107+ # vertex_count
108+ len(indices),
109+ # texture
110+ ('*'.join(textures) if len(textures)>0 else "").encode('cp932')
111+ ))
112+ # 面
113+ for i in indices:
114+ assert(i<vertexCount)
115+ for i in range(0, len(indices), 3):
116+ # reverse triangle
117+ model.indices.append(indices[i])
118+ model.indices.append(indices[i+1])
119+ model.indices.append(indices[i+2])
120+
121+ # bones
122+ boneNameMap={}
123+ for i, b in enumerate(self.skeleton.bones):
124+
125+ # name
126+ boneNameMap[b.name]=i
127+ v=englishmap.getUnicodeBoneName(b.name)
128+ if not v:
129+ v=[b.name, b.name]
130+ assert(v)
131+ bone=pmd.Bone(v[1].encode('cp932'))
132+
133+ # english name
134+ bone_english_name=toCP932(b.name)
135+ if len(bone_english_name)>=20:
136+ print(bone_english_name)
137+ #assert(len(bone_english_name)<20)
138+ bone.english_name=bone_english_name
139+
140+ if len(v)>=3:
141+ # has type
142+ if v[2]==5:
143+ b.ik_index=self.skeleton.indexByName('eyes')
144+ bone.type=v[2]
781145 else:
782- try:
783- return self.getIndex(self.__boneByName(name))
784- except:
785- return 0
786-
787- def __boneByName(self, name):
788- return self.boneMap[name]
789-
790- def __getBone(self, parent, b):
791- if len(b.children)==0:
792- parent.type=7
793- return
794-
795- for i, c in enumerate(b.children):
796- bone=Bone(c.name,
797- bl.bone.getHeadLocal(c),
798- bl.bone.getTailLocal(c),
799- bl.bone.isConnected(c))
800- self.__addBone(bone)
801- if parent:
802- bone.parent_index=parent.index
803- #if i==0:
804- if bone.isConnect or (not parent.tail_index and parent.tail==bone.pos):
805- parent.tail_index=bone.index
806- self.__getBone(bone, c)
807-
808- def __addBone(self, bone):
809- bone.index=len(self.bones)
810- self.bones.append(bone)
811- self.boneMap[bone.name]=bone
812-
813-
814-class PmdExporter(object):
815-
816- __slots__=[
817- 'armatureObj',
818- 'oneSkinMesh',
819- 'englishName',
820- 'englishComment',
821- 'name',
822- 'comment',
823- 'skeleton',
824- ]
825- def setup(self):
826- self.armatureObj=None
827-
828- # 木構造を構築する
829- object_node_map={}
830- for o in bl.object.each():
831- object_node_map[o]=Node(o)
832- for o in bl.object.each():
833- node=object_node_map[o]
834- if node.o.parent:
835- object_node_map[node.o.parent].children.append(node)
836-
837- # ルートを得る
838- root=object_node_map[bl.object.getActive()]
839- o=root.o
840- self.englishName=o.name
841- self.englishComment=o[bl.MMD_COMMENT] if bl.MMD_COMMENT in o else 'blender export\n'
842- self.name=o[bl.MMD_MB_NAME] if bl.MMD_MB_NAME in o else 'Blenderエクスポート'
843- self.comment=o[bl.MMD_MB_COMMENT] if bl.MMD_MB_COMMENT in o else 'Blnderエクスポート\n'
844-
845- # ワンスキンメッシュを作る
846- self.oneSkinMesh=OneSkinMesh()
847- self.__createOneSkinMesh(root)
848- bl.message(self.oneSkinMesh)
849- if len(self.oneSkinMesh.morphList)==0:
850- # create emtpy skin
851- self.oneSkinMesh.createEmptyBasicSkin()
852-
853- # skeleton
854- self.skeleton=BoneBuilder()
855- self.skeleton.build(self.armatureObj)
856-
857- def __createOneSkinMesh(self, node):
858- ############################################################
859- # search armature modifier
860- ############################################################
861- for m in node.o.modifiers:
862- if bl.modifier.isType(m, 'ARMATURE'):
863- armatureObj=bl.modifier.getArmatureObject(m)
864- if not self.armatureObj:
865- self.armatureObj=armatureObj
866- elif self.armatureObj!=armatureObj:
867- print("warning! found multiple armature. ignored.",
868- armatureObj.name)
869-
870- if node.o.type.upper()=='MESH':
871- self.oneSkinMesh.addMesh(node.o)
872-
873- for child in node.children:
874- self.__createOneSkinMesh(child)
875-
876- def write(self, path):
877- model=pmd.Model(1.0)
878- model.name=self.name.encode('cp932')
879- model.comment=self.comment.encode('cp932')
880-
881- # 頂点
882- model.vertices=[pmd.Vertex(
883- # convert right-handed z-up to left-handed y-up
884- common.Vector3(pos[0], pos[2], pos[1]),
146+ bone.type=b.type
147+
148+ bone.parent_index=b.parent_index
149+ bone.tail_index=b.tail_index
150+ bone.ik_index=b.ik_index
151+
152+ # convert right-handed z-up to left-handed y-up
153+ bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
154+ bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
155+ bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
156+
157+ model.bones.append(bone)
158+
159+ # IK
160+ for ik in self.skeleton.ik_list:
161+ solver=pmd.IK()
162+ solver.index=self.skeleton.getIndex(ik.target)
163+ solver.target=self.skeleton.getIndex(ik.effector)
164+ solver.length=ik.length
165+ b=self.skeleton.bones[ik.effector.parent_index]
166+ for i in range(solver.length):
167+ solver.children.append(self.skeleton.getIndex(b))
168+ b=self.skeleton.bones[b.parent_index]
169+ solver.iterations=ik.iterations
170+ solver.weight=ik.weight
171+ model.ik_list.append(solver)
172+
173+ # 表情
174+ for i, m in enumerate(self.oneSkinMesh.morphList):
175+ v=englishmap.getUnicodeSkinName(m.name)
176+ if not v:
177+ v=[m.name, m.name, 0]
178+ assert(v)
179+ # morph
180+ morph=pmd.Morph(v[1].encode("cp932"))
181+ morph.english_name=m.name.encode("cp932")
182+ m.type=v[2]
183+ morph.type=v[2]
184+ for index, offset in m.offsets:
885185 # convert right-handed z-up to left-handed y-up
886- common.Vector3(attribute.nx, attribute.nz, attribute.ny),
887- # reverse vertical
888- common.Vector2(attribute.u, 1.0-attribute.v),
889- self.skeleton.indexByName(b0),
890- self.skeleton.indexByName(b1),
891- int(100*weight),
892- # edge flag, 0: enable edge, 1: not edge
893- 0
894- )
895- for pos, attribute, b0, b1, weight in self.oneSkinMesh.vertexArray.zip()]
896-
897- # 面とマテリアル
898- vertexCount=self.oneSkinMesh.getVertexCount()
899- for material_name, indices in self.oneSkinMesh.vertexArray.each():
900- #print('material:', material_name)
901- try:
902- m=bl.material.get(material_name)
903- except KeyError as e:
904- m=DefaultMatrial()
905- def get_texture_name(texture):
906- pos=texture.replace("\\", "/").rfind("/")
907- if pos==-1:
908- return texture
909- else:
910- return texture[pos+1:]
911- textures=[get_texture_name(path)
912- for path in bl.material.eachEnalbeTexturePath(m)]
913- print(textures)
914- # マテリアル
915- model.materials.append(pmd.Material(
916- # diffuse_color
917- common.RGB(m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2]),
918- m.alpha,
919- # specular_factor
920- 0 if m.specular_toon_size<1e-5 else m.specular_hardness*10,
921- # specular_color
922- common.RGB(m.specular_color[0], m.specular_color[1], m.specular_color[2]),
923- # ambient_color
924- common.RGB(m.mirror_color[0], m.mirror_color[1], m.mirror_color[2]),
925- # flag
926- 1 if m.subsurface_scattering.use else 0,
927- # toon
928- 0,
929- # vertex_count
930- len(indices),
931- # texture
932- ('*'.join(textures) if len(textures)>0 else "").encode('cp932')
933- ))
934- # 面
935- for i in indices:
936- assert(i<vertexCount)
937- for i in xrange(0, len(indices), 3):
938- # reverse triangle
939- model.indices.append(indices[i])
940- model.indices.append(indices[i+1])
941- model.indices.append(indices[i+2])
942-
943- # bones
944- boneNameMap={}
945- for i, b in enumerate(self.skeleton.bones):
946-
947- # name
948- boneNameMap[b.name]=i
949- v=englishmap.getUnicodeBoneName(b.name)
950- if not v:
951- v=[b.name, b.name]
952- assert(v)
953- bone=pmd.Bone(v[1].encode('cp932'))
954-
955- # english name
956- bone_english_name=toCP932(b.name)
957- if len(bone_english_name)>=20:
958- print(bone_english_name)
959- #assert(len(bone_english_name)<20)
960- bone.english_name=bone_english_name
961-
962- if len(v)>=3:
963- # has type
964- if v[2]==5:
965- b.ik_index=self.skeleton.indexByName('eyes')
966- bone.type=v[2]
186+ morph.append(index, offset[0], offset[2], offset[1])
187+ morph.vertex_count=len(m.offsets)
188+
189+ # 表情枠
190+ # type==0はbase
191+ for i, m in enumerate(self.oneSkinMesh.morphList):
192+ if m.type==3:
193+ model.morph_indices.append(i)
194+ for i, m in enumerate(self.oneSkinMesh.morphList):
195+ if m.type==2:
196+ model.morph_indices.append(i)
197+ for i, m in enumerate(self.oneSkinMesh.morphList):
198+ if m.type==1:
199+ model.morph_indices.append(i)
200+ for i, m in enumerate(self.oneSkinMesh.morphList):
201+ if m.type==4:
202+ model.morph_indices.append(i)
203+
204+ # ボーングループ
205+ for g in self.skeleton.bone_groups:
206+ name=englishmap.getUnicodeBoneGroupName(g[0])
207+ if not name:
208+ name=g[0]
209+ englishName=g[0]
210+
211+ model.bone_group_list.append(pmd.BoneGroup(
212+ (name+'\n').encode('cp932'),
213+ (englishName+'\n').encode('cp932')
214+ ))
215+
216+ # ボーングループメンバー
217+ for i, b in enumerate(self.skeleton.bones):
218+ if i==0:
219+ continue
220+ if b.type in [6, 7]:
221+ continue
222+ model.bone_display_list.append((i, self.skeleton.getBoneGroup(b)))
223+
224+ # English
225+ model.english_name=self.englishName.encode('cp932')
226+ model.english_comment=self.englishComment.encode('cp932')
227+
228+ # toon
229+ toonMeshObject=None
230+ for o in bl.object.each():
231+ try:
232+ if o.name.startswith(bl.TOON_TEXTURE_OBJECT):
233+ toonMeshObject=o
234+ except:
235+ p(o.name)
236+ break
237+ if toonMeshObject:
238+ toonMesh=bl.object.getData(toonMeshObject)
239+ toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
240+ for i in range(10):
241+ t=bl.material.getTexture(toonMaterial, i)
242+ if t:
243+ model.toon_textures[i]=("%s" % t.name).encode('cp932')
967244 else:
968- bone.type=b.type
969-
970- bone.parent_index=b.parent_index
971- bone.tail_index=b.tail_index
972- bone.ik_index=b.ik_index
973-
974- # convert right-handed z-up to left-handed y-up
975- bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
976- bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
977- bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
978-
979- model.bones.append(bone)
980-
981- # IK
982- for ik in self.skeleton.ik_list:
983- solver=pmd.IK()
984- solver.index=self.skeleton.getIndex(ik.target)
985- solver.target=self.skeleton.getIndex(ik.effector)
986- solver.length=ik.length
987- b=self.skeleton.bones[ik.effector.parent_index]
988- for i in xrange(solver.length):
989- solver.children.append(self.skeleton.getIndex(b))
990- b=self.skeleton.bones[b.parent_index]
991- solver.iterations=ik.iterations
992- solver.weight=ik.weight
993- model.ik_list.append(solver)
994-
995- # 表情
996- for i, m in enumerate(self.oneSkinMesh.morphList):
997- v=englishmap.getUnicodeSkinName(m.name)
998- if not v:
999- v=[m.name, m.name, 0]
1000- assert(v)
1001- # morph
1002- morph=pmd.Morph(v[1].encode("cp932"))
1003- morph.english_name=m.name.encode("cp932")
1004- m.type=v[2]
1005- morph.type=v[2]
1006- for index, offset in m.offsets:
1007- # convert right-handed z-up to left-handed y-up
1008- morph.append(index, offset[0], offset[2], offset[1])
1009- morph.vertex_count=len(m.offsets)
1010-
1011- # 表情枠
1012- # type==0はbase
1013- for i, m in enumerate(self.oneSkinMesh.morphList):
1014- if m.type==3:
1015- model.morph_indices.append(i)
1016- for i, m in enumerate(self.oneSkinMesh.morphList):
1017- if m.type==2:
1018- model.morph_indices.append(i)
1019- for i, m in enumerate(self.oneSkinMesh.morphList):
1020- if m.type==1:
1021- model.morph_indices.append(i)
1022- for i, m in enumerate(self.oneSkinMesh.morphList):
1023- if m.type==4:
1024- model.morph_indices.append(i)
1025-
1026- # ボーングループ
1027- for g in self.skeleton.bone_groups:
1028- name=englishmap.getUnicodeBoneGroupName(g[0])
1029- if not name:
1030- name=g[0]
1031- englishName=g[0]
1032-
1033- model.bone_group_list.append(pmd.BoneGroup(
1034- (name+'\n').encode('cp932'),
1035- (englishName+'\n').encode('cp932')
1036- ))
1037-
1038- # ボーングループメンバー
1039- for i, b in enumerate(self.skeleton.bones):
1040- if i==0:
1041- continue
1042- if b.type in [6, 7]:
1043- continue
1044- model.bone_display_list.append((i, self.skeleton.getBoneGroup(b)))
1045-
1046- # English
1047- model.english_name=self.englishName.encode('cp932')
1048- model.english_comment=self.englishComment.encode('cp932')
1049-
1050- # toon
1051- toonMeshObject=None
1052- for o in bl.object.each():
1053- try:
1054- if o.name.startswith(bl.TOON_TEXTURE_OBJECT):
1055- toonMeshObject=o
1056- except:
1057- p(o.name)
1058- break
1059- if toonMeshObject:
1060- toonMesh=bl.object.getData(toonMeshObject)
1061- toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
1062- for i in range(10):
1063- t=bl.material.getTexture(toonMaterial, i)
1064- if t:
1065- model.toon_textures[i]=("%s" % t.name).encode('cp932')
1066- else:
1067- model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
1068- else:
1069- for i in range(10):
1070245 model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
1071-
1072- # rigid body
1073- rigidNameMap={}
1074- for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
1075- name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name
1076- print(name)
1077- rigidNameMap[name]=i
1078- boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]]
1079- if boneIndex==0:
1080- boneIndex=0xFFFF
1081- bone=self.skeleton.bones[0]
1082- else:
1083- bone=self.skeleton.bones[boneIndex]
1084- rigidBody=pmd.RigidBody(
1085- name.encode('cp932'),
1086- boneIndex,
1087- shape_position=common.Vector3(
1088- obj.location.x-bone.pos[0],
1089- obj.location.z-bone.pos[2],
1090- obj.location.y-bone.pos[1]),
1091- shape_rotation=common.Vector3(
1092- -obj.rotation_euler[0],
1093- -obj.rotation_euler[2],
1094- -obj.rotation_euler[1]),
1095- collision_group=obj[bl.RIGID_GROUP],
1096- no_collision_group=obj[bl.RIGID_INTERSECTION_GROUP],
1097- mode=obj[bl.RIGID_PROCESS_TYPE],
1098- mass=obj[bl.RIGID_WEIGHT],
1099- linear_damping=obj[bl.RIGID_LINEAR_DAMPING],
1100- angular_damping=obj[bl.RIGID_ANGULAR_DAMPING],
1101- restitution=obj[bl.RIGID_RESTITUTION],
1102- friction=obj[bl.RIGID_FRICTION])
1103- if obj[bl.RIGID_SHAPE_TYPE]==0:
1104- rigidBody.shape_type=pmd.SHAPE_SPHERE
1105- rigidBody.shape_size=common.Vector3(obj.scale[0], 0, 0)
1106- elif obj[bl.RIGID_SHAPE_TYPE]==1:
1107- rigidBody.shape_type=pmd.SHAPE_BOX
1108- rigidBody.shape_size=common.Vector3(obj.scale[0], obj.scale[1], obj.scale[2])
1109- elif obj[bl.RIGID_SHAPE_TYPE]==2:
1110- rigidBody.shape_type=pmd.SHAPE_CAPSULE
1111- rigidBody.shape_size=common.Vector3(obj.scale[0], obj.scale[2], 0)
1112- model.rigidbodies.append(rigidBody)
1113-
1114- # constraint
1115- model.joints=[pmd.Joint(
1116- name=obj[bl.CONSTRAINT_NAME].encode('cp932'),
1117- rigidbody_index_a=rigidNameMap[obj[bl.CONSTRAINT_A]],
1118- rigidbody_index_b=rigidNameMap[obj[bl.CONSTRAINT_B]],
1119- position=common.Vector3(
1120- obj.location[0],
1121- obj.location[2],
1122- obj.location[1]),
1123- rotation=common.Vector3(
1124- -obj.rotation_euler[0],
1125- -obj.rotation_euler[2],
1126- -obj.rotation_euler[1]),
1127- translation_limit_min=common.Vector3(
1128- obj[bl.CONSTRAINT_POS_MIN][0],
1129- obj[bl.CONSTRAINT_POS_MIN][1],
1130- obj[bl.CONSTRAINT_POS_MIN][2]
1131- ),
1132- translation_limit_max=common.Vector3(
1133- obj[bl.CONSTRAINT_POS_MAX][0],
1134- obj[bl.CONSTRAINT_POS_MAX][1],
1135- obj[bl.CONSTRAINT_POS_MAX][2]
1136- ),
1137- rotation_limit_min=common.Vector3(
1138- obj[bl.CONSTRAINT_ROT_MIN][0],
1139- obj[bl.CONSTRAINT_ROT_MIN][1],
1140- obj[bl.CONSTRAINT_ROT_MIN][2]),
1141- rotation_limit_max=common.Vector3(
1142- obj[bl.CONSTRAINT_ROT_MAX][0],
1143- obj[bl.CONSTRAINT_ROT_MAX][1],
1144- obj[bl.CONSTRAINT_ROT_MAX][2]),
1145- spring_constant_translation=common.Vector3(
1146- obj[bl.CONSTRAINT_SPRING_POS][0],
1147- obj[bl.CONSTRAINT_SPRING_POS][1],
1148- obj[bl.CONSTRAINT_SPRING_POS][2]),
1149- spring_constant_rotation=common.Vector3(
1150- obj[bl.CONSTRAINT_SPRING_ROT][0],
1151- obj[bl.CONSTRAINT_SPRING_ROT][1],
1152- obj[bl.CONSTRAINT_SPRING_ROT][2])
1153- )
1154- for obj in self.oneSkinMesh.constraints]
1155-
1156- # 書き込み
1157- bl.message('write: %s' % path)
1158- return writer.write(io.open(path, 'wb'), model)
246+ else:
247+ for i in range(10):
248+ model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
249+
250+ # rigid body
251+ rigidNameMap={}
252+ for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
253+ name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name
254+ print(name)
255+ rigidNameMap[name]=i
256+ boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]]
257+ if boneIndex==0:
258+ boneIndex=-1
259+ bone=self.skeleton.bones[0]
260+ else:
261+ bone=self.skeleton.bones[boneIndex]
262+ if obj[bl.RIGID_SHAPE_TYPE]==0:
263+ shape_type=pmd.SHAPE_SPHERE
264+ shape_size=common.Vector3(obj.scale[0], 0, 0)
265+ elif obj[bl.RIGID_SHAPE_TYPE]==1:
266+ shape_type=pmd.SHAPE_BOX
267+ shape_size=common.Vector3(obj.scale[0], obj.scale[1], obj.scale[2])
268+ elif obj[bl.RIGID_SHAPE_TYPE]==2:
269+ shape_type=pmd.SHAPE_CAPSULE
270+ shape_size=common.Vector3(obj.scale[0], obj.scale[2], 0)
271+ rigidBody=pmd.RigidBody(
272+ name.encode('cp932'),
273+ collision_group=obj[bl.RIGID_GROUP],
274+ no_collision_group=obj[bl.RIGID_INTERSECTION_GROUP],
275+ bone_index=boneIndex,
276+ shape_position=common.Vector3(
277+ obj.location.x-bone.pos[0],
278+ obj.location.z-bone.pos[2],
279+ obj.location.y-bone.pos[1]),
280+ shape_rotation=common.Vector3(
281+ -obj.rotation_euler[0],
282+ -obj.rotation_euler[2],
283+ -obj.rotation_euler[1]),
284+ shape_type=shape_type,
285+ shape_size=shape_size,
286+ mass=obj[bl.RIGID_WEIGHT],
287+ linear_damping=obj[bl.RIGID_LINEAR_DAMPING],
288+ angular_damping=obj[bl.RIGID_ANGULAR_DAMPING],
289+ restitution=obj[bl.RIGID_RESTITUTION],
290+ friction=obj[bl.RIGID_FRICTION],
291+ mode=obj[bl.RIGID_PROCESS_TYPE]
292+ )
293+ model.rigidbodies.append(rigidBody)
294+
295+ # constraint
296+ model.joints=[pmd.Joint(
297+ name=obj[bl.CONSTRAINT_NAME].encode('cp932'),
298+ rigidbody_index_a=rigidNameMap[obj[bl.CONSTRAINT_A]],
299+ rigidbody_index_b=rigidNameMap[obj[bl.CONSTRAINT_B]],
300+ position=common.Vector3(
301+ obj.location[0],
302+ obj.location[2],
303+ obj.location[1]),
304+ rotation=common.Vector3(
305+ -obj.rotation_euler[0],
306+ -obj.rotation_euler[2],
307+ -obj.rotation_euler[1]),
308+ translation_limit_min=common.Vector3(
309+ obj[bl.CONSTRAINT_POS_MIN][0],
310+ obj[bl.CONSTRAINT_POS_MIN][1],
311+ obj[bl.CONSTRAINT_POS_MIN][2]
312+ ),
313+ translation_limit_max=common.Vector3(
314+ obj[bl.CONSTRAINT_POS_MAX][0],
315+ obj[bl.CONSTRAINT_POS_MAX][1],
316+ obj[bl.CONSTRAINT_POS_MAX][2]
317+ ),
318+ rotation_limit_min=common.Vector3(
319+ obj[bl.CONSTRAINT_ROT_MIN][0],
320+ obj[bl.CONSTRAINT_ROT_MIN][1],
321+ obj[bl.CONSTRAINT_ROT_MIN][2]),
322+ rotation_limit_max=common.Vector3(
323+ obj[bl.CONSTRAINT_ROT_MAX][0],
324+ obj[bl.CONSTRAINT_ROT_MAX][1],
325+ obj[bl.CONSTRAINT_ROT_MAX][2]),
326+ spring_constant_translation=common.Vector3(
327+ obj[bl.CONSTRAINT_SPRING_POS][0],
328+ obj[bl.CONSTRAINT_SPRING_POS][1],
329+ obj[bl.CONSTRAINT_SPRING_POS][2]),
330+ spring_constant_rotation=common.Vector3(
331+ obj[bl.CONSTRAINT_SPRING_ROT][0],
332+ obj[bl.CONSTRAINT_SPRING_ROT][1],
333+ obj[bl.CONSTRAINT_SPRING_ROT][2])
334+ )
335+ for obj in self.oneSkinMesh.constraints]
336+
337+ # 書き込み
338+ bl.message('write: %s' % path)
339+ return writer.write(io.open(path, 'wb'), model)
1159340
1160341
1161342 def _execute(filepath=''):
@@ -1163,10 +344,11 @@ def _execute(filepath=''):
1163344 if not active:
1164345 print("abort. no active object.")
1165346 return
1166- exporter=PmdExporter()
347+
348+ exporter=oneskinmesh.Exporter()
1167349 exporter.setup()
1168350 print(exporter)
1169- exporter.write(filepath)
1170- bl.object.activate(active)
1171351
352+ write(exporter, filepath)
353+ bl.object.activate(active)
1172354
--- a/blender26-meshio/export_pmx.py
+++ b/blender26-meshio/export_pmx.py
@@ -1,6 +1,18 @@
11 # coding: utf-8
22
3+from . import bl
4+
5+
36 def _execute(filepath):
47 print(filepath)
8+ active=bl.object.getActive()
9+ if not active:
10+ print("abort. no active object.")
11+ return
12+ exporter=PmdExporter()
13+ exporter.setup()
14+ print(exporter)
15+ exporter.write(filepath)
16+ bl.object.activate(active)
517 return {'FINISHED'}
618
--- /dev/null
+++ b/blender26-meshio/oneskinmesh.py
@@ -0,0 +1,801 @@
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+
--- a/pymeshio/pmd/__init__.py
+++ b/pymeshio/pmd/__init__.py
@@ -458,7 +458,7 @@ class RigidBody(object):
458458 angular_damping,
459459 restitution,
460460 friction,
461- mode,
461+ mode
462462 ):
463463 self.name=name
464464 self.bone_index=bone_index
旧リポジトリブラウザで表示