1 """Common functions for the Blender nif import and export scripts."""
2
3 __version__ = "2.5.0"
4 __requiredpyffiversion__ = "2.0.4"
5 __requiredblenderversion__ = "245"
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
42 """Compare version strings."""
43 def version_intlist(version):
44 """Convert version string to list of integers."""
45 return [int(x) for x in version.__str__().split(".")]
46 return cmp(version_intlist(version1), version_intlist(version2))
47
48
49
50
51
52 import Blender
53 __blenderversion__ = Blender.Get('version')
54
55 if cmp_versions(__blenderversion__, __requiredblenderversion__) == -1:
56 print("""--------------------------
57 ERROR\nThis script requires Blender %s or higher.
58 It seems that you have an older version installed (%s).
59 Get a newer version at http://www.blender.org/
60 --------------------------"""%(__requiredblenderversion__, __blenderversion__))
61 Blender.Draw.PupMenu("ERROR%t|Blender outdated, check console for details")
62 raise ImportError
63
64
65
66 try:
67 from pyffi import __version__ as __pyffiversion__
68 except ImportError:
69 print("""--------------------------
70 ERROR\nThis script requires the Python File Format Interface (PyFFI).
71 Make sure that PyFFI resides in your Python path or in your Blender scripts folder.
72 If you do not have it: http://pyffi.sourceforge.net/
73 --------------------------""")
74 Blender.Draw.PupMenu("ERROR%t|PyFFI not found, check console for details")
75 raise
76
77
78
79 if cmp_versions(__pyffiversion__, __requiredpyffiversion__) == -1:
80 print("""--------------------------
81 ERROR\nThis script requires Python File Format Interface %s or higher.
82 It seems that you have an older version installed (%s).
83 Get a newer version at http://pyffi.sourceforge.net/
84 --------------------------"""%(__requiredpyffiversion__, __pyffiversion__))
85 Blender.Draw.PupMenu("ERROR%t|PyFFI outdated, check console for details")
86 raise ImportError
87
88
89
90 from pyffi.formats.nif import NifFormat
91 from pyffi.formats.egm import EgmFormat
92
93
94
95 from Blender import Draw, Registry
96 import logging
97 import sys
98 import os
99
101 """Set up loggers."""
102 niftoolslogger = logging.getLogger("niftools")
103 niftoolslogger.setLevel(logging.WARNING)
104 pyffilogger = logging.getLogger("pyffi")
105 pyffilogger.setLevel(logging.WARNING)
106 loghandler = logging.StreamHandler()
107 loghandler.setLevel(logging.DEBUG)
108 logformatter = logging.Formatter("%(name)s:%(levelname)s:%(message)s")
109 loghandler.setFormatter(logformatter)
110 niftoolslogger.addHandler(loghandler)
111 pyffilogger.addHandler(loghandler)
112
113
114 init_loggers()
115
117 """Abstract base class for import and export. Contains utility functions
118 that are commonly used in both import and export."""
119
120 EXTRA_SHADER_TEXTURES = [
121 "EnvironmentMapIndex",
122 "NormalMapIndex",
123 "SpecularIntensityIndex",
124 "EnvironmentIntensityIndex",
125 "LightCubeMapIndex",
126 "ShadowTextureIndex"]
127 """Names (ordered by default index) of shader texture slots for
128 Sid Meier's Railroads and similar games.
129 """
130
131 USED_EXTRA_SHADER_TEXTURES = {
132 "Sid Meier's Railroads": (3, 0, 4, 1, 5, 2),
133 "Civilization IV": (3, 0, 1, 2)}
134 """The default ordering of the extra data blocks for different games."""
135
136 progress_bar = 0
137 """Level of the progress bar."""
138
140 """Message wrapper for the Blender progress bar."""
141
142 if progbar is None:
143 if self.progress_bar > 0.89:
144
145 self.progress_bar = 0
146 Blender.Window.DrawProgressBar(0, message)
147 self.progress_bar += 0.1
148 else:
149 self.progress_bar = progbar
150
151 Blender.Window.DrawProgressBar(self.progress_bar, message)
152
154 """Convert a bone name to a name that can be used by Blender: turns
155 'Bip01 R xxx' into 'Bip01 xxx.R', and similar for L.
156
157 @param name: The bone name as in the nif file.
158 @type name: C{str}
159 @return: Bone name in Blender convention.
160 @rtype: C{str}
161 """
162 if name.startswith("Bip01 L "):
163 return "Bip01 " + name[8:] + ".L"
164 elif name.startswith("Bip01 R "):
165 return "Bip01 " + name[8:] + ".R"
166 return name
167
169 """Convert a bone name to a name that can be used by the nif file:
170 turns 'Bip01 xxx.R' into 'Bip01 R xxx', and similar for L.
171
172 @param name: The bone name as in Blender.
173 @type name: C{str}
174 @return: Bone name in nif convention.
175 @rtype: C{str}
176 """
177 if name.startswith("Bip01 "):
178 if name.endswith(".L"):
179 return "Bip01 L " + name[6:-2]
180 elif name.endswith(".R"):
181 return "Bip01 R " + name[6:-2]
182 return name
183
185 if flags & 6 == 4:
186 return Blender.IpoCurve.ExtendTypes.CONST
187 elif flags & 6 == 0:
188 return Blender.IpoCurve.ExtendTypes.CYCLIC
189
190 self.logger.warning(
191 "Unsupported cycle mode in nif, using clamped.")
192 return Blender.IpoCurve.ExtendTypes.CONST
193
195 if extend == Blender.IpoCurve.ExtendTypes.CONST:
196 return 4
197 elif extend == Blender.IpoCurve.ExtendTypes.CYCLIC:
198 return 0
199
200 self.logger.warning(
201 "Unsupported extend type in blend, using clamped.")
202 return 4
203
205 """Class which handles configuration of nif import and export in Blender.
206
207 Important: keep every instance of this class in a global variable
208 (otherwise gui elements might go out of skope which will crash
209 Blender)."""
210
211 WELCOME_MESSAGE = 'Blender NIF Scripts %s (running on Blender %s, PyFFI %s)'%(__version__, __blenderversion__, __pyffiversion__)
212 CONFIG_NAME = "nifscripts"
213 TARGET_IMPORT = 0
214 TARGET_EXPORT = 1
215
216 XORIGIN = 50
217 XCOLUMNSKIP = 390
218 XCOLUMNSEP = 10
219 YORIGIN = -40
220 YLINESKIP = 20
221 YLINESEP = 10
222
223
224
225
226 DEFAULTS = dict(
227 IMPORT_FILE = Blender.sys.join(
228 Blender.sys.dirname(Blender.sys.progname), "import.nif"),
229 EXPORT_FILE = Blender.sys.join(
230 Blender.sys.dirname(Blender.sys.progname), "export.nif"),
231 IMPORT_REALIGN_BONES = 1,
232 IMPORT_ANIMATION = True,
233 IMPORT_SCALE_CORRECTION = 0.1,
234 EXPORT_SCALE_CORRECTION = 10.0,
235 IMPORT_TEXTURE_PATH = [],
236 EXPORT_FLATTENSKIN = False,
237 EXPORT_VERSION = 'Oblivion',
238 EPSILON = 0.005,
239 LOG_LEVEL = logging.WARNING,
240 IMPORT_SKELETON = 0,
241 IMPORT_KEYFRAMEFILE = '',
242 IMPORT_EGMFILE = '',
243 IMPORT_EGMANIM = True,
244 IMPORT_EGMANIMSCALE = 1.0,
245 EXPORT_ANIMATION = 0,
246 EXPORT_ANIMSEQUENCENAME = '',
247 EXPORT_FORCEDDS = True,
248 EXPORT_SKINPARTITION = True,
249 EXPORT_BONESPERVERTEX = 4,
250 EXPORT_BONESPERPARTITION = 18,
251 EXPORT_PADBONES = False,
252 EXPORT_STRIPIFY = True,
253 EXPORT_STITCHSTRIPS = False,
254 EXPORT_SMOOTHOBJECTSEAMS = True,
255 IMPORT_MERGESKELETONROOTS = True,
256 IMPORT_SENDGEOMETRIESTOBINDPOS = True,
257 IMPORT_SENDDETACHEDGEOMETRIESTONODEPOS = True,
258 IMPORT_SENDBONESTOBINDPOS = True,
259 IMPORT_APPLYSKINDEFORM = False,
260 IMPORT_EXTRANODES = True,
261 EXPORT_BHKLISTSHAPE = False,
262 EXPORT_OB_BSXFLAGS = 2,
263 EXPORT_OB_MASS = 10.0,
264 EXPORT_OB_SOLID = True,
265 EXPORT_OB_MOTIONSYSTEM = 7,
266 EXPORT_OB_UNKNOWNBYTE1 = 1,
267 EXPORT_OB_UNKNOWNBYTE2 = 1,
268 EXPORT_OB_QUALITYTYPE = 1,
269 EXPORT_OB_WIND = 0,
270 EXPORT_OB_LAYER = 1,
271 EXPORT_OB_MATERIAL = 9,
272 EXPORT_OB_MALLEABLECONSTRAINT = False,
273 EXPORT_OB_PRN = "NONE",
274 EXPORT_FO3_SF_ZBUF = True,
275 EXPORT_FO3_SF_SMAP = False,
276 EXPORT_FO3_SF_SFRU = False,
277 EXPORT_FO3_SF_WINDOW_ENVMAP = False,
278 EXPORT_FO3_SF_EMPT = True,
279 EXPORT_FO3_SF_UN31 = True,
280 EXPORT_FO3_FADENODE = False,
281 EXPORT_FO3_SHADER_TYPE = 1,
282 EXPORT_FO3_BODYPARTS = True,
283 EXPORT_MW_NIFXNIFKF = False,
284 EXPORT_EXTRA_SHADER_TEXTURES = True,
285 PROFILE = '',
286 IMPORT_EXPORTEMBEDDEDTEXTURES = False,
287 EXPORT_OPTIMIZE_MATERIALS = True,
288 IMPORT_COMBINESHAPES = True)
289
291 """Initialize and load configuration."""
292
293 if sys.platform in ('linux-i386','linux2'):
294 os.system("clear")
295 elif sys.platform in ('win32','dos','ms-dos'):
296 os.system("cls")
297
298
299 print self.WELCOME_MESSAGE
300
301
302 self.guiElements = {}
303 self.guiEvents = []
304 self.guiEventIds = {}
305 self.config = {}
306 self.target = None
307 self.callback = None
308 self.texpathIndex = 0
309 self.texpathCurrent = ''
310
311
312 self.xPos = self.XORIGIN
313 self.yPos = self.YORIGIN + Blender.Window.GetAreaSize()[1]
314
315
316 self.load()
317
318 - def run(self, target, filename, callback):
319 """Run the config gui."""
320 self.target = target
321 self.callback = callback
322
323
324
325
326 if self.target == self.TARGET_IMPORT:
327 self.config["IMPORT_FILE"] = filename
328 elif self.target == self.TARGET_EXPORT:
329 self.config["EXPORT_FILE"] = filename
330 self.save()
331
332
333 self.texpathIndex = 0
334 self.updateTexpathCurrent()
335 Draw.Register(self.guiDraw, self.guiEvent, self.guiButtonEvent)
336
338 """Save and validate configuration to Blender registry."""
339 Registry.SetKey(self.CONFIG_NAME, self.config, True)
340 self.load()
341
343 """Load the configuration stored in the Blender registry and checks
344 configuration for incompatible values.
345 """
346
347 self.config = dict(**self.DEFAULTS)
348
349 savedconfig = Blender.Registry.GetKey(self.CONFIG_NAME, True)
350
351 try:
352 self.config["IMPORT_TEXTURE_PATH"] = savedconfig["TEXTURE_SEARCH_PATH"]
353 except:
354 pass
355 try:
356 self.config["IMPORT_FILE"] = Blender.sys.join(
357 savedconfig["NIF_IMPORT_PATH"], savedconfig["NIF_IMPORT_FILE"])
358 except:
359 pass
360 try:
361 self.config["EXPORT_FILE"] = savedconfig["NIF_EXPORT_FILE"]
362 except:
363 pass
364 try:
365 self.config["IMPORT_REALIGN_BONES"] = savedconfig["REALIGN_BONES"]
366 except:
367 pass
368 try:
369 if self.config["IMPORT_REALIGN_BONES"] == True:
370 self.config["IMPORT_REALIGN_BONES"] = 1
371 elif self.config["IMPORT_REALIGN_BONES"] == False:
372 self.config["IMPORT_REALIGN_BONES"] = 0
373 except:
374 pass
375 try:
376 if savedconfig["IMPORT_SKELETON"] == True:
377 self.config["IMPORT_SKELETON"] = 1
378 elif savedconfig["IMPORT_SKELETON"] == False:
379 self.config["IMPORT_SKELETON"] = 0
380 except:
381 pass
382
383 if savedconfig:
384 for key, val in self.DEFAULTS.iteritems():
385 try:
386 savedval = savedconfig[key]
387 except KeyError:
388 pass
389 else:
390 if isinstance(savedval, val.__class__):
391 self.config[key] = savedval
392
393 Blender.Registry.SetKey(self.CONFIG_NAME, self.config, True)
394
395 self.updateLogLevel("LOG_LEVEL", self.config["LOG_LEVEL"])
396
398 """Return event id from event name, and register event if it is new."""
399 try:
400 event_id = self.guiEventIds[event_name]
401 except KeyError:
402 event_id = len(self.guiEvents)
403 self.guiEventIds[event_name] = event_id
404 self.guiEvents.append(event_name)
405 if event_id >= 16383:
406 raise RuntimeError("Maximum number of events exceeded")
407 return event_id
408
410 """Vertical skip."""
411 self.yPos -= self.YLINESEP
412
414 """Start a new column."""
415 self.xPos += self.XCOLUMNSKIP + self.XCOLUMNSEP
416 self.yPos = self.YORIGIN + Blender.Window.GetAreaSize()[1]
417
418 - def drawSlider(
419 self, text, event_name, min_val, max_val, callback, val = None,
420 num_items = 1, item = 0):
421 """Draw a slider."""
422 if val is None:
423 val = self.config[event_name]
424 width = self.XCOLUMNSKIP//num_items
425 self.guiElements[event_name] = Draw.Slider(
426 text,
427 self.eventId(event_name),
428 self.xPos + item*width, self.yPos, width, self.YLINESKIP,
429 val, min_val, max_val,
430 0,
431 "",
432 callback)
433 if item + 1 == num_items:
434 self.yPos -= self.YLINESKIP
435
436 - def drawLabel(self, text, event_name, num_items = 1, item = 0):
437 """Draw a line of text."""
438 width = self.XCOLUMNSKIP//num_items
439 self.guiElements[event_name] = Draw.Label(
440 text,
441 self.xPos + item*width, self.yPos, width, self.YLINESKIP)
442 if item + 1 == num_items:
443 self.yPos -= self.YLINESKIP
444
445 - def drawList(self, text, event_name_prefix, val):
446 """Create elements to select a list of things.
447
448 Registers events PREFIX_ITEM, PREFIX_PREV, PREFIX_NEXT, PREFIX_REMOVE
449 and PREFIX_ADD."""
450 self.guiElements["%s_ITEM"%event_name_prefix] = Draw.String(
451 text,
452 self.eventId("%s_ITEM"%event_name_prefix),
453 self.xPos, self.yPos, self.XCOLUMNSKIP-90, self.YLINESKIP,
454 val, 255)
455 self.guiElements["%s_PREV"%event_name_prefix] = Draw.PushButton(
456 '<',
457 self.eventId("%s_PREV"%event_name_prefix),
458 self.xPos+self.XCOLUMNSKIP-90, self.yPos, 20, self.YLINESKIP)
459 self.guiElements["%s_NEXT"%event_name_prefix] = Draw.PushButton(
460 '>',
461 self.eventId("%s_NEXT"%event_name_prefix),
462 self.xPos+self.XCOLUMNSKIP-70, self.yPos, 20, self.YLINESKIP)
463 self.guiElements["%s_REMOVE"%event_name_prefix] = Draw.PushButton(
464 'X',
465 self.eventId("%s_REMOVE"%event_name_prefix),
466 self.xPos+self.XCOLUMNSKIP-50, self.yPos, 20, self.YLINESKIP)
467 self.guiElements["%s_ADD"%event_name_prefix] = Draw.PushButton(
468 '...',
469 self.eventId("%s_ADD"%event_name_prefix),
470 self.xPos+self.XCOLUMNSKIP-30, self.yPos, 30, self.YLINESKIP)
471 self.yPos -= self.YLINESKIP
472
473 - def drawToggle(self, text, event_name, val = None, num_items = 1, item = 0):
474 """Draw a toggle button."""
475 if val == None:
476 val = self.config[event_name]
477 width = self.XCOLUMNSKIP//num_items
478 self.guiElements[event_name] = Draw.Toggle(
479 text,
480 self.eventId(event_name),
481 self.xPos + item*width, self.yPos, width, self.YLINESKIP,
482 val)
483 if item + 1 == num_items:
484 self.yPos -= self.YLINESKIP
485
495
496 - def drawNumber(
497 self, text, event_name, min_val, max_val, callback, val = None,
498 num_items = 1, item = 0):
499 """Draw an input widget for numbers."""
500 if val is None:
501 val = self.config[event_name]
502 width = self.XCOLUMNSKIP//num_items
503 self.guiElements[event_name] = Draw.Number(
504 text,
505 self.eventId(event_name),
506 self.xPos + item*width, self.yPos, width, self.YLINESKIP,
507 val,
508 min_val, max_val,
509 "",
510 callback)
511 if item + 1 == num_items:
512 self.yPos -= self.YLINESKIP
513
515 """Create elements to select a file.
516
517 Registers events PREFIX_ITEM, PREFIX_REMOVE, PREFIX_ADD."""
518 if val is None:
519 val = self.config[event_name_prefix]
520 self.guiElements["%s_ITEM"%event_name_prefix] = Draw.String(
521 text,
522 self.eventId("%s_ITEM"%event_name_prefix),
523 self.xPos, self.yPos, self.XCOLUMNSKIP-50, self.YLINESKIP,
524 val, 255)
525 self.guiElements["%s_REMOVE"%event_name_prefix] = Draw.PushButton(
526 'X',
527 self.eventId("%s_REMOVE"%event_name_prefix),
528 self.xPos+self.XCOLUMNSKIP-50, self.yPos, 20, self.YLINESKIP)
529 self.guiElements["%s_ADD"%event_name_prefix] = Draw.PushButton(
530 '...',
531 self.eventId("%s_ADD"%event_name_prefix),
532 self.xPos+self.XCOLUMNSKIP-30, self.yPos, 30, self.YLINESKIP)
533 self.yPos -= self.YLINESKIP
534
535 - def drawString(self, text, event_name, max_length, callback, val = None,
536 num_items = 1, item = 0):
537 """Create elements to input a string."""
538 if val is None:
539 val = self.config[event_name]
540 width = self.XCOLUMNSKIP//num_items
541 self.guiElements[event_name] = Draw.String(
542 text,
543 self.eventId(event_name),
544 self.xPos + item*width, self.yPos, width, self.YLINESKIP,
545 val,
546 max_length,
547 "",
548 callback)
549 if item + 1 == num_items:
550 self.yPos -= self.YLINESKIP
551
553 """Draw config GUI."""
554
555 self.xPos = self.XORIGIN
556 self.yPos = self.YORIGIN + Blender.Window.GetAreaSize()[1]
557
558
559 self.drawLabel(
560 text = self.WELCOME_MESSAGE,
561 event_name = "LABEL_WELCOME_MESSAGE")
562 self.drawYSep()
563
564 self.drawNumber(
565 text = "Log Level",
566 event_name = "LOG_LEVEL",
567 min_val = 0, max_val = 99,
568 callback = self.updateLogLevel,
569 num_items = 4, item = 0)
570 self.drawPushButton(
571 text = "Warn",
572 event_name = "LOG_LEVEL_WARN",
573 num_items = 4, item = 1)
574 self.drawPushButton(
575 text = "Info",
576 event_name = "LOG_LEVEL_INFO",
577 num_items = 4, item = 2)
578 self.drawPushButton(
579 text = "Debug",
580 event_name = "LOG_LEVEL_DEBUG",
581 num_items = 4, item = 3)
582 self.drawYSep()
583
584 self.drawSlider(
585 text = "Scale Correction: ",
586 event_name = "SCALE_CORRECTION",
587 val = self.config["EXPORT_SCALE_CORRECTION"],
588 min_val = 0.01, max_val = 100.0,
589 callback = self.updateScale)
590 self.drawYSep()
591
592
593 if self.target == self.TARGET_IMPORT:
594 self.drawLabel(
595 text = "Texture Search Paths:",
596 event_name = "TEXPATH_TEXT")
597 self.drawList(
598 text = "",
599 event_name_prefix = "TEXPATH",
600 val = self.texpathCurrent)
601 self.drawYSep()
602
603 self.drawToggle(
604 text = "Import Animation",
605 event_name = "IMPORT_ANIMATION")
606 self.drawYSep()
607
608 self.drawToggle(
609 text = "Import Extra Nodes",
610 event_name = "IMPORT_EXTRANODES")
611 self.drawYSep()
612
613 self.drawToggle(
614 text = "Import Skeleton Only + Parent Selected",
615 event_name = "IMPORT_SKELETON_1",
616 val = (self.config["IMPORT_SKELETON"] == 1))
617 self.drawToggle(
618 text = "Import Geometry Only + Parent To Selected Armature",
619 event_name = "IMPORT_SKELETON_2",
620 val = (self.config["IMPORT_SKELETON"] == 2))
621 self.drawYSep()
622
623 self.drawToggle(
624 text = "Save Embedded Textures As DDS",
625 event_name = "IMPORT_EXPORTEMBEDDEDTEXTURES")
626 self.drawYSep()
627
628 self.drawToggle(
629 text = "Combine Multi-Material Shapes Into Single Mesh",
630 event_name = "IMPORT_COMBINESHAPES")
631 self.drawYSep()
632
633 self.drawLabel(
634 text = "Keyframe File:",
635 event_name = "IMPORT_KEYFRAMEFILE_TEXT")
636 self.drawFileBrowse(
637 text = "",
638 event_name_prefix = "IMPORT_KEYFRAMEFILE")
639 self.drawYSep()
640
641 self.drawLabel(
642 text = "FaceGen EGM File:",
643 event_name = "IMPORT_EGMFILE_TEXT")
644 self.drawFileBrowse(
645 text = "",
646 event_name_prefix = "IMPORT_EGMFILE")
647 self.drawToggle(
648 text="Animate",
649 event_name="IMPORT_EGMANIM",
650 num_items=2, item=0)
651 self.drawSlider(
652 text="Scale: ",
653 event_name="IMPORT_EGMANIMSCALE",
654 val=self.config["IMPORT_EGMANIMSCALE"],
655 min_val=0.01, max_val=100.0,
656 callback=self.updateEgmAnimScale,
657 num_items=2, item=1)
658 self.drawYSep()
659
660 self.drawPushButton(
661 text = "Restore Default Settings",
662 event_name = "IMPORT_SETTINGS_DEFAULT")
663 self.drawYSep()
664
665 self.drawLabel(
666 text = "... and if skinning fails with default settings:",
667 event_name = "IMPORT_SETTINGS_SKINNING_TEXT")
668 self.drawPushButton(
669 text = "Use The Force Luke",
670 event_name = "IMPORT_SETTINGS_SKINNING")
671 self.drawYSep()
672
673
674 if self.target == self.TARGET_EXPORT:
675 self.drawToggle(
676 text = "Export Geometry + Animation (.nif)",
677 event_name = "EXPORT_ANIMATION_0",
678 val = ((self.config["EXPORT_ANIMATION"] == 0)
679 or self.config["EXPORT_MW_NIFXNIFKF"]))
680 self.drawToggle(
681 text = "Export Geometry Only (.nif)",
682 event_name = "EXPORT_ANIMATION_1",
683 val = ((self.config["EXPORT_ANIMATION"] == 1)
684 or self.config["EXPORT_MW_NIFXNIFKF"]))
685 self.drawToggle(
686 text = "Export Animation Only (.kf)",
687 event_name = "EXPORT_ANIMATION_2",
688 val = ((self.config["EXPORT_ANIMATION"] == 2)
689 or self.config["EXPORT_MW_NIFXNIFKF"]))
690 self.drawYSep()
691
692 self.drawString(
693 text = "Anim Seq Name: ",
694 event_name = "EXPORT_ANIMSEQUENCENAME",
695 max_length = 128,
696 callback = self.updateAnimSequenceName)
697 self.drawYSep()
698
699 self.drawToggle(
700 text = "Force DDS Extension",
701 event_name = "EXPORT_FORCEDDS")
702 self.drawYSep()
703
704 self.drawToggle(
705 text = "Stripify Geometries",
706 event_name = "EXPORT_STRIPIFY",
707 num_items = 2, item = 0)
708 self.drawToggle(
709 text = "Stitch Strips",
710 event_name = "EXPORT_STITCHSTRIPS",
711 num_items = 2, item = 1)
712 self.drawYSep()
713
714 self.drawToggle(
715 text = "Smoothen Inter-Object Seams",
716 event_name = "EXPORT_SMOOTHOBJECTSEAMS")
717 self.drawYSep()
718
719 self.drawToggle(
720 text = "Flatten Skin",
721 event_name = "EXPORT_FLATTENSKIN")
722 self.drawToggle(
723 text = "Export Skin Partition",
724 event_name = "EXPORT_SKINPARTITION",
725 num_items = 3, item = 0)
726 self.drawToggle(
727 text = "Pad & Sort Bones",
728 event_name = "EXPORT_PADBONES",
729 num_items = 3, item = 1)
730 self.drawNumber(
731 text = "Max Bones",
732 event_name = "EXPORT_BONESPERPARTITION",
733 min_val = 4, max_val = 18,
734 callback = self.updateBonesPerPartition,
735 num_items = 3, item = 2)
736 self.drawYSep()
737
738 self.drawToggle(
739 text = "Combine Materials to Increase Performance",
740 event_name = "EXPORT_OPTIMIZE_MATERIALS")
741 self.drawYSep()
742
743 games_list = sorted(filter(lambda x: x != '?', NifFormat.games.keys()))
744 versions_list = sorted(NifFormat.versions.keys(), key=lambda x: NifFormat.versions[x])
745 V = self.xPos
746 H = HH = self.yPos
747 j = 0
748 MAXJ = 7
749 for i, game in enumerate(games_list):
750 if j >= MAXJ:
751 H = HH
752 j = 0
753 V += 150
754 state = (self.config["EXPORT_VERSION"] == game)
755 self.guiElements["GAME_%s"%game.upper()] = Draw.Toggle(game, self.eventId("GAME_%s"%game), V, H-j*20, 150, 20, state)
756 j += 1
757 j = 0
758 V += 160
759 for i, version in enumerate(versions_list):
760 if j >= MAXJ:
761 H = HH
762 j = 0
763 V += 70
764 state = (self.config["EXPORT_VERSION"] == version)
765 self.guiElements["VERSION_%s"%version] = Draw.Toggle(version, self.eventId("VERSION_%s"%version), V, H-j*20, 70, 20, state)
766 j += 1
767 self.yPos -= 20*min(MAXJ, max(len(NifFormat.versions), len(NifFormat.games)))
768 self.drawYSep()
769
770 if self.config["EXPORT_VERSION"] in games_list:
771 self.guiElements["EXPORT_RESET"] = Draw.PushButton(
772 "Restore Default Settings For Selected Game",
773 self.eventId("GAME_%s"%self.config["EXPORT_VERSION"]),
774 self.xPos, self.yPos, self.XCOLUMNSKIP, self.YLINESKIP)
775 self.yPos -= self.YLINESKIP
776 self.drawYSep()
777
778 self.drawPushButton(
779 text = "Ok",
780 event_name = "OK",
781 num_items = 3, item = 0)
782
783 self.drawPushButton(
784 text = "Cancel",
785 event_name = "CANCEL",
786 num_items = 3, item = 2)
787
788
789 if self.target == self.TARGET_IMPORT:
790 self.drawNextColumn()
791
792 self.drawToggle(
793 text = "Realign Bone Tail Only",
794 event_name = "IMPORT_REALIGN_BONES_1",
795 val = (self.config["IMPORT_REALIGN_BONES"] == 1),
796 num_items = 2, item = 0)
797 self.drawToggle(
798 text = "Realign Bone Tail + Roll",
799 event_name = "IMPORT_REALIGN_BONES_2",
800 val = (self.config["IMPORT_REALIGN_BONES"] == 2),
801 num_items = 2, item = 1)
802 self.drawToggle(
803 text="Merge Skeleton Roots",
804 event_name="IMPORT_MERGESKELETONROOTS")
805 self.drawToggle(
806 text="Send Geometries To Bind Position",
807 event_name="IMPORT_SENDGEOMETRIESTOBINDPOS")
808 self.drawToggle(
809 text="Send Detached Geometries To Node Position",
810 event_name="IMPORT_SENDDETACHEDGEOMETRIESTONODEPOS")
811 self.drawToggle(
812 text="Send Bones To Bind Position",
813 event_name="IMPORT_SENDBONESTOBINDPOS")
814 self.drawToggle(
815 text = "Apply Skin Deformation",
816 event_name = "IMPORT_APPLYSKINDEFORM")
817 self.drawYSep()
818
819
820
821 if (self.target == self.TARGET_EXPORT
822 and self.config["EXPORT_VERSION"] in ("Oblivion", "Fallout 3")):
823 self.drawNextColumn()
824
825 self.drawLabel(
826 text = "Collision Options",
827 event_name = "EXPORT_OB_COLLISIONHTML")
828 self.drawPushButton(
829 text = "Static",
830 event_name = "EXPORT_OB_RIGIDBODY_STATIC",
831 num_items = 5, item = 0)
832 self.drawPushButton(
833 text = "Anim Static",
834 event_name = "EXPORT_OB_RIGIDBODY_ANIMATED",
835 num_items = 5, item = 1)
836 self.drawPushButton(
837 text = "Clutter",
838 event_name = "EXPORT_OB_RIGIDBODY_CLUTTER",
839 num_items = 5, item = 2)
840 self.drawPushButton(
841 text = "Weapon",
842 event_name = "EXPORT_OB_RIGIDBODY_WEAPON",
843 num_items = 5, item = 3)
844 self.drawPushButton(
845 text = "Creature",
846 event_name = "EXPORT_OB_RIGIDBODY_CREATURE",
847 num_items = 5, item = 4)
848 self.drawToggle(
849 text = "Stone",
850 event_name = "EXPORT_OB_MATERIAL_STONE",
851 val = self.config["EXPORT_OB_MATERIAL"] == 0,
852 num_items = 6, item = 0)
853 self.drawToggle(
854 text = "Cloth",
855 event_name = "EXPORT_OB_MATERIAL_CLOTH",
856 val = self.config["EXPORT_OB_MATERIAL"] == 1,
857 num_items = 6, item = 1)
858 self.drawToggle(
859 text = "Glass",
860 event_name = "EXPORT_OB_MATERIAL_GLASS",
861 val = self.config["EXPORT_OB_MATERIAL"] == 3,
862 num_items = 6, item = 2)
863 self.drawToggle(
864 text = "Metal",
865 event_name = "EXPORT_OB_MATERIAL_METAL",
866 val = self.config["EXPORT_OB_MATERIAL"] == 5,
867 num_items = 6, item = 3)
868 self.drawToggle(
869 text = "Skin",
870 event_name = "EXPORT_OB_MATERIAL_SKIN",
871 val = self.config["EXPORT_OB_MATERIAL"] == 7,
872 num_items = 6, item = 4)
873 self.drawToggle(
874 text = "Wood",
875 event_name = "EXPORT_OB_MATERIAL_WOOD",
876 val = self.config["EXPORT_OB_MATERIAL"] == 9,
877 num_items = 6, item = 5)
878 self.drawNumber(
879 text = "Material: ",
880 event_name = "EXPORT_OB_MATERIAL",
881 min_val = 0, max_val = 30,
882 callback = self.updateObMaterial)
883 self.drawNumber(
884 text = "BSX Flags: ",
885 event_name = "EXPORT_OB_BSXFLAGS",
886 min_val = 0, max_val = 63,
887 callback = self.updateObBSXFlags,
888 num_items = 2, item = 0)
889 self.drawSlider(
890 text = "Mass: ",
891 event_name = "EXPORT_OB_MASS",
892 min_val = 0.1, max_val = 1500.0,
893 callback = self.updateObMass,
894 num_items = 2, item = 1)
895 self.drawNumber(
896 text = "Layer: ",
897 event_name = "EXPORT_OB_LAYER",
898 min_val = 0, max_val = 57,
899 callback = self.updateObLayer,
900 num_items = 3, item = 0)
901 self.drawNumber(
902 text = "Motion System: ",
903 event_name = "EXPORT_OB_MOTIONSYSTEM",
904 min_val = 0, max_val = 9,
905 callback = self.updateObMotionSystem,
906 num_items = 3, item = 1)
907 self.drawNumber(
908 text = "Quality Type: ",
909 event_name = "EXPORT_OB_QUALITYTYPE",
910 min_val = 0, max_val = 8,
911 callback = self.updateObQualityType,
912 num_items = 3, item = 2)
913 self.drawNumber(
914 text = "Unk Byte 1: ",
915 event_name = "EXPORT_OB_UNKNOWNBYTE1",
916 min_val = 1, max_val = 2,
917 callback = self.updateObUnknownByte1,
918 num_items = 3, item = 0)
919 self.drawNumber(
920 text = "Unk Byte 2: ",
921 event_name = "EXPORT_OB_UNKNOWNBYTE2",
922 min_val = 1, max_val = 2,
923 callback = self.updateObUnknownByte2,
924 num_items = 3, item = 1)
925 self.drawNumber(
926 text = "Wind: ",
927 event_name = "EXPORT_OB_WIND",
928 min_val = 0, max_val = 1,
929 callback = self.updateObWind,
930 num_items = 3, item = 2)
931 self.drawToggle(
932 text = "Solid",
933 event_name = "EXPORT_OB_SOLID",
934 num_items = 2, item = 0)
935 self.drawToggle(
936 text = "Hollow",
937 event_name = "EXPORT_OB_HOLLOW",
938 val = not self.config["EXPORT_OB_SOLID"],
939 num_items = 2, item = 1)
940 self.drawYSep()
941
942 self.drawToggle(
943 text = "Use bhkListShape",
944 event_name = "EXPORT_BHKLISTSHAPE",
945 num_items = 2, item = 0)
946 self.drawToggle(
947 text = "Use bhkMalleableConstraint",
948 event_name = "EXPORT_OB_MALLEABLECONSTRAINT",
949 num_items = 2, item = 1)
950 self.drawYSep()
951
952 self.drawLabel(
953 text = "Weapon Body Location",
954 event_name = "LABEL_WEAPON_LOCATION")
955 self.drawToggle(
956 text = "None",
957 val = self.config["EXPORT_OB_PRN"] == "NONE",
958 event_name = "EXPORT_OB_PRN_NONE",
959 num_items = 7, item = 0)
960 self.drawToggle(
961 text = "Back",
962 val = self.config["EXPORT_OB_PRN"] == "BACK",
963 event_name = "EXPORT_OB_PRN_BACK",
964 num_items = 7, item = 1)
965 self.drawToggle(
966 text = "Side",
967 val = self.config["EXPORT_OB_PRN"] == "SIDE",
968 event_name = "EXPORT_OB_PRN_SIDE",
969 num_items = 7, item = 2)
970 self.drawToggle(
971 text = "Quiver",
972 val = self.config["EXPORT_OB_PRN"] == "QUIVER",
973 event_name = "EXPORT_OB_PRN_QUIVER",
974 num_items = 7, item = 3)
975 self.drawToggle(
976 text = "Shield",
977 val = self.config["EXPORT_OB_PRN"] == "SHIELD",
978 event_name = "EXPORT_OB_PRN_SHIELD",
979 num_items = 7, item = 4)
980 self.drawToggle(
981 text = "Helm",
982 val = self.config["EXPORT_OB_PRN"] == "HELM",
983 event_name = "EXPORT_OB_PRN_HELM",
984 num_items = 7, item = 5)
985 self.drawToggle(
986 text = "Ring",
987 val = self.config["EXPORT_OB_PRN"] == "RING",
988 event_name = "EXPORT_OB_PRN_RING",
989 num_items = 7, item = 6)
990 self.drawYSep()
991
992
993 if (self.target == self.TARGET_EXPORT
994 and self.config["EXPORT_VERSION"] == "Morrowind"):
995 self.drawNextColumn()
996
997 self.drawToggle(
998 text = "Export nif + xnif + kf",
999 event_name = "EXPORT_MW_NIFXNIFKF")
1000
1001
1002 if (self.target == self.TARGET_EXPORT
1003 and (self.config["EXPORT_VERSION"]
1004 in NifImportExport.USED_EXTRA_SHADER_TEXTURES)):
1005 self.drawNextColumn()
1006
1007 self.drawToggle(
1008 text = "Export Extra Shader Textures",
1009 event_name = "EXPORT_EXTRA_SHADER_TEXTURES")
1010
1011
1012 if (self.target == self.TARGET_EXPORT
1013 and self.config["EXPORT_VERSION"] == "Fallout 3"):
1014 self.drawNextColumn()
1015
1016 self.drawLabel(
1017 text = "Shader Options",
1018 event_name = "LABEL_FO3_SHADER_OPTIONS")
1019 self.drawPushButton(
1020 text = "Default",
1021 event_name = "EXPORT_FO3_SHADER_OPTION_DEFAULT",
1022 num_items = 3, item = 0)
1023 self.drawPushButton(
1024 text = "Skin",
1025 event_name = "EXPORT_FO3_SHADER_OPTION_SKIN",
1026 num_items = 3, item = 1)
1027 self.drawPushButton(
1028 text = "Cloth",
1029 event_name = "EXPORT_FO3_SHADER_OPTION_CLOTH",
1030 num_items = 3, item = 2)
1031 self.drawToggle(
1032 text = "Default Type",
1033 val = self.config["EXPORT_FO3_SHADER_TYPE"] == 1,
1034 event_name = "EXPORT_FO3_SHADER_TYPE_DEFAULT",
1035 num_items = 2, item = 0)
1036 self.drawToggle(
1037 text = "Skin Type",
1038 val = self.config["EXPORT_FO3_SHADER_TYPE"] == 14,
1039 event_name = "EXPORT_FO3_SHADER_TYPE_SKIN",
1040 num_items = 2, item = 1)
1041 self.drawToggle(
1042 text = "Z Buffer",
1043 event_name = "EXPORT_FO3_SF_ZBUF",
1044 num_items = 3, item = 0)
1045 self.drawToggle(
1046 text = "Shadow Map",
1047 event_name = "EXPORT_FO3_SF_SMAP",
1048 num_items = 3, item = 1)
1049 self.drawToggle(
1050 text = "Shadow Frustum",
1051 event_name = "EXPORT_FO3_SF_SFRU",
1052 num_items = 3, item = 2)
1053 self.drawToggle(
1054 text = "Window Envmap",
1055 event_name = "EXPORT_FO3_SF_WINDOW_ENVMAP",
1056 num_items = 3, item = 0)
1057 self.drawToggle(
1058 text = "Empty",
1059 event_name = "EXPORT_FO3_SF_EMPT",
1060 num_items = 3, item = 1)
1061 self.drawToggle(
1062 text = "Unknown 31",
1063 event_name = "EXPORT_FO3_SF_UN31",
1064 num_items = 3, item = 2)
1065 self.drawYSep()
1066
1067 self.drawToggle(
1068 text = "Use BSFadeNode Root",
1069 event_name = "EXPORT_FO3_FADENODE")
1070 self.drawYSep()
1071
1072 self.drawToggle(
1073 text = "Export Dismember Body Parts",
1074 event_name = "EXPORT_FO3_BODYPARTS")
1075 self.drawYSep()
1076
1077
1078
1079
1413
1415 """Event handler for GUI elements."""
1416
1417 if evt == Draw.ESCKEY:
1418 self.callback = None
1419 self.guiExit()
1420
1421 Draw.Redraw(1)
1422
1424 """Close config GUI and call callback function."""
1425 Draw.Exit()
1426 if not self.callback: return
1427
1428 if not self.config["PROFILE"]:
1429
1430 self.callback(**self.config)
1431 else:
1432
1433 import cProfile
1434 import pstats
1435 prof = cProfile.Profile()
1436 prof.runctx('self.callback(**self.config)', locals(), globals())
1437 prof.dump_stats(self.config["PROFILE"])
1438 stats = pstats.Stats(self.config["PROFILE"])
1439 stats.strip_dirs()
1440 stats.sort_stats('cumulative')
1441 stats.print_stats()
1442
1443 - def addTexturePath(self, texture_path):
1444 texture_path = Blender.sys.dirname(texture_path)
1445 if texture_path == '' or not Blender.sys.exists(texture_path):
1446 Draw.PupMenu('No path selected or path does not exist%t|Ok')
1447 else:
1448 if texture_path not in self.config["IMPORT_TEXTURE_PATH"]:
1449 self.config["IMPORT_TEXTURE_PATH"].append(texture_path)
1450 self.texpathIndex = self.config["IMPORT_TEXTURE_PATH"].index(texture_path)
1451 self.updateTexpathCurrent()
1452
1454 """Update self.texpathCurrent string."""
1455 if self.config["IMPORT_TEXTURE_PATH"]:
1456 self.texpathCurrent = self.config["IMPORT_TEXTURE_PATH"][self.texpathIndex]
1457 else:
1458 self.texpathCurrent = ''
1459
1461 if keyframefile == '' or not Blender.sys.exists(keyframefile):
1462 Draw.PupMenu('No file selected or file does not exist%t|Ok')
1463 else:
1464 self.config["IMPORT_KEYFRAMEFILE"] = keyframefile
1465
1467 if egmfile == '' or not Blender.sys.exists(egmfile):
1468 Draw.PupMenu('No file selected or file does not exist%t|Ok')
1469 else:
1470 self.config["IMPORT_EGMFILE"] = egmfile
1471
1473 self.config["LOG_LEVEL"] = val
1474 logging.getLogger("niftools").setLevel(val)
1475 logging.getLogger("pyffi").setLevel(val)
1476
1478 self.config["EXPORT_SCALE_CORRECTION"] = val
1479 self.config["IMPORT_SCALE_CORRECTION"] = 1.0 / self.config["EXPORT_SCALE_CORRECTION"]
1480
1482 self.config["EXPORT_BONESPERPARTITION"] = val
1483 self.config["EXPORT_PADBONES"] = False
1484
1486 self.config["EXPORT_OB_BSXFLAGS"] = val
1487
1489 self.config["EXPORT_OB_MATERIAL"] = val
1490
1492 self.config["EXPORT_OB_LAYER"] = val
1493
1495 self.config["EXPORT_OB_MASS"] = val
1496
1498 self.config["EXPORT_OB_MOTIONSYSTEM"] = val
1499
1501 self.config["EXPORT_OB_QUALITYTYPE"] = val
1502
1504 self.config["EXPORT_OB_UNKNOWNBYTE1"] = val
1505
1507 self.config["EXPORT_OB_UNKNOWNBYTE2"] = val
1508
1510 self.config["EXPORT_OB_WIND"] = val
1511
1513 self.config["EXPORT_ANIMSEQUENCENAME"] = val
1514
1516 self.config["IMPORT_EGMANIMSCALE"] = val
1517