OSDN Git Service

refactoring export_pmd
[meshio/pymeshio.git] / blender26-meshio / oneskinmesh.py
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