soc-2008-mxcurioni: merged changes to revision 14798, compilation works for rendering...
[blender-staging.git] / release / scripts / md2_import.py
1 #!BPY
2
3 """
4 Name: 'MD2 (.md2)'
5 Blender: 239
6 Group: 'Import'
7 Tooltip: 'Import from Quake file format (.md2).'
8 """
9
10 __author__ = 'Bob Holcomb'
11 __version__ = '0.16'
12 __url__ = ["Bob's site, http://bane.servebeer.com",
13      "Support forum, http://scourage.servebeer.com/phpbb/", "blender", "elysiun"]
14 __email__ = ["Bob Holcomb, bob_holcomb:hotmail*com", "scripts"]
15 __bpydoc__ = """\
16 This script imports a Quake 2 file (MD2), textures, 
17 and animations into blender for editing.  Loader is based on MD2 loader from www.gametutorials.com-Thanks DigiBen! and the md3 blender loader by PhaethonH <phaethon@linux.ucla.edu><br>
18
19  Additional help from: Shadwolf, Skandal, Rojo and Campbell Barton<br>
20  Thanks Guys!
21 """
22
23 # ***** BEGIN GPL LICENSE BLOCK *****
24 #
25 # Script copyright (C) Bob Holcomb
26 #
27 # This program is free software; you can redistribute it and/or
28 # modify it under the terms of the GNU General Public License
29 # as published by the Free Software Foundation; either version 2
30 # of the License, or (at your option) any later version.
31 #
32 # This program is distributed in the hope that it will be useful,
33 # but WITHOUT ANY WARRANTY; without even the implied warranty of
34 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35 # GNU General Public License for more details.
36 #
37 # You should have received a copy of the GNU General Public License
38 # along with this program; if not, write to the Free Software Foundation,
39 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
40 #
41 # ***** END GPL LICENCE BLOCK *****
42 # --------------------------------------------------------------------------
43
44
45 import Blender
46 from Blender import Mesh, Object, sys
47 from Blender.BGL import *
48 from Blender.Draw import *
49 from Blender.Window import *
50 from Blender.Mathutils import Vector
51 import struct
52 from types import *
53
54
55 ######################################################
56 # Main Body
57 ######################################################
58
59 #returns the string from a null terminated string
60 def asciiz (s):
61   n = 0
62   while (ord(s[n]) != 0):
63     n = n + 1
64   return s[0:n]
65
66
67 ######################################################
68 # MD2 Model Constants
69 ######################################################
70 MD2_MAX_TRIANGLES=4096
71 MD2_MAX_VERTICES=2048
72 MD2_MAX_TEXCOORDS=2048
73 MD2_MAX_FRAMES=512
74 MD2_MAX_SKINS=32
75 MD2_MAX_FRAMESIZE=(MD2_MAX_VERTICES * 4 + 128)
76
77 ######################################################
78 # MD2 data structures
79 ######################################################
80 class md2_alias_triangle(object):
81         __slots__ = 'vertices', 'lightnormalindex'
82         binary_format="<3BB" #little-endian (<), 3 Unsigned char
83         
84         def __init__(self):
85                 self.vertices=[0]*3
86                 self.lightnormalindex=0
87
88         def load(self, file):
89                 temp_data = file.read(struct.calcsize(self.binary_format))
90                 data = struct.unpack(self.binary_format, temp_data)
91                 self.vertices[0]=data[0]
92                 self.vertices[1]=data[1]
93                 self.vertices[2]=data[2]
94                 self.lightnormalindex=data[3]
95                 return self
96
97         def dump(self):
98                 print "MD2 Alias_Triangle Structure"
99                 print "vertex: ", self.vertices[0]
100                 print "vertex: ", self.vertices[1]
101                 print "vertex: ", self.vertices[2]
102                 print "lightnormalindex: ",self.lightnormalindex
103                 print ""
104
105 class md2_face(object):
106         
107         binary_format="<3h3h" #little-endian (<), 3 short, 3 short
108         
109         __slots__ = 'vertex_index', 'texture_index'
110         
111         def __init__(self):
112                 self.vertex_index = [ 0, 0, 0 ]
113                 self.texture_index = [ 0, 0, 0]
114
115         def load (self, file):
116                 temp_data=file.read(struct.calcsize(self.binary_format))
117                 data=struct.unpack(self.binary_format, temp_data)
118                 self.vertex_index[0]=data[0]
119                 self.vertex_index[1]=data[1]
120                 self.vertex_index[2]=data[2]
121                 self.texture_index[0]=data[3]
122                 self.texture_index[1]=data[4]
123                 self.texture_index[2]=data[5]
124                 return self
125
126         def dump (self):
127                 print "MD2 Face Structure"
128                 print "vertex index: ", self.vertex_index[0]
129                 print "vertex index: ", self.vertex_index[1]
130                 print "vertex index: ", self.vertex_index[2]
131                 print "texture index: ", self.texture_index[0]
132                 print "texture index: ", self.texture_index[1]
133                 print "texture index: ", self.texture_index[2]
134                 print ""
135
136 class md2_tex_coord(object):
137         __slots__ = 'u', 'v'
138         binary_format="<2h" #little-endian (<), 2 unsigned short
139         
140         def __init__(self):
141                 self.u=0
142                 self.v=0
143
144         def load (self, file):
145                 temp_data=file.read(struct.calcsize(self.binary_format))
146                 data=struct.unpack(self.binary_format, temp_data)
147                 self.u=data[0]
148                 self.v=data[1]
149                 return self
150
151         def dump (self):
152                 print "MD2 Texture Coordinate Structure"
153                 print "texture coordinate u: ",self.u
154                 print "texture coordinate v: ",self.v
155                 print ""
156
157
158 class md2_skin(object):
159         __slots__ = 'name'
160         binary_format="<64s" #little-endian (<), char[64]
161
162         def __init__(self):
163                 self.name=""
164
165         def load (self, file):
166                 temp_data=file.read(struct.calcsize(self.binary_format))
167                 data=struct.unpack(self.binary_format, temp_data)
168                 self.name=asciiz(data[0])
169                 return self
170
171         def dump (self):
172                 print "MD2 Skin"
173                 print "skin name: ",self.name
174                 print ""
175
176 class md2_alias_frame(object):
177         __slots__ = 'scale', 'translate', 'name', 'vertices'
178         binary_format="<3f3f16s" #little-endian (<), 3 float, 3 float char[16]
179         #did not add the "3bb" to the end of the binary format
180         #because the alias_vertices will be read in through
181         #thier own loader
182
183         def __init__(self):
184                 self.scale=[0.0]*3
185                 self.translate=[0.0]*3
186                 self.name=""
187                 self.vertices=[]
188
189
190         def load (self, file):
191                 temp_data=file.read(struct.calcsize(self.binary_format))
192                 data=struct.unpack(self.binary_format, temp_data)
193                 self.scale[0]=data[0]
194                 self.scale[1]=data[1]
195                 self.scale[2]=data[2]
196                 self.translate[0]=data[3]
197                 self.translate[1]=data[4]
198                 self.translate[2]=data[5]
199                 self.name=asciiz(data[6])
200                 return self
201
202         def dump (self):
203                 print "MD2 Alias Frame"
204                 print "scale x: ",self.scale[0]
205                 print "scale y: ",self.scale[1]
206                 print "scale z: ",self.scale[2]
207                 print "translate x: ",self.translate[0]
208                 print "translate y: ",self.translate[1]
209                 print "translate z: ",self.translate[2]
210                 print "name: ",self.name
211                 print ""
212
213 class md2_obj(object):
214         __slots__ =\
215         'tex_coords', 'faces', 'frames',\
216         'skins', 'ident', 'version',\
217         'skin_width', 'skin_height',\
218         'frame_size', 'num_skins', 'num_vertices',\
219         'num_tex_coords', 'num_faces', 'num_GL_commands',\
220         'num_frames', 'offset_skins', 'offset_tex_coords',\
221         'offset_faces', 'offset_frames', 'offset_GL_commands'
222         
223         '''
224         #Header Structure
225         ident=0                         #int 0  This is used to identify the file
226         version=0                       #int 1  The version number of the file (Must be 8)
227         skin_width=0            #int 2  The skin width in pixels
228         skin_height=0           #int 3  The skin height in pixels
229         frame_size=0            #int 4  The size in bytes the frames are
230         num_skins=0                     #int 5  The number of skins associated with the model
231         num_vertices=0          #int 6  The number of vertices (constant for each frame)
232         num_tex_coords=0        #int 7  The number of texture coordinates
233         num_faces=0                     #int 8  The number of faces (polygons)
234         num_GL_commands=0       #int 9  The number of gl commands
235         num_frames=0            #int 10 The number of animation frames
236         offset_skins=0          #int 11 The offset in the file for the skin data
237         offset_tex_coords=0     #int 12 The offset in the file for the texture data
238         offset_faces=0          #int 13 The offset in the file for the face data
239         offset_frames=0         #int 14 The offset in the file for the frames data
240         offset_GL_commands=0#int 15     The offset in the file for the gl commands data
241         offset_end=0            #int 16 The end of the file offset
242         '''
243         binary_format="<17i"  #little-endian (<), 17 integers (17i)
244
245         #md2 data objects
246
247         def __init__ (self):
248                 self.tex_coords=[]
249                 self.faces=[]
250                 self.frames=[]
251                 self.skins=[]
252
253
254         def load (self, file):
255                 temp_data = file.read(struct.calcsize(self.binary_format))
256                 data = struct.unpack(self.binary_format, temp_data)
257
258                 self.ident=data[0]
259                 self.version=data[1]
260
261                 if (self.ident!=844121161 or self.version!=8):
262                         print "Not a valid MD2 file"
263                         Exit()
264
265                 self.skin_width=data[2]
266                 self.skin_height=data[3]
267                 self.frame_size=data[4]
268
269                 #make the # of skin objects for model
270                 self.num_skins=data[5]
271                 for i in xrange(0,self.num_skins):
272                         self.skins.append(md2_skin())
273
274                 self.num_vertices=data[6]
275
276                 #make the # of texture coordinates for model
277                 self.num_tex_coords=data[7]
278                 for i in xrange(0,self.num_tex_coords):
279                         self.tex_coords.append(md2_tex_coord())
280
281                 #make the # of triangle faces for model
282                 self.num_faces=data[8]
283                 for i in xrange(0,self.num_faces):
284                         self.faces.append(md2_face())
285
286                 self.num_GL_commands=data[9]
287
288                 #make the # of frames for the model
289                 self.num_frames=data[10]
290                 for i in xrange(0,self.num_frames):
291                         self.frames.append(md2_alias_frame())
292                         #make the # of vertices for each frame
293                         for j in xrange(0,self.num_vertices):
294                                 self.frames[i].vertices.append(md2_alias_triangle())
295
296                 self.offset_skins=data[11]
297                 self.offset_tex_coords=data[12]
298                 self.offset_faces=data[13]
299                 self.offset_frames=data[14]
300                 self.offset_GL_commands=data[15]
301
302                 #load the skin info
303                 file.seek(self.offset_skins,0)
304                 for i in xrange(0, self.num_skins):
305                         self.skins[i].load(file)
306                         #self.skins[i].dump()
307
308                 #load the texture coordinates
309                 file.seek(self.offset_tex_coords,0)
310                 for i in xrange(0, self.num_tex_coords):
311                         self.tex_coords[i].load(file)
312                         #self.tex_coords[i].dump()
313
314                 #load the face info
315                 file.seek(self.offset_faces,0)
316                 for i in xrange(0, self.num_faces):
317                         self.faces[i].load(file)
318                         #self.faces[i].dump()
319
320                 #load the frames
321                 file.seek(self.offset_frames,0)
322                 for i in xrange(0, self.num_frames):
323                         self.frames[i].load(file)
324                         #self.frames[i].dump()
325                         for j in xrange(0,self.num_vertices):
326                                 self.frames[i].vertices[j].load(file)
327                                 #self.frames[i].vertices[j].dump()
328                 return self
329
330         def dump (self):
331                 print "Header Information"
332                 print "ident: ", self.ident
333                 print "version: ", self.version
334                 print "skin width: ", self.skin_width
335                 print "skin height: ", self.skin_height
336                 print "frame size: ", self.frame_size
337                 print "number of skins: ", self.num_skins
338                 print "number of texture coordinates: ", self.num_tex_coords
339                 print "number of faces: ", self.num_faces
340                 print "number of frames: ", self.num_frames
341                 print "number of vertices: ", self.num_vertices
342                 print "offset skins: ", self.offset_skins
343                 print "offset texture coordinates: ", self.offset_tex_coords
344                 print "offset faces: ", self.offset_faces
345                 print "offset frames: ",self.offset_frames
346                 print ""
347
348 ######################################################
349 # Import functions
350 ######################################################
351 def load_textures(md2, texture_filename):
352         #did the user specify a texture they wanted to use?
353         if texture_filename:
354                 if (Blender.sys.exists(texture_filename)):
355                         try:    return Blender.Image.Load(texture_filename)
356                         except: return -1 # could not load?
357                         
358         #does the model have textures specified with it?
359         if int(md2.num_skins) > 0:
360                 for i in xrange(0,md2.num_skins):
361                         #md2.skins[i].dump()
362                         if (Blender.sys.exists(md2.skins[i].name)):
363                                 try:    return Blender.Image.Load(md2.skins[i].name)
364                                 except: return -1
365         
366
367 def animate_md2(md2, mesh):
368         ######### Animate the verts through keyframe animation
369         
370         # Fast access to the meshes vertex coords
371         verts = [v.co for v in mesh.verts] 
372         scale = g_scale.val
373         
374         for i in xrange(1, md2.num_frames):
375                 frame = md2.frames[i]
376                 #update the vertices
377                 for j in xrange(md2.num_vertices):
378                         x=(frame.scale[0] * frame.vertices[j].vertices[0] + frame.translate[0]) * scale
379                         y=(frame.scale[1] * frame.vertices[j].vertices[1] + frame.translate[1]) * scale
380                         z=(frame.scale[2] * frame.vertices[j].vertices[2] + frame.translate[2]) * scale
381                         
382                         #put the vertex in the right spot
383                         verts[j][:] = y,-x,z
384                         
385                 mesh.insertKey(i,"absolute")
386                 # mesh.insertKey(i)
387                 
388                 #not really necissary, but I like playing with the frame counter
389                 Blender.Set("curframe", i)
390         
391         
392         # Make the keys animate in the 3d view.
393         key = mesh.key
394         key.relative = False
395         
396         # Add an IPO to teh Key
397         ipo = Blender.Ipo.New('Key', 'md2')
398         key.ipo = ipo
399         # Add a curve to the IPO
400         curve = ipo.addCurve('Basis')
401         
402         # Add 2 points to cycle through the frames.
403         curve.append((1, 0))
404         curve.append((md2.num_frames, (md2.num_frames-1)/10.0))
405         curve.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
406         
407
408
409 def load_md2(md2_filename, texture_filename):
410         #read the file in
411         file=open(md2_filename,"rb")
412         WaitCursor(1)
413         DrawProgressBar(0.0, 'Loading MD2')
414         md2=md2_obj()
415         md2.load(file)
416         #md2.dump()
417         file.close()
418
419         ######### Creates a new mesh
420         mesh = Mesh.New()
421
422         uv_coord=[]
423         #uv_list=[]
424         verts_extend = []
425         #load the textures to use later
426         #-1 if there is no texture to load
427         mesh_image=load_textures(md2, texture_filename)
428         if mesh_image == -1 and texture_filename:
429                 print 'MD2 Import, Warning, texture "%s" could not load'
430
431         ######### Make the verts
432         DrawProgressBar(0.25,"Loading Vertex Data")
433         frame = md2.frames[0]
434         scale = g_scale.val
435         
436         def tmp_get_vertex(i):
437                 #use the first frame for the mesh vertices
438                 x=(frame.scale[0]*frame.vertices[i].vertices[0]+frame.translate[0])*scale
439                 y=(frame.scale[1]*frame.vertices[i].vertices[1]+frame.translate[1])*scale
440                 z=(frame.scale[2]*frame.vertices[i].vertices[2]+frame.translate[2])*scale
441                 return y,-x,z
442         
443         mesh.verts.extend( [tmp_get_vertex(i) for i in xrange(0,md2.num_vertices)] )
444         del tmp_get_vertex
445         
446         ######## Make the UV list
447         DrawProgressBar(0.50,"Loading UV Data")
448         
449         w = float(md2.skin_width)
450         h = float(md2.skin_height)
451         if w <= 0.0: w = 1.0
452         if h <= 0.0: h = 1.0
453         #for some reason quake2 texture maps are upside down, flip that
454         uv_list = [Vector(co.u/w, 1-(co.v/h)) for co in md2.tex_coords]
455         del w, h
456         
457         ######### Make the faces
458         DrawProgressBar(0.75,"Loading Face Data")
459         faces = []
460         face_uvs = []
461         for md2_face in md2.faces:
462                 f = md2_face.vertex_index[0], md2_face.vertex_index[2], md2_face.vertex_index[1]
463                 uv = uv_list[md2_face.texture_index[0]], uv_list[md2_face.texture_index[2]], uv_list[md2_face.texture_index[1]]
464                 
465                 if f[2] == 0:
466                         # EEKADOODLE :/
467                         f= f[1], f[2], f[0]
468                         uv= uv[1], uv[2], uv[0]
469                 
470                 #ditto in reverse order with the texture verts
471                 faces.append(f)
472                 face_uvs.append(uv)
473         
474         
475         face_mapping = mesh.faces.extend(faces, indexList=True)
476         print len(faces)
477         print len(mesh.faces)
478         mesh.faceUV= True  #turn on face UV coordinates for this mesh
479         mesh_faces = mesh.faces
480         for i, uv in enumerate(face_uvs):
481                 if face_mapping[i] != None:
482                         f = mesh_faces[face_mapping[i]]
483                         f.uv = uv
484                         if (mesh_image!=-1):
485                                 f.image=mesh_image
486         
487         scn= Blender.Scene.GetCurrent()
488         mesh_obj= scn.objects.new(mesh)
489         animate_md2(md2, mesh)
490         DrawProgressBar(0.98,"Loading Animation Data")
491         
492         #locate the Object containing the mesh at the cursor location
493         cursor_pos=Blender.Window.GetCursorPos()
494         mesh_obj.setLocation(float(cursor_pos[0]),float(cursor_pos[1]),float(cursor_pos[2]))
495         DrawProgressBar (1.0, "") 
496         WaitCursor(0)
497
498 #***********************************************
499 # MAIN
500 #***********************************************
501
502 # Import globals
503 g_md2_filename=Create("*.md2")
504 #g_md2_filename=Create("/d/warvet/tris.md2")
505 g_texture_filename=Create('')
506 # g_texture_filename=Create("/d/warvet/warvet.jpg")
507
508 g_filename_search=Create("*.md2")
509 g_texture_search=Create('')
510 # g_texture_search=Create("/d/warvet/warvet.jpg")
511
512 #Globals
513 g_scale=Create(1.0)
514
515 # Events
516 EVENT_NOEVENT=1
517 EVENT_LOAD_MD2=2
518 EVENT_CHOOSE_FILENAME=3
519 EVENT_CHOOSE_TEXTURE=4
520 EVENT_SAVE_MD2=5
521 EVENT_EXIT=100
522
523 ######################################################
524 # Callbacks for Window functions
525 ######################################################
526 def filename_callback(input_filename):
527         global g_md2_filename
528         g_md2_filename.val=input_filename
529
530 def texture_callback(input_texture):
531         global g_texture_filename
532         g_texture_filename.val=input_texture
533
534 ######################################################
535 # GUI Loader
536 ######################################################
537
538
539 def draw_gui():
540         global g_scale
541         global g_md2_filename
542         global g_texture_filename
543         global EVENT_NOEVENT,EVENT_LOAD_MD2,EVENT_CHOOSE_FILENAME,EVENT_CHOOSE_TEXTURE,EVENT_EXIT
544
545         ########## Titles
546         glClear(GL_COLOR_BUFFER_BIT)
547         glRasterPos2d(8, 125)
548         Text("MD2 loader")
549
550         ######### Parameters GUI Buttons
551         BeginAlign()
552         g_md2_filename = String("MD2 file to load: ", EVENT_NOEVENT, 10, 55, 210, 18,
553                                                         g_md2_filename.val, 255, "MD2 file to load")
554         ########## MD2 File Search Button
555         Button("Browse",EVENT_CHOOSE_FILENAME,220,55,80,18)
556         EndAlign()
557
558         BeginAlign()
559         g_texture_filename = String("Texture file to load: ", EVENT_NOEVENT, 10, 35, 210, 18,
560                                                                 g_texture_filename.val, 255, "Texture file to load-overrides MD2 file")
561         ########## Texture Search Button
562         Button("Browse",EVENT_CHOOSE_TEXTURE,220,35,80,18)
563         EndAlign()
564
565         ########## Scale slider-default is 1/8 which is a good scale for md2->blender
566         g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 75, 210, 18,
567                                         1.0, 0.001, 10.0, 1, "Scale factor for obj Model");
568
569         ######### Draw and Exit Buttons
570         Button("Load",EVENT_LOAD_MD2 , 10, 10, 80, 18)
571         Button("Exit",EVENT_EXIT , 170, 10, 80, 18)
572
573 def event(evt, val):    
574         if (evt == QKEY and not val):
575                 Blender.Draw.Exit()
576
577 def bevent(evt):
578         global g_md2_filename
579         global g_texture_filename
580         global EVENT_NOEVENT,EVENT_LOAD_MD2,EVENT_SAVE_MD2,EVENT_EXIT
581
582         ######### Manages GUI events
583         if (evt==EVENT_EXIT):
584                 Blender.Draw.Exit()
585         elif (evt==EVENT_CHOOSE_FILENAME):
586                 FileSelector(filename_callback, "MD2 File Selection")
587         elif (evt==EVENT_CHOOSE_TEXTURE):
588                 FileSelector(texture_callback, "Texture Selection")
589         elif (evt==EVENT_LOAD_MD2):
590                 if not Blender.sys.exists(g_md2_filename.val):
591                         PupMenu('Model file does not exist')
592                         return
593                 else:
594                         load_md2(g_md2_filename.val, g_texture_filename.val)
595                         Blender.Redraw()
596                         Blender.Draw.Exit()
597                         return
598
599 if __name__ == '__main__':
600         Register(draw_gui, event, bevent)