OSDN Git Service

implement shape key
[meshio/pymeshio.git] / blender26-meshio / import_mqo.py
1 #!BPY
2 # coding: utf-8
3 """ 
4 Name: 'Metasequoia(.mqo)...'
5 Blender: 245
6 Group: 'Import'
7 Tooltip: 'Import from Metasequoia file format (.mqo)'
8 """
9 __author__=['ousttrue']
10 __url__ = ["http://gunload.web.fc2.com/blender/"]
11 __bpydoc__= '''\
12
13 MQO Importer
14
15 This script imports a mqo into Blender for editing.
16
17 20080123: update.
18 20091125: modify for linux.
19 20100310: rewrite.
20 20100311: create armature from mikoto bone.
21 20100505: C extension.
22 20100606: integrate 2.4 and 2.5.
23 20100619: fix multibyte object name.
24 20100626: refactoring.
25 20100724: update for Blender2.53.
26 20100731: add full python module.
27 20101005: update for Blender2.54.
28 20101228: update for Blender2.55.
29 20110429: update for Blender2.57b.
30 20110918: update for Blender2.59.
31 20111002: update for pymeshio-2.1.0
32 '''
33
34 bl_addon_info = {
35         'category': 'Import/Export',
36         'name': 'Import: Metasequioa Model Format (.mqo)',
37         'author': 'ousttrue',
38         'version': (2, 0),
39         'blender': (2, 5, 3),
40         'location': 'File > Import',
41         'description': 'Import from the Metasequioa Model Format (.mqo)',
42         'warning': '', # used for warning icon and text in addons panel
43         'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
44         'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
45         }
46
47 import os
48 import sys
49 from .pymeshio.mqo import reader
50
51 # for 2.5
52 import bpy
53
54 # wrapper
55 from . import bl
56
57 def createMqoMaterial(m):
58     material = bpy.data.materials.new(m.name.decode("cp932"))
59     # shader
60     if m.shader==1:
61         material.diffuse_shader='FRESNEL'
62     else:
63         material.diffuse_shader='LAMBERT'
64     # diffuse
65     material.diffuse_color=[m.color.r, m.color.g, m.color.b]
66     material.diffuse_intensity=m.diffuse
67     material.alpha=m.color.a
68     # other
69     material.ambient = m.ambient
70     #material.specular = m.specular
71     material.emit=m.emit
72     material.use_shadeless=True
73     return material
74
75
76 def has_mikoto(mqo):
77     return False
78
79
80 def __createMaterials(mqo, directory):
81     """
82     create blender materials and renturn material list.
83     """
84     materials = []
85     textureMap={}
86     imageMap={}
87     if len(mqo.materials)>0:
88         for material_index, m in enumerate(mqo.materials):
89             # material
90             material=createMqoMaterial(m)
91             materials.append(material)
92             # texture
93             texture_name=m.tex.decode("cp932")
94             if texture_name!=b'':
95                 if texture_name in textureMap:
96                     texture=textureMap[texture_name]
97                 else:
98                     # load texture image
99                     if os.path.isabs(texture_name):
100                         # absolute
101                         path = texture_name
102                     else:
103                         # relative
104                         path = os.path.join(directory, texture_name)
105                     # texture
106                     path=path.replace("\\", "/")
107                     if os.path.exists(path):
108                         print("create texture:", path)
109                         texture, image=bl.texture.create(path)
110                         textureMap[texture_name]=texture
111                         imageMap[material_index]=image
112                     else:
113                         print("%s not exits" % path)
114                         continue
115                 bl.material.addTexture(material, texture)
116     else:
117         # default material
118         pass
119     return materials, imageMap
120
121
122 def __createObjects(mqo, root, materials, imageMap, scale):
123     """
124     create blender mesh objects.
125     """
126     # tree stack
127     stack=[root]    
128     objects=[]
129     for o in mqo.objects:
130         mesh, mesh_object=bl.mesh.create(o.name.decode("cp932"))
131
132         # add hierarchy
133         stack_depth=len(stack)-1
134         #print(o.depth, stack_depth)
135         if o.depth<stack_depth:
136             for i in range(stack_depth-o.depth):
137                 stack.pop()
138         bl.object.makeParent(stack[-1], mesh_object)
139         stack.append(mesh_object)
140
141         obj_name=o.name.decode("cp932")
142         if obj_name.startswith('sdef'):
143             objects.append(mesh_object)
144         elif obj_name.startswith('anchor'):
145             bl.object.setLayerMask(mesh_object, [0, 1])
146         elif obj_name.startswith('bone'):
147             bl.object.setLayerMask(mesh_object, [0, 1])
148
149         # geometry
150         vertices=[(v.x * scale, -v.z * scale, v.y * scale) for v in o.vertices]
151         faces=[]
152         materialMap={}
153         for f in o.faces:
154             face_indices=[]
155             # flip face
156             for i in reversed(range(f.index_count)):
157                 face_indices.append(f.getIndex(i))
158             faces.append(face_indices)
159             materialMap[f.material_index]=True
160         bl.mesh.addGeometry(mesh, vertices, faces)
161
162         # blender limits 16 materials per mesh
163         for i, material_index in enumerate(materialMap.keys()):
164             if i>=16:
165                 # split a mesh ?
166                 print("over 16 materials!")
167                 break
168             bl.mesh.addMaterial(mesh, materials[material_index])
169             materialMap[material_index]=i
170  
171         # set face params
172         assert(len(o.faces)==len(mesh.faces))
173         bl.mesh.addUV(mesh)
174         for i, (f, face) in enumerate(zip(o.faces, mesh.faces)):
175             uv_array=[]
176             # ToDo FIX
177             # flip face
178             for j in reversed(range(f.index_count)):
179                 uv_array.append((f.getUV(j).x, 1.0-f.getUV(j).y))
180             bl.mesh.setFaceUV(mesh, i, face, uv_array, 
181                     imageMap.get(f.material_index, None))
182             if f.material_index in materialMap:
183                 bl.face.setMaterial(face, materialMap[f.material_index])
184             bl.face.setSmooth(face, True)
185
186         # mirror modifier
187         if o.mirror:
188             bl.modifier.addMirror(mesh_object)
189
190         # set smoothing
191         bl.mesh.setSmooth(mesh, o.smoothing)
192
193         # calc normal
194         bl.mesh.recalcNormals(mesh_object)
195
196     return objects
197
198
199 ###############################################################################
200 # for mqo mikoto bone.
201 ###############################################################################
202 class MikotoBone(object):
203     __slots__=[
204             'name',
205             'iHead', 'iTail', 'iUp',
206             'vHead', 'vTail', 'vUp',
207             'parent', 'isFloating',
208             'children',
209             ]
210     def __init__(self, face=None, vertices=None, materials=None):
211         self.parent=None
212         self.isFloating=False
213         self.children=[]
214         if not face:
215             self.name='root'
216             return
217
218         self.name=materials[face.material_index].name.encode('utf-8')
219
220         i0=face.getIndex(0)
221         i1=face.getIndex(1)
222         i2=face.getIndex(2)
223         v0=vertices[i0]
224         v1=vertices[i1]
225         v2=vertices[i2]
226         e01=v1-v0
227         e12=v2-v1
228         e20=v0-v2
229         sqNorm0=e01.getSqNorm()
230         sqNorm1=e12.getSqNorm()
231         sqNorm2=e20.getSqNorm()
232         if sqNorm0>sqNorm1:
233             if sqNorm1>sqNorm2:
234                 # e01 > e12 > e20
235                 self.iHead=i2
236                 self.iTail=i1
237                 self.iUp=i0
238             else:
239                 if sqNorm0>sqNorm2:
240                     # e01 > e20 > e12
241                     self.iHead=i2
242                     self.iTail=i0
243                     self.iUp=i1
244                 else:
245                     # e20 > e01 > e12
246                     self.iHead=i1
247                     self.iTail=i0
248                     self.iUp=i2
249         else:
250             # 0 < 1
251             if sqNorm1<sqNorm2:
252                 # e20 > e12 > e01
253                 self.iHead=i1
254                 self.iTail=i2
255                 self.iUp=i0
256             else:
257                 if sqNorm0<sqNorm2:
258                     # e12 > e20 > e01
259                     self.iHead=i0
260                     self.iTail=i2
261                     self.iUp=i1
262                 else:
263                     # e12 > e01 > e20
264                     self.iHead=i0
265                     self.iTail=i1
266                     self.iUp=i2
267         self.vHead=vertices[self.iHead]
268         self.vTail=vertices[self.iTail]
269         self.vUp=vertices[self.iUp]
270
271         if self.name.endswith('[]'):
272             basename=self.name[0:-2]
273             # expand LR name
274             if self.vTail.x>0:
275                 self.name="%s_L" % basename
276             else:
277                 self.name="%s_R" % basename
278
279
280     def setParent(self, parent, floating=False):
281         if floating:
282             self.isFloating=True
283         self.parent=parent
284         parent.children.append(self)
285
286     def printTree(self, indent=''):
287         print("%s%s" % (indent, self.name))
288         for child in self.children:
289             child.printTree(indent+'  ')
290
291
292 def build_armature(armature, mikotoBone, parent=None):
293     """
294     create a armature bone.
295     """
296     bone = Armature.Editbone()
297     bone.name = mikotoBone.name.encode('utf-8')
298     armature.bones[bone.name] = bone
299
300     bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())
301     bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())
302     if parent:
303         bone.parent=parent
304         if mikotoBone.isFloating:
305             pass
306         else:
307             bone.options=[Armature.CONNECTED]
308
309     for child in mikotoBone.children:
310         build_armature(armature, child, bone)
311
312
313 def create_armature(mqo):
314     """
315     create armature
316     """
317     boneObject=None
318     for o in mqo.objects:
319         if o.name.startswith('bone'):
320             boneObject=o
321             break
322     if not boneObject:
323         return
324
325     tailMap={}
326     for f in boneObject.faces:
327         if f.index_count!=3:
328             print("invalid index_count: %d" % f.index_count)
329             continue
330         b=MikotoBone(f, boneObject.vertices, mqo.materials)
331         tailMap[b.iTail]=b
332
333     #################### 
334     # build mikoto bone tree
335     #################### 
336     mikotoRoot=MikotoBone()
337
338     for b in tailMap.values():
339         # each bone has unique parent or is root bone.
340         if b.iHead in tailMap:
341             b.setParent(tailMap[b.iHead])
342         else: 
343             isFloating=False
344             for e in boneObject.edges:
345                 if  b.iHead==e.indices[0]:
346                     # floating bone
347                     if e.indices[1] in tailMap:
348                         b.setParent(tailMap[e.indices[1]], True)
349                         isFloating=True
350                         break
351                 elif b.iHead==e.indices[1]:
352                     # floating bone
353                     if e.indices[0] in tailMap:
354                         b.setParent(tailMap[e.indices[0]], True)
355                         isFloating=True
356                         break
357             if isFloating:
358                 continue
359
360             # no parent bone
361             b.setParent(mikotoRoot, True)
362
363     if len(mikotoRoot.children)==0:
364         print("no root bone")
365         return
366
367     if len(mikotoRoot.children)==1:
368         # single root
369         mikotoRoot=mikotoRoot.children[0]
370         mikotoRoot.parent=None
371     else:
372         mikotoRoot.vHead=Vector3(0, 10, 0)
373         mikotoRoot.vTail=Vector3(0, 0, 0)
374
375     #################### 
376     # create armature
377     #################### 
378     armature = Armature.New()
379     # link to object
380     armature_object = scene.objects.new(armature)
381     # create action
382     act = Armature.NLA.NewAction()
383     act.setActive(armature_object)
384     # set XRAY
385     armature_object.drawMode |= Object.DrawModes.XRAY
386     # armature settings
387     armature.drawType = Armature.OCTAHEDRON
388     armature.envelopes = False
389     armature.vertexGroups = True
390     armature.mirrorEdit = True
391     armature.drawNames=True
392
393     # edit bones
394     armature.makeEditable()
395     build_armature(armature, mikotoRoot)
396     armature.update()
397
398     return armature_object
399         
400
401 class TrianglePlane(object):
402     """
403     mikoto\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#\e(B
404     (\e$BIT40A4\e(B)
405     """
406     __slots__=['normal', 
407             'v0', 'v1', 'v2',
408             ]
409     def __init__(self, v0, v1, v2):
410         self.v0=v0
411         self.v1=v1
412         self.v2=v2
413
414     def isInsideXY(self, p):
415         v0=Vector2(self.v0.x, self.v0.y)
416         v1=Vector2(self.v1.x, self.v1.y)
417         v2=Vector2(self.v2.x, self.v2.y)
418         e01=v1-v0
419         e12=v2-v1
420         e20=v0-v2
421         c0=Vector2.cross(e01, p-v0)
422         c1=Vector2.cross(e12, p-v1)
423         c2=Vector2.cross(e20, p-v2)
424         if c0>=0 and c1>=0 and c2>=0:
425             return True
426         if c0<=0 and c1<=0 and c2<=0:
427             return True
428
429     def isInsideYZ(self, p):
430         v0=Vector2(self.v0.y, self.v0.z)
431         v1=Vector2(self.v1.y, self.v1.z)
432         v2=Vector2(self.v2.y, self.v2.z)
433         e01=v1-v0
434         e12=v2-v1
435         e20=v0-v2
436         c0=Vector2.cross(e01, p-v0)
437         c1=Vector2.cross(e12, p-v1)
438         c2=Vector2.cross(e20, p-v2)
439         if c0>=0 and c1>=0 and c2>=0:
440             return True
441         if c0<=0 and c1<=0 and c2<=0:
442             return True
443
444     def isInsideZX(self, p):
445         v0=Vector2(self.v0.z, self.v0.x)
446         v1=Vector2(self.v1.z, self.v1.x)
447         v2=Vector2(self.v2.z, self.v2.x)
448         e01=v1-v0
449         e12=v2-v1
450         e20=v0-v2
451         c0=Vector2.cross(e01, p-v0)
452         c1=Vector2.cross(e12, p-v1)
453         c2=Vector2.cross(e20, p-v2)
454         if c0>=0 and c1>=0 and c2>=0:
455             return True
456         if c0<=0 and c1<=0 and c2<=0:
457             return True
458
459
460 class MikotoAnchor(object):
461     """
462     mikoto\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#\e(B
463     """
464     __slots__=[
465             "triangles", "bbox",
466             ]
467     def __init__(self):
468         self.triangles=[]
469         self.bbox=None
470
471     def push(self, face, vertices):
472         if face.index_count==3:
473             self.triangles.append(TrianglePlane(
474                 vertices[face.indices[0]],
475                 vertices[face.indices[1]],
476                 vertices[face.indices[2]]
477                 ))
478         elif face.index_count==4:
479             self.triangles.append(TrianglePlane(
480                 vertices[face.indices[0]],
481                 vertices[face.indices[1]],
482                 vertices[face.indices[2]]
483                 ))
484             self.triangles.append(TrianglePlane(
485                 vertices[face.indices[2]],
486                 vertices[face.indices[3]],
487                 vertices[face.indices[0]]
488                 ))
489         # bounding box
490         if not self.bbox:
491             self.bbox=BoundingBox(vertices[face.indices[0]])
492         for i in face.indices:
493             self.bbox.expand(vertices[i])
494
495
496     def calcWeight(self, v):
497         if not self.bbox.isInside(v):
498             return 0
499
500         if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):
501             return 1.0
502         else:
503             return 0
504         
505     def anyXY(self, x, y):
506         for t in self.triangles:
507             if t.isInsideXY(Vector2(x, y)):
508                 return True
509         return False
510
511     def anyYZ(self, y, z):
512         for t in self.triangles:
513             if t.isInsideYZ(Vector2(y, z)):
514                 return True
515         return False
516
517     def anyZX(self, z, x):
518         for t in self.triangles:
519             if t.isInsideZX(Vector2(z, x)):
520                 return True
521         return False
522
523
524 def create_bone_weight(scene, mqo, armature_object, objects):
525     """
526     create mikoto bone weight.
527     """
528     anchorMap={}
529     # setup mikoto anchors
530     for o in mqo.objects:
531         if o.name.startswith("anchor"):
532             for f in o.faces:
533                 name=mqo.materials[f.material_index].name
534                 if name.endswith('[]'):
535                     basename=name[0:-2]
536                     v=o.vertices[f.indices[0]]
537                     if(v.x>0):
538                         # L
539                         name_L=basename+'_L'
540                         if not name_L in anchorMap:
541                             anchorMap[name_L]=MikotoAnchor()
542                         anchorMap[name_L].push(f, o.vertices)
543                     elif(v.x<0):
544                         # R
545                         name_R=basename+'_R'
546                         if not name_R in anchorMap:
547                             anchorMap[name_R]=MikotoAnchor()
548                         anchorMap[name_R].push(f, o.vertices)
549                     else:
550                         print("no side", v)
551                 else:
552                     if not name in anchorMap:
553                         anchorMap[name]=MikotoAnchor()
554                     anchorMap[name].push(f, o.vertices)
555
556     for o in objects:
557         # add armature modifier
558         mod=o.modifiers.append(Modifier.Types.ARMATURE)
559         mod[Modifier.Settings.OBJECT] = armature_object
560         mod[Modifier.Settings.ENVELOPES] = False
561         o.makeDisplayList()
562         # create vertex group
563         mesh=o.getData(mesh=True)
564         for name in anchorMap.keys():
565             mesh.addVertGroup(name)
566         mesh.update()
567                  
568     # assing vertices to vertex group
569     for o in objects:
570         mesh=o.getData(mesh=True)
571         for i, mvert in enumerate(mesh.verts):
572             hasWeight=False
573             for name, anchor in anchorMap.items():
574                 weight=anchor.calcWeight(mvert.co)
575                 if weight>0:
576                     mesh.assignVertsToGroup(
577                             name, [i], weight, Mesh.AssignModes.ADD)
578                     hasWeight=True
579             if not hasWeight:
580                 # debug orphan vertex
581                 print('orphan', mvert)
582         mesh.update()
583
584
585 def _execute(filepath='', scale=0.1):
586     # read mqo model
587     model=reader.read_from_file(filepath)
588     if not model:
589         bl.message("fail to load %s" % filepath)
590         return
591
592     # create materials
593     materials, imageMap=__createMaterials(model, os.path.dirname(filepath))
594     if len(materials)==0:
595         materials.append(bl.material.create('default'))
596
597     # create objects
598     root=bl.object.createEmpty(os.path.basename(filepath))
599     objects=__createObjects(model, root, materials, imageMap, scale)
600
601     if has_mikoto(model):
602         # create mikoto bone
603         armature_object=create_armature(model)
604         if armature_object:
605             root.makeParent([armature_object])
606
607             # create bone weight
608             create_bone_weight(model, armature_object, objects)
609