add to contrib/py/scripts/addons/io_export_md3.py
[blender-addons-contrib.git] / io_export_md3.py
1
2 __author__ = ["Xembie"]
3 __version__ = '0.7'
4 __url__ = ["www.blender.org"]
5
6 """
7 Name: 'Quake Model 3 (.md3)...'
8 Blender: 253
9 Group: 'Export'
10 Tooltip: 'Save a Quake Model 3 File'
11 """
12
13 import bpy,struct,math,os
14
15 MAX_QPATH = 64
16
17 MD3_IDENT = "IDP3"
18 MD3_VERSION = 15
19 MD3_MAX_TAGS = 16
20 MD3_MAX_SURFACES = 32
21 MD3_MAX_FRAMES = 1024
22 MD3_MAX_SHADERS = 256
23 MD3_MAX_VERTICES = 4096
24 MD3_MAX_TRIANGLES = 8192
25 MD3_XYZ_SCALE = 64.0
26
27 class md3Vert:
28         xyz = []
29         normal = 0
30         binaryFormat = "<3hH"
31         
32         def __init__(self):
33                 self.xyz = [0.0, 0.0, 0.0]
34                 self.normal = 0
35                 
36         def GetSize(self):
37                 return struct.calcsize(self.binaryFormat)
38         
39         # copied from PhaethonH <phaethon@linux.ucla.edu> md3.py
40         def Decode(self, latlng):
41                 lat = (latlng >> 8) & 0xFF;
42                 lng = (latlng) & 0xFF;
43                 lat *= math.pi / 128;
44                 lng *= math.pi / 128;
45                 x = math.cos(lat) * math.sin(lng)
46                 y = math.sin(lat) * math.sin(lng)
47                 z =                 math.cos(lng)
48                 retval = [ x, y, z ]
49                 return retval
50         
51         # copied from PhaethonH <phaethon@linux.ucla.edu> md3.py
52         def Encode(self, normal):
53                 x = normal[0]
54                 y = normal[1]
55                 z = normal[2]
56                 # normalize
57                 l = math.sqrt((x*x) + (y*y) + (z*z))
58                 if l == 0:
59                         return 0
60                 x = x/l
61                 y = y/l
62                 z = z/l
63                 
64                 if (x == 0.0) & (y == 0.0) :
65                         if z > 0.0:
66                                 return 0
67                         else:
68                                 return (128 << 8)
69                 
70                 lng = math.acos(z) * 255 / (2 * math.pi)
71                 lat = math.atan2(y, x) * 255 / (2 * math.pi)
72                 retval = ((int(lat) & 0xFF) << 8) | (int(lng) & 0xFF)
73                 return retval
74                 
75         def Save(self, file):
76                 tmpData = [0] * 4
77                 tmpData[0] = int(self.xyz[0] * MD3_XYZ_SCALE)
78                 tmpData[1] = int(self.xyz[1] * MD3_XYZ_SCALE)
79                 tmpData[2] = int(self.xyz[2] * MD3_XYZ_SCALE)
80                 tmpData[3] = self.normal
81                 data = struct.pack(self.binaryFormat, tmpData[0], tmpData[1], tmpData[2], tmpData[3])
82                 file.write(data)
83                 
84 class md3TexCoord:
85         u = 0.0
86         v = 0.0
87
88         binaryFormat = "<2f"
89
90         def __init__(self):
91                 self.u = 0.0
92                 self.v = 0.0
93                 
94         def GetSize(self):
95                 return struct.calcsize(self.binaryFormat)
96
97         def Save(self, file):
98                 tmpData = [0] * 2
99                 tmpData[0] = self.u
100                 tmpData[1] = 1.0 - self.v
101                 data = struct.pack(self.binaryFormat, tmpData[0], tmpData[1])
102                 file.write(data)
103
104 class md3Triangle:
105         indexes = []
106
107         binaryFormat = "<3i"
108
109         def __init__(self):
110                 self.indexes = [ 0, 0, 0 ]
111                 
112         def GetSize(self):
113                 return struct.calcsize(self.binaryFormat)
114
115         def Save(self, file):
116                 tmpData = [0] * 3
117                 tmpData[0] = self.indexes[0]
118                 tmpData[1] = self.indexes[2] # reverse
119                 tmpData[2] = self.indexes[1] # reverse
120                 data = struct.pack(self.binaryFormat,tmpData[0], tmpData[1], tmpData[2])
121                 file.write(data)
122
123 class md3Shader:
124         name = ""
125         index = 0
126         
127         binaryFormat = "<%dsi" % MAX_QPATH
128
129         def __init__(self):
130                 self.name = ""
131                 self.index = 0
132                 
133         def GetSize(self):
134                 return struct.calcsize(self.binaryFormat)
135
136         def Save(self, file):
137                 tmpData = [0] * 2
138                 tmpData[0] = self.name
139                 tmpData[1] = self.index
140                 data = struct.pack(self.binaryFormat, tmpData[0], tmpData[1])
141                 file.write(data)
142
143 class md3Surface:
144         ident = ""
145         name = ""
146         flags = 0
147         numFrames = 0
148         numShaders = 0
149         numVerts = 0
150         numTriangles = 0
151         ofsTriangles = 0
152         ofsShaders = 0
153         ofsUV = 0
154         ofsVerts = 0
155         ofsEnd = 0
156         shaders = []
157         triangles = []
158         uv = []
159         verts = []
160         
161         binaryFormat = "<4s%ds10i" % MAX_QPATH  # 1 int, name, then 10 ints
162         
163         def __init__(self):
164                 self.ident = ""
165                 self.name = ""
166                 self.flags = 0
167                 self.numFrames = 0
168                 self.numShaders = 0
169                 self.numVerts = 0
170                 self.numTriangles = 0
171                 self.ofsTriangles = 0
172                 self.ofsShaders = 0
173                 self.ofsUV = 0
174                 self.ofsVerts = 0
175                 self.ofsEnd
176                 self.shaders = []
177                 self.triangles = []
178                 self.uv = []
179                 self.verts = []
180                 
181         def GetSize(self):
182                 sz = struct.calcsize(self.binaryFormat)
183                 self.ofsTriangles = sz
184                 for t in self.triangles:
185                         sz += t.GetSize()
186                 self.ofsShaders = sz
187                 for s in self.shaders:
188                         sz += s.GetSize()
189                 self.ofsUV = sz
190                 for u in self.uv:
191                         sz += u.GetSize()
192                 self.ofsVerts = sz
193                 for v in self.verts:
194                         sz += v.GetSize()
195                 self.ofsEnd = sz
196                 return self.ofsEnd
197         
198         def Save(self, file):
199                 self.GetSize()
200                 tmpData = [0] * 12
201                 tmpData[0] = self.ident
202                 tmpData[1] = self.name
203                 tmpData[2] = self.flags
204                 tmpData[3] = self.numFrames
205                 tmpData[4] = self.numShaders
206                 tmpData[5] = self.numVerts
207                 tmpData[6] = self.numTriangles
208                 tmpData[7] = self.ofsTriangles
209                 tmpData[8] = self.ofsShaders
210                 tmpData[9] = self.ofsUV
211                 tmpData[10] = self.ofsVerts
212                 tmpData[11] = self.ofsEnd
213                 data = struct.pack(self.binaryFormat, tmpData[0],tmpData[1],tmpData[2],tmpData[3],tmpData[4],tmpData[5],tmpData[6],tmpData[7],tmpData[8],tmpData[9],tmpData[10],tmpData[11])
214                 file.write(data)
215
216                 # write the tri data
217                 for t in self.triangles:
218                         t.Save(file)
219
220                 # save the shader coordinates
221                 for s in self.shaders:
222                         s.Save(file)
223
224                 # save the uv info
225                 for u in self.uv:
226                         u.Save(file)
227
228                 # save the verts
229                 for v in self.verts:
230                         v.Save(file)
231
232 class md3Tag:
233         name = ""
234         origin = []
235         axis = []
236         
237         binaryFormat="<%ds3f9f" % MAX_QPATH
238         
239         def __init__(self):
240                 self.name = ""
241                 self.origin = [0, 0, 0]
242                 self.axis = [0, 0, 0, 0, 0, 0, 0, 0, 0]
243                 
244         def GetSize(self):
245                 return struct.calcsize(self.binaryFormat)
246                 
247         def Save(self, file):
248                 tmpData = [0] * 13
249                 tmpData[0] = self.name
250                 tmpData[1] = float(self.origin[0])
251                 tmpData[2] = float(self.origin[1])
252                 tmpData[3] = float(self.origin[2])
253                 tmpData[4] = float(self.axis[0])
254                 tmpData[5] = float(self.axis[1])
255                 tmpData[6] = float(self.axis[2])
256                 tmpData[7] = float(self.axis[3])
257                 tmpData[8] = float(self.axis[4])
258                 tmpData[9] = float(self.axis[5])
259                 tmpData[10] = float(self.axis[6])
260                 tmpData[11] = float(self.axis[7])
261                 tmpData[12] = float(self.axis[8])
262                 data = struct.pack(self.binaryFormat, tmpData[0],tmpData[1],tmpData[2],tmpData[3],tmpData[4],tmpData[5],tmpData[6], tmpData[7], tmpData[8], tmpData[9], tmpData[10], tmpData[11], tmpData[12])
263                 file.write(data)
264         
265 class md3Frame:
266         mins = 0
267         maxs = 0
268         localOrigin = 0
269         radius = 0.0
270         name = ""
271         
272         binaryFormat="<3f3f3ff16s"
273         
274         def __init__(self):
275                 self.mins = [0, 0, 0]
276                 self.maxs = [0, 0, 0]
277                 self.localOrigin = [0, 0, 0]
278                 self.radius = 0.0
279                 self.name = ""
280                 
281         def GetSize(self):
282                 return struct.calcsize(self.binaryFormat)
283
284         def Save(self, file):
285                 tmpData = [0] * 11
286                 tmpData[0] = self.mins[0]
287                 tmpData[1] = self.mins[1]
288                 tmpData[2] = self.mins[2]
289                 tmpData[3] = self.maxs[0]
290                 tmpData[4] = self.maxs[1]
291                 tmpData[5] = self.maxs[2]
292                 tmpData[6] = self.localOrigin[0]
293                 tmpData[7] = self.localOrigin[1]
294                 tmpData[8] = self.localOrigin[2]
295                 tmpData[9] = self.radius
296                 tmpData[10] = self.name
297                 data = struct.pack(self.binaryFormat, tmpData[0],tmpData[1],tmpData[2],tmpData[3],tmpData[4],tmpData[5],tmpData[6],tmpData[7], tmpData[8], tmpData[9], tmpData[10])
298                 file.write(data)
299
300 class md3Object:
301         # header structure
302         ident = ""                      # this is used to identify the file (must be IDP3)
303         version = 0                     # the version number of the file (Must be 15)
304         name = ""
305         flags = 0
306         numFrames = 0
307         numTags = 0
308         numSurfaces = 0
309         numSkins = 0
310         ofsFrames = 0
311         ofsTags = 0
312         ofsSurfaces = 0
313         ofsEnd = 0
314         frames = []
315         tags = []
316         surfaces = []
317
318         binaryFormat="<4si%ds9i" % MAX_QPATH  # little-endian (<), 17 integers (17i)
319
320         def __init__(self):
321                 self.ident = 0
322                 self.version = 0
323                 self.name = ""
324                 self.flags = 0
325                 self.numFrames = 0
326                 self.numTags = 0
327                 self.numSurfaces = 0
328                 self.numSkins = 0
329                 self.ofsFrames = 0
330                 self.ofsTags = 0
331                 self.ofsSurfaces = 0
332                 self.ofsEnd = 0
333                 self.frames = []
334                 self.tags = []
335                 self.surfaces = []
336
337         def GetSize(self):
338                 self.ofsFrames = struct.calcsize(self.binaryFormat)
339                 self.ofsTags = self.ofsFrames
340                 for f in self.frames:
341                         self.ofsTags += f.GetSize()
342                 self.ofsSurfaces += self.ofsTags
343                 for t in self.tags:
344                         self.ofsSurfaces += t.GetSize()
345                 self.ofsEnd = self.ofsSurfaces
346                 for s in self.surfaces:
347                         self.ofsEnd += s.GetSize()
348                 return self.ofsEnd
349                 
350         def Save(self, file):
351                 self.GetSize()
352                 tmpData = [0] * 12
353                 tmpData[0] = self.ident
354                 tmpData[1] = self.version
355                 tmpData[2] = self.name
356                 tmpData[3] = self.flags
357                 tmpData[4] = self.numFrames
358                 tmpData[5] = self.numTags
359                 tmpData[6] = self.numSurfaces
360                 tmpData[7] = self.numSkins
361                 tmpData[8] = self.ofsFrames
362                 tmpData[9] = self.ofsTags
363                 tmpData[10] = self.ofsSurfaces
364                 tmpData[11] = self.ofsEnd
365
366                 data = struct.pack(self.binaryFormat, tmpData[0],tmpData[1],tmpData[2],tmpData[3],tmpData[4],tmpData[5],tmpData[6],tmpData[7], tmpData[8], tmpData[9], tmpData[10], tmpData[11])
367                 file.write(data)
368
369                 for f in self.frames:
370                         f.Save(file)
371                         
372                 for t in self.tags:
373                         t.Save(file)
374                         
375                 for s in self.surfaces:
376                         s.Save(file)
377
378
379 def message(log,msg):
380   if log:
381     log.write(msg + "\n")
382   else:
383     print(msg)
384
385 class md3Settings:
386   def __init__(self,
387                savepath,
388                name,
389                logpath,
390                overwrite=True,
391                dumpall=False,
392                ignoreuvs=False,
393                scale=1.0,
394                offsetx=0.0,
395                offsety=0.0,
396                offsetz=0.0):
397     self.savepath = savepath
398     self.name = name
399     self.logpath = logpath
400     self.overwrite = overwrite
401     self.dumpall = dumpall
402     self.ignoreuvs = ignoreuvs
403     self.scale = scale
404     self.offsetx = offsetx
405     self.offsety = offsety
406     self.offsetz = offsetz
407
408 def print_md3(log,md3,dumpall):
409   message(log,"Header Information")
410   message(log,"Ident: " + str(md3.ident))
411   message(log,"Version: " + str(md3.version))
412   message(log,"Name: " + md3.name)
413   message(log,"Flags: " + str(md3.flags))
414   message(log,"Number of Frames: " + str(md3.numFrames))
415   message(log,"Number of Tags: " + str(md3.numTags))
416   message(log,"Number of Surfaces: " + str(md3.numSurfaces))
417   message(log,"Number of Skins: " + str(md3.numSkins))
418   message(log,"Offset Frames: " + str(md3.ofsFrames))
419   message(log,"Offset Tags: " + str(md3.ofsTags))
420   message(log,"Offset Surfaces: " + str(md3.ofsSurfaces))
421   message(log,"Offset end: " + str(md3.ofsEnd))
422   if dumpall:
423     message(log,"Frames:")
424     for f in md3.frames:
425       message(log," Mins: " + str(f.mins[0]) + " " + str(f.mins[1]) + " " + str(f.mins[2]))
426       message(log," Maxs: " + str(f.maxs[0]) + " " + str(f.maxs[1]) + " " + str(f.maxs[2]))
427       message(log," Origin(local): " + str(f.localOrigin[0]) + " " + str(f.localOrigin[1]) + " " + str(f.localOrigin[2]))
428       message(log," Radius: " + str(f.radius))
429       message(log," Name: " + f.name)
430
431     message(log,"Tags:")
432     for t in md3.tags:
433       message(log," Name: " + t.name)
434       message(log," Origin: " + str(t.origin[0]) + " " + str(t.origin[1]) + " " + str(t.origin[2]))
435       message(log," Axis[0]: " + str(t.axis[0]) + " " + str(t.axis[1]) + " " + str(t.axis[2]))
436       message(log," Axis[1]: " + str(t.axis[3]) + " " + str(t.axis[4]) + " " + str(t.axis[5]))
437       message(log," Axis[2]: " + str(t.axis[6]) + " " + str(t.axis[7]) + " " + str(t.axis[8]))
438
439     message(log,"Surfaces:")
440     for s in md3.surfaces:
441       message(log," Ident: " + s.ident)
442       message(log," Name: " + s.name)
443       message(log," Flags: " + str(s.flags))
444       message(log," # of Frames: " + str(s.numFrames))
445       message(log," # of Shaders: " + str(s.numShaders))
446       message(log," # of Verts: " + str(s.numVerts))
447       message(log," # of Triangles: " + str(s.numTriangles))
448       message(log," Offset Triangles: " + str(s.ofsTriangles))
449       message(log," Offset UVs: " + str(s.ofsUV))
450       message(log," Offset Verts: " + str(s.ofsVerts))
451       message(log," Offset End: " + str(s.ofsEnd))
452       message(log," Shaders:")
453       for shader in s.shaders:
454         message(log,"  Name: " + shader.name)
455         message(log,"  Index: " + str(shader.index))
456       message(log," Triangles:")
457       for tri in s.triangles:
458         message(log,"  Indexes: " + str(tri.indexes[0]) + " " + str(tri.indexes[1]) + " " + str(tri.indexes[2]))
459       message(log," UVs:")
460       for uv in s.uv:
461         message(log,"  U: " + str(uv.u))
462         message(log,"  V: " + str(uv.v)) 
463       message(log," Verts:")
464       for vert in s.verts:
465         message(log,"  XYZ: " + str(vert.xyz[0]) + " " + str(vert.xyz[1]) + " " + str(vert.xyz[2]))
466         message(log,"  Normal: " + str(vert.normal))
467
468   shader_count = 0
469   vert_count = 0
470   tri_count = 0
471   for surface in md3.surfaces:
472     shader_count += surface.numShaders
473     tri_count += surface.numTriangles
474     vert_count += surface.numVerts
475
476   if md3.numTags >= MD3_MAX_TAGS:
477     message(log,"!Warning: Tag limit reached! " + str(md3.numTags))
478   if md3.numSurfaces >= MD3_MAX_SURFACES:
479     message(log,"!Warning: Surface limit reached! " + str(md3.numSurfaces))
480   if md3.numFrames >= MD3_MAX_FRAMES:
481     message(log,"!Warning: Frame limit reached! " + str(md3.numFrames))
482   if shader_count >= MD3_MAX_SHADERS:
483     message(log,"!Warning: Shader limit reached! " + str(shader_count))
484   if vert_count >= MD3_MAX_VERTICES:
485     message(log,"!Warning: Vertex limit reached! " + str(vert_count))
486   if tri_count >= MD3_MAX_TRIANGLES:
487     message(log,"!Warning: Triangle limit reached! " + str(tri_count))
488
489 def save_md3(settings):
490   if settings.logpath:
491     if settings.overwrite:
492       log = open(settings.logpath,"w")
493     else:
494       log = open(settings.logpath,"a")
495   else:
496     log = 0
497   message(log,"##########Exporting MD3##########")
498   bpy.ops.object.mode_set(mode='OBJECT')
499   md3 = md3Object()
500   md3.ident = MD3_IDENT
501   md3.version = MD3_VERSION
502   md3.name = settings.name
503   md3.numFrames = (bpy.context.scene.frame_end + 1) - bpy.context.scene.frame_start
504
505   for obj in bpy.context.selected_objects:
506     if obj.type == 'MESH':
507       nsurface = md3Surface()
508       nsurface.name = obj.name
509       nsurface.ident = MD3_IDENT
510  
511       vertlist = []
512
513       for f,face in enumerate(obj.data.faces):
514         ntri = md3Triangle()
515         if len(face.verts) != 3:
516           message(log,"Found a nontriangle face in object " + obj.name)
517           continue
518
519         for v,vert_index in enumerate(face.verts):
520           uv_u = round(obj.data.active_uv_texture.data[f].uv[v][0],5)
521           uv_v = round(obj.data.active_uv_texture.data[f].uv[v][1],5)
522
523           match = 0
524           match_index = 0
525           for i,vi in enumerate(vertlist):
526             if vi == vert_index:
527               if settings.ignoreuvs:
528                 match = 1#there is a uv match for all
529                 match_index = i
530               else:
531                 if nsurface.uv[i].u == uv_u and nsurface.uv[i].v == uv_v:
532                   match = 1
533                   match_index = i
534
535           if match == 0:
536             vertlist.append(vert_index)
537             ntri.indexes[v] = nsurface.numVerts
538             ntex = md3TexCoord()
539             ntex.u = uv_u
540             ntex.v = uv_v
541             nsurface.uv.append(ntex)
542             nsurface.numVerts += 1
543           else:
544             ntri.indexes[v] = match_index
545         nsurface.triangles.append(ntri)
546         nsurface.numTriangles += 1
547
548       if obj.data.active_uv_texture:
549         nshader = md3Shader()
550         nshader.name = obj.data.active_uv_texture.name
551         nshader.index = nsurface.numShaders
552         nsurface.shaders.append(nshader)
553         nsurface.numShaders += 1
554       if nsurface.numShaders < 1: #we should add a blank as a placeholder
555         nshader = md3Shader()
556         nshader.name = "NULL"
557         nsurface.shaders.append(nshader)
558         nsurface.numShaders += 1
559
560       for frame in range(bpy.context.scene.frame_start,bpy.context.scene.frame_end + 1):
561         bpy.context.scene.set_frame(frame)
562         fobj = obj.create_mesh(bpy.context.scene,True,'PREVIEW')
563         fobj.calc_normals()
564         nframe = md3Frame()
565         nframe.name = str(frame)
566         for vi in vertlist:
567             vert = fobj.verts[vi]
568             nvert = md3Vert()
569             nvert.xyz = vert.co * obj.matrix_world
570             nvert.xyz[0] = (round(nvert.xyz[0] + obj.matrix_world[3][0],5) * settings.scale) + settings.offsetx
571             nvert.xyz[1] = (round(nvert.xyz[1] + obj.matrix_world[3][1],5) * settings.scale) + settings.offsety
572             nvert.xyz[2] = (round(nvert.xyz[2] + obj.matrix_world[3][2],5) * settings.scale) + settings.offsetz
573             nvert.normal = nvert.Encode(vert.normal)
574             for i in range(0,3):
575               nframe.mins[i] = min(nframe.mins[i],nvert.xyz[i])
576               nframe.maxs[i] = max(nframe.maxs[i],nvert.xyz[i])
577             minlength = math.sqrt(math.pow(nframe.mins[0],2) + math.pow(nframe.mins[1],2) + math.pow(nframe.mins[2],2))
578             maxlength = math.sqrt(math.pow(nframe.maxs[0],2) + math.pow(nframe.maxs[1],2) + math.pow(nframe.maxs[2],2))
579             nframe.radius = round(max(minlength,maxlength),5)
580             nsurface.verts.append(nvert) 
581         md3.frames.append(nframe)
582         nsurface.numFrames += 1
583         bpy.data.meshes.remove(fobj)
584       md3.surfaces.append(nsurface)
585       md3.numSurfaces += 1
586
587     elif obj.type == 'EMPTY':
588       md3.numTags += 1
589       for frame in range(bpy.context.scene.frame_start,bpy.context.scene.frame_end + 1):
590         bpy.context.scene.set_frame(frame)
591         ntag = md3Tag()
592         ntag.origin[0] = (round(obj.matrix_world[3][0] * settings.scale,5)) + settings.offsetx
593         ntag.origin[1] = (round(obj.matrix_world[3][1] * settings.scale,5)) + settings.offsety
594         ntag.origin[2] = (round(obj.matrix_world[3][2] * settings.scale,5)) + settings.offsetz
595         ntag.axis[0] = obj.matrix_world[0][0]
596         ntag.axis[1] = obj.matrix_world[0][1]
597         ntag.axis[2] = obj.matrix_world[0][2]
598         ntag.axis[3] = obj.matrix_world[1][0]
599         ntag.axis[4] = obj.matrix_world[1][1]
600         ntag.axis[5] = obj.matrix_world[1][2]
601         ntag.axis[6] = obj.matrix_world[2][0]
602         ntag.axis[7] = obj.matrix_world[2][1]
603         ntag.axis[8] = obj.matrix_world[2][2]
604         md3.tags.append(ntag)
605   
606   if md3.numSurfaces < 1:
607     message(log,"Select a mesh to export!")
608     if log:
609       log.close()
610     return
611
612   file = open(settings.savepath, "wb")
613   md3.Save(file)
614   print_md3(log,md3,settings.dumpall)
615   file.close()
616
617   message(log,"MD3: " + settings.name + " saved to " + settings.savepath)
618   if log:
619     print("Logged to",settings.logpath)
620     log.close()
621
622 from bpy.props import *
623 class ExportMD3(bpy.types.Operator):
624   '''Export to Quake Model 3 (.md3)'''
625   bl_idname = "export.md3"
626   bl_label = 'Export MD3'
627
628   filepath = StringProperty(subtype = 'FILE_PATH',name="File Path", description="Filepath for exporting", maxlen= 1024, default= "")
629   md3name = StringProperty(name="MD3 Name", description="MD3 header name / skin path (64 bytes)",maxlen=64,default="")
630   md3log = StringProperty(name="MD3 Log", description="MD3 log file path",maxlen=1024,default="export_md3.log")
631   md3overwritelog = BoolProperty(name="Overwrite log", description="Overwrite log (off == append)", default=True)
632   md3dumpall = BoolProperty(name="Dump all", description="Dump all data for md3 to log",default=False)
633   md3ignoreuvs = BoolProperty(name="Ignore UVs", description="Ignores uv influence on mesh generation. Use if uv map not made.",default=False)
634   md3scale = FloatProperty(name="Scale", description="Scale all objects from world origin (0,0,0)",default=1.0,precision=5)
635   md3offsetx = FloatProperty(name="Offset X", description="Transition scene along x axis",default=0.0,precision=5)
636   md3offsety = FloatProperty(name="Offset Y", description="Transition scene along y axis",default=0.0,precision=5)
637   md3offsetz = FloatProperty(name="Offset Z", description="Transition scene along z axis",default=0.0,precision=5)
638
639   def execute(self, context):
640    settings = md3Settings(savepath = self.properties.filepath,
641                           name = self.properties.md3name,
642                           logpath = self.properties.md3log,
643                           overwrite = self.properties.md3overwritelog,
644                           dumpall = self.properties.md3dumpall,
645                           ignoreuvs = self.properties.md3ignoreuvs,
646                           scale = self.properties.md3scale,
647                           offsetx = self.properties.md3offsetx,
648                           offsety = self.properties.md3offsety,
649                           offsetz = self.properties.md3offsetz)
650    save_md3(settings)
651    return {'FINISHED'}
652
653   def invoke(self, context, event):
654     wm = context.manager
655     wm.add_fileselect(self)
656     return {'RUNNING_MODAL'}
657
658   def poll(self, context):
659     return context.active_object != None
660
661 def menu_func(self, context):
662   newpath = os.path.splitext(bpy.context.main.filepath)[0] + ".md3"
663   self.layout.operator(ExportMD3.bl_idname, text="Quake Model 3 (.md3)").filepath = newpath 
664
665 def register():
666   bpy.types.register(ExportMD3)
667   bpy.types.INFO_MT_file_export.append(menu_func)
668
669 def unregister():
670   bpy.types.unregister(ExportMD3)
671   bpy.types.INFO_MT_file_export.remove(menu_func)
672
673 if __name__ == "__main__":
674   register()