Newer
Older
"""Common functions for the Blender nif import and export scripts."""
__requiredpyffiversion__ = "2.0.2"
__requiredblenderversion__ = "245"
# ***** BEGIN LICENSE BLOCK *****
#
# BSD License
#
# Copyright (c) 2005-2009, NIF File Format Library and Tools
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the NIF File Format Library and Tools project may not be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ***** END LICENCE BLOCK *****
# utility functions
def cmp_versions(version1, version2):
"""Compare version strings."""
def version_intlist(version):
"""Convert version string to list of integers."""
return [int(x) for x in version.__str__().split(".")]
return cmp(version_intlist(version1), version_intlist(version2))
# things to do on import and export
# check Blender version
__blenderversion__ = Blender.Get('version')
if cmp_versions(__blenderversion__, __requiredblenderversion__) == -1:
print("""--------------------------
ERROR\nThis script requires Blender %s or higher.
It seems that you have an older version installed (%s).
Get a newer version at http://www.blender.org/
--------------------------"""%(__requiredblenderversion__, __blenderversion__))
Blender.Draw.PupMenu("ERROR%t|Blender outdated, check console for details")
raise ImportError
# check if PyFFI is installed and import NifFormat
try:
from pyffi import __version__ as __pyffiversion__
from pyffi.formats.nif import NifFormat
print("""--------------------------
ERROR\nThis script requires the Python File Format Interface (PyFFI).
Make sure that PyFFI resides in your Python path or in your Blender scripts folder.
If you do not have it: http://pyffi.sourceforge.net/
Blender.Draw.PupMenu("ERROR%t|PyFFI not found, check console for details")
raise
# check PyFFI version
if cmp_versions(__pyffiversion__, __requiredpyffiversion__) == -1:
print("""--------------------------
ERROR\nThis script requires Python File Format Interface %s or higher.
It seems that you have an older version installed (%s).
Get a newer version at http://pyffi.sourceforge.net/
--------------------------"""%(__requiredpyffiversion__, __pyffiversion__))
Blender.Draw.PupMenu("ERROR%t|PyFFI outdated, check console for details")
raise ImportError
import logging
import sys
import os
def initLoggers():
"""Set up loggers."""
niftoolslogger = logging.getLogger("niftools")
niftoolslogger.setLevel(logging.WARNING)
pyffilogger = logging.getLogger("pyffi")
pyffilogger.setLevel(logging.WARNING)
loghandler = logging.StreamHandler()
loghandler.setLevel(logging.DEBUG)
logformatter = logging.Formatter("%(name)s:%(levelname)s:%(message)s")
loghandler.setFormatter(logformatter)
niftoolslogger.addHandler(loghandler)
pyffilogger.addHandler(loghandler)
# set up the loggers: call it as a function to avoid polluting namespace
initLoggers()
class NifImportExport:
"""Abstract base class for import and export. Contains utility functions
that are commonly used in both import and export."""
EXTRA_SHADER_TEXTURES = [
"EnvironmentMapIndex",
"NormalMapIndex",
"SpecularIntensityIndex",
"EnvironmentIntensityIndex",
"LightCubeMapIndex",
"ShadowTextureIndex"]
"""Names (ordered by default index) of shader texture slots for
Sid Meier's Railroads and similar games.
USED_EXTRA_SHADER_TEXTURES = {
"Sid Meier's Railroads": (3, 0, 4, 1, 5, 2),
"Civilization IV": (3, 0, 1, 2)}
"""The default ordering of the extra data blocks for different games."""
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def getBoneNameForBlender(self, name):
"""Convert a bone name to a name that can be used by Blender: turns
'Bip01 R xxx' into 'Bip01 xxx.R', and similar for L.
@param name: The bone name as in the nif file.
@type name: C{str}
@return: Bone name in Blender convention.
@rtype: C{str}
"""
if name.startswith("Bip01 L "):
return "Bip01 " + name[8:] + ".L"
elif name.startswith("Bip01 R "):
return "Bip01 " + name[8:] + ".R"
return name
def getBoneNameForNif(self, name):
"""Convert a bone name to a name that can be used by the nif file:
turns 'Bip01 xxx.R' into 'Bip01 R xxx', and similar for L.
@param name: The bone name as in Blender.
@type name: C{str}
@return: Bone name in nif convention.
@rtype: C{str}
"""
if name.startswith("Bip01 "):
if name.endswith(".L"):
return "Bip01 L " + name[6:-2]
elif name.endswith(".R"):
return "Bip01 R " + name[6:-2]
return name
def get_extend_from_flags(self, flags):
if flags & 6 == 4: # 0b100
return Blender.IpoCurve.ExtendTypes.CONST
elif flags & 6 == 0: # 0b000
return Blender.IpoCurve.ExtendTypes.CYCLIC
self.logger.warning(
"Unsupported cycle mode in nif, using clamped.")
return Blender.IpoCurve.ExtendTypes.CONST
def get_flags_from_extend(self, extend):
if extend == Blender.IpoCurve.ExtendTypes.CONST:
return 4 # 0b100
elif extend == Blender.IpoCurve.ExtendTypes.CYCLIC:
return 0
self.logger.warning(
"Unsupported extend type in blend, using clamped.")
return 4
"""Class which handles configuration of nif import and export in Blender.
Important: keep every instance of this class in a global variable
(otherwise gui elements might go out of skope which will crash
Blender)."""
WELCOME_MESSAGE = 'Blender NIF Scripts %s (running on Blender %s, PyFFI %s)'%(__version__, __blenderversion__, __pyffiversion__)
TARGET_IMPORT = 0 # config target value when importing
TARGET_EXPORT = 1 # config target value when exporting
# GUI stuff
XORIGIN = 50 # x coordinate of origin
XCOLUMNSKIP = 390 # width of a column
XCOLUMNSEP = 10 # width of the column separator
YORIGIN = -40 # y coordinate of origin relative to Blender.Window.GetAreaSize()[1]
YLINESKIP = 20 # height of a line
YLINESEP = 10 # height of a line separator
# the DEFAULTS dict defines the valid config variables, default values,
# and their type
# IMPORTANT: don't start dictionary keys with an underscore
# the Registry module doesn't like that, apparently
DEFAULTS = dict(
IMPORT_FILE = Blender.sys.join(
Blender.sys.dirname(Blender.sys.progname), "import.nif"),
EXPORT_FILE = Blender.sys.join(
Blender.sys.dirname(Blender.sys.progname), "export.nif"),
IMPORT_REALIGN_BONES = 1, # 0 = no, 1 = tail, 2 = tail+rotation
IMPORT_ANIMATION = True,
IMPORT_SCALE_CORRECTION = 0.1,
EXPORT_SCALE_CORRECTION = 10.0, # 1/import scale correction
EXPORT_FLATTENSKIN = False,
EXPORT_VERSION = 'Oblivion',
EPSILON = 0.005, # used for checking equality with floats
LOG_LEVEL = logging.WARNING, # log level
Amorilia
committed
IMPORT_SKELETON = 0, # 0 = normal import, 1 = import file as skeleton, 2 = import mesh and attach to skeleton
IMPORT_KEYFRAMEFILE = '', # keyframe file for animations
IMPORT_EGMFILE = '', # FaceGen EGM file for morphs
EXPORT_ANIMATION = 0, # export everything (1=geometry only, 2=animation only)
EXPORT_ANIMSEQUENCENAME = '', # sequence name of the kf file
EXPORT_FORCEDDS = True, # force dds extension on texture files
EXPORT_SKINPARTITION = True, # generate skin partition
EXPORT_BONESPERVERTEX = 4,
EXPORT_BONESPERPARTITION = 18,
EXPORT_PADBONES = False,
EXPORT_STRIPIFY = True,
EXPORT_STITCHSTRIPS = False,
EXPORT_SMOOTHOBJECTSEAMS = True,
IMPORT_MERGESKELETONROOTS = True,
IMPORT_SENDGEOMETRIESTOBINDPOS = True,
IMPORT_SENDDETACHEDGEOMETRIESTONODEPOS = True,
IMPORT_APPLYSKINDEFORM = False,
IMPORT_EXTRANODES = True,
EXPORT_OB_BSXFLAGS = 2,
EXPORT_OB_SOLID = True,
EXPORT_OB_UNKNOWNBYTE1 = 1,
EXPORT_OB_UNKNOWNBYTE2 = 1,
EXPORT_OB_WIND = 0,
EXPORT_OB_LAYER = 1, # static
EXPORT_OB_MATERIAL = 9, # wood
EXPORT_OB_MALLEABLECONSTRAINT = False, # use malleable constraint for ragdoll and hinge
EXPORT_OB_PRN = "NONE", # determines bone where to attach weapon
EXPORT_FO3_SF_ZBUF = True, # use these shader flags?
EXPORT_FO3_SF_SMAP = False,
EXPORT_FO3_SF_SFRU = False,
EXPORT_FO3_SF_WINDOW_ENVMAP = False,
EXPORT_FO3_SF_EMPT = True,
EXPORT_FO3_SF_UN31 = True,
EXPORT_FO3_FADENODE = False,
EXPORT_FO3_SHADER_TYPE = 1, # shader_default
EXPORT_MW_NIFXNIFKF = False,
EXPORT_EXTRA_SHADER_TEXTURES = True,
PROFILE = '', # name of file where Python profiler dumps the profile; set to empty string to turn off profiling
EXPORT_OPTIMIZE_MATERIALS = True,
IMPORT_COMBINESHAPES = True)
def __init__(self):
"""Initialize and load configuration."""
# clears the console window
if sys.platform in ('linux-i386','linux2'):
os.system("clear")
elif sys.platform in ('win32','dos','ms-dos'):
os.system("cls")
# print scripts info
print self.WELCOME_MESSAGE
# initialize all instance variables
self.guiElements = {} # dictionary of gui elements (buttons, strings, sliders, ...)
self.guiEvents = [] # list of events
self.guiEventIds = {} # dictionary of event ids
self.config = {} # configuration dictionary
self.target = None # import or export
self.callback = None # function to call when config gui is done
self.texpathIndex = 0
self.texpathCurrent = ''
# reset GUI coordinates
self.xPos = self.XORIGIN
self.yPos = self.YORIGIN + Blender.Window.GetAreaSize()[1]
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# load configuration
self.load()
def run(self, target, filename, callback):
"""Run the config gui."""
self.target = target # import or export
self.callback = callback # function to call when config gui is done
# save file name
# (the key where to store the file name depends
# on the target)
if self.target == self.TARGET_IMPORT:
self.config["IMPORT_FILE"] = filename
elif self.target == self.TARGET_EXPORT:
self.config["EXPORT_FILE"] = filename
self.save()
# prepare and run gui
self.texpathIndex = 0
self.updateTexpathCurrent()
Draw.Register(self.guiDraw, self.guiEvent, self.guiButtonEvent)
def save(self):
"""Save and validate configuration to Blender registry."""
Registry.SetKey(self.CONFIG_NAME, self.config, True)
self.load() # for validation
def load(self):
"""Load the configuration stored in the Blender registry and checks
configuration for incompatible values.
"""
# copy defaults
self.config = dict(**self.DEFAULTS)
# read configuration
savedconfig = Blender.Registry.GetKey(self.CONFIG_NAME, True)
# port config keys from old versions to current version
try:
self.config["IMPORT_TEXTURE_PATH"] = savedconfig["TEXTURE_SEARCH_PATH"]
except:
pass
Amorilia
committed
try:
self.config["IMPORT_FILE"] = Blender.sys.join(
savedconfig["NIF_IMPORT_PATH"], savedconfig["NIF_IMPORT_FILE"])
Amorilia
committed
except:
pass
try:
self.config["EXPORT_FILE"] = savedconfig["NIF_EXPORT_FILE"]
except:
pass
try:
self.config["IMPORT_REALIGN_BONES"] = savedconfig["REALIGN_BONES"]
except:
pass
try:
if self.config["IMPORT_REALIGN_BONES"] == True:
self.config["IMPORT_REALIGN_BONES"] = 1
elif self.config["IMPORT_REALIGN_BONES"] == False:
self.config["IMPORT_REALIGN_BONES"] = 0
Amorilia
committed
try:
if savedconfig["IMPORT_SKELETON"] == True:
self.config["IMPORT_SKELETON"] = 1
elif savedconfig["IMPORT_SKELETON"] == False:
self.config["IMPORT_SKELETON"] = 0
except:
pass
# merge configuration with defaults
if savedconfig:
for key, val in self.DEFAULTS.iteritems():
try:
savedval = savedconfig[key]
except KeyError:
pass
else:
if isinstance(savedval, val.__class__):
self.config[key] = savedval
# store configuration
Blender.Registry.SetKey(self.CONFIG_NAME, self.config, True)
# special case: set log level here
self.updateLogLevel("LOG_LEVEL", self.config["LOG_LEVEL"])
def eventId(self, event_name):
"""Return event id from event name, and register event if it is new."""
try:
event_id = self.guiEventIds[event_name]
except KeyError:
event_id = len(self.guiEvents)
self.guiEventIds[event_name] = event_id
self.guiEvents.append(event_name)
if event_id >= 16383:
raise RuntimeError("Maximum number of events exceeded")
return event_id
def drawYSep(self):
"""Vertical skip."""
self.yPos -= self.YLINESEP
def drawNextColumn(self):
"""Start a new column."""
self.xPos += self.XCOLUMNSKIP + self.XCOLUMNSEP
self.yPos = self.YORIGIN + Blender.Window.GetAreaSize()[1]
def drawSlider(
self, text, event_name, min_val, max_val, callback, val = None,
num_items = 1, item = 0):
if val is None:
val = self.config[event_name]
width = self.XCOLUMNSKIP//num_items
self.guiElements[event_name] = Draw.Slider(
text,
self.eventId(event_name),
self.xPos + item*width, self.yPos, width, self.YLINESKIP,
val, min_val, max_val,
0, # realtime
"", # tooltip,
callback)
if item + 1 == num_items:
self.yPos -= self.YLINESKIP
def drawLabel(self, text, event_name, num_items = 1, item = 0):
width = self.XCOLUMNSKIP//num_items
self.guiElements[event_name] = Draw.Label(
text,
self.xPos + item*width, self.yPos, width, self.YLINESKIP)
if item + 1 == num_items:
self.yPos -= self.YLINESKIP
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
def drawList(self, text, event_name_prefix, val):
"""Create elements to select a list of things.
Registers events PREFIX_ITEM, PREFIX_PREV, PREFIX_NEXT, PREFIX_REMOVE
and PREFIX_ADD."""
self.guiElements["%s_ITEM"%event_name_prefix] = Draw.String(
text,
self.eventId("%s_ITEM"%event_name_prefix),
self.xPos, self.yPos, self.XCOLUMNSKIP-90, self.YLINESKIP,
val, 255)
self.guiElements["%s_PREV"%event_name_prefix] = Draw.PushButton(
'<',
self.eventId("%s_PREV"%event_name_prefix),
self.xPos+self.XCOLUMNSKIP-90, self.yPos, 20, self.YLINESKIP)
self.guiElements["%s_NEXT"%event_name_prefix] = Draw.PushButton(
'>',
self.eventId("%s_NEXT"%event_name_prefix),
self.xPos+self.XCOLUMNSKIP-70, self.yPos, 20, self.YLINESKIP)
self.guiElements["%s_REMOVE"%event_name_prefix] = Draw.PushButton(
'X',
self.eventId("%s_REMOVE"%event_name_prefix),
self.xPos+self.XCOLUMNSKIP-50, self.yPos, 20, self.YLINESKIP)
self.guiElements["%s_ADD"%event_name_prefix] = Draw.PushButton(
'...',
self.eventId("%s_ADD"%event_name_prefix),
self.xPos+self.XCOLUMNSKIP-30, self.yPos, 30, self.YLINESKIP)
self.yPos -= self.YLINESKIP
def drawToggle(self, text, event_name, val = None, num_items = 1, item = 0):
if val == None:
val = self.config[event_name]
width = self.XCOLUMNSKIP//num_items
self.guiElements[event_name] = Draw.Toggle(
text,
self.eventId(event_name),
self.xPos + item*width, self.yPos, width, self.YLINESKIP,
val)
if item + 1 == num_items:
self.yPos -= self.YLINESKIP
def drawPushButton(self, text, event_name, num_items = 1, item = 0):
"""Draw a push button."""
width = self.XCOLUMNSKIP//num_items
self.guiElements[event_name] = Draw.PushButton(
text,
self.eventId(event_name),
self.xPos + item*width, self.yPos, width, self.YLINESKIP)
if item + 1 == num_items:
self.yPos -= self.YLINESKIP
def drawNumber(
self, text, event_name, min_val, max_val, callback, val = None,
num_items = 1, item = 0):
"""Draw an input widget for numbers."""
if val is None:
val = self.config[event_name]
width = self.XCOLUMNSKIP//num_items
self.guiElements[event_name] = Draw.Number(
text,
self.eventId(event_name),
self.xPos + item*width, self.yPos, width, self.YLINESKIP,
val,
min_val, max_val,
"", # tooltip
callback)
if item + 1 == num_items:
self.yPos -= self.YLINESKIP
def drawFileBrowse(self, text, event_name_prefix, val = None):
"""Create elements to select a file.
Registers events PREFIX_ITEM, PREFIX_REMOVE, PREFIX_ADD."""
if val is None:
val = self.config[event_name_prefix]
self.guiElements["%s_ITEM"%event_name_prefix] = Draw.String(
text,
self.eventId("%s_ITEM"%event_name_prefix),
self.xPos, self.yPos, self.XCOLUMNSKIP-50, self.YLINESKIP,
val, 255)
self.guiElements["%s_REMOVE"%event_name_prefix] = Draw.PushButton(
'X',
self.eventId("%s_REMOVE"%event_name_prefix),
self.xPos+self.XCOLUMNSKIP-50, self.yPos, 20, self.YLINESKIP)
self.guiElements["%s_ADD"%event_name_prefix] = Draw.PushButton(
'...',
self.eventId("%s_ADD"%event_name_prefix),
self.xPos+self.XCOLUMNSKIP-30, self.yPos, 30, self.YLINESKIP)
self.yPos -= self.YLINESKIP
def drawString(self, text, event_name, max_length, callback, val = None,
num_items = 1, item = 0):
"""Create elements to input a string."""
if val is None:
val = self.config[event_name]
width = self.XCOLUMNSKIP//num_items
self.guiElements[event_name] = Draw.String(
text,
self.eventId(event_name),
self.xPos + item*width, self.yPos, width, self.YLINESKIP,
val,
max_length,
"", # tooltip
callback)
if item + 1 == num_items:
self.yPos -= self.YLINESKIP
def guiDraw(self):
"""Draw config GUI."""
# reset position
self.xPos = self.XORIGIN
self.yPos = self.YORIGIN + Blender.Window.GetAreaSize()[1]
Amorilia
committed
self.drawLabel(
text = self.WELCOME_MESSAGE,
event_name = "LABEL_WELCOME_MESSAGE")
self.drawYSep()
self.drawNumber(
text = "Log Level",
event_name = "LOG_LEVEL",
min_val = 0, max_val = 99,
callback = self.updateLogLevel,
num_items = 4, item = 0)
self.drawPushButton(
text = "Warn",
event_name = "LOG_LEVEL_WARN",
num_items = 4, item = 1)
self.drawPushButton(
text = "Info",
event_name = "LOG_LEVEL_INFO",
num_items = 4, item = 2)
self.drawPushButton(
text = "Debug",
event_name = "LOG_LEVEL_DEBUG",
num_items = 4, item = 3)
self.drawYSep()
self.drawSlider(
text = "Scale Correction: ",
event_name = "SCALE_CORRECTION",
val = self.config["EXPORT_SCALE_CORRECTION"],
min_val = 0.01, max_val = 100.0,
callback = self.updateScale)
self.drawYSep()
# import-only options
if self.target == self.TARGET_IMPORT:
self.drawLabel(
text = "Texture Search Paths:",
event_name = "TEXPATH_TEXT")
self.drawList(
text = "",
event_name_prefix = "TEXPATH",
val = self.texpathCurrent)
self.drawYSep()
self.drawToggle(
text = "Import Animation",
event_name = "IMPORT_ANIMATION")
self.drawToggle(
text = "Import Extra Nodes",
event_name = "IMPORT_EXTRANODES")
self.drawYSep()
self.drawToggle(
text = "Import Skeleton Only + Parent Selected",
event_name = "IMPORT_SKELETON_1",
val = (self.config["IMPORT_SKELETON"] == 1))
self.drawToggle(
text = "Import Geometry Only + Parent To Selected Armature",
event_name = "IMPORT_SKELETON_2",
val = (self.config["IMPORT_SKELETON"] == 2))
self.drawYSep()
self.drawToggle(
text = "Save Embedded Textures As DDS",
event_name = "IMPORT_EXPORTEMBEDDEDTEXTURES")
self.drawYSep()
text = "Combine Multi-Material Shapes Into Single Mesh",
event_name = "IMPORT_COMBINESHAPES")
self.drawYSep()
self.drawLabel(
text = "Keyframe File:",
event_name = "IMPORT_KEYFRAMEFILE_TEXT")
self.drawFileBrowse(
text = "",
event_name_prefix = "IMPORT_KEYFRAMEFILE")
self.drawYSep()
self.drawLabel(
text = "FaceGen EGM File:",
event_name = "IMPORT_EGMFILE_TEXT")
self.drawFileBrowse(
text = "",
event_name_prefix = "IMPORT_EGMFILE")
self.drawYSep()
Amorilia
committed
self.drawPushButton(
text = "Restore Default Settings",
event_name = "IMPORT_SETTINGS_DEFAULT")
self.drawYSep()
self.drawLabel(
text = "... and if skinning fails with default settings:",
event_name = "IMPORT_SETTINGS_SKINNING_TEXT")
self.drawPushButton(
text = "Use The Force Luke",
event_name = "IMPORT_SETTINGS_SKINNING")
self.drawYSep()
# export-only options
if self.target == self.TARGET_EXPORT:
self.drawToggle(
text = "Export Geometry + Animation (.nif)",
event_name = "EXPORT_ANIMATION_0",
val = ((self.config["EXPORT_ANIMATION"] == 0)
or self.config["EXPORT_MW_NIFXNIFKF"]))
self.drawToggle(
text = "Export Geometry Only (.nif)",
event_name = "EXPORT_ANIMATION_1",
val = ((self.config["EXPORT_ANIMATION"] == 1)
or self.config["EXPORT_MW_NIFXNIFKF"]))
val = ((self.config["EXPORT_ANIMATION"] == 2)
or self.config["EXPORT_MW_NIFXNIFKF"]))
self.drawString(
text = "Anim Seq Name: ",
event_name = "EXPORT_ANIMSEQUENCENAME",
max_length = 128,
callback = self.updateAnimSequenceName)
self.drawYSep()
self.drawToggle(
text = "Force DDS Extension",
event_name = "EXPORT_FORCEDDS")
self.drawYSep()
self.drawToggle(
text = "Stripify Geometries",
event_name = "EXPORT_STRIPIFY",
num_items = 2, item = 0)
self.drawToggle(
text = "Stitch Strips",
event_name = "EXPORT_STITCHSTRIPS",
num_items = 2, item = 1)
self.drawYSep()
self.drawToggle(
text = "Smoothen Inter-Object Seams",
event_name = "EXPORT_SMOOTHOBJECTSEAMS")
self.drawYSep()
self.drawToggle(
text = "Flatten Skin",
event_name = "EXPORT_FLATTENSKIN")
self.drawToggle(
text = "Export Skin Partition",
event_name = "EXPORT_SKINPARTITION",
num_items = 3, item = 0)
self.drawToggle(
text = "Pad & Sort Bones",
event_name = "EXPORT_PADBONES",
num_items = 3, item = 1)
self.drawNumber(
text = "Max Bones",
event_name = "EXPORT_BONESPERPARTITION",
min_val = 4, max_val = 18,
callback = self.updateBonesPerPartition,
num_items = 3, item = 2)
self.drawYSep()
self.drawToggle(
text = "Combine Materials to Increase Performance",
event_name = "EXPORT_OPTIMIZE_MATERIALS")
self.drawYSep()
games_list = sorted(filter(lambda x: x != '?', NifFormat.games.keys()))
versions_list = sorted(NifFormat.versions.keys(), key=lambda x: NifFormat.versions[x])
j = 0
MAXJ = 7
for i, game in enumerate(games_list):
if j >= MAXJ:
H = HH
j = 0
V += 150
state = (self.config["EXPORT_VERSION"] == game)
self.guiElements["GAME_%s"%game.upper()] = Draw.Toggle(game, self.eventId("GAME_%s"%game), V, H-j*20, 150, 20, state)
j += 1
j = 0
V += 160
for i, version in enumerate(versions_list):
if j >= MAXJ:
H = HH
j = 0
V += 70
state = (self.config["EXPORT_VERSION"] == version)
self.guiElements["VERSION_%s"%version] = Draw.Toggle(version, self.eventId("VERSION_%s"%version), V, H-j*20, 70, 20, state)
j += 1
self.yPos -= 20*min(MAXJ, max(len(NifFormat.versions), len(NifFormat.games)))
self.drawYSep()
if self.config["EXPORT_VERSION"] in games_list:
self.guiElements["EXPORT_RESET"] = Draw.PushButton(
"Restore Default Settings For Selected Game",
self.eventId("GAME_%s"%self.config["EXPORT_VERSION"]),
self.xPos, self.yPos, self.XCOLUMNSKIP, self.YLINESKIP)
self.yPos -= self.YLINESKIP
self.drawYSep()
self.drawPushButton(
text = "Ok",
event_name = "OK",
num_items = 3, item = 0)
# (item 1 is whitespace)
self.drawPushButton(
text = "Cancel",
event_name = "CANCEL",
num_items = 3, item = 2)
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
# advanced import settings
if self.target == self.TARGET_IMPORT:
self.drawNextColumn()
self.drawToggle(
text = "Realign Bone Tail Only",
event_name = "IMPORT_REALIGN_BONES_1",
val = (self.config["IMPORT_REALIGN_BONES"] == 1),
num_items = 2, item = 0)
self.drawToggle(
text = "Realign Bone Tail + Roll",
event_name = "IMPORT_REALIGN_BONES_2",
val = (self.config["IMPORT_REALIGN_BONES"] == 2),
num_items = 2, item = 1)
self.drawToggle(
text="Merge Skeleton Roots",
event_name="IMPORT_MERGESKELETONROOTS")
self.drawToggle(
text="Send Geometries To Bind Position",
event_name="IMPORT_SENDGEOMETRIESTOBINDPOS")
self.drawToggle(
text="Send Detached Geometries To Node Position",
event_name="IMPORT_SENDDETACHEDGEOMETRIESTONODEPOS")
self.drawToggle(
text="Send Bones To Bind Position",
event_name="IMPORT_SENDBONESTOBINDPOS")
self.drawToggle(
text = "Apply Skin Deformation",
event_name = "IMPORT_APPLYSKINDEFORM")
self.drawYSep()
if (self.target == self.TARGET_EXPORT
and self.config["EXPORT_VERSION"] in ("Oblivion", "Fallout 3")):
self.drawLabel(
text = "Collision Options",
event_name = "EXPORT_OB_COLLISIONHTML")
self.drawPushButton(
text = "Static",
event_name = "EXPORT_OB_RIGIDBODY_STATIC",
num_items = 5, item = 0)
self.drawPushButton(
text = "Anim Static",
event_name = "EXPORT_OB_RIGIDBODY_ANIMATED",
num_items = 5, item = 1)
self.drawPushButton(
text = "Clutter",
event_name = "EXPORT_OB_RIGIDBODY_CLUTTER",
num_items = 5, item = 2)
self.drawPushButton(
text = "Weapon",
event_name = "EXPORT_OB_RIGIDBODY_WEAPON",
num_items = 5, item = 3)
self.drawPushButton(
text = "Creature",
event_name = "EXPORT_OB_RIGIDBODY_CREATURE",
num_items = 5, item = 4)
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
self.drawToggle(
text = "Stone",
event_name = "EXPORT_OB_MATERIAL_STONE",
val = self.config["EXPORT_OB_MATERIAL"] == 0,
num_items = 6, item = 0)
self.drawToggle(
text = "Cloth",
event_name = "EXPORT_OB_MATERIAL_CLOTH",
val = self.config["EXPORT_OB_MATERIAL"] == 1,
num_items = 6, item = 1)
self.drawToggle(
text = "Glass",
event_name = "EXPORT_OB_MATERIAL_GLASS",
val = self.config["EXPORT_OB_MATERIAL"] == 3,
num_items = 6, item = 2)
self.drawToggle(
text = "Metal",
event_name = "EXPORT_OB_MATERIAL_METAL",
val = self.config["EXPORT_OB_MATERIAL"] == 5,
num_items = 6, item = 3)
self.drawToggle(
text = "Skin",
event_name = "EXPORT_OB_MATERIAL_SKIN",
val = self.config["EXPORT_OB_MATERIAL"] == 7,
num_items = 6, item = 4)
self.drawToggle(
text = "Wood",
event_name = "EXPORT_OB_MATERIAL_WOOD",
val = self.config["EXPORT_OB_MATERIAL"] == 9,
num_items = 6, item = 5)
self.drawNumber(
text = "Material: ",
event_name = "EXPORT_OB_MATERIAL",
min_val = 0, max_val = 30,
callback = self.updateObMaterial)
self.drawNumber(
text = "BSX Flags: ",
event_name = "EXPORT_OB_BSXFLAGS",
min_val = 0, max_val = 63,
callback = self.updateObBSXFlags,
num_items = 2, item = 0)
self.drawSlider(
text = "Mass: ",
event_name = "EXPORT_OB_MASS",
min_val = 0.1, max_val = 1500.0,
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
num_items = 2, item = 1)
self.drawNumber(
text = "Layer: ",
event_name = "EXPORT_OB_LAYER",
min_val = 0, max_val = 57,
callback = self.updateObLayer,
num_items = 3, item = 0)
self.drawNumber(
text = "Motion System: ",
event_name = "EXPORT_OB_MOTIONSYSTEM",
min_val = 0, max_val = 9,
callback = self.updateObMotionSystem,
num_items = 3, item = 1)
self.drawNumber(
text = "Quality Type: ",
event_name = "EXPORT_OB_QUALITYTYPE",
min_val = 0, max_val = 8,
callback = self.updateObQualityType,
num_items = 3, item = 2)
self.drawNumber(
text = "Unk Byte 1: ",
event_name = "EXPORT_OB_UNKNOWNBYTE1",
min_val = 1, max_val = 2,
callback = self.updateObUnknownByte1,
num_items = 3, item = 0)
self.drawNumber(
text = "Unk Byte 2: ",
event_name = "EXPORT_OB_UNKNOWNBYTE2",
min_val = 1, max_val = 2,
callback = self.updateObUnknownByte2,
num_items = 3, item = 1)
self.drawNumber(
text = "Wind: ",
event_name = "EXPORT_OB_WIND",
min_val = 0, max_val = 1,
callback = self.updateObWind,
num_items = 3, item = 2)
self.drawToggle(
text = "Solid",
event_name = "EXPORT_OB_SOLID",
num_items = 2, item = 0)
self.drawToggle(
text = "Hollow",
event_name = "EXPORT_OB_HOLLOW",
val = not self.config["EXPORT_OB_SOLID"],
num_items = 2, item = 1)
self.drawYSep()
self.drawToggle(
text = "Use bhkListShape",
event_name = "EXPORT_BHKLISTSHAPE",
num_items = 2, item = 0)
self.drawToggle(
text = "Use bhkMalleableConstraint",
event_name = "EXPORT_OB_MALLEABLECONSTRAINT",
num_items = 2, item = 1)
text = "Weapon Body Location",
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
event_name = "LABEL_WEAPON_LOCATION")
self.drawToggle(
text = "None",
val = self.config["EXPORT_OB_PRN"] == "NONE",
event_name = "EXPORT_OB_PRN_NONE",
num_items = 7, item = 0)
self.drawToggle(
text = "Back",
val = self.config["EXPORT_OB_PRN"] == "BACK",
event_name = "EXPORT_OB_PRN_BACK",
num_items = 7, item = 1)
self.drawToggle(
text = "Side",
val = self.config["EXPORT_OB_PRN"] == "SIDE",
event_name = "EXPORT_OB_PRN_SIDE",
num_items = 7, item = 2)
self.drawToggle(
text = "Quiver",
val = self.config["EXPORT_OB_PRN"] == "QUIVER",
event_name = "EXPORT_OB_PRN_QUIVER",
num_items = 7, item = 3)
self.drawToggle(
text = "Shield",
val = self.config["EXPORT_OB_PRN"] == "SHIELD",
event_name = "EXPORT_OB_PRN_SHIELD",
num_items = 7, item = 4)
self.drawToggle(
text = "Helm",
val = self.config["EXPORT_OB_PRN"] == "HELM",
event_name = "EXPORT_OB_PRN_HELM",
num_items = 7, item = 5)
self.drawToggle(
text = "Ring",
val = self.config["EXPORT_OB_PRN"] == "RING",
event_name = "EXPORT_OB_PRN_RING",
num_items = 7, item = 6)
self.drawYSep()
# export-only options for morrowind
if (self.target == self.TARGET_EXPORT
and self.config["EXPORT_VERSION"] == "Morrowind"):
self.drawNextColumn()
self.drawToggle(
text = "Export nif + xnif + kf",
event_name = "EXPORT_MW_NIFXNIFKF")
# export-only options for civ4 and rrt
if (self.target == self.TARGET_EXPORT
and (self.config["EXPORT_VERSION"]
in NifImportExport.USED_EXTRA_SHADER_TEXTURES)):
self.drawNextColumn()
self.drawToggle(
text = "Export Extra Shader Textures",
event_name = "EXPORT_EXTRA_SHADER_TEXTURES")
# export-only options for fallout 3
if (self.target == self.TARGET_EXPORT
and self.config["EXPORT_VERSION"] == "Fallout 3"):
self.drawNextColumn()
self.drawLabel(
text = "Shader Options",
event_name = "LABEL_FO3_SHADER_OPTIONS")
self.drawPushButton(
text = "Default",
event_name = "EXPORT_FO3_SHADER_OPTION_DEFAULT",
num_items = 3, item = 0)
self.drawPushButton(
text = "Skin",
event_name = "EXPORT_FO3_SHADER_OPTION_SKIN",
num_items = 3, item = 1)
self.drawPushButton(
text = "Cloth",
event_name = "EXPORT_FO3_SHADER_OPTION_CLOTH",
num_items = 3, item = 2)
self.drawToggle(