OSDN Git Service

f75052c1c236eeb59f78933c214ba8effb3950da
[meshio/meshio.git] / bl24.py
1 # coding: utf-8
2 import sys
3 import os
4 import Blender
5 from Blender import Mathutils
6 import bpy
7
8
9 # ファイルシステムの文字コード
10 # 改造版との共用のため
11 FS_ENCODING=sys.getfilesystemencoding()
12 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
13     INTERNAL_ENCODING='utf-8'
14 else:
15     INTERNAL_ENCODING=FS_ENCODING
16
17
18 ###############################################################################
19 # Writer
20 ###############################################################################
21 class Writer(object):
22     def __init__(self, path, encoding):
23         self.io=open(path, "wb")
24         self.encoding=encoding
25
26     def write(self, s):
27         self.io.write(s)
28
29     def flush(self):
30         self.io.flush()
31
32     def close(self):
33         self.io.close()
34
35
36 ###############################################################################
37 # ProgressBar
38 ###############################################################################
39 class ProgressBar(object):
40     def __init__(self, base):
41         print("#### %s ####" % base)
42         self.base=base
43         self.start=Blender.sys.time() 
44         self.set('<start>', 0)
45
46     def advance(self, message, progress):
47         self.progress+=float(progress)
48         self._print(message)
49
50     def set(self, message, progress):
51         self.progress=float(progress)
52         self._print(message)
53
54     def _print(self, message):
55         print(message)
56         message="%s: %s" % (self.base, message)
57         if message.__class__ is unicode:
58             message=message.encode(FS_ENCODING)
59         Blender.Window.DrawProgressBar(self.progress, message)
60
61     def finish(self):
62         self.progress=1.0
63         message='finished in %.2f sec' % (Blender.sys.time()-self.start)
64         self.set(message, 1.0)
65
66
67 ###############################################################################
68 # for mqo mikoto bone.
69 ###############################################################################
70 class MikotoBone(object):
71     __slots__=[
72             'name',
73             'iHead', 'iTail', 'iUp',
74             'vHead', 'vTail', 'vUp',
75             'parent', 'isFloating',
76             'children',
77             ]
78     def __init__(self, face=None, vertices=None, materials=None):
79         self.parent=None
80         self.isFloating=False
81         self.children=[]
82         if not face:
83             self.name='root'
84             return
85
86         self.name=materials[face.material_index].name.encode('utf-8')
87
88         i0=face.indices[0]
89         i1=face.indices[1]
90         i2=face.indices[2]
91         v0=vertices[i0]
92         v1=vertices[i1]
93         v2=vertices[i2]
94         e01=v1-v0
95         e12=v2-v1
96         e20=v0-v2
97         sqNorm0=e01.getSqNorm()
98         sqNorm1=e12.getSqNorm()
99         sqNorm2=e20.getSqNorm()
100         if sqNorm0>sqNorm1:
101             if sqNorm1>sqNorm2:
102                 # e01 > e12 > e20
103                 self.iHead=i2
104                 self.iTail=i1
105                 self.iUp=i0
106             else:
107                 if sqNorm0>sqNorm2:
108                     # e01 > e20 > e12
109                     self.iHead=i2
110                     self.iTail=i0
111                     self.iUp=i1
112                 else:
113                     # e20 > e01 > e12
114                     self.iHead=i1
115                     self.iTail=i0
116                     self.iUp=i2
117         else:
118             # 0 < 1
119             if sqNorm1<sqNorm2:
120                 # e20 > e12 > e01
121                 self.iHead=i1
122                 self.iTail=i2
123                 self.iUp=i0
124             else:
125                 if sqNorm0<sqNorm2:
126                     # e12 > e20 > e01
127                     self.iHead=i0
128                     self.iTail=i2
129                     self.iUp=i1
130                 else:
131                     # e12 > e01 > e20
132                     self.iHead=i0
133                     self.iTail=i1
134                     self.iUp=i2
135         self.vHead=vertices[self.iHead]
136         self.vTail=vertices[self.iTail]
137         self.vUp=vertices[self.iUp]
138
139         if self.name.endswith('[]'):
140             basename=self.name[0:-2]
141             # expand LR name
142             if self.vTail.x>0:
143                 self.name="%s_L" % basename
144             else:
145                 self.name="%s_R" % basename
146
147
148     def setParent(self, parent, floating=False):
149         if floating:
150             self.isFloating=True
151         self.parent=parent
152         parent.children.append(self)
153
154     def printTree(self, indent=''):
155         print("%s%s" % (indent, self.name))
156         for child in self.children:
157             child.printTree(indent+'  ')
158
159
160 def build_armature(armature, mikotoBone, parent=None):
161     """
162     create a armature bone.
163     """
164     bone = Armature.Editbone()
165     bone.name = mikotoBone.name.encode('utf-8')
166     armature.bones[bone.name] = bone
167
168     bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())
169     bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())
170     if parent:
171         bone.parent=parent
172         if mikotoBone.isFloating:
173             pass
174         else:
175             bone.options=[Armature.CONNECTED]
176
177     for child in mikotoBone.children:
178         build_armature(armature, child, bone)
179
180
181 def create_armature(scene, mqo):
182     """
183     create armature
184     """
185     boneObject=None
186     for o in mqo.objects:
187         if o.name.startswith('bone'):
188             boneObject=o
189             break
190     if not boneObject:
191         return
192
193     tailMap={}
194     for f in boneObject.faces:
195         if f.index_count!=3:
196             print("invalid index_count: %d" % f.index_count)
197             continue
198         b=MikotoBone(f, boneObject.vertices, mqo.materials)
199         tailMap[b.iTail]=b
200
201     #################### 
202     # build mikoto bone tree
203     #################### 
204     mikotoRoot=MikotoBone()
205
206     for b in tailMap.values():
207         # each bone has unique parent or is root bone.
208         if b.iHead in tailMap:
209             b.setParent(tailMap[b.iHead])
210         else: 
211             isFloating=False
212             for e in boneObject.edges:
213                 if  b.iHead==e.indices[0]:
214                     # floating bone
215                     if e.indices[1] in tailMap:
216                         b.setParent(tailMap[e.indices[1]], True)
217                         isFloating=True
218                         break
219                 elif b.iHead==e.indices[1]:
220                     # floating bone
221                     if e.indices[0] in tailMap:
222                         b.setParent(tailMap[e.indices[0]], True)
223                         isFloating=True
224                         break
225             if isFloating:
226                 continue
227
228             # no parent bone
229             b.setParent(mikotoRoot, True)
230
231     if len(mikotoRoot.children)==0:
232         print("no root bone")
233         return
234
235     if len(mikotoRoot.children)==1:
236         # single root
237         mikotoRoot=mikotoRoot.children[0]
238         mikotoRoot.parent=None
239     else:
240         mikotoRoot.vHead=Vector3(0, 10, 0)
241         mikotoRoot.vTail=Vector3(0, 0, 0)
242
243     #################### 
244     # create armature
245     #################### 
246     armature = Armature.New()
247     # link to object
248     armature_object = scene.objects.new(armature)
249     # create action
250     act = Armature.NLA.NewAction()
251     act.setActive(armature_object)
252     # set XRAY
253     armature_object.drawMode |= Object.DrawModes.XRAY
254     # armature settings
255     armature.drawType = Armature.OCTAHEDRON
256     armature.envelopes = False
257     armature.vertexGroups = True
258     armature.mirrorEdit = True
259     armature.drawNames=True
260
261     # edit bones
262     armature.makeEditable()
263     build_armature(armature, mikotoRoot)
264     armature.update()
265
266     return armature_object
267         
268
269 class TrianglePlane(object):
270     """
271     mikoto方式ボーンのアンカーウェイト計算用。
272     (不完全)
273     """
274     __slots__=['normal', 
275             'v0', 'v1', 'v2',
276             ]
277     def __init__(self, v0, v1, v2):
278         self.v0=v0
279         self.v1=v1
280         self.v2=v2
281
282     def isInsideXY(self, p):
283         v0=Vector2(self.v0.x, self.v0.y)
284         v1=Vector2(self.v1.x, self.v1.y)
285         v2=Vector2(self.v2.x, self.v2.y)
286         e01=v1-v0
287         e12=v2-v1
288         e20=v0-v2
289         c0=Vector2.cross(e01, p-v0)
290         c1=Vector2.cross(e12, p-v1)
291         c2=Vector2.cross(e20, p-v2)
292         if c0>=0 and c1>=0 and c2>=0:
293             return True
294         if c0<=0 and c1<=0 and c2<=0:
295             return True
296
297     def isInsideYZ(self, p):
298         v0=Vector2(self.v0.y, self.v0.z)
299         v1=Vector2(self.v1.y, self.v1.z)
300         v2=Vector2(self.v2.y, self.v2.z)
301         e01=v1-v0
302         e12=v2-v1
303         e20=v0-v2
304         c0=Vector2.cross(e01, p-v0)
305         c1=Vector2.cross(e12, p-v1)
306         c2=Vector2.cross(e20, p-v2)
307         if c0>=0 and c1>=0 and c2>=0:
308             return True
309         if c0<=0 and c1<=0 and c2<=0:
310             return True
311
312     def isInsideZX(self, p):
313         v0=Vector2(self.v0.z, self.v0.x)
314         v1=Vector2(self.v1.z, self.v1.x)
315         v2=Vector2(self.v2.z, self.v2.x)
316         e01=v1-v0
317         e12=v2-v1
318         e20=v0-v2
319         c0=Vector2.cross(e01, p-v0)
320         c1=Vector2.cross(e12, p-v1)
321         c2=Vector2.cross(e20, p-v2)
322         if c0>=0 and c1>=0 and c2>=0:
323             return True
324         if c0<=0 and c1<=0 and c2<=0:
325             return True
326
327
328 class MikotoAnchor(object):
329     """
330     mikoto方式スケルトンのアンカー。
331     """
332     __slots__=[
333             "triangles", "bbox",
334             ]
335     def __init__(self):
336         self.triangles=[]
337         self.bbox=None
338
339     def push(self, face, vertices):
340         if face.index_count==3:
341             self.triangles.append(TrianglePlane(
342                 vertices[face.indices[0]],
343                 vertices[face.indices[1]],
344                 vertices[face.indices[2]]
345                 ))
346         elif face.index_count==4:
347             self.triangles.append(TrianglePlane(
348                 vertices[face.indices[0]],
349                 vertices[face.indices[1]],
350                 vertices[face.indices[2]]
351                 ))
352             self.triangles.append(TrianglePlane(
353                 vertices[face.indices[2]],
354                 vertices[face.indices[3]],
355                 vertices[face.indices[0]]
356                 ))
357         # bounding box
358         if not self.bbox:
359             self.bbox=BoundingBox(vertices[face.indices[0]])
360         for i in face.indices:
361             self.bbox.expand(vertices[i])
362
363
364     def calcWeight(self, v):
365         if not self.bbox.isInside(v):
366             return 0
367
368         if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):
369             return 1.0
370         else:
371             return 0
372         
373     def anyXY(self, x, y):
374         for t in self.triangles:
375             if t.isInsideXY(Vector2(x, y)):
376                 return True
377         return False
378
379     def anyYZ(self, y, z):
380         for t in self.triangles:
381             if t.isInsideYZ(Vector2(y, z)):
382                 return True
383         return False
384
385     def anyZX(self, z, x):
386         for t in self.triangles:
387             if t.isInsideZX(Vector2(z, x)):
388                 return True
389         return False
390
391
392 def create_bone_weight(scene, mqo, armature_object, objects):
393     """
394     create mikoto bone weight.
395     """
396     anchorMap={}
397     # setup mikoto anchors
398     for o in mqo.objects:
399         if o.name.startswith("anchor"):
400             for f in o.faces:
401                 name=mqo.materials[f.material_index].name
402                 if name.endswith('[]'):
403                     basename=name[0:-2]
404                     v=o.vertices[f.indices[0]]
405                     if(v.x>0):
406                         # L
407                         name_L=basename+'_L'
408                         if not name_L in anchorMap:
409                             anchorMap[name_L]=MikotoAnchor()
410                         anchorMap[name_L].push(f, o.vertices)
411                     elif(v.x<0):
412                         # R
413                         name_R=basename+'_R'
414                         if not name_R in anchorMap:
415                             anchorMap[name_R]=MikotoAnchor()
416                         anchorMap[name_R].push(f, o.vertices)
417                     else:
418                         print("no side", v)
419                 else:
420                     if not name in anchorMap:
421                         anchorMap[name]=MikotoAnchor()
422                     anchorMap[name].push(f, o.vertices)
423
424     for o in objects:
425         # add armature modifier
426         mod=o.modifiers.append(Modifier.Types.ARMATURE)
427         mod[Modifier.Settings.OBJECT] = armature_object
428         mod[Modifier.Settings.ENVELOPES] = False
429         o.makeDisplayList()
430         # create vertex group
431         mesh=o.getData(mesh=True)
432         for name in anchorMap.keys():
433             mesh.addVertGroup(name)
434         mesh.update()
435                  
436     # assing vertices to vertex group
437     for o in objects:
438         mesh=o.getData(mesh=True)
439         for i, mvert in enumerate(mesh.verts):
440             hasWeight=False
441             for name, anchor in anchorMap.items():
442                 weight=anchor.calcWeight(mvert.co)
443                 if weight>0:
444                     mesh.assignVertsToGroup(
445                             name, [i], weight, Mesh.AssignModes.ADD)
446                     hasWeight=True
447             if not hasWeight:
448                 # debug orphan vertex
449                 print('orphan', mvert)
450         mesh.update()
451
452 ###############################################################################
453 def createEmptyObject(scene, name):
454     empty=scene.objects.new("Empty")
455     empty.setName(name)
456     return empty
457
458 def createMaterial(name):
459     material = Blender.Material.New(name)
460     return material
461
462 def createMqoMaterial(m):
463     material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))
464     material.mode |= Blender.Material.Modes.SHADELESS
465     material.rgbCol = [m.color.r, m.color.g, m.color.b]
466     material.alpha = m.color.a
467     material.amb = m.ambient
468     material.spec = m.specular
469     material.hard = int(255 * m.power)
470     return material
471
472 def createPmdMaterial(m):
473     material=Blender.Material.New()
474     material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
475     material.setRef(1)
476     material.diffuseSize = 3.14/2
477     material.setDiffuseSmooth(0)
478     material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
479     material.setSpecSize(0)
480     material.setSpec(0)
481     material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
482     material.setAlpha(m.diffuse.a)
483     material.setSpec(m.shinness*0.1)
484     material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
485     material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
486     material.enableSSS=True if m.flag==1 else False
487     return material
488
489 def createTexture(path):
490     image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))
491     texture = Blender.Texture.New(path.encode(INTERNAL_ENCODING))
492     texture.type = Blender.Texture.Types.IMAGE
493     texture.image = image
494     return texture, image
495
496 def materialAddTexture(material, texture):
497     material.mode = material.mode | Blender.Material.Modes.TEXFACE
498     material.setTexture(0, texture, Blender.Texture.TexCo.UV)
499
500
501 def createMesh(scene, name):
502     mesh = Blender.Mesh.New()
503     mesh_object=scene.objects.new(mesh, name.encode(INTERNAL_ENCODING))
504     return mesh, mesh_object
505
506
507 def objectMakeParent(parent, child):
508     parent.makeParent([child])
509
510
511 def meshAddMaterial(mesh, material):
512     mesh.materials+=[material]
513
514
515 def meshAddMqoGeometry(mesh, o, materials, imageMap, scale):
516     # add vertices
517     mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy
518     mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])
519     # add faces
520     mesh_faces=[]
521     for face in o.faces:
522         face_indices=[]
523         for i in xrange(face.index_count):
524             face_indices.append(face.getIndex(i)+1)
525         mesh_faces.append(face_indices)
526     #new_faces=mesh.faces.extend([face.indices for face in o.faces], 
527     new_faces=mesh.faces.extend(mesh_faces,
528             #ignoreDups=True, 
529             indexList=True)
530     mesh.update()
531     
532     # gather used materials
533     materialMap = {}
534     if new_faces:
535         for i in new_faces:
536             if type(i) is int:
537                 materialMap[o.faces[i].material_index]=True
538
539     # blender limits 16 materials per mesh
540     # separate mesh ?
541     for i, material_index in enumerate(materialMap.keys()):
542         if i>=16:
543             print("over 16 materials!")
544             break
545         mesh.materials+=[materials[material_index]]
546         materialMap[material_index]=i
547     
548     # set face params
549     for i, f in enumerate(o.faces):       
550         if not type(new_faces[i]) is int:
551             continue
552
553         face=mesh.faces[new_faces[i]]
554
555         uv_array=[]
556         for i in xrange(f.index_count):
557             uv_array.append(Blender.Mathutils.Vector(
558                 f.getUV(i).x, 
559                 1.0-f.getUV(i).y)
560                 )
561         try:
562             face.uv=uv_array
563         except Exception as msg:
564             #print msg
565             #print face.index, uv_array
566             pass
567     
568         if f.material_index in materialMap:
569             face.mat = materialMap[f.material_index]
570
571         face.smooth = 1
572
573     # rmeove dummy 0 vertex
574     mesh.verts.delete(0)
575         
576     mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH
577     mesh.maxSmoothAngle = int(o.smoothing)
578     mesh.smooth()
579     mesh.calcNormals()
580     mesh.flipNormals()
581     mesh.update()
582
583     # mirror modifier
584     if o.mirror:
585         mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)
586
587 def getTexture(m, dirname):
588     tex=""
589     aplane=""
590     # texture
591     for texture in m.getTextures():
592         if texture and texture.tex and texture.tex.getImage():
593             image=texture.tex.getImage()
594             if not image:
595                 continue
596             imagePath=Blender.sys.expandpath(image.getFilename())
597             if len(dirname)>0 and imagePath.startswith(dirname):
598                 # 相対パスに変換する
599                 imagePath=imagePath[len(dirname)+1:len(imagePath)]
600             if texture.mtCol>0:
601                 tex=" tex(\"%s\")" % imagePath
602             elif texture.mtAlpha>0:
603                 aplane=" aplane(\"%s\")" % imagePath
604     return tex, aplane
605
606 def objectDuplicate(scene, obj):
607     mesh, dumy=createMesh(scene, obj.name.decode(INTERNAL_ENCODING))
608     # not apply modifiers
609     mesh.getFromObject(obj.name, 1)
610     # apply matrix
611     dumy.setMatrix(obj.matrixWorld)
612     return mesh, dumy
613
614 def objectDelete(scene, obj):
615     scene.objects.unlink(obj)
616
617 def faceVertexCount(face):
618     return len(face.v)
619
620 def faceVertices(face):
621     # flip
622     return [v.index for v in reversed(face.v)]
623
624 def meshHasUV(mesh):
625     return mesh.faceUV
626
627 def faceHasUV(mesh, i, face):
628     return len(face.uv)>0
629
630 def faceGetUV(mesh, i, face, count):
631     # flip
632     return reversed(face.uv)
633
634 def materialToMqo(m):
635     return "\"%s\" shader(3) col(%f %f %f %f)" % (
636             m.name, m.rgbCol[0], m.rgbCol[1], m.rgbCol[2], m.alpha)
637
638 def faceMaterialIndex(face):
639     return face.mat
640
641 def objectGetData(o):
642     return o.getData(mesh=True)
643
644 def objectAddArmatureModifier(o, armature_object):
645     mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
646     mod[Blender.Modifier.Settings.OBJECT] = armature_object
647     mod[Blender.Modifier.Settings.ENVELOPES] = False
648
649 def objectSelect(o):
650     o.select(True)
651
652 def objectGetPose(o):
653     return o.getPose()
654
655 def poseBoneLimit(n, b):
656     if n.endswith("_t"):
657         return
658     if n.startswith("knee_"):
659         b.lockYRot=True
660         b.lockZRot=True
661         b.limitX=True
662         b.limitMin=[0, 0, 0]
663         b.limitMax=[180, 0, 0]
664     elif n.startswith("ankle_"):
665         b.lockYRot=True
666
667 def enterEditMode():
668     Blender.Window.EditMode(1)
669
670 def exitEditMode():
671     Blender.Window.EditMode(0)
672
673 def objectDeselectAll():
674     for o in bpy.data.scenes.active.objects:
675         o.select(False)
676
677 def objectActivate(scene, o):
678     o.select(True )
679     scene.objects.active=o
680
681 def meshAddVertexGroup(meshObject, name):
682     meshObject.getData(mesh=True).addVertGroup(name)
683
684 def meshUseVertexUv(mesh):
685     mesh.vertexUV = 1
686
687 def vertexSetNormal(mvert, normal):
688     mvert.no=Mathutils.Vector(*normal)
689
690 def vertexSetUv(mvert, uv):
691     mvert.uvco=uv
692
693 def meshAssignVertexGroup(meshObject, name, index, weight):
694     meshObject.getData(mesh=True).assignVertsToGroup(name, 
695             [index], weight, Blender.Mesh.AssignModes.ADD)
696
697 def meshCreateVerteicesAndFaces(mesh, vertices, faces):
698     mesh.verts.extend(vertices)
699     mesh.faces.extend(faces, ignoreDups=True)
700
701 def meshAddUV(mesh):
702     mesh.addUVLayer('NewUV')
703
704 def meshVertsDelete(mesh, remove_vertices):
705     mesh.verts.delete(remove_vertices)
706
707 def createArmature(scene):
708     armature = Blender.Armature.New()
709     armature_object = scene.objects.new(armature)
710
711     # set XRAY
712     armature_object.drawMode = (
713             armature_object.drawMode | Blender.Object.DrawModes.XRAY)
714     # armature settings
715     armature.drawType = Blender.Armature.OCTAHEDRON
716     armature.drawNames=True
717     armature.envelopes = False
718     armature.vertexGroups = True
719     armature.mirrorEdit = True
720
721     return armature, armature_object
722
723 def armatureMakeEditable(scene, armature_object):
724     # create armature
725     armature_object.getData().makeEditable()
726
727 def createIkConstraint(armature_object, p_bone, effector_name, ik):
728     cSetting = Blender.Constraint.Settings
729     # IK solver
730     constraint = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
731     constraint[cSetting.CHAINLEN]=len(ik.children)
732     constraint[cSetting.TARGET]=armature_object
733     constraint[cSetting.USETIP]=False
734     constraint[cSetting.BONE]=effector_name
735     # not used. place folder when export.
736     constraint[cSetting.ROTWEIGHT]=ik.weight
737     constraint[cSetting.ITERATIONS]=ik.iterations * 10
738     return constraint
739
740 def createArmatureBone(armature, name):
741     bone=Blender.Armature.Editbone()
742     bone.name=name.encode(INTERNAL_ENCODING)
743     armature.bones[name]=bone
744     return bone
745
746 def boneSetConnected(bone):
747     bone.options+=[Blender.Armature.CONNECTED]
748
749 def createVector(x, y, z):
750     return Mathutils.Vector(x, y, z)
751
752 def armatureUpdate(armature):
753     armature.update()
754
755 def boneLayerMask(bone, layers):
756     mask=0
757     for i, enable in enumerate(layers):
758         if enable!=0:
759             mask+=(1<<i)
760     bone.layerMask=mask
761
762 def objectPinShape(o):
763     o.pinShape=True
764
765 def objectAddShapeKey(o, name):
766     mesh=o.getData(mesh=True)
767     mesh.insertKey()
768     block=mesh.key.blocks[-1]
769     block.name=name.encode(INTERNAL_ENCODING)
770     return block
771
772 def objectActivateShapeKey(o, index):
773     o.activeShape=index
774
775 def shapeKeyAssign(shapeKey, index, pos):
776     shapeKey.data[index]=pos
777
778 def objectIsVisible(obj):
779     return obj.restrictDisplay
780
781 def meshVertexGroupNames(meshObject):
782     return meshObject.getData(mesh=True).getVertGroupNames()
783
784 def faceNormal(face):
785     return face.no
786
787 def meshFaceUv(mesh, i, face):
788     return face.uv
789
790 def armatureModifierGetObject(m):
791     return m[Blender.Modifier.Settings.OBJECT]
792
793 def objectHasShapeKey(o):
794     return o.getData(mesh=True).key
795
796 def objectShapeKeys(o):
797     return o.getData(mesh=True).key.blocks
798
799 def meshVertexGroup(meshObject, name):
800     indices=[]
801     for index in meshObject.getData(mesh=True).getVertsFromGroup(name):
802         indices.append(index)
803     return indices
804
805 def materialGet(scene, material_name):
806     return Blender.Material.Get(material_name)
807
808 def modifierIsArmature(m):
809     return m.name=="Armature"
810
811 def boneHeadLocal(b):
812     return b.head['ARMATURESPACE'][0:3]
813
814 def boneTailLocal(b):
815     return b.tail['ARMATURESPACE'][0:3]
816
817 def boneIsConnected(b):
818     return Blender.Armature.CONNECTED in b.options
819
820 def constraintIsIKSolver(c):
821     return c.type==Blender.Constraint.Type.IKSOLVER
822
823 def ikChainLen(c):
824     return c[Blender.Constraint.Settings.CHAINLEN]
825
826 def ikTarget(c):
827     return c[Blender.Constraint.Settings.BONE]
828
829 def ikItration(c):
830     return c[Blender.Constraint.Settings.ITERATIONS]
831
832 def ikRotationWeight(c):
833     return c[Blender.Constraint.Settings.ROTWEIGHT]
834
835 def shapeKeyGet(b, index):
836     return b.data[index]
837
838 def shapeKeys(b):
839     return b.data
840