NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

paperDollImpl.py (107380B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\paperDoll\paperDollImpl.py
      2 from __future__ import absolute_import
      3 import datetime
      4 import os
      5 import sys
      6 import remotefilecache
      7 import trinity
      8 import blue
      9 import telemetry
     10 import uthread
     11 import log
     12 import yaml
     13 import TextureCompositor.TextureCompositor as tc
     14 import RenderTargetManager.RenderTargetManager as rtm
     15 import legacy_r_drive
     16 import eve.common.script.paperDoll.paperDollDefinitions as pdDef
     17 import eve.common.script.paperDoll.paperDollDataManagement as pdDm
     18 import eve.client.script.paperDoll.commonClientFunctions as pdCcf
     19 import eve.common.script.paperDoll.paperDollCommonFunctions as pdCf
     20 import eve.common.script.paperDoll.paperDollConfiguration as pdCfg
     21 from . import paperDollPortrait as pdPor
     22 from .projectedDecals import ProjectedDecal, DecalBaker
     23 from .SkinLightmapRenderer import SkinLightmapRenderer
     24 from eve.client.script.paperDoll import renderDrivers
     25 from eve.client.script.paperDoll import paperDollStatistics as statistics
     26 TC_MARKERS_ENABLED = False
     27 
     28 def AddMarker(m):
     29     if TC_MARKERS_ENABLED:
     30         trinity.graphs.AddMarker('frameTime', m)
     31 
     32 
     33 RTM = rtm.RenderTargetManager()
     34 COMPRESS_DXT1 = trinity.TR2DXT_COMPRESS_SQUISH_DXT1
     35 COMPRESS_DXT5 = trinity.TR2DXT_COMPRESS_SQUISH_DXT5
     36 COMPRESS_DXT5n = trinity.TR2DXT_COMPRESS_RT_DXT5N
     37 
     38 class UVCalculator(object):
     39     UVs = {pdDef.DOLL_PARTS.HEAD: pdDef.HEAD_UVS,
     40      pdDef.DOLL_PARTS.BODY: pdDef.BODY_UVS,
     41      pdDef.DOLL_PARTS.HAIR: pdDef.HAIR_UVS,
     42      pdDef.DOLL_PARTS.ACCESSORIES: pdDef.ACCE_UVS}
     43 
     44     @staticmethod
     45     def BoxCoversX(x, box):
     46         if x >= box[0] and x < box[2]:
     47             return True
     48         return False
     49 
     50     @staticmethod
     51     def GetHeightAt(x, returnUVs):
     52         height = 0
     53         newX = x
     54         for r in returnUVs:
     55             if UVCalculator.BoxCoversX(x, r):
     56                 if r[3] > height:
     57                     height = r[3]
     58                     newX = r[2]
     59 
     60         return (height, newX)
     61 
     62     @staticmethod
     63     def BoxIntersects(x, y, box, uv):
     64         for vert in ((x, y),
     65          (x + box[0], y),
     66          (x, y + box[1]),
     67          (x + box[0], y + box[1])):
     68             if vert[0] > uv[0] and vert[0] < uv[2] and vert[1] > uv[1] and vert[1] < uv[3]:
     69                 return True
     70 
     71         return False
     72 
     73     @staticmethod
     74     def BoxFits(x, y, box, target, returnUVs):
     75         if x < 0 or x + box[0] > target[0] or y < 0:
     76             return False
     77         for r in returnUVs:
     78             if UVCalculator.BoxIntersects(x, y, box, r):
     79                 return False
     80 
     81         return True
     82 
     83     @staticmethod
     84     def PackTextures(textures, target):
     85         watermark = 0
     86         returnUVs = []
     87         xinc = 0.001953125
     88         for t in textures:
     89             candidates = {}
     90             minInc = 100000
     91             x = 0.0
     92             while x < 1.0:
     93                 currentX = x
     94                 h, x = UVCalculator.GetHeightAt(currentX, returnUVs)
     95                 inc = h + t[1]
     96                 if UVCalculator.BoxFits(currentX, h, t, target, returnUVs):
     97                     if inc < minInc:
     98                         minInc = inc
     99                         candidates[inc] = (currentX, h)
    100                 if x == currentX:
    101                     x += xinc
    102 
    103             if candidates:
    104                 final = candidates[minInc]
    105                 returnUVs.append((final[0],
    106                  final[1],
    107                  final[0] + t[0],
    108                  final[1] + t[1]))
    109                 watermark = final[1] + t[1]
    110 
    111         if watermark > target[1]:
    112             factor = target[1] / watermark
    113             for ix in xrange(len(returnUVs)):
    114                 returnUVs[ix] = [returnUVs[ix][0] * factor,
    115                  returnUVs[ix][1] * factor,
    116                  returnUVs[ix][2] * factor,
    117                  returnUVs[ix][3] * factor]
    118 
    119         return returnUVs
    120 
    121     @staticmethod
    122     def GetMapSize(modifier):
    123         tag = pdDef.ACCESSORIES_CATEGORIES.ACCESSORIES
    124         path = modifier.mapL.get(tag) or modifier.mapD.get(tag)
    125         if path:
    126             param = trinity.TriTextureParameter()
    127             param.resourcePath = path
    128             starttime = datetime.datetime.now()
    129             timeout_t = starttime + datetime.timedelta(seconds=10)
    130             while not (param.resource.isPrepared and param.resource.isGood) and datetime.datetime.now() < timeout_t:
    131                 pdCf.Yield()
    132 
    133             return (param.resource.width, param.resource.height)
    134 
    135     @staticmethod
    136     def CalculateAccessoryUVs(buildDataManager):
    137         accesorieModifiers = buildDataManager.GetModifiersByPart(pdDef.DOLL_PARTS.ACCESSORIES, showHidden=True)
    138         targetBox = (1.0, 1.0)
    139         textures = []
    140         for modifier in iter(accesorieModifiers):
    141             adjustedSize = (0.0, 0.0)
    142             amSize = modifier.accessoryMapSize or UVCalculator.GetMapSize(modifier)
    143             if amSize:
    144                 adjustedSize = [amSize[0] / 512.0, amSize[1] / 512.0]
    145                 if adjustedSize[0] > 0.5:
    146                     adjustedSize[0] = 0.5
    147                 if adjustedSize[1] > 0.5:
    148                     adjustedSize[1] = 0.5
    149             textures.append(adjustedSize)
    150 
    151         result = UVCalculator.PackTextures(textures, targetBox)
    152         for ix in xrange(len(accesorieModifiers)):
    153             mod = accesorieModifiers[ix]
    154             mod.ulUVs = (result[ix][0], result[ix][1])
    155             mod.lrUVs = (result[ix][2], result[ix][3])
    156 
    157     @staticmethod
    158     def ApplyUVs(buildDataManager, renderDriver):
    159         UVCalculator.CalculateAccessoryUVs(buildDataManager)
    160         defaultUVs = dict(UVCalculator.UVs)
    161         modifierGenerator = (modifier for modifier in buildDataManager.GetSortedModifiers() if modifier.weight > 0 and modifier.IsMeshContainingModifier())
    162         for modifier in modifierGenerator:
    163             meshes = list(modifier.meshes)
    164             if modifier.clothData:
    165                 meshes.append(modifier.clothData)
    166             modifierUVCategory = buildDataManager.CategoryToPart(modifier.categorie)
    167             for mesh in iter(meshes):
    168                 uvCategory = modifierUVCategory
    169                 if uvCategory == pdDef.DOLL_PARTS.HAIR and hasattr(mesh, 'decalAreas') and mesh.decalAreas:
    170                     for area in mesh.decalAreas:
    171                         if 'stubble' in area.name.lower():
    172                             uvCategory = pdDef.DOLL_PARTS.HEAD
    173                             break
    174 
    175                 if hasattr(mesh, 'opaqueAreas') and hasattr(mesh, 'decalAreas') and hasattr(mesh, 'transparentAreas'):
    176                     for area in tuple(mesh.opaqueAreas) + tuple(mesh.decalAreas) + tuple(mesh.transparentAreas):
    177                         for category in defaultUVs:
    178                             if category.lower() + 'uv' in area.name.lower():
    179                                 uvCategory = category
    180                                 break
    181 
    182                 fxs = pdCcf.GetEffectsFromMesh(mesh)
    183                 for fx in iter(fxs):
    184                     if fx.effectResource:
    185                         starttime = datetime.datetime.now()
    186                         timeout_t = starttime + datetime.timedelta(seconds=60)
    187                         while not (fx.effectResource.isPrepared and fx.effectResource.isGood) and datetime.datetime.now() < timeout_t:
    188                             pdCf.Yield()
    189 
    190                         if not fx.effectResource.isGood:
    191                             raise ValueError('Paperdoll::Effect resource failed to load.')
    192                     fx.StartUpdate()
    193                     transformUV0 = None
    194                     for effectParameter in iter(fx.parameters):
    195                         if effectParameter.name == 'TransformUV0':
    196                             transformUV0 = effectParameter
    197                             break
    198 
    199                     if not transformUV0:
    200                         transformUV0 = trinity.Tr2Vector4Parameter()
    201                         transformUV0.name = 'TransformUV0'
    202                         fx.parameters.append(transformUV0)
    203                     computedUVs = None
    204                     if uvCategory == pdDef.DOLL_PARTS.ACCESSORIES:
    205                         width = defaultUVs[uvCategory][2] - defaultUVs[uvCategory][0]
    206                         height = defaultUVs[uvCategory][3] - defaultUVs[uvCategory][1]
    207                         computedUVs = (defaultUVs[uvCategory][0] + modifier.ulUVs[0] * width,
    208                          defaultUVs[uvCategory][1] + modifier.ulUVs[1] * height,
    209                          defaultUVs[uvCategory][0] + modifier.lrUVs[0] * width,
    210                          defaultUVs[uvCategory][1] + modifier.lrUVs[1] * height)
    211                     else:
    212                         computedUVs = (defaultUVs[uvCategory][0],
    213                          defaultUVs[uvCategory][1],
    214                          defaultUVs[uvCategory][2],
    215                          defaultUVs[uvCategory][3])
    216                     transformUV0.value = computedUVs
    217                     fx.EndUpdate()
    218 
    219             renderDriver.OnModifierUVChanged(modifier)
    220 
    221 
    222 class UpdateRuleBundle(object):
    223     __metaclass__ = telemetry.ZONE_PER_METHOD
    224 
    225     def __init__(self):
    226         self.forcesLooseTop = False
    227         self.hidesBootShin = False
    228         self.swapTops = False
    229         self.swapBottom = False
    230         self.swapSocks = False
    231         self.undoMaskedShaders = False
    232         self.rebuildHair = False
    233         self.mapsToComposite = list(pdDef.MAPS)
    234         self.blendShapesOnly = False
    235         self.decalsOnly = False
    236         self.doDecals = False
    237         self.doBlendShapes = False
    238         self.doStubble = False
    239         self.meshesChanged = False
    240         self.partsToComposite = []
    241         self.dirtyDecalModifiers = []
    242         self.videoMemoryFailure = False
    243         self.textureLoadingFailure = False
    244 
    245     def __str__(self):
    246         s = '{0}\n'.format(id(self))
    247         for key, value in self.__dict__.iteritems():
    248             s += '\t{0} := {1}\n'.format(key, value)
    249 
    250         return s
    251 
    252     def DiscoverState(self, dirtyModifiers, avatar):
    253         allModifiersBlendShapes = False
    254         someModifiersBlendShapes = False
    255         allModifiersDecals = False
    256         someModifiersDecals = False
    257         for modifier in iter(dirtyModifiers):
    258             isBlendShapeModifier = modifier.IsBlendshapeModifier()
    259             isDecalModifier = not isBlendShapeModifier and modifier.decalData is not None and (modifier.IsDirty or not modifier.IsTextureContainingModifier())
    260             if modifier.stubblePath:
    261                 self.doStubble = True
    262             if isDecalModifier:
    263                 self.dirtyDecalModifiers.append(modifier)
    264             someModifiersBlendShapes = isBlendShapeModifier or someModifiersBlendShapes
    265             allModifiersBlendShapes = isBlendShapeModifier and allModifiersBlendShapes
    266             someModifiersDecals = isDecalModifier or someModifiersDecals
    267             allModifiersDecals = isDecalModifier and allModifiersDecals
    268 
    269         self.blendShapesOnly = allModifiersBlendShapes
    270         self.decalsOnly = allModifiersDecals and avatar is not None
    271         self.doBlendShapes = someModifiersBlendShapes
    272         self.doDecals = someModifiersDecals and avatar is not None
    273 
    274 
    275 class MapBundle(object):
    276     __metaclass__ = telemetry.ZONE_PER_METHOD
    277 
    278     def __init__(self):
    279         self.diffuseMap = None
    280         self.specularMap = None
    281         self.normalMap = None
    282         self.maskMap = None
    283         self.baseResolution = None
    284         self.hashKeys = {}
    285         self.mapResolutions = {pdDef.MASK_MAP: pdCfg.PerformanceOptions.maskMapTextureSize}
    286         self.mapResolutionFactors = {}
    287         self.SetBaseResolution(2048, 1024)
    288 
    289     def __getitem__(self, idx):
    290         if idx == pdDef.DIFFUSE_MAP:
    291             return self.diffuseMap
    292         if idx == pdDef.SPECULAR_MAP:
    293             return self.specularMap
    294         if idx == pdDef.NORMAL_MAP:
    295             return self.normalMap
    296         if idx == pdDef.MASK_MAP:
    297             return self.maskMap
    298         raise AttributeError()
    299 
    300     def SetBaseResolution(self, width, height):
    301         self.baseResolution = (width, height)
    302         for mapType in pdDef.RESIZABLE_MAPS:
    303             factor = self.mapResolutionFactors.get(mapType, pdCfg.PerformanceOptions.textureSizeFactors[mapType])
    304             self.SetMapTypeResolutionFactor(mapType, factor)
    305 
    306     def SetMapByTypeIndex(self, idx, mapToSet, hashKey = None):
    307         if idx == pdDef.DIFFUSE_MAP:
    308             self.diffuseMap = mapToSet
    309         elif idx == pdDef.SPECULAR_MAP:
    310             self.specularMap = mapToSet
    311         elif idx == pdDef.NORMAL_MAP:
    312             self.normalMap = mapToSet
    313         elif idx == pdDef.MASK_MAP:
    314             self.maskMap = mapToSet
    315         else:
    316             raise ValueError('paperDoll::MapBundle::SetMapByTypeIndex - Index provided is not defined or supported. It must be defined in paperDoll.MAPS')
    317         if hashKey:
    318             self.hashKeys[idx] = hashKey
    319 
    320     def GetResolutionByMapType(self, mapType):
    321         return self.mapResolutions.get(mapType, self.baseResolution)
    322 
    323     def SetMapTypeResolutionFactor(self, mapType, factor):
    324         oldFactor = self.mapResolutionFactors.get(mapType, 1)
    325         self.mapResolutionFactors[mapType] = factor
    326         shift = factor - 1
    327         newResolution = (self.baseResolution[0] >> shift, self.baseResolution[1] >> shift)
    328         if newResolution[0] < 16:
    329             self.mapResolutionFactors[mapType] = oldFactor
    330             return
    331         oldResolution = self.mapResolutions.get(mapType, self.baseResolution)
    332         self.mapResolutions[mapType] = newResolution
    333 
    334     def AllMapsGood(self):
    335         for each in iter(self):
    336             if not each or not each.isGood:
    337                 return False
    338 
    339         return True
    340 
    341     def __iter__(self):
    342         yield self.diffuseMap
    343         yield self.specularMap
    344         yield self.normalMap
    345         yield self.maskMap
    346 
    347     def ReCreate(self, includeFixedSizeMaps = True):
    348         del self.diffuseMap
    349         del self.specularMap
    350         del self.normalMap
    351         if includeFixedSizeMaps:
    352             del self.maskMap
    353         self.diffuseMap = None
    354         self.specularMap = None
    355         self.normalMap = None
    356         if includeFixedSizeMaps:
    357             self.maskMap = None
    358         for key in list(self.hashKeys.iterkeys()):
    359             if not self[key]:
    360                 del self.hashKeys[key]
    361 
    362     def __repr__(self):
    363         s = 'Base resolution: w{}\th{}'.format(self.baseResolution[0], self.baseResolution[1])
    364         for mapType in pdDef.MAPS:
    365             if self[mapType]:
    366                 cMap = self[mapType]
    367                 s = '{}\n{}\tw{}\th{}'.format(s, pdDef.MAPNAMES[mapType], cMap.width, cMap.height)
    368 
    369         return s
    370 
    371     def __del__(self):
    372         del self.diffuseMap
    373         del self.specularMap
    374         del self.normalMap
    375         del self.maskMap
    376 
    377 
    378 class CompressionSettings():
    379 
    380     @telemetry.ZONE_METHOD
    381     def __init__(self, compressTextures = True, generateMipmap = False, qualityLevel = None):
    382         self.compressTextures = compressTextures
    383         self.generateMipmap = generateMipmap
    384         self.qualityLevel = qualityLevel or trinity.TR2DXT_COMPRESS_SQ_RANGE_FIT
    385         self.compressNormalMap = True
    386         self.compressSpecularMap = True
    387         self.compressDiffuseMap = True
    388         self.compressMaskMap = True
    389 
    390     @telemetry.ZONE_METHOD
    391     def __repr__(self):
    392         return '%i%i%i%i%i%i' % (self.compressTextures,
    393          self.compressNormalMap,
    394          self.compressSpecularMap,
    395          self.compressDiffuseMap,
    396          self.compressMaskMap,
    397          self.generateMipmap)
    398 
    399     @telemetry.ZONE_METHOD
    400     def SetMapCompression(self, compressNormalMap = True, compressSpecularMap = True, compressDiffuseMap = True, compressMaskMap = True):
    401         self.compressNormalMap = compressNormalMap
    402         self.compressSpecularMap = compressSpecularMap
    403         self.compressDiffuseMap = compressDiffuseMap
    404         self.compressMaskMap = compressMaskMap
    405 
    406     @telemetry.ZONE_METHOD
    407     def AllowCompress(self, textureType):
    408         if textureType == pdDef.DIFFUSE_MAP:
    409             return self.compressDiffuseMap
    410         if textureType == pdDef.SPECULAR_MAP:
    411             return self.compressSpecularMap
    412         if textureType == pdDef.NORMAL_MAP:
    413             return self.compressNormalMap
    414         if textureType == pdDef.MASK_MAP:
    415             return self.compressMaskMap
    416 
    417 
    418 class Factory(object, CompressionSettings, pdDm.ModifierLoader):
    419     shaderPreload = None
    420     texturePreload = None
    421 
    422     def setclothSimulationActive(self, value):
    423         value = False
    424         self._clothSimulationActive = value
    425         pdDm.ModifierLoader.setclothSimulationActive(self, value)
    426 
    427     clothSimulationActive = property(fget=lambda self: self._clothSimulationActive, fset=lambda self, value: self.setclothSimulationActive(value))
    428 
    429     @telemetry.ZONE_METHOD
    430     def __init__(self, gender = None):
    431         object.__init__(self)
    432         CompressionSettings.__init__(self, compressTextures=False)
    433         pdDm.ModifierLoader.__init__(self)
    434         if gender:
    435             log.LogWarn('PaperDoll: Deprication warning, Gender is no longer a property of the Factory.')
    436         self.allowTextureCache = False
    437         self.verbose = False
    438         self.skipCompositing = False
    439         self.HeadsLoaded = False
    440         self.preloadedResources = list()
    441         self.calculatedSubrects = dict()
    442         self.CalculateSubrects()
    443         self.PreloadedGenericHeadModifiers = {pdDef.GENDER.MALE: None,
    444          pdDef.GENDER.FEMALE: None}
    445         self.PreloadShaders()
    446         uthread.new(self.PreloadGenericHeadsOnceLoaded_t)
    447         if pdCfg.PerformanceOptions.preloadNudeAssets:
    448             uthread.new(self.PreloadNudeResources_t)
    449 
    450     def FactoryReady(self):
    451         if self.shaderPreload is None:
    452             return False
    453         if self.texturePreload is None:
    454             return False
    455         if not all([ hasattr(effect.effectResource, 'isGood') for effect in self.shaderPreload ]):
    456             return False
    457         if not all([ hasattr(tex.resource, 'isGood') for tex in self.texturePreload ]):
    458             return False
    459         return self.IsLoaded and self.HeadsLoaded and all([ effect.effectResource.isGood for effect in self.shaderPreload ]) and all([ tex.resource.isGood for tex in self.texturePreload ])
    460 
    461     def CalculateSubrects(self):
    462         for i in range(3, 13):
    463             width = 2 ** i
    464             height = 2 ** (i - 1)
    465             self.calculatedSubrects[pdDef.DOLL_PARTS.HEAD, width] = self.CalcSubRect(pdDef.HEAD_UVS, width, height)
    466             self.calculatedSubrects[pdDef.DOLL_PARTS.HAIR, width] = self.CalcSubRect(pdDef.HAIR_UVS, width, height)
    467             self.calculatedSubrects[pdDef.DOLL_PARTS.BODY, width] = self.CalcSubRect(pdDef.BODY_UVS, width, height)
    468 
    469     def GetSubRect(self, bodypart, w, h, modifier = None, uvs = None):
    470         key = (bodypart, w)
    471         subrect = self.calculatedSubrects.get(key)
    472         if subrect is None:
    473             if not uvs and modifier:
    474                 uvs = modifier.GetUVsForCompositing(bodypart)
    475             if uvs:
    476                 subrect = self.CalcSubRect(uvs, w, h)
    477         return subrect
    478 
    479     @telemetry.ZONE_METHOD
    480     def CalcSubRect(self, inputUV, w, h):
    481         return (int(w * inputUV[0]),
    482          int(h * inputUV[1]),
    483          int(w * inputUV[2]),
    484          int(h * inputUV[3]))
    485 
    486     def PreloadNudeResources_t(self):
    487         while not self.IsLoaded:
    488             pdCf.Yield()
    489 
    490         for genderData in self.resData.genderData.itervalues():
    491             for respath in pdDef.DEFAULT_NUDE_PARTS:
    492                 entry = genderData.GetPathsToEntries()[respath]
    493                 filenames = entry.files
    494                 for filename in filenames:
    495                     if '_4k' in filename:
    496                         continue
    497                     if 'wrinkle' in filename or filename.startswith('skinmap'):
    498                         continue
    499                     if filename.endswith('.png'):
    500                         ddsTest = filename[:-4] + '.dds'
    501                         if ddsTest in filenames:
    502                             continue
    503                     resourcePath = entry.GetFullResPath(filename)
    504                     resource = blue.resMan.GetResource(resourcePath)
    505                     if resource and resource not in self.preloadedResources:
    506                         self.preloadedResources.append(resource)
    507 
    508     def PreloadGenericHeadsOnceLoaded_t(self):
    509         while not self.IsLoaded:
    510             pdCf.Yield()
    511 
    512         self.PreloadedGenericHeadModifiers[pdDef.GENDER.MALE] = self.CollectBuildData(pdDef.GENDER.MALE, 'head/head_generic')
    513         self.PreloadedGenericHeadModifiers[pdDef.GENDER.FEMALE] = self.CollectBuildData(pdDef.GENDER.FEMALE, 'head/head_generic')
    514         self.HeadsLoaded = True
    515 
    516     @staticmethod
    517     @telemetry.ZONE_FUNCTION
    518     def PreloadShaders():
    519         if Factory.shaderPreload is not None:
    520             return
    521         Factory.shaderPreload = []
    522         for path in pdDef.EFFECT_PRELOAD_PATHS:
    523             effect = trinity.Tr2Effect()
    524             effect.effectFilePath = 'res:/graphics/effect/managed/interior/avatar/{0}'.format(path)
    525             Factory.shaderPreload.append(effect)
    526 
    527         for path in tc.COMPOSITE_SHADER_PATHS:
    528             effect = trinity.Tr2Effect()
    529             effect.effectFilePath = '{0}/{1}'.format(tc.EFFECT_LOCATION, path)
    530             Factory.shaderPreload.append(effect)
    531 
    532         Factory.texturePreload = []
    533         for path in pdDef.TEXTURE_PRELOAD_PATHS:
    534             texture = trinity.TriTextureParameter()
    535             texture.resourcePath = path
    536             Factory.texturePreload.append(texture)
    537 
    538     @staticmethod
    539     @telemetry.ZONE_FUNCTION
    540     def ApplyMorphTargetsToMeshes(meshes, morphTargets, blendShapeMeshCache = None, meshGeometryResPaths = None):
    541         blendShapeMeshCache = blendShapeMeshCache if blendShapeMeshCache is not None else {}
    542         loadingGrannyResources = {}
    543         meshGenerator = (mesh for mesh in meshes if not (blendShapeMeshCache.get(mesh.name) and blendShapeMeshCache.get(mesh.name)[1].isGood))
    544         for mesh in meshGenerator:
    545             index = mesh.meshIndex
    546             path = mesh.geometryResPath
    547             if not path and meshGeometryResPaths is not None:
    548                 path = meshGeometryResPaths.get(mesh.name, None)
    549                 if not path and mesh.name[-1].isdigit():
    550                     for meshName in meshGeometryResPaths:
    551                         if meshName.startswith(mesh.name[:-1]):
    552                             path = meshGeometryResPaths.get(meshName, None)
    553                             break
    554 
    555                     if not path and mesh.name[-2].isdigit:
    556                         for meshName in meshGeometryResPaths:
    557                             if meshName.startswith(mesh.name[:-2]):
    558                                 path = meshGeometryResPaths.get(meshName, None)
    559                                 break
    560 
    561             if not path:
    562                 continue
    563             gr2 = path
    564             grannyResource = blue.resMan.GetResource(gr2, 'raw')
    565             loadingGrannyResources[mesh.name] = grannyResource
    566 
    567         doYield = True
    568         while doYield:
    569             doYield = False
    570             for lgr in loadingGrannyResources.itervalues():
    571                 if not lgr.isPrepared:
    572                     doYield = True
    573                     break
    574 
    575             if doYield:
    576                 pdCf.Yield(frameNice=False)
    577 
    578         if loadingGrannyResources:
    579             meshCount = len(meshes)
    580             for mIdx in xrange(meshCount):
    581                 mesh = meshes[mIdx]
    582                 grannyResource = loadingGrannyResources.get(mesh.name)
    583                 index = mesh.meshIndex
    584                 if grannyResource and grannyResource.isGood and grannyResource.meshCount > index:
    585                     geometryRes = grannyResource.CreateGeometryRes()
    586                     geometryRes.name = 'PaperDoll: {0} morphed'.format(mesh.name)
    587                     morphNames = grannyResource.GetAllMeshMorphNamesNoDigits(index)
    588                     blendShapeMeshCache[mesh.name] = (grannyResource, geometryRes, morphNames)
    589                 elif loadingGrannyResources.get(mesh.name):
    590                     del loadingGrannyResources[mesh.name]
    591 
    592         meshGenerator = (mesh for mesh in meshes if blendShapeMeshCache.get(mesh.name))
    593         for mesh in meshGenerator:
    594             grannyResource, geometryRes, morphNames = blendShapeMeshCache[mesh.name]
    595             weights = [ morphTargets.get(name, 0.0) for name in morphNames ]
    596             isValid = False
    597             if len(weights) > 0:
    598                 try:
    599                     grannyResource.BakeBlendshape(mesh.meshIndex, weights, geometryRes)
    600                     isValid = True
    601                 except RuntimeError:
    602                     pass
    603 
    604                 if isValid:
    605                     if mesh.geometry != geometryRes:
    606                         mesh.SetGeometryRes(geometryRes)
    607             if not isValid:
    608                 del blendShapeMeshCache[mesh.name]
    609 
    610     @staticmethod
    611     @telemetry.ZONE_FUNCTION
    612     def ProcessBlendshapesForCloth(clothMeshes, morphTargets, clothMorphValues):
    613         clothMorphValues = clothMorphValues if clothMorphValues is not None else {}
    614         for clothActor in clothMeshes:
    615             if clothActor.morphRes is None:
    616                 clothActor.morphRes = blue.resMan.GetResource(clothActor.resPath.lower().replace('.apb', '_bs.gr2'), 'raw')
    617 
    618         pdCf.WaitForAll(clothMeshes, lambda x: not x.morphRes.isPrepared)
    619         for mesh in clothMeshes:
    620             if not mesh.clothingRes.isGood:
    621                 raise ValueError('Paperdoll::Clothing mesh failed to load.')
    622 
    623         for clothActor in clothMeshes:
    624             if clothActor.morphRes.meshCount > 0:
    625                 count = clothActor.morphRes.GetMeshMorphCount(clothActor.morphResMeshIndex)
    626                 morphNames = []
    627                 for i in xrange(count):
    628                     name = clothActor.morphRes.GetMeshMorphName(clothActor.morphResMeshIndex, i)
    629                     morphNames.append(pdCcf.StripDigits(name))
    630 
    631                 weights = [ morphTargets.get(name, 0.0) for name in morphNames ]
    632                 key = str(clothActor)
    633                 if key not in clothMorphValues or weights != clothMorphValues[key]:
    634                     clothMorphValues[key] = weights
    635                     clothActor.SetMorphResWeights(weights)
    636 
    637     @staticmethod
    638     @telemetry.ZONE_FUNCTION
    639     def BindMapsToMeshes(meshes, mapBundle):
    640         colorNdotLLookupMap = 'ColorNdotLLookupMap'
    641         fresnelLookupMap = 'FresnelLookupMap'
    642         ndotLLibrary = 'res:/Texture/Global/NdotLLibrary.png'
    643         for mesh in iter(meshes):
    644             effects = pdCcf.GetEffectsFromMesh(mesh, allowShaderMaterial=False)
    645             for fx in (effect for effect in effects if effect is not None):
    646                 fx.StartUpdate()
    647                 try:
    648                     for r in iter(fx.resources):
    649                         if mapBundle.diffuseMap and r.name == pdDef.MAPNAMES[pdDef.DIFFUSE_MAP]:
    650                             Factory.SetResourceTexture(r, mapBundle.diffuseMap)
    651                         elif mapBundle.specularMap and r.name == pdDef.MAPNAMES[pdDef.SPECULAR_MAP]:
    652                             Factory.SetResourceTexture(r, mapBundle.specularMap)
    653                         elif mapBundle.normalMap and r.name == pdDef.MAPNAMES[pdDef.NORMAL_MAP]:
    654                             Factory.SetResourceTexture(r, mapBundle.normalMap)
    655                         elif mapBundle.maskMap and r.name == pdDef.MAPNAMES[pdDef.MASK_MAP]:
    656                             Factory.SetResourceTexture(r, mapBundle.maskMap)
    657 
    658                     ndotLres = None
    659                     fresnelLookupMapRes = None
    660                     for r in iter(fx.resources):
    661                         if r.name == colorNdotLLookupMap:
    662                             ndotLres = r
    663                         elif r.name == fresnelLookupMap:
    664                             fresnelLookupMapRes = r
    665 
    666                     if ndotLres is None:
    667                         res = trinity.TriTextureParameter()
    668                         res.name = colorNdotLLookupMap
    669                         res.resourcePath = ndotLLibrary
    670                         fx.resources.append(res)
    671                     else:
    672                         ndotLres.resourcePath = ndotLLibrary
    673                     if fresnelLookupMapRes is None:
    674                         res = trinity.TriTextureParameter()
    675                         res.name = fresnelLookupMap
    676                         res.resourcePath = pdDef.FRESNEL_LOOKUP_MAP
    677                         fx.resources.append(res)
    678                     else:
    679                         fresnelLookupMapRes.resourcePath = pdDef.FRESNEL_LOOKUP_MAP
    680                 finally:
    681                     fx.EndUpdate()
    682 
    683     @telemetry.ZONE_METHOD
    684     def _CreateBiped(self, avatar, geometryRes, name = 'FactoryCreated'):
    685         biped = trinity.Tr2SkinnedModel()
    686         biped.skinScale = (1.0, 1.0, 1.0)
    687         biped.name = name
    688         if geometryRes:
    689             biped.geometryResPath = geometryRes
    690         biped.skeletonName = 'Root'
    691         avatar.visualModel = biped
    692         avatar.scaling = (1.0, 1.0, 1.0)
    693         return avatar
    694 
    695     @telemetry.ZONE_METHOD
    696     def CreateVisualModel(self, gender = pdDef.GENDER.FEMALE, geometryRes = None):
    697         biped = trinity.Tr2SkinnedModel()
    698         if not geometryRes:
    699             if gender == pdDef.GENDER.FEMALE:
    700                 geometryRes = pdCcf.INTERIOR_FEMALE_GEOMETRY_RESPATH
    701             else:
    702                 geometryRes = pdCcf.INTERIOR_MALE_GEOMETRY_RESPATH
    703         biped.geometryResPath = geometryRes
    704         biped.name = gender
    705         biped.skeletonName = 'Root'
    706         return biped
    707 
    708     @telemetry.ZONE_METHOD
    709     def CreateAvatar(self, geometryRes, name = 'FactoryCreated'):
    710         avatar = trinity.WodExtSkinnedObject()
    711         avatar = self._CreateBiped(avatar, geometryRes, name)
    712         return avatar
    713 
    714     @telemetry.ZONE_METHOD
    715     def CreateInteriorAvatar(self, geometryRes, name = 'FactoryCreated'):
    716         avatar = trinity.Tr2IntSkinnedObject()
    717         avatar = self._CreateBiped(avatar, geometryRes, name)
    718         return avatar
    719 
    720     @telemetry.ZONE_METHOD
    721     def RemoveAvatarFromScene(self, avatar, scene):
    722         if avatar is None or scene is None:
    723             return
    724         try:
    725             scene.RemoveDynamic(avatar)
    726         except AttributeError:
    727             scene.Avatar = None
    728             sys.exc_clear()
    729 
    730     @telemetry.ZONE_METHOD
    731     def AddAvatarToScene(self, avatar, scene = None):
    732         if avatar is None:
    733             raise AttributeError('No avatar passed to Factory::AddAvatarToScene')
    734         if scene is not None:
    735             if hasattr(scene, 'Avatar'):
    736                 scene.Avatar = avatar
    737                 return
    738             scene.AddDynamic(avatar)
    739             return
    740         dev = trinity.device
    741         if dev.scene:
    742             dev.scene.AddDynamic(avatar)
    743         else:
    744             dev.scene = trinity.Tr2InteriorScene()
    745             dev.scene.AddDynamic(avatar)
    746 
    747     @telemetry.ZONE_METHOD
    748     def CreateGrannyAnimation(self, avatar, grannyFile):
    749         import trinity
    750         remotefilecache.prefetch_folder(os.path.dirname(grannyFile))
    751         animation = trinity.Tr2GrannyAnimation()
    752         animation.resPath = grannyFile
    753         trinity.WaitForResourceLoads()
    754         animation.animationEnabled = True
    755         clip = animation.grannyRes.GetAnimationName(0)
    756         animation.PlayAnimationEx(clip, 0, 0, 1)
    757         avatar.animationUpdater = animation
    758         return animation
    759 
    760     def RebindAnimations(self, avatar, visualModel):
    761         if avatar:
    762             avatar.ResetAnimationBindings()
    763         elif visualModel:
    764             visualModel.ResetAnimationBindings()
    765 
    766     @telemetry.ZONE_METHOD
    767     def CreateAnimationOffsets(self, avatar, doll):
    768         if doll.boneOffsets:
    769             setOffset = None
    770             if avatar.animationUpdater and getattr(avatar.animationUpdater, 'network', None):
    771                 setOffset = avatar.animationUpdater.network.boneOffset.SetOffset
    772             elif avatar.animationUpdater and getattr(avatar.animationUpdater, 'boneOffset', None):
    773                 setOffset = avatar.animationUpdater.boneOffset.SetOffset
    774             if setOffset:
    775                 for bone in doll.boneOffsets.iterkeys():
    776                     trans = doll.boneOffsets[bone][pdCcf.TRANSLATION]
    777                     setOffset(bone, *trans)
    778 
    779     @telemetry.ZONE_METHOD
    780     def CompositeStepsFunction(self, buildDataManager, gender, mapType, partsToComposite, comp, w, h, lod = None):
    781         genericHeadMod = self.PreloadedGenericHeadModifiers.get(gender)
    782         modifierList = [ modifier for modifier in buildDataManager.GetSortedModifiers() if modifier.IsTextureContainingModifier() and modifier.weight > 0 and modifier.categorie != pdDef.HEAD_CATEGORIES.HEAD ]
    783         if mapType == pdDef.DIFFUSE_MAP:
    784             genTex = genericHeadMod.mapD
    785         elif mapType == pdDef.SPECULAR_MAP:
    786             genTex = genericHeadMod.mapSRG
    787         elif mapType == pdDef.NORMAL_MAP:
    788             genTex = genericHeadMod.mapN
    789         elif mapType == pdDef.MASK_MAP:
    790             genTex = genericHeadMod.mapMask
    791         isNormalMap = mapType == pdDef.NORMAL_MAP
    792         if not partsToComposite or pdDef.DOLL_PARTS.BODY in partsToComposite:
    793             pdCf.BeFrameNice()
    794             baseBodyTexture = genTex.get(pdDef.DOLL_PARTS.BODY, '')
    795             if baseBodyTexture:
    796                 comp.CopyBlitTexture(baseBodyTexture, self.GetSubRect(pdDef.DOLL_PARTS.BODY, w, h), isNormalMap=isNormalMap, alphaMultiplier=0.0 if isNormalMap else 1.0)
    797             self._CompositeTexture(modifierList, pdDef.DOLL_PARTS.BODY, comp, mapType, w, h, addAlpha=True, lod=lod)
    798         if not partsToComposite or pdDef.DOLL_PARTS.HEAD in partsToComposite or pdDef.DOLL_PARTS.HAIR in partsToComposite:
    799             pdCf.BeFrameNice()
    800             baseHeadTexture = genTex.get(pdDef.DOLL_PARTS.HEAD, '')
    801             if baseHeadTexture:
    802                 comp.CopyBlitTexture(baseHeadTexture, self.GetSubRect(pdDef.DOLL_PARTS.HEAD, w, h), isNormalMap=isNormalMap, alphaMultiplier=0.0 if isNormalMap else 1.0)
    803             self._CompositeTexture(modifierList, pdDef.DOLL_PARTS.HEAD, comp, mapType, w, h, addAlpha=False, lod=lod)
    804         if not partsToComposite or pdDef.DOLL_PARTS.HAIR in partsToComposite:
    805             pdCf.BeFrameNice()
    806             self._CompositeTexture(modifierList, pdDef.DOLL_PARTS.HAIR, comp, mapType, w, h, addAlpha=False, lod=lod)
    807         if not partsToComposite or pdDef.DOLL_PARTS.ACCESSORIES in partsToComposite:
    808             pdCf.BeFrameNice()
    809             self._CompositeTexture(modifierList, pdDef.DOLL_PARTS.ACCESSORIES, comp, mapType, w, h, addAlpha=False, lod=lod)
    810 
    811     @telemetry.ZONE_METHOD
    812     def CompositeCombinedTexture(self, mapType, gender, buildDataManager, mapBundle, textureCompositor, compressionSettings = None, partsToComposite = None, copyToExistingTexture = False, lod = None):
    813         renderTarget = None
    814         try:
    815             if compressionSettings is None:
    816                 compressionSettings = self
    817             w, h = mapBundle.GetResolutionByMapType(mapType)
    818             textureFormat = trinity.PIXEL_FORMAT.B8G8R8A8_UNORM
    819             renderTarget = RTM.GetRenderTarget(textureFormat, w, h, locked=True)
    820             w = renderTarget.width
    821             h = renderTarget.height
    822             textureCompositor.renderTarget = renderTarget
    823             textureCompositor.targetWidth = w
    824             while not self.FactoryReady():
    825                 pdCf.Yield()
    826 
    827             textureCompositor.Start(clear=False)
    828             if mapBundle[mapType]:
    829                 textureCompositor.CopyBlitTexture(mapBundle[mapType])
    830             self.CompositeStepsFunction(buildDataManager, gender, mapType, partsToComposite, textureCompositor, w, h, lod=lod)
    831             if copyToExistingTexture and mapBundle[mapType] and mapBundle[mapType].isGood:
    832                 tex = textureCompositor.Finalize(textureToCopyTo=mapBundle[mapType], compressionSettings=compressionSettings, mapType=mapType)
    833             else:
    834                 tex = textureCompositor.Finalize(compressionSettings=compressionSettings, mapType=mapType)
    835             textureCompositor.renderTarget = None
    836             return tex
    837         except (trinity.ALOutOfMemoryError, tc.TextureLoadingError):
    838             raise
    839         except TaskletExit:
    840             RTM.ReturnLockedRenderTarget(renderTarget)
    841             if textureCompositor:
    842                 textureCompositor.renderTarget = None
    843             raise
    844         finally:
    845             RTM.ReturnLockedRenderTarget(renderTarget)
    846 
    847     @telemetry.ZONE_METHOD
    848     def _CompositeDiffuseMap(self, modifierList, bodyPart, textureCompositor, width, height, addAlpha = False, lod = 0):
    849         for m in iter(modifierList):
    850             pdCf.BeFrameNice()
    851             if m.stubblePath and lod in [pdDef.LOD_SKIN, pdDef.LOD_SCATTER_SKIN]:
    852                 continue
    853             subrect = self.GetSubRect(bodyPart, width, height, m)
    854             srcRect = None
    855             texture = m.mapD
    856             skipAlpha = False
    857             useAlphaTest = False
    858             if m.colorize and bodyPart in m.mapL:
    859                 if bodyPart not in m.mapO:
    860                     m.mapO[bodyPart] = pdDef.TEXTURE_STUB
    861                 if bodyPart not in m.mapZ:
    862                     m.mapZ[bodyPart] = pdDef.TEXTURE_STUB
    863                 if m.categorie in (pdDef.BODY_CATEGORIES.SKINTONE,
    864                  pdDef.BODY_CATEGORIES.SKINTYPE,
    865                  pdDef.HEAD_CATEGORIES.MAKEUP,
    866                  pdDef.BODY_CATEGORIES.TATTOO,
    867                  pdDef.BODY_CATEGORIES.SCARS,
    868                  pdDef.DOLL_EXTRA_PARTS.BODYSHAPES,
    869                  pdDef.HEAD_CATEGORIES.FACEMODIFIERS):
    870                     if pdDef.MAKEUP_GROUPS.IMPLANTS in m.group:
    871                         skipAlpha = False
    872                         addAlpha = True
    873                     elif pdDef.MAKEUP_GROUPS.EYELASHES in m.group or pdDef.MAKEUP_GROUPS.EYEBROWS in m.group:
    874                         skipAlpha = False
    875                         addAlpha = True
    876                         useAlphaTest = True
    877                     else:
    878                         skipAlpha = True
    879                         addAlpha = True
    880                 if m.categorie == pdDef.DOLL_PARTS.HAIR and bodyPart == pdDef.DOLL_PARTS.HEAD:
    881                     skipAlpha = False
    882                     addAlpha = True
    883                 if m.pattern == '':
    884                     colors = m.colorizeData
    885                     if bodyPart == pdDef.DOLL_PARTS.HAIR:
    886                         textureCompositor.ColorizedCopyBlitTexture(m.mapL[bodyPart], m.mapZ[bodyPart], m.mapO[bodyPart], colors[0], colors[1], colors[2], subrect)
    887                     else:
    888                         textureCompositor.ColorizedBlitTexture(m.mapL[bodyPart], m.mapZ[bodyPart], m.mapO[bodyPart], colors[0], colors[1], colors[2], subrect=subrect, addAlpha=addAlpha, skipAlpha=skipAlpha, useAlphaTest=useAlphaTest, weight=m.weight)
    889                 else:
    890                     colors = m.patternData
    891                     patternDir = self.GetPatternDir()
    892                     patternPath = '{0}/{1}_z.dds'.format(patternDir, m.pattern)
    893                     if legacy_r_drive.loadFromContent:
    894                         if not os.path.exists(pdDef.OUTSOURCING_JESSICA_PATH):
    895                             patternPath = patternPath.replace('.dds', '.tga')
    896                     textureCompositor.PatternBlitTexture(patternPath, m.mapL[bodyPart], m.mapZ[bodyPart], m.mapO[bodyPart], colors[0], colors[1], colors[2], colors[3], colors[4], subrect, patternTransform=colors[5], patternRotation=colors[6], addAlpha=addAlpha, skipAlpha=skipAlpha)
    897             if bodyPart in texture and not (m.colorize and m.categorie == pdDef.BODY_CATEGORIES.TATTOO):
    898                 maskname = tname = texture[bodyPart]
    899                 if not (isinstance(tname, basestring) and pdDef.SKIN_GENERIC_PATH in tname.lower()):
    900                     if bodyPart in texture:
    901                         maskname = texture[bodyPart]
    902                     elif bodyPart in m.mapL:
    903                         maskname = m.mapL[bodyPart]
    904                     skipAlpha = False
    905                     if m.categorie in (pdDef.BODY_CATEGORIES.SKINTONE,
    906                      pdDef.BODY_CATEGORIES.SKINTYPE,
    907                      pdDef.HEAD_CATEGORIES.MAKEUP,
    908                      pdDef.BODY_CATEGORIES.TATTOO,
    909                      pdDef.BODY_CATEGORIES.SCARS,
    910                      pdDef.DOLL_EXTRA_PARTS.BODYSHAPES,
    911                      pdDef.HEAD_CATEGORIES.ARCHETYPES,
    912                      pdDef.HEAD_CATEGORIES.FACEMODIFIERS):
    913                         if pdDef.MAKEUP_GROUPS.EYELASHES in m.group:
    914                             skipAlpha = False
    915                             addAlpha = False
    916                         else:
    917                             skipAlpha = True
    918                             addAlpha = True
    919                     if m.categorie == pdDef.DOLL_PARTS.HAIR and bodyPart == pdDef.DOLL_PARTS.HEAD:
    920                         skipAlpha = False
    921                         addAlpha = True
    922                     if bodyPart == pdDef.DOLL_PARTS.HAIR:
    923                         textureCompositor.CopyBlitTexture(tname, subrect=subrect, srcRect=srcRect)
    924                     else:
    925                         textureCompositor.BlitTexture(tname, maskname, m.weight, subrect=subrect, addAlpha=addAlpha, skipAlpha=skipAlpha, srcRect=srcRect, multAlpha=False)
    926 
    927     @telemetry.ZONE_METHOD
    928     def _CompositeSpecularMap(self, modifierList, bodyPart, textureCompositor, width, height, addAlpha = False, lod = 0):
    929         for m in iter(modifierList):
    930             pdCf.BeFrameNice()
    931             if m.stubblePath and lod in [pdDef.LOD_SKIN, pdDef.LOD_SCATTER_SKIN]:
    932                 continue
    933             subrect = self.GetSubRect(bodyPart, width, height, m)
    934             srcRect = None
    935             skipAlpha = False
    936             maskname = tname = m.mapSRG.get(bodyPart)
    937             if bodyPart in m.mapSRG and not (m.categorie == pdDef.BODY_CATEGORIES.TATTOO or isinstance(tname, basestring) and pdDef.SKIN_GENERIC_PATH in tname.lower()):
    938                 if bodyPart in m.mapD:
    939                     maskname = m.mapD[bodyPart]
    940                 elif bodyPart in m.mapL:
    941                     maskname = m.mapL[bodyPart]
    942                 skipAlpha = True
    943                 addAlpha = True
    944                 if m.useSkin:
    945                     tname = tname.replace('_S.', '_SR.')
    946                 doColorizedStep = False
    947                 if m.colorize and m.specularColorData:
    948                     doColorizedStep = bodyPart in m.mapL
    949                     if bodyPart == pdDef.DOLL_PARTS.HAIR:
    950                         textureCompositor.CopyBlitTexture(tname, subrect=subrect, srcRect=srcRect)
    951                         doColorizedStep = True
    952                     if doColorizedStep:
    953                         if bodyPart not in m.mapO:
    954                             m.mapO[bodyPart] = pdDef.TEXTURE_STUB
    955                         if bodyPart not in m.mapZ:
    956                             m.mapZ[bodyPart] = pdDef.TEXTURE_STUB
    957                         colors = m.specularColorData
    958                         textureCompositor.ColorizedBlitTexture(m.mapSRG[bodyPart], m.mapZ[bodyPart], m.mapO[bodyPart], colors[0], colors[1], colors[2], mask=maskname, subrect=subrect, addAlpha=addAlpha, skipAlpha=skipAlpha, weight=m.weight)
    959                 if not doColorizedStep:
    960                     textureCompositor.BlitTexture(tname, maskname, m.weight, subrect=subrect, addAlpha=addAlpha, skipAlpha=skipAlpha, srcRect=srcRect, multAlpha=False)
    961                 if m.colorize and m.specularColorData and bodyPart in m.mapZ:
    962                     values = (m.specularColorData[0][3],
    963                      m.specularColorData[1][3],
    964                      m.specularColorData[2][3],
    965                      1.0)
    966                     textureCompositor.BlitAlphaIntoAlphaWithMaskAndZones(tname, maskname, m.mapZ[bodyPart], values, subrect=subrect, srcRect=srcRect)
    967                 else:
    968                     textureCompositor.BlitAlphaIntoAlphaWithMask(tname, maskname, subrect=subrect, srcRect=srcRect)
    969 
    970     @telemetry.ZONE_METHOD
    971     def _CompositeNormalMap(self, modifierList, bodyPart, textureCompositor, width, height, addAlpha = False, lod = None):
    972         for m in iter(modifierList):
    973             pdCf.BeFrameNice()
    974             if m.categorie == pdDef.DOLL_EXTRA_PARTS.BODYSHAPES and lod > pdDef.LOD_0:
    975                 continue
    976             texture = m.mapN
    977             subrect = self.GetSubRect(bodyPart, width, height, m)
    978             srcRect = None
    979             skipAlpha = False
    980             if bodyPart in texture and m.respath != 'skin/generic':
    981                 maskname = tname = texture[bodyPart]
    982                 if bodyPart in m.mapD:
    983                     maskname = m.mapD[bodyPart]
    984                 elif bodyPart in m.mapL:
    985                     maskname = m.mapL[bodyPart]
    986                 skipAlpha = False
    987                 if m.categorie in (pdDef.BODY_CATEGORIES.SKINTONE,
    988                  pdDef.BODY_CATEGORIES.SKINTYPE,
    989                  pdDef.HEAD_CATEGORIES.MAKEUP,
    990                  pdDef.BODY_CATEGORIES.TATTOO,
    991                  pdDef.BODY_CATEGORIES.SCARS,
    992                  pdDef.DOLL_EXTRA_PARTS.BODYSHAPES,
    993                  pdDef.HEAD_CATEGORIES.ARCHETYPES,
    994                  pdDef.HEAD_CATEGORIES.FACEMODIFIERS):
    995                     skipAlpha = True
    996                     addAlpha = True
    997                 elif m.categorie == pdDef.DOLL_PARTS.HAIR and bodyPart == pdDef.DOLL_PARTS.HEAD:
    998                     skipAlpha = False
    999                     addAlpha = True
   1000                 if bodyPart == pdDef.DOLL_PARTS.HAIR:
   1001                     textureCompositor.CopyBlitTexture(tname, subrect=subrect, srcRect=srcRect, isNormalMap=True)
   1002                 else:
   1003                     textureCompositor.BlitTexture(tname, maskname, m.weight, subrect=subrect, addAlpha=addAlpha, skipAlpha=skipAlpha, srcRect=srcRect, multAlpha=False, isNormalMap=True)
   1004             mapD = m.mapD.get(bodyPart)
   1005             mapMN = m.mapMN.get(bodyPart)
   1006             mapTN = m.mapTN.get(bodyPart)
   1007             if mapMN:
   1008                 textureCompositor.MaskedNormalBlitTexture(mapMN, m.weight, subrect, srcRect=srcRect)
   1009             if mapTN:
   1010                 textureCompositor.TwistNormalBlitTexture(mapTN, m.weight, subrect, srcRect=srcRect)
   1011             if mapD and m.categorie not in (pdDef.BODY_CATEGORIES.SKINTONE,
   1012              pdDef.BODY_CATEGORIES.SKINTYPE,
   1013              pdDef.BODY_CATEGORIES.TATTOO,
   1014              pdDef.BODY_CATEGORIES.SCARS,
   1015              pdDef.DOLL_EXTRA_PARTS.BODYSHAPES,
   1016              pdDef.HEAD_CATEGORIES.ARCHETYPES,
   1017              pdDef.HEAD_CATEGORIES.FACEMODIFIERS):
   1018                 if m.categorie in pdDef.CATEGORIES_THATCLEAN_MATERIALMAP:
   1019                     textureCompositor.SubtractAlphaFromAlpha(mapD, subrect, srcRect=srcRect)
   1020                 mapMM = m.mapMaterial.get(bodyPart)
   1021                 if mapMM is not None:
   1022                     textureCompositor.BlitTextureIntoAlphaWithMask(mapMM, mapD, subrect, srcRect=srcRect)
   1023                 else:
   1024                     textureCompositor.BlitTextureIntoAlphaWithMask(mapD, mapD, subrect, srcRect=srcRect)
   1025 
   1026     @telemetry.ZONE_METHOD
   1027     def _CompositeMaskMap(self, modifierList, bodyPart, textureCompositor, width, height, addAlpha = False, lod = None):
   1028         for m in iter(modifierList):
   1029             pdCf.BeFrameNice()
   1030             texture = m.mapMask
   1031             subrect = self.GetSubRect(bodyPart, width, height, m)
   1032             srcRect = None
   1033             skipAlpha = False
   1034             if bodyPart in texture:
   1035                 maskname = tname = texture[bodyPart]
   1036                 tname = texture[bodyPart].lower()
   1037                 textureCompositor.BlitTexture(tname, maskname, m.weight, subrect=subrect, addAlpha=addAlpha, skipAlpha=skipAlpha, srcRect=srcRect, multAlpha=False)
   1038                 if not (isinstance(tname, basestring) and pdDef.SKIN_GENERIC_PATH in tname):
   1039                     skipAlpha = False
   1040                     if m.categorie in (pdDef.BODY_CATEGORIES.SKINTONE,
   1041                      pdDef.BODY_CATEGORIES.SKINTYPE,
   1042                      pdDef.HEAD_CATEGORIES.MAKEUP,
   1043                      pdDef.BODY_CATEGORIES.TATTOO,
   1044                      pdDef.BODY_CATEGORIES.SCARS,
   1045                      pdDef.DOLL_EXTRA_PARTS.BODYSHAPES,
   1046                      pdDef.HEAD_CATEGORIES.ARCHETYPES,
   1047                      pdDef.HEAD_CATEGORIES.FACEMODIFIERS):
   1048                         skipAlpha = True
   1049                         addAlpha = True
   1050                     if m.categorie == pdDef.DOLL_PARTS.HAIR and bodyPart == pdDef.DOLL_PARTS.HEAD:
   1051                         skipAlpha = False
   1052                         addAlpha = True
   1053                     textureCompositor.BlitTexture(tname, maskname, m.weight, subrect=subrect, addAlpha=addAlpha, skipAlpha=skipAlpha, srcRect=srcRect, multAlpha=False)
   1054 
   1055     @telemetry.ZONE_METHOD
   1056     def _CompositeTexture(self, modifierList, bodyPart, textureCompositor, textureType, width, height, addAlpha = False, lod = None):
   1057         partSpecificModifierGenerator = (m for m in modifierList if bodyPart in m.GetAffectedTextureParts())
   1058         if textureType == pdDef.DIFFUSE_MAP:
   1059             return self._CompositeDiffuseMap(partSpecificModifierGenerator, bodyPart, textureCompositor, width, height, addAlpha, lod)
   1060         if textureType == pdDef.SPECULAR_MAP:
   1061             return self._CompositeSpecularMap(partSpecificModifierGenerator, bodyPart, textureCompositor, width, height, addAlpha, lod)
   1062         if textureType == pdDef.NORMAL_MAP:
   1063             return self._CompositeNormalMap(partSpecificModifierGenerator, bodyPart, textureCompositor, width, height, addAlpha, lod)
   1064         if textureType == pdDef.MASK_MAP:
   1065             return self._CompositeMaskMap(partSpecificModifierGenerator, bodyPart, textureCompositor, width, height, addAlpha, lod)
   1066         raise ValueError('Paperdoll::Unsupported texturetype passed to compositing')
   1067 
   1068     @staticmethod
   1069     @telemetry.ZONE_FUNCTION
   1070     def SetResourceTexture(effectResource, texture):
   1071         if isinstance(texture, str):
   1072             if effectResource.resourcePath != texture:
   1073                 effectResource.resourcePath = texture
   1074         else:
   1075             effectResource.resourcePath = ''
   1076             effectResource.SetResource(texture)
   1077 
   1078     @telemetry.ZONE_METHOD
   1079     def BuildMeshForAvatar(self, avatar, avatarName, buildSources, createDynamicLOD = False, bodyShapesActive = {}, overrideLod = 0, weldthreshold = 1e-09, meshName = 'mesh', addMesh = False, asyncLOD = False):
   1080         builder = trinity.WodAvatar2Builder()
   1081         avatarName = str(avatarName)
   1082         if asyncLOD:
   1083             builder.createGPUMesh = True
   1084             builder.effectPath = 'res:/Graphics/Effect/Managed/Interior/Avatar/SkinnedAvatarBRDFDouble.fx'
   1085             if overrideLod == 2:
   1086                 weldthreshold = 0
   1087         for each in iter(buildSources):
   1088             builder.sourceMeshesInfo.append(each)
   1089 
   1090         realBsNames = {}
   1091         realBsNames['fatshape'] = 'FatShape'
   1092         realBsNames['thinshape'] = 'ThinShape'
   1093         realBsNames['muscularshape'] = 'MuscularShape'
   1094         realBsNames['softshape'] = 'SoftShape'
   1095         if bodyShapesActive:
   1096             for bs in bodyShapesActive:
   1097                 for suffix in ['',
   1098                  '1',
   1099                  '2',
   1100                  '3',
   1101                  '4',
   1102                  '5',
   1103                  '6']:
   1104                     blendshape1 = trinity.WodAvatar2BuilderBlend()
   1105                     if bs in realBsNames:
   1106                         blendshape1.name = realBsNames[bs] + suffix
   1107                     else:
   1108                         blendshape1.name = bs + suffix
   1109                     blendshape1.power = bodyShapesActive[bs]
   1110                     builder.blendshapeInfo.append(blendshape1)
   1111 
   1112         cachePath = blue.paths.ResolvePath('cache:/Avatars/' + avatarName)
   1113         if not os.path.exists(cachePath):
   1114             os.makedirs(cachePath)
   1115         builder.outputName = str('cache:/Avatars/' + avatarName + '/' + meshName + '.gr2')
   1116         builder.weldThreshold = weldthreshold
   1117 
   1118         class BuildEvent:
   1119 
   1120             def __init__(self):
   1121                 self.isDone = False
   1122                 self.succeeded = False
   1123 
   1124             def __call__(self, success):
   1125                 self.isDone = True
   1126                 self.succeeded = success
   1127 
   1128             def Wait(self):
   1129                 while not self.isDone:
   1130                     blue.pyos.synchro.Yield()
   1131 
   1132         def BuildSkinnedModel(avatarBuilder, lod):
   1133             avatarBuilder.lod = lod
   1134             avatarBuilder.PrepareForBuild()
   1135             evt = BuildEvent()
   1136             avatarBuilder.BuildAsync(evt)
   1137             evt.Wait()
   1138             if not evt.succeeded:
   1139                 return None
   1140             ret = avatarBuilder.GetSkinnedModel()
   1141             return ret
   1142 
   1143         if asyncLOD:
   1144             return BuildSkinnedModel(builder, overrideLod)
   1145 
   1146     @telemetry.ZONE_METHOD
   1147     def RemoveMeshesFromVisualModel(self, visualModel, ignoreMeshes = None):
   1148         if ignoreMeshes:
   1149             remIdxs = []
   1150             vmMeshCount = len(visualModel.meshes)
   1151             for i in xrange(vmMeshCount):
   1152                 if visualModel.meshes[i] not in ignoreMeshes:
   1153                     remIdxs.insert(0, i)
   1154 
   1155             for i in remIdxs:
   1156                 del visualModel.meshes[i]
   1157 
   1158         else:
   1159             del visualModel.meshes[:]
   1160 
   1161     @telemetry.ZONE_METHOD
   1162     def AppendMeshesToVisualModel(self, visualModel, meshes):
   1163         for mesh in iter(meshes):
   1164             if type(mesh) is trinity.Tr2Mesh and mesh not in visualModel.meshes:
   1165                 visualModel.meshes.append(mesh)
   1166 
   1167     @telemetry.ZONE_METHOD
   1168     def RemoveMeshesFromVisualModelByCategory(self, visualModel, category):
   1169         remList = []
   1170         for each in iter(visualModel.meshes):
   1171             if each.name.startswith(category):
   1172                 remList.append(each)
   1173 
   1174         for each in iter(remList):
   1175             visualModel.meshes.remove(each)
   1176 
   1177     @telemetry.ZONE_METHOD
   1178     def ReplaceMeshesOnAvatar(self, visualModel, meshes):
   1179         remList = []
   1180         for each in iter(meshes):
   1181             for every in iter(visualModel.meshes):
   1182                 if every.name == each.name:
   1183                     remList.append(every)
   1184 
   1185         for each in iter(remList):
   1186             visualModel.meshes.remove(each)
   1187 
   1188         self.AppendMeshesToVisualModel(visualModel, meshes)
   1189 
   1190     @telemetry.ZONE_METHOD
   1191     def GetDNAFromYamlFile(self, filename):
   1192         if blue.paths.exists(filename):
   1193             fileStream = blue.paths.GetFileContentsWithYield(filename)
   1194             yamlStr = fileStream.Read()
   1195             if len(yamlStr) == 0:
   1196                 raise Exception('PaperDoll::Factory::GetDNAFromYamlFile - Error, loading an empty file!')
   1197             dna = pdCf.NastyYamlLoad(yamlStr)
   1198             return dna
   1199         raise Exception('No %s file found' % filename)
   1200 
   1201 
   1202 class RedundantUpdateException(Exception):
   1203     pass
   1204 
   1205 
   1206 class OutOfVideoMemoryException(Exception):
   1207     pass
   1208 
   1209 
   1210 class TextureLoadingFailureException(Exception):
   1211     pass
   1212 
   1213 
   1214 class Doll(object):
   1215     __metaclass__ = telemetry.ZONE_PER_METHOD
   1216     __nextInstanceID = -1
   1217 
   1218     @staticmethod
   1219     def GetInstanceID():
   1220         Doll.__nextInstanceID += 1
   1221         return Doll.__nextInstanceID
   1222 
   1223     @staticmethod
   1224     def InstanceEquals(dollA, dollB):
   1225         return dollA.instanceID == dollB.instanceID
   1226 
   1227     def __init__(self, name, gender = pdDef.GENDER.FEMALE):
   1228         object.__init__(self)
   1229         self.instanceID = Doll.GetInstanceID()
   1230         self.mapBundle = MapBundle()
   1231         self.compressionSettings = None
   1232         self.hasDelayedUpdateCallPending = False
   1233         self.onUpdateDoneListeners = []
   1234         self.onUpdateDoneCallbacks = []
   1235         self.name = name
   1236         self.buildDataManager = pdDm.BuildDataManager()
   1237         self.UVs = {pdDef.DOLL_PARTS.HEAD: pdDef.DEFAULT_UVS,
   1238          pdDef.DOLL_PARTS.BODY: pdDef.DEFAULT_UVS,
   1239          pdDef.DOLL_PARTS.HAIR: pdDef.DEFAULT_UVS,
   1240          pdDef.DOLL_PARTS.ACCESSORIES: pdDef.DEFAULT_UVS}
   1241         self.morphsLoaded = False
   1242         self.isOptimized = False
   1243         self.hasUpdatedOnce = False
   1244         self.currentLOD = 0
   1245         self.previousLOD = 0
   1246         self.avatarType = 'interior'
   1247         self.gender = gender
   1248         self.busyBaking = False
   1249         self.busyLoadingDNA = False
   1250         self.busyHandlingBlendshapes = False
   1251         self.lastUpdateRedundant = False
   1252         self.hairColorizeData = []
   1253         self.boneOffsets = {}
   1254         self.bindPose = None
   1255         self.skinLightmapRenderer = None
   1256         self.__updateFrameStamp = -1
   1257         self.__useFastShader = False
   1258         self.renderDriver = renderDrivers.RenderDriverNCC()
   1259         self.lastRenderDriverClass = None
   1260         self._currentUpdateTasklet = None
   1261         self._UpdateTaskletChildren = []
   1262         self.decalBaker = None
   1263         trinity.device.RegisterResource(self)
   1264         self.deviceResetAvatar = None
   1265         self.useMaskedShaders = False
   1266         self.useDXT5N = False
   1267         self.blendShapeMeshCache = {}
   1268         self.clothMorphValues = {}
   1269 
   1270     def __del__(self):
   1271         self.StopPaperDolling()
   1272         self.skinLightmapRenderer = None
   1273         del self.mapBundle
   1274 
   1275     def GetName(self):
   1276         return self.name
   1277 
   1278     def GetProjectedDecals(self):
   1279         modifiers = self.buildDataManager.GetModifiersByCategory(pdDef.BODY_CATEGORIES.TATTOO)
   1280         modifiers = [ modifier for modifier in modifiers if modifier.decalData ]
   1281         return modifiers
   1282 
   1283     def OnInvalidate(self, level):
   1284         if self.skinLightmapRenderer is not None:
   1285             self.skinLightmapRenderer.StopRendering()
   1286             self.deviceResetAvatar = self.skinLightmapRenderer.skinnedObject
   1287             del self.skinLightmapRenderer
   1288             self.skinLightmapRenderer = None
   1289 
   1290     def OnCreate(self, dev):
   1291         if self.currentLOD == pdDef.LOD_SCATTER_SKIN:
   1292 
   1293             def skinCreate_t():
   1294                 if self.skinLightmapRenderer is None:
   1295                     self.skinLightmapRenderer = SkinLightmapRenderer()
   1296                 self.skinLightmapRenderer.SetSkinnedObject(self.deviceResetAvatar)
   1297                 self.skinLightmapRenderer.StartRendering()
   1298                 self.deviceResetAvatar = None
   1299 
   1300             t = uthread.new(skinCreate_t)
   1301             uthread.schedule(t)
   1302 
   1303     def StopPaperDolling(self, clearMaps = True):
   1304         for each in self.buildDataManager.GetModifiersAsList():
   1305             each.ClearCachedData()
   1306 
   1307         if clearMaps:
   1308             self.mapBundle.ReCreate()
   1309         self.decalBaker = None
   1310 
   1311     def GetMorphsFromGr2(self, gr2Path):
   1312         names = {}
   1313         resMan = blue.resMan
   1314         gr2Res = resMan.GetResource(gr2Path, 'raw')
   1315         while not gr2Res.isPrepared:
   1316             pdCf.Yield()
   1317 
   1318         meshCount = gr2Res.GetMeshCount()
   1319         for i in xrange(meshCount):
   1320             morphCount = gr2Res.GetMeshMorphCount(i)
   1321             for j in xrange(morphCount):
   1322                 name = gr2Res.GetMeshMorphName(i, j)
   1323                 data = names.get(i, [])
   1324                 data.append(name)
   1325                 names[i] = data
   1326 
   1327         return (gr2Res, names)
   1328 
   1329     def _AnalyzeBuildDataForRules(self, updateRuleBundle, buildDataManager):
   1330         modifierList = buildDataManager.GetModifiersAsList()
   1331         forcesLooseTop = False
   1332         hidesBootShin = False
   1333         swapTops = False
   1334         swapBottom = False
   1335         swapSocks = False
   1336         useMaskedShaders = False
   1337         for modifier in iter(modifierList):
   1338             useMaskedShaders = True
   1339             metaData = modifier.metaData
   1340             if metaData:
   1341                 forcesLooseTop = forcesLooseTop or metaData.forcesLooseTop
   1342                 hidesBootShin = hidesBootShin or metaData.hidesBootShin
   1343                 swapTops = swapTops or metaData.swapTops
   1344                 swapBottom = swapBottom or metaData.swapBottom
   1345                 swapSocks = swapSocks or metaData.swapSocks
   1346 
   1347         if not useMaskedShaders and self.useMaskedShaders:
   1348             updateRuleBundle.undoMaskedShaders = True
   1349         if hidesBootShin:
   1350             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.FEET, pdDef.BODY_CATEGORIES.FEETTUCKED)
   1351         else:
   1352             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.FEETTUCKED, pdDef.BODY_CATEGORIES.FEET)
   1353         if forcesLooseTop:
   1354             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.BOTTOMOUTERTUCKED, pdDef.BODY_CATEGORIES.BOTTOMOUTER)
   1355         else:
   1356             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.BOTTOMOUTER, pdDef.BODY_CATEGORIES.BOTTOMOUTERTUCKED)
   1357         if swapTops:
   1358             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.TOPMIDDLE, pdDef.BODY_CATEGORIES.TOPTIGHT)
   1359         else:
   1360             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.TOPTIGHT, pdDef.BODY_CATEGORIES.TOPMIDDLE)
   1361         if swapBottom:
   1362             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.TOPUNDERWEARTUCKED, pdDef.BODY_CATEGORIES.TOPUNDERWEAR)
   1363         else:
   1364             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.TOPUNDERWEAR, pdDef.BODY_CATEGORIES.TOPUNDERWEARTUCKED)
   1365         if swapSocks:
   1366             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.SOCKS, pdDef.BODY_CATEGORIES.SOCKSTUCKED)
   1367         else:
   1368             buildDataManager.ChangeDesiredOrder(pdDef.BODY_CATEGORIES.SOCKSTUCKED, pdDef.BODY_CATEGORIES.SOCKS)
   1369         self.useMaskedShaders = useMaskedShaders
   1370         updateRuleBundle.forcesLooseTop = forcesLooseTop
   1371         updateRuleBundle.hidesBootShin = hidesBootShin
   1372         updateRuleBundle.swapTops = swapTops
   1373         updateRuleBundle.swapBottom = swapBottom
   1374         updateRuleBundle.swapSocks = swapSocks
   1375 
   1376     def AdjustGr2PathForLod(self, gr2Path, resDataEntry):
   1377         adjustedGr2Path = resDataEntry.GetGeoLodMatch(gr2Path, self.currentLOD)
   1378         return adjustedGr2Path
   1379 
   1380     def __AdjustMeshForLod(self, mesh, resDataEntry):
   1381         mesh.geometryResPath = self.AdjustGr2PathForLod(mesh.geometryResPath, resDataEntry)
   1382 
   1383     def AdjustRedFileForLod(self, redfile, resDataEntry):
   1384         if not (resDataEntry and pdCfg.PerformanceOptions.useLodForRedfiles):
   1385             return redfile
   1386         return resDataEntry.GetGeoLodMatch(redfile, self.currentLOD)
   1387 
   1388     def _ProcessRules(self, factory, buildDataManager, modifierList, updateRuleBundle, useCloth = False):
   1389         if not modifierList:
   1390             return
   1391         if updateRuleBundle.rebuildHair:
   1392             hairModifiers = [ x for x in buildDataManager.GetHairModifiers() if x not in modifierList ]
   1393             modifierList.extend(hairModifiers)
   1394         loadedObjects = {}
   1395         modifiersWithDataToLoad = [ modifier for modifier in modifierList if modifier.weight > 0 and (modifier.redfile and not modifier.meshes or modifier.IsMeshDirty()) ]
   1396         modLen = len(modifiersWithDataToLoad)
   1397         remIdx = []
   1398         AdjustRedFileForLod = self.AdjustRedFileForLod
   1399         GetResDataEntryByFullResPath = factory.resData.GetEntryByFullResPath
   1400 
   1401         def LoadObject(filename):
   1402             if not filename:
   1403                 return None
   1404             return blue.resMan.LoadObject(filename)
   1405 
   1406         for i in xrange(modLen):
   1407             modifier = modifiersWithDataToLoad[i]
   1408             redfile = modifier.clothOverride if useCloth and modifier.clothOverride else modifier.redfile
   1409             if id(modifier) not in loadedObjects:
   1410                 resDataEntry = GetResDataEntryByFullResPath(redfile)
   1411                 redfileLod = AdjustRedFileForLod(redfile, resDataEntry)
   1412                 self.renderDriver.OnModifierRedfileLoaded(modifier, redfileLod)
   1413                 if not modifier.meshes or redfileLod != modifier.redfile:
   1414                     objToLoad = LoadObject(redfileLod)
   1415                     if objToLoad is None:
   1416                         objToLoad = LoadObject(redfile)
   1417                     loadedObjects[id(modifier)] = objToLoad
   1418                 else:
   1419                     remIdx.insert(0, i)
   1420             pdCf.BeFrameNice()
   1421 
   1422         for i in iter(remIdx):
   1423             del modifiersWithDataToLoad[i]
   1424 
   1425         pathCache = {}
   1426         for modifier in iter(modifiersWithDataToLoad):
   1427             modifier.ClearCachedData()
   1428             newBodyPart = loadedObjects.get(id(modifier), None)
   1429             if newBodyPart and hasattr(newBodyPart, 'meshes'):
   1430                 for i, mesh in enumerate(newBodyPart.meshes):
   1431                     oldPath = mesh.geometryResPath
   1432                     newPath = pathCache.get(oldPath)
   1433                     if newPath:
   1434                         mesh.geometryResPath = newPath
   1435                     else:
   1436                         resDataEntry = GetResDataEntryByFullResPath(modifier.redfile)
   1437                         self.__AdjustMeshForLod(mesh, resDataEntry)
   1438                         pathCache[oldPath] = mesh.geometryResPath
   1439                     if modifier.categorie == pdDef.DOLL_PARTS.HAIR:
   1440                         if mesh.name == pdCcf.HAIR_MESH_SHAPE:
   1441                             pdCcf.MoveAreas(mesh.opaqueAreas, mesh.transparentAreas)
   1442                             pdCcf.MoveAreas(mesh.decalAreas, mesh.transparentAreas)
   1443                         else:
   1444                             pdCcf.MoveAreas(mesh.opaqueAreas, mesh.decalAreas)
   1445                     if mesh.name in pdCcf.DRAPE_TUCK_NAMES:
   1446                         pdCcf.MoveAreas(mesh.opaqueAreas, mesh.transparentAreas)
   1447                     if modifier.categorie not in pdDef.CATEGORIES_CONTAINING_GROUPS:
   1448                         mesh.name = '{0}{1}'.format(modifier.categorie, mesh.meshIndex)
   1449                     else:
   1450                         mesh.name = '{0}{1}'.format(modifier.name, mesh.meshIndex)
   1451                     if self.blendShapeMeshCache.get(mesh.name):
   1452                         del self.blendShapeMeshCache[mesh.name]
   1453                     modifier.meshes.append(mesh)
   1454                     modifier.meshGeometryResPaths[mesh.name] = mesh.geometryResPath
   1455 
   1456     @telemetry.ZONE_METHOD
   1457     def __ApplyUVs(self):
   1458         UVCalculator.ApplyUVs(self.buildDataManager, self.renderDriver)
   1459 
   1460     def _HandleBlendShapes(self, removedModifiers = None):
   1461         self.decalBaker = None
   1462         self.SpawnUpdateChildTasklet(self.__HandleBlendShapes_t, removedModifiers)
   1463 
   1464     @telemetry.ZONE_METHOD
   1465     def __HandleClothBlendShapes_t(self, clothMeshes, morphTargets):
   1466         pdCf.BeFrameNice()
   1467         Factory.ProcessBlendshapesForCloth(clothMeshes, morphTargets, clothMorphValues=self.clothMorphValues)
   1468 
   1469     @telemetry.ZONE_METHOD
   1470     def __HandleBlendShapes_t(self, removedModifiers):
   1471         try:
   1472             self.busyHandlingBlendshapes = True
   1473             pdCf.BeFrameNice()
   1474             modifierList = self.buildDataManager.GetModifiersAsList()
   1475             morphTargets = self.buildDataManager.GetMorphTargets()
   1476             if morphTargets:
   1477                 meshGeometryResPaths = {}
   1478                 for modifier in iter(modifierList):
   1479                     meshGeometryResPaths.update(modifier.meshGeometryResPaths)
   1480 
   1481                 meshesIncludingClothMeshes = self.buildDataManager.GetMeshes(includeClothMeshes=True)
   1482                 clothMeshes = []
   1483                 self.SpawnUpdateChildTasklet(self.__HandleClothBlendShapes_t, clothMeshes, morphTargets)
   1484                 meshes = [ mesh for mesh in meshesIncludingClothMeshes if type(mesh) == trinity.Tr2Mesh ]
   1485                 Factory.ApplyMorphTargetsToMeshes(meshes, morphTargets, self.blendShapeMeshCache, meshGeometryResPaths)
   1486                 self.renderDriver.OnApplyMorphTargets(meshes, morphTargets)
   1487                 for mesh in iter(meshes):
   1488                     if mesh.name not in self.blendShapeMeshCache:
   1489                         mesh.deferGeometryLoad = False
   1490 
   1491         finally:
   1492             for mesh in self.buildDataManager.GetMeshes(alternativeModifierList=removedModifiers, includeClothMeshes=True):
   1493                 if self.blendShapeMeshCache.get(mesh.name):
   1494                     del self.blendShapeMeshCache[mesh.name]
   1495                 key = str(mesh)
   1496                 if self.clothMorphValues.get(key):
   1497                     del self.clothMorphValues[key]
   1498 
   1499             self.busyHandlingBlendshapes = False
   1500 
   1501     def ApplyBoneOffsets(self):
   1502         self.boneOffsets = {}
   1503         modifiersDone = {}
   1504         for modifier in (modifier for modifier in self.buildDataManager.GetModifiersAsList() if modifier.poseData):
   1505             if modifier.respath not in modifiersDone:
   1506                 modifiersDone[modifier.respath] = None
   1507                 weight = modifier.weight
   1508                 for bone in iter(modifier.poseData):
   1509                     if bone not in self.boneOffsets:
   1510                         newT = modifier.poseData[bone][pdCcf.TRANSLATION]
   1511                         newR = modifier.poseData[bone][pdCcf.ROTATION]
   1512                         self.boneOffsets[bone] = {}
   1513                         self.boneOffsets[bone][pdCcf.TRANSLATION] = (newT[0] * weight, newT[1] * weight, newT[2] * weight)
   1514                         self.boneOffsets[bone][pdCcf.ROTATION] = (newR[0] * weight, newR[1] * weight, newR[2] * weight)
   1515                     else:
   1516                         prevT = self.boneOffsets[bone][pdCcf.TRANSLATION]
   1517                         prevR = self.boneOffsets[bone][pdCcf.ROTATION]
   1518                         newT = modifier.poseData[bone][pdCcf.TRANSLATION]
   1519                         newR = modifier.poseData[bone][pdCcf.ROTATION]
   1520                         self.boneOffsets[bone][pdCcf.TRANSLATION] = (prevT[0] + newT[0] * weight, prevT[1] + newT[1] * weight, prevT[2] + newT[2] * weight)
   1521                         self.boneOffsets[bone][pdCcf.ROTATION] = (prevR[0] + newR[0] * weight, prevR[1] + newR[1] * weight, prevR[2] + newR[2] * weight)
   1522 
   1523     def _EnsureCompleteBody(self, factory):
   1524 
   1525         def BodyPartSearch(modifier, categoryStartsWith):
   1526             return modifier.IsMeshContainingModifier() and modifier.categorie.startswith(categoryStartsWith)
   1527 
   1528         def EnsureBodyPart(bodyModifiers, categoryStartsWith, defaultPart):
   1529             if not any(map(lambda x: BodyPartSearch(x, categoryStartsWith), bodyModifiers)):
   1530                 self.AddResource(defaultPart, 1.0, factory, privilegedCaller=True)
   1531 
   1532         headModifier = self.buildDataManager.GetModifierByResPath(pdDef.DEFAULT_NUDE_HEAD)
   1533         if not headModifier:
   1534             self.AddResource(pdDef.DEFAULT_NUDE_HEAD, 1.0, factory, privilegedCaller=True)
   1535         if self.currentLOD != 2:
   1536             makeupModifiers = self.buildDataManager.GetModifiersByCategory(pdDef.HEAD_CATEGORIES.MAKEUP)
   1537             foundEyelashes = False
   1538             for makeup in iter(makeupModifiers):
   1539                 if pdDef.MAKEUP_GROUPS.EYELASHES in makeup.group:
   1540                     foundEyelashes = True
   1541                     break
   1542 
   1543             if not foundEyelashes:
   1544                 self.AddResource(pdDef.DEFAULT_NUDE_EYELASHES, 1.0, factory, privilegedCaller=True)
   1545         bodyModifiers = self.buildDataManager.GetModifiersByPart(pdDef.DOLL_PARTS.BODY)
   1546         EnsureBodyPart(bodyModifiers, 'bottom', pdDef.DEFAULT_NUDE_LEGS)
   1547         EnsureBodyPart(bodyModifiers, pdDef.BODY_CATEGORIES.FEET, pdDef.DEFAULT_NUDE_FEET)
   1548         EnsureBodyPart(bodyModifiers, pdDef.BODY_CATEGORIES.HANDS, pdDef.DEFAULT_NUDE_HANDS)
   1549         EnsureBodyPart(bodyModifiers, 'top', pdDef.DEFAULT_NUDE_TORSO)
   1550 
   1551     def Load(self, filename, factory):
   1552         self.LoadYaml(filename, factory)
   1553 
   1554     def LoadYaml(self, filename, factory):
   1555         dna = factory.GetDNAFromYamlFile(filename)
   1556         self.LoadDNA(dna, factory)
   1557 
   1558     def CreateModifierFromFootPrint(self, factory, footPrint):
   1559 
   1560         def doDecal(modifier):
   1561             decal = footPrint.get(pdDef.DNA_STRINGS.DECALDATA)
   1562             if decal:
   1563                 modifier.decalData = ProjectedDecal.Load(decal)
   1564 
   1565         pathlower = footPrint[pdDef.DNA_STRINGS.PATH].lower()
   1566         modifier = None
   1567         if not factory.resData.QueryPathByGender(self.gender, pathlower):
   1568             if footPrint.get(pdDef.DNA_STRINGS.DECALDATA):
   1569                 modifier = pdDm.BuildData(pathlower)
   1570                 modifier.weight = float(footPrint[pdDef.DNA_STRINGS.WEIGHT])
   1571                 doDecal(modifier)
   1572         else:
   1573             variation = footPrint.get(pdDef.DNA_STRINGS.VARIATION)
   1574             modifier = self._AddResource(pathlower, footPrint.get(pdDef.DNA_STRINGS.WEIGHT), factory, variation=variation)
   1575             modifier.pattern = footPrint.get(pdDef.DNA_STRINGS.PATTERN, '')
   1576             modifier.tuck = footPrint.get(pdDef.DNA_STRINGS.TUCK, False)
   1577             if isinstance(footPrint.get(pdDef.DNA_STRINGS.COLORS), basestring):
   1578                 colorData = eval(footPrint.get(pdDef.DNA_STRINGS.COLORS))
   1579             else:
   1580                 colorData = footPrint.get(pdDef.DNA_STRINGS.COLORS)
   1581             colorizeDataSet = False
   1582             if colorData:
   1583                 if modifier.pattern:
   1584                     modifier.patternData = colorData
   1585                     if len(modifier.patternData) == 5:
   1586                         modifier.patternData.append((0, 0, 8, 8))
   1587                         modifier.patternData.append(0.0)
   1588                 else:
   1589                     modifier.colorizeData = colorData
   1590                     colorizeDataSet = True
   1591             if pdDef.DNA_STRINGS.SPECULARCOLORS in footPrint:
   1592                 modifier.specularColorData = footPrint.get(pdDef.DNA_STRINGS.SPECULARCOLORS)
   1593             colorVariation = footPrint.get(pdDef.DNA_STRINGS.COLORVARIATION, '')
   1594             if colorVariation and not (colorVariation == 'default' and colorizeDataSet):
   1595                 modifier.SetColorVariation(footPrint[pdDef.DNA_STRINGS.COLORVARIATION])
   1596             doDecal(modifier)
   1597         return modifier
   1598 
   1599     def SortModifiersForBatchAdding(self, modifiers):
   1600 
   1601         def BatchAddModifierKey(modifier):
   1602             if modifier.categorie == pdDef.DOLL_EXTRA_PARTS.UTILITYSHAPES:
   1603                 return 0
   1604             elif modifier.categorie == pdDef.DOLL_EXTRA_PARTS.DEPENDANTS:
   1605                 return 1
   1606             else:
   1607                 return 2
   1608 
   1609         modifiers.sort(key=lambda x: x.categorie)
   1610         modifiers.sort(key=lambda x: BatchAddModifierKey(x))
   1611         return modifiers
   1612 
   1613     def LoadDNA(self, data, factory, tattoos = None, buildDataManager = None):
   1614         self.busyLoadingDNA = True
   1615         if data[0] in pdDef.GENDER:
   1616             self.gender = data[0]
   1617             data = data[1:]
   1618         buildDataManager = buildDataManager or self.buildDataManager
   1619         modifiers = []
   1620         for footPrint in iter(data):
   1621             modifier = self.CreateModifierFromFootPrint(factory, footPrint)
   1622             if modifier:
   1623                 modifiers.append(modifier)
   1624                 pdCf.BeFrameNice()
   1625 
   1626         modifiers = self.SortModifiersForBatchAdding(modifiers)
   1627         for modifier in iter(modifiers):
   1628             buildDataManager.AddModifier(modifier)
   1629 
   1630         self.busyLoadingDNA = False
   1631 
   1632     def GetDNA(self, preserveTypes = False, getHiddenModifiers = False, getWeightless = False):
   1633         dna = [self.gender]
   1634         modifiers = self.buildDataManager.GetSortedModifiers(showHidden=getHiddenModifiers)
   1635         for modifier in iter(modifiers):
   1636             ow = self.buildDataManager.GetOcclusionWeight(modifier)
   1637             if getWeightless or ow + modifier.weight > 0.0:
   1638                 data = modifier.GetFootPrint(preserveTypes, occlusionWeight=ow)
   1639                 dna.append(data)
   1640 
   1641         return dna
   1642 
   1643     def MatchDNA(self, factory, dna):
   1644         self.busyLoadingDNA = True
   1645         if dna[0] in pdDef.GENDER:
   1646             self.gender = dna[0]
   1647             tempDNA = list(dna[1:])
   1648         else:
   1649             tempDNA = list(dna)
   1650         for modifier in self.buildDataManager.GetSortedModifiers():
   1651             parent_modifiers = self.buildDataManager.GetParentModifiers(modifier)
   1652             for par_mod in parent_modifiers:
   1653                 self.buildDataManager.RemoveModifier(par_mod)
   1654 
   1655             self.buildDataManager.RemoveModifier(modifier)
   1656 
   1657         modifiers = []
   1658         for footPrint in tempDNA:
   1659             modifier = self.CreateModifierFromFootPrint(factory, footPrint)
   1660             if modifier:
   1661                 modifiers.append(modifier)
   1662 
   1663         modifiers = self.SortModifiersForBatchAdding(modifiers)
   1664         for modifier in iter(modifiers):
   1665             self.buildDataManager.AddModifier(modifier)
   1666 
   1667         self.busyLoadingDNA = False
   1668 
   1669     def Save(self, filename):
   1670         f = file(filename, 'w')
   1671         dnaData = self.GetDNA()
   1672         yaml.dump(dnaData, f, default_flow_style=False)
   1673         f.close()
   1674 
   1675     def GetBuildDataManager(self):
   1676         return self.buildDataManager
   1677 
   1678     def GetBuildDataByCategory(self, category, coalesceToPart = True):
   1679         modifiers = self.buildDataManager.GetModifiersByCategory(category)
   1680         if coalesceToPart and not modifiers:
   1681             if category in pdDef.DOLL_PARTS + pdDef.DOLL_EXTRA_PARTS:
   1682                 modifiers = self.buildDataManager.GetModifiersByPart(category)
   1683         return modifiers
   1684 
   1685     def GetBuildDataByResPath(self, resPath, includeFuture = False):
   1686         return self.buildDataManager.GetModifierByResPath(resPath, includeFuture=includeFuture)
   1687 
   1688     def _AddResource(self, res, weight, factory, colorization = None, variation = None, colorVariation = None, privilegedCaller = False, rawColorVariation = None):
   1689         parts = res.split(pdDef.SEPERATOR_CHAR)
   1690         if len(parts) == 1:
   1691             raise Exception('Can not add a whole category to the character')
   1692         pdCf.BeFrameNice()
   1693         modifier = None
   1694         removedModifiers = self.buildDataManager.GetDirtyModifiers(removedBit=True)
   1695         for removedModifier in iter(removedModifiers):
   1696             if removedModifier.respath == res:
   1697                 removedModifier.weight = weight
   1698                 removedModifier.dependantModifiers = {}
   1699                 modifier = removedModifier
   1700                 break
   1701 
   1702         modifier = factory.CollectBuildData(self.gender, res, weight)
   1703         categorie = modifier.categorie
   1704         l1rModifier = None
   1705         if modifier.metaData.lod1Replacement:
   1706             modifier.lodCutoff = pdDef.LOD_0
   1707             l1rModifier = self._AddResource(modifier.metaData.lod1Replacement, 1.0, factory, privilegedCaller=privilegedCaller)
   1708             l1rModifier.lodCutoff = pdDef.LOD_99
   1709             l1rModifier.lodCutin = pdDef.LOD_1
   1710             self.buildDataManager.HideModifier(l1rModifier)
   1711         if modifier.metaData.lod2Replacement:
   1712             if l1rModifier:
   1713                 l1rModifier.lodCutoff = pdDef.LOD_1
   1714             else:
   1715                 modifier.lodCutoff = pdDef.LOD_1
   1716             l2rModifier = self._AddResource(modifier.metaData.lod2Replacement, 1.0, factory, privilegedCaller=privilegedCaller)
   1717             l2rModifier.lodCutoff = pdDef.LOD_99
   1718             l2rModifier.lodCutin = pdDef.LOD_2
   1719             self.buildDataManager.HideModifier(l2rModifier)
   1720         self.ApplyVariationsToModifier(modifier, colorization, variation, colorVariation, rawColorVariation=rawColorVariation)
   1721         dependantModifiersFullData = modifier.GetDependantModifiersFullData()
   1722         if dependantModifiersFullData:
   1723             for parsedValues in iter(dependantModifiersFullData):
   1724                 dependantModifier = self._AddResource(parsedValues[0], parsedValues[3], factory, variation=parsedValues[2], colorVariation=parsedValues[1], privilegedCaller=privilegedCaller)
   1725                 modifier.AddDependantModifier(dependantModifier)
   1726 
   1727         if categorie in [pdDef.DOLL_PARTS.HAIR]:
   1728             if categorie == pdDef.DOLL_PARTS.HAIR:
   1729                 hairModifiers = self.buildDataManager.GetModifiersByCategory(categorie)
   1730                 if hairModifiers:
   1731                     modifier.colorizeData = hairModifiers[0].colorizeData
   1732                 elif self.hairColorizeData:
   1733                     modifier.colorizeData = self.hairColorizeData
   1734             self.buildDataManager.SetModifiersByCategory(categorie, [modifier], privilegedCaller=privilegedCaller)
   1735         else:
   1736             self.buildDataManager.AddModifier(modifier, privilegedCaller=privilegedCaller)
   1737         return modifier
   1738 
   1739     def SetItemType(self, factory, itemType, weight = 1.0, rawColorVariation = None):
   1740         if type(itemType) is not tuple:
   1741             item = factory.GetItemType(itemType, gender=self.gender)
   1742         if item is None:
   1743             log.LogError('Unable to Set Item type:', itemType, 'on doll')
   1744             return
   1745         self.RemoveResource(item[0], factory)
   1746         return self.AddItemType(factory, item, weight, rawColorVariation)
   1747 
   1748     def AddItemType(self, factory, itemType, weight = 1.0, rawColorVariation = None):
   1749         if type(itemType) is not tuple:
   1750             itemType = factory.GetItemType(itemType, gender=self.gender)
   1751         if type(itemType) is tuple:
   1752             return self.AddResource(itemType[0].lower(), weight, factory, variation=itemType[1], colorVariation=itemType[2], rawColorVariation=rawColorVariation)
   1753 
   1754     def ApplyVariationsToModifier(self, modifier, colorization = None, variation = None, colorVariation = None, rawColorVariation = None):
   1755         if colorization is not None:
   1756             modifier.SetColorizeData(colorization)
   1757         if variation is not None:
   1758             modifier.SetVariation(variation)
   1759         if colorVariation is not None:
   1760             modifier.SetColorVariation(colorVariation)
   1761         if rawColorVariation is not None:
   1762             if colorVariation:
   1763                 log.LogWarn('paperDoll::Doll:: ApplyVariationsToModifier: applying both colorVariation and rawColorVariation')
   1764             modifier.SetColorVariationDirectly(rawColorVariation)
   1765 
   1766     def AddResource(self, res, weight, factory, colorization = None, variation = None, colorVariation = None, privilegedCaller = False, rawColorVariation = None):
   1767         modifier = None
   1768         if type(res) is ProjectedDecal:
   1769             decalName = res.label or 'Decal {0}'.format(res.layer)
   1770             modifier = pdDm.BuildData(name=decalName, categorie=pdDef.BODY_CATEGORIES.TATTOO)
   1771             modifier.decalData = res
   1772             self.ApplyVariationsToModifier(modifier, colorization, variation, colorVariation)
   1773             self.buildDataManager.AddModifier(modifier, privilegedCaller=privilegedCaller)
   1774         else:
   1775             modifier = self._AddResource(res, weight, factory, colorization=colorization, variation=variation, colorVariation=colorVariation, privilegedCaller=privilegedCaller, rawColorVariation=rawColorVariation)
   1776         return modifier
   1777 
   1778     def AddResources(self, resList, weight, factory):
   1779         for each in iter(resList):
   1780             self.AddResource(each, weight, factory)
   1781 
   1782     def RemoveResource(self, res, factory, privilegedCaller = False):
   1783         parts = res.split(pdDef.SEPERATOR_CHAR)
   1784         if len(parts) == 1:
   1785             raise Exception('Can not remove a whole category from the character')
   1786         categorie = str(parts[0].lower())
   1787         modifiers = self.buildDataManager.GetModifiersByCategory(categorie, showHidden=True)
   1788         targetFun = lambda modifier: str(modifier.respath.lower()) == str(res.lower())
   1789         target = None
   1790         for modifier in modifiers:
   1791             if targetFun(modifier):
   1792                 target = modifier
   1793                 break
   1794 
   1795         if target:
   1796             self.buildDataManager.RemoveModifier(target, privilegedCaller=privilegedCaller)
   1797         return target
   1798 
   1799     def HandleChangedDependencies(self, factory):
   1800         changedModifiers = self.buildDataManager.GetDirtyModifiers(changedBit=True)
   1801         for modifier in iter(changedModifiers):
   1802             self.buildDataManager.ReverseOccludeModifiersByModifier(modifier, modifier.lastVariation)
   1803             self.buildDataManager.OccludeModifiersByModifier(modifier)
   1804             resPaths = modifier.GetDependantModifierResPaths() or []
   1805             for dependantModifier in iter(modifier.GetDependantModifiers()):
   1806                 if dependantModifier.respath not in resPaths:
   1807                     modifier.RemoveDependantModifier(dependantModifier)
   1808                     self.buildDataManager.RemoveParentModifier(modifier, dependantModifier)
   1809                     self.buildDataManager.RemoveModifier(dependantModifier, privilegedCaller=True)
   1810                 else:
   1811                     resPaths.remove(dependantModifier.respath)
   1812 
   1813             if resPaths:
   1814                 dependantModifiersFullData = modifier.GetDependantModifiersFullData()
   1815                 for entry in dependantModifiersFullData:
   1816                     if entry[0] in resPaths:
   1817                         dependantModifier = self._AddResource(entry[0], entry[3], factory, variation=entry[2], colorVariation=entry[3], privilegedCaller=True)
   1818                         modifier.AddDependantModifier(dependantModifier)
   1819                         self.buildDataManager.AddParentModifier(dependantModifier, modifier)
   1820 
   1821     def QueryModifiers(self, modifierList, fun):
   1822         return all(map(fun, modifierList))
   1823 
   1824     def SpawnUpdateChildTasklet(self, fun, *args):
   1825         t = uthread.new(fun, *args)
   1826         t.context = 'paperDoll::Doll::Update'
   1827         uthread.schedule(t)
   1828         self._UpdateTaskletChildren.append(t)
   1829 
   1830     def ReapUpdateChildTasklets(self):
   1831         try:
   1832             for t in self._UpdateTaskletChildren:
   1833                 if t:
   1834                     t.kill()
   1835 
   1836         finally:
   1837             del self._UpdateTaskletChildren[:]
   1838 
   1839     def _DelayedUpdateCall(self, factory, avatar, visualModel, LODMode, channel = None):
   1840         self.hasDelayedUpdateCallPending = True
   1841         pdCf.Yield()
   1842         while self._currentUpdateTasklet and self._currentUpdateTasklet.alive:
   1843             pdCf.Yield()
   1844 
   1845         self.hasDelayedUpdateCallPending = False
   1846         self.Update(factory, avatar, visualModel, LODMode, channel=channel)
   1847 
   1848     def Update(self, factory, avatar = None, visualModel = None, LODMode = False, channel = None):
   1849         currentFrame = trinity.GetCurrentFrameCounter()
   1850         statistics.updateCount.Inc()
   1851         if self.hasDelayedUpdateCallPending:
   1852             return
   1853         if self.__updateFrameStamp == currentFrame:
   1854             log.LogWarn('paperDoll::Doll::Update is being called more than once on the same frame for the same doll instance! Fix your code so you only call Update after all needed changes have been applied.')
   1855             return
   1856         if self._currentUpdateTasklet and self._currentUpdateTasklet.alive and self.__updateFrameStamp < currentFrame:
   1857             self.KillUpdate()
   1858             if not self.hasDelayedUpdateCallPending:
   1859                 uthread.new(self._DelayedUpdateCall, factory, avatar, visualModel, LODMode, channel=channel)
   1860             return
   1861         self.__updateFrameStamp = currentFrame
   1862         self._currentUpdateTasklet = uthread.new(self.Update_t, *(factory,
   1863          avatar,
   1864          visualModel,
   1865          channel))
   1866         self._currentUpdateTasklet.context = 'paperDoll::Doll::Update'
   1867         if blue.os.GetWallclockTimeNow() - blue.os.GetWallclockTime() < 50000:
   1868             uthread.schedule(self._currentUpdateTasklet)
   1869 
   1870     def KillUpdate(self):
   1871         if self._currentUpdateTasklet and self._currentUpdateTasklet.alive:
   1872             self._currentUpdateTasklet.raise_exception(TaskletExit, 'Preemptively killing Update')
   1873 
   1874     def IsBusyUpdating(self):
   1875         return self._currentUpdateTasklet and self._currentUpdateTasklet.alive
   1876 
   1877     def IsReady(self):
   1878         return not self.IsBusyUpdating() and self.hasUpdatedOnce
   1879 
   1880     def Update_t(self, factory, avatar, visualModel, channel = None):
   1881         updateRuleBundle = UpdateRuleBundle()
   1882         sTime = blue.os.GetTime()
   1883         buildDataManager = self.buildDataManager
   1884         try:
   1885             while self.busyLoadingDNA or not factory.FactoryReady():
   1886                 pdCf.Yield()
   1887 
   1888             LODMode = self.previousLOD != self.currentLOD
   1889             buildDataManager.Lock()
   1890             if LODMode:
   1891                 pdCf.Yield()
   1892             else:
   1893                 pdCf.BeFrameNice()
   1894             if avatar and hasattr(avatar, 'visualModel') and not visualModel:
   1895                 visualModel = avatar.visualModel
   1896             AddMarker('Update Start')
   1897             self.renderDriver.OnBeginUpdate(self)
   1898             if pdCfg.PerformanceOptions.EnsureCompleteBody:
   1899                 self._EnsureCompleteBody(factory)
   1900             self.HandleChangedDependencies(factory)
   1901             buildDataManager.ApplyLimitsToModifierWeights()
   1902             gdm = buildDataManager.GetDirtyModifiers
   1903             addedModifiers = gdm(addedBit=True, getWeightless=False)
   1904             removedModifiers = gdm(removedBit=True) if self.hasUpdatedOnce else []
   1905             changedModifiers = gdm(changedBit=True) if self.hasUpdatedOnce else []
   1906             if self.hasUpdatedOnce:
   1907                 dirtyModifiers = removedModifiers + addedModifiers + changedModifiers
   1908                 addedAndChangedModifiers = addedModifiers + changedModifiers
   1909             else:
   1910                 dirtyModifiers = addedModifiers
   1911                 addedAndChangedModifiers = addedModifiers
   1912             if not dirtyModifiers and self.mapBundle.AllMapsGood():
   1913                 raise RedundantUpdateException('Warning - Call made to PaperDoll.Update() when no modifier is dirty!')
   1914             pdCf.BeFrameNice()
   1915             self._AnalyzeBuildDataForRules(updateRuleBundle, buildDataManager)
   1916             updateRuleBundle.DiscoverState(dirtyModifiers, avatar)
   1917             if not (updateRuleBundle.blendShapesOnly or updateRuleBundle.decalsOnly):
   1918                 self._ProcessRules(factory, buildDataManager, addedAndChangedModifiers, updateRuleBundle)
   1919                 self.ApplyBoneOffsets()
   1920             updateRuleBundle.meshesChanged = not self.hasUpdatedOnce or any((modifier for modifier in addedAndChangedModifiers if modifier.IsMeshContainingModifier() and (modifier.IsMeshDirty() or modifier.HasWeightPulse()))) or any((modifier.IsMeshContainingModifier() for modifier in removedModifiers))
   1921             if not updateRuleBundle.blendShapesOnly and updateRuleBundle.meshesChanged and avatar:
   1922                 factory.CreateAnimationOffsets(avatar, self)
   1923             if updateRuleBundle.doDecals:
   1924                 self.BakeDecals_t(factory, updateRuleBundle.dirtyDecalModifiers)
   1925             if updateRuleBundle.doBlendShapes and not updateRuleBundle.meshesChanged:
   1926                 self._HandleBlendShapes(removedModifiers)
   1927             if self.currentLOD <= pdDef.LOD_SKIN:
   1928                 hashStubble = pdPor.PortraitTools.GetStubbleHash(addedAndChangedModifiers)
   1929             else:
   1930                 hashStubble = None
   1931             if updateRuleBundle.meshesChanged:
   1932                 self.__ApplyUVs()
   1933             if visualModel and (self.currentLOD <= self.previousLOD or not self.mapBundle.AllMapsGood()):
   1934                 textureCompositingStartTime = blue.os.GetTime()
   1935                 if self.hasUpdatedOnce and self.mapBundle.AllMapsGood():
   1936                     updateRuleBundle.partsToComposite = buildDataManager.GetPartsFromMaps(dirtyModifiers)
   1937                 self.WaitForChildTaskletsToFinish()
   1938                 if self.hasUpdatedOnce and self.mapBundle.AllMapsGood():
   1939                     updateRuleBundle.mapsToComposite = buildDataManager.GetMapsToComposite(dirtyModifiers)
   1940                 if buildDataManager.desiredOrderChanged:
   1941                     if (self.mapBundle.AllMapsGood() or updateRuleBundle.partsToComposite) and pdDef.DOLL_PARTS.BODY not in updateRuleBundle.partsToComposite:
   1942                         updateRuleBundle.partsToComposite.append(pdDef.DOLL_PARTS.BODY)
   1943                     updateRuleBundle.mapsToComposite = list(pdDef.MAPS)
   1944                 blendShapeVector = [ modifier.weight for modifier in buildDataManager.GetModifiersAsList() if modifier.IsBlendshapeModifier() and modifier.weight != 0.0 ]
   1945                 hES = (self.compressionSettings, hashStubble, blendShapeVector)
   1946                 hashKey = buildDataManager.HashForMaps(hashableElements=hES)
   1947                 mapsTypesComposited = self.CompositeTextures(factory, hashKey, updateRuleBundle)
   1948                 if updateRuleBundle.videoMemoryFailure:
   1949                     raise OutOfVideoMemoryException('Out of video memory!')
   1950                 if updateRuleBundle.textureLoadingFailure:
   1951                     raise TextureLoadingFailureException('Texture failed to load!')
   1952                 if not updateRuleBundle.blendShapesOnly:
   1953                     self.WaitForTexturesToComposite(factory, hashKey, mapsTypesComposited, textureCompositingStartTime)
   1954                 else:
   1955                     timeCompositedSec = statistics.GetTimeDiffInSeconds(textureCompositingStartTime)
   1956                     statistics.compositeTime.Add(timeCompositedSec)
   1957             compSet = self.compressionSettings if self.compressionSettings is not None else factory
   1958             self.useDXT5N = compSet and compSet.compressTextures and compSet.AllowCompress(pdDef.NORMAL_MAP)
   1959             meshes = buildDataManager.GetMeshes()
   1960             if updateRuleBundle.meshesChanged:
   1961                 self._HandleBlendShapes(removedModifiers)
   1962             self.WaitForChildTaskletsToFinish()
   1963             if updateRuleBundle.meshesChanged:
   1964                 self.ConfigureMaskedShader(updateRuleBundle)
   1965                 self.renderDriver.ApplyShaders(self, meshes)
   1966                 if visualModel:
   1967                     factory.RemoveMeshesFromVisualModel(visualModel)
   1968                 if visualModel:
   1969                     factory.AppendMeshesToVisualModel(visualModel, meshes)
   1970                     visualModel.ResetAnimationBindings()
   1971             Factory.BindMapsToMeshes(meshes, self.mapBundle)
   1972             if self.currentLOD <= pdDef.LOD_SKIN and updateRuleBundle.doStubble:
   1973 
   1974                 def doStubble():
   1975                     pdPor.PortraitTools.HandleRemovedStubble(removedModifiers, buildDataManager)
   1976                     pdPor.PortraitTools.HandleUpdatedStubble(addedAndChangedModifiers, buildDataManager)
   1977                     factory.RebindAnimations(avatar, visualModel)
   1978 
   1979                 self.SpawnUpdateChildTasklet(doStubble)
   1980             self.renderDriver.OnFinalizeAvatar(visualModel, avatar, updateRuleBundle, self, factory)
   1981             self.previousLOD = self.currentLOD
   1982             self.WaitForChildTaskletsToFinish()
   1983             self.renderDriver.OnEndUpdate(avatar, visualModel, self, factory)
   1984             factory.RebindAnimations(avatar, visualModel)
   1985             if avatar:
   1986                 freq = pdCfg.PerformanceOptions.updateFreq.get(self.overrideLod, 0)
   1987                 if freq == 0:
   1988                     avatar.updatePeriod = 0
   1989                 else:
   1990                     avatar.updatePeriod = 1.0 / freq
   1991             if self.currentLOD == pdDef.LOD_SCATTER_SKIN and self.skinLightmapRenderer is None:
   1992                 self.skinLightmapRenderer = SkinLightmapRenderer()
   1993                 self.skinLightmapRenderer.SetSkinnedObject(avatar)
   1994                 self.skinLightmapRenderer.StartRendering()
   1995             elif self.skinLightmapRenderer is not None and visualModel and avatar and not LODMode:
   1996                 self.skinLightmapRenderer.SetSkinnedObject(avatar)
   1997             buildDataManager.NotifyUpdate()
   1998             self.lastUpdateRedundant = False
   1999             self.hasUpdatedOnce = True
   2000         except RedundantUpdateException as err:
   2001             self.lastUpdateRedundant = True
   2002             sys.exc_clear()
   2003         except TaskletExit as err:
   2004             self.lastUpdateRedundant = True
   2005             raise
   2006         except (OutOfVideoMemoryException, TextureLoadingFailureException) as err:
   2007             self.lastUpdateRedundant = True
   2008             newSize = map(lambda x: x / 2, self.mapBundle.baseResolution)
   2009             if trinity.device.scene and trinity.device.scene.shadowSize > newSize[0]:
   2010                 trinity.device.scene.shadowSize /= 2
   2011             self.hasUpdatedOnce = False
   2012             self.SetTextureSize(newSize)
   2013             sys.exc_clear()
   2014         except Exception as err:
   2015             log.LogException(str(err))
   2016             sys.exc_clear()
   2017         finally:
   2018             self.ReapUpdateChildTasklets()
   2019             buildDataManager.UnLock()
   2020             timeUpdatingSec = statistics.GetTimeDiffInSeconds(sTime)
   2021             statistics.updateTime.Add(timeUpdatingSec)
   2022             if not updateRuleBundle.videoMemoryFailure and not updateRuleBundle.textureLoadingFailure:
   2023                 uthread.new(self.NotifyUpdateDoneListeners_t)
   2024                 uthread.new(self.NotifyUpdateDoneCallbacks_t)
   2025             self._currentUpdateTasklet = None
   2026             if updateRuleBundle.videoMemoryFailure or updateRuleBundle.textureLoadingFailure:
   2027                 buildDataManager.SetAllAsDirty()
   2028                 uthread.new(self.Update, factory, avatar, visualModel)
   2029             AddMarker('End Start')
   2030             if channel is not None:
   2031                 channel.send(None)
   2032 
   2033     def NotifyUpdateDoneListeners_t(self):
   2034         for listener in self.onUpdateDoneListeners:
   2035             uthread.new(listener)
   2036 
   2037     def AddUpdateDoneListener(self, callBack):
   2038         if callBack not in self.onUpdateDoneListeners:
   2039             self.onUpdateDoneListeners.append(callBack)
   2040 
   2041     def NotifyUpdateDoneCallbacks_t(self):
   2042         for callback in self.onUpdateDoneCallbacks[:]:
   2043             uthread.new(callback, self)
   2044             self.onUpdateDoneCallbacks.remove(callback)
   2045 
   2046     def AddUpdateDoneCallback(self, callBack):
   2047         self.onUpdateDoneCallbacks.append(callBack)
   2048 
   2049     def WaitForChildTaskletsToFinish(self):
   2050         pdCf.WaitForAll(self._UpdateTaskletChildren, lambda x: x.alive)
   2051 
   2052     def WaitForTexturesToComposite(self, factory, hashKey, mapsTypesComposited, textureCompositingStartTime):
   2053         self.WaitForChildTaskletsToFinish()
   2054         statistics.mapsComposited.Add(len(mapsTypesComposited))
   2055         if factory.allowTextureCache and mapsTypesComposited:
   2056             mapsToSave = [ self.mapBundle[mapType] for mapType in mapsTypesComposited if self.mapBundle[mapType] is not None and self.mapBundle[mapType].isGood ]
   2057             uthread.new(pdDm.SaveMapsToDisk, hashKey, mapsToSave)
   2058         timeCompositedSec = statistics.GetTimeDiffInSeconds(textureCompositingStartTime)
   2059         statistics.compositeTime.Add(timeCompositedSec)
   2060 
   2061     def ConfigureMaskedShader(self, updateRuleBundle):
   2062         if self.useMaskedShaders or updateRuleBundle.undoMaskedShaders:
   2063             applicableModifiers = (modifier for modifier in self.buildDataManager.GetModifiersAsList() if modifier.categorie in pdDef.MASKING_CATEGORIES)
   2064             for modifier in applicableModifiers:
   2065                 for mesh in iter(modifier.meshes):
   2066                     self.ConfigureMeshForMaskedShader(mesh, remove=updateRuleBundle.undoMaskedShaders)
   2067 
   2068     def ConfigureMeshForMaskedShader(self, mesh, remove = False):
   2069         decalToOpaque = []
   2070         fx = pdCcf.GetEffectsFromAreaList(mesh.opaqueAreas)
   2071         for f in iter(fx):
   2072             for p in f.parameters:
   2073                 if p.name == 'CutMaskInfluence':
   2074                     p.value = 0.999
   2075 
   2076         for decalArea in mesh.decalAreas:
   2077             f = decalArea.effect
   2078             if f is None:
   2079                 continue
   2080             for p in f.parameters:
   2081                 if p.name == 'CutMaskInfluence':
   2082                     if remove:
   2083                         if abs(p.value - 0.85) < 0.001:
   2084                             decalToOpaque.append(decalArea)
   2085                         p.value = 0.0
   2086                     elif abs(p.value - 0.85) > 0.001 and abs(p.value - 0.999) > 0.001:
   2087                         p.value = 1.01
   2088 
   2089         if remove:
   2090             for area in decalToOpaque:
   2091                 mesh.decalAreas.remove(area)
   2092                 mesh.opaqueAreas.append(area)
   2093 
   2094         else:
   2095             pdCcf.MoveAreas(mesh.opaqueAreas, mesh.decalAreas)
   2096         for transparentArea in mesh.transparentAreas:
   2097             f = transparentArea.effect
   2098             if f is None:
   2099                 continue
   2100             for p in f.parameters:
   2101                 if p.name == 'CutMaskInfluence':
   2102                     p.value = 1.0
   2103 
   2104     def BakeDecals_t(self, factory, decalModifiers):
   2105         if decalModifiers:
   2106             createTargetAvatar = False
   2107             if self.decalBaker is None:
   2108                 createTargetAvatar = True
   2109                 self.decalBaker = DecalBaker(factory)
   2110             elif not self.decalBaker.HasAvatar():
   2111                 createTargetAvatar = True
   2112             self.decalBaker.Initialize()
   2113             if createTargetAvatar:
   2114                 self.decalBaker.CreateTargetAvatarFromDoll(self)
   2115                 self._UpdateTaskletChildren.append(self.decalBaker.avatarShaderSettingTasklet)
   2116             self.decalBaker.SetSize(self.textureResolution)
   2117             for decalModifier in decalModifiers:
   2118                 try:
   2119                     decalModifier.IsDirty = True
   2120                     try:
   2121                         self.decalBaker.BakeDecalToModifier(decalModifier.decalData, decalModifier)
   2122                     except TypeError:
   2123                         return
   2124                     finally:
   2125                         self._UpdateTaskletChildren.append(self.decalBaker.decalSettingTasklet)
   2126                         self._UpdateTaskletChildren.append(self.decalBaker.bakingTasklet)
   2127 
   2128                     while not self.decalBaker.isReady:
   2129                         pdCf.Yield()
   2130 
   2131                     decalModifier.mapL.update(decalModifier.mapD)
   2132                     decalModifier.colorize = True
   2133                 except TaskletExit:
   2134                     decalModifier.IsDirty = True
   2135                     raise
   2136 
   2137     def LogDNA(self, factory):
   2138         if factory.verbose:
   2139             lines = yaml.dump(self.GetDNA(), default_flow_style=False).splitlines()
   2140             log.LogInfo('Building paperDoll: ' + self.name)
   2141             for each in iter(lines):
   2142                 log.LogInfo(each)
   2143 
   2144     def CompositeTextures(self, factory, hashKey, updateRuleBundle):
   2145         if not updateRuleBundle.blendShapesOnly and factory.allowTextureCache:
   2146             textureCache = {}
   2147             mapCount = len(pdDef.MAPS)
   2148             for i in xrange(mapCount):
   2149                 tex = pdDm.FindCachedMap(hashKey, self.mapBundle.GetResolutionByMapType(i)[0], pdDef.MAPS[i])
   2150                 textureCache[i] = tex
   2151 
   2152             pdCf.WaitForAll(textureCache.values(), lambda x: x is not None and not x.isPrepared)
   2153             for i in xrange(mapCount):
   2154                 if textureCache.get(i):
   2155                     if textureCache[i].isGood:
   2156                         self.mapBundle.SetMapByTypeIndex(i, textureCache[i], hashKey)
   2157                     else:
   2158                         updateRuleBundle.textureLoadingFailure = True
   2159                         log.LogError('Paperdoll::Texture failed to load.  Index %d' % i)
   2160                         raise TextureLoadingFailureException('Paperdoll::Texture failed to load.  Index %d' % i)
   2161 
   2162         if self.compressionSettings is not None:
   2163             self.compressionSettings.generateMipmap = False
   2164         mapsTypesComposited = []
   2165         for mapTypeIndex in updateRuleBundle.mapsToComposite:
   2166             existingMap = self.mapBundle[mapTypeIndex]
   2167             if existingMap and existingMap.isGood and hashKey == self.mapBundle.hashKeys.get(mapTypeIndex):
   2168                 continue
   2169             else:
   2170                 mapsTypesComposited.append(mapTypeIndex)
   2171 
   2172         def CompositeTasklet_t(mapTypeIndex, copyToExistingTexture):
   2173             textureCompositor = tc.TextureCompositor(resData=factory.resData)
   2174             pdCf.BeFrameNice()
   2175             texture = None
   2176             try:
   2177                 texture = factory.CompositeCombinedTexture(mapTypeIndex, self.gender, self.buildDataManager, self.mapBundle, textureCompositor, self.compressionSettings, partsToComposite=updateRuleBundle.partsToComposite, copyToExistingTexture=copyToExistingTexture, lod=self.currentLOD)
   2178             except (trinity.ALOutOfMemoryError, tc.TextureLoadingError):
   2179                 info = sys.exc_info()
   2180                 if info[0] == trinity.ALOutOfMemoryError:
   2181                     updateRuleBundle.videoMemoryFailure = True
   2182                 else:
   2183                     updateRuleBundle.textureLoadingFailure = True
   2184                 sys.exc_clear()
   2185 
   2186             if texture:
   2187                 self.mapBundle.SetMapByTypeIndex(mapTypeIndex, texture, hashKey)
   2188 
   2189         copyToExistingTexture = len(mapsTypesComposited) == 1 and not updateRuleBundle.meshesChanged
   2190         for mapTypeIndex in mapsTypesComposited:
   2191             self.SpawnUpdateChildTasklet(CompositeTasklet_t, mapTypeIndex, copyToExistingTexture)
   2192 
   2193         return mapsTypesComposited
   2194 
   2195     def LoadBindPose(self):
   2196         if not self.bindPose:
   2197             filename = 'res:/Graphics/Character/Global/FaceSetup/BaseFemaleBindPose.yaml'
   2198             if self.gender == pdDef.GENDER.MALE:
   2199                 filename = 'res:/Graphics/Character/Global/FaceSetup/BaseMaleBindPose.yaml'
   2200             self.bindPose = pdDm.LoadYamlFileNicely(filename)
   2201 
   2202     def LoadClothData(self, modifierList):
   2203         clothMeshes = []
   2204         clothModifierGenerator = (modifier for modifier in modifierList if modifier.clothPath and not modifier.clothData)
   2205         for modifier in clothModifierGenerator:
   2206             clothLoadPath = modifier.clothPath
   2207             if not clothLoadPath:
   2208                 modifier.clothData = None
   2209                 continue
   2210             if clothLoadPath:
   2211                 clothData = None
   2212                 modifier.clothData = clothData
   2213 
   2214         pdCf.WaitForAll(clothMeshes, lambda x: not x.clothingRes.isPrepared)
   2215         for mesh in clothMeshes:
   2216             if not mesh.clothingRes.isGood:
   2217                 raise ValueError('Paperdoll::Clothing mesh failed to load.')
   2218 
   2219         return clothMeshes
   2220 
   2221     def SetTextureSize(self, *args):
   2222         if type(args[0]) in (list, tuple):
   2223             x, y = args[0][:2]
   2224         else:
   2225             x, y = args
   2226         if self.skinLightmapRenderer is not None:
   2227             self.skinLightmapRenderer.Refresh()
   2228         oldX, oldY = self.mapBundle.baseResolution
   2229         self.mapBundle.SetBaseResolution(x, y)
   2230         self.mapBundle.ReCreate(includeFixedSizeMaps=False)
   2231         for modifier in self.buildDataManager.GetModifiersAsList():
   2232             if modifier.IsTextureContainingModifier() and not modifier.IsDirty:
   2233                 modifier.IsDirty = True
   2234 
   2235     def getbusyUpdating(self):
   2236         return self._currentUpdateTasklet is not None or any(map(lambda x: x.alive, self._UpdateTaskletChildren))
   2237 
   2238     busyUpdating = property(fget=lambda self: self.getbusyUpdating())
   2239 
   2240     def setoverrideLod(self, newLod, affectTextureSize = True):
   2241         oldLod = self.currentLOD
   2242         self.currentLOD = newLod
   2243         self.previousLOD = oldLod
   2244         self.buildDataManager.SetLOD(newLod)
   2245         if oldLod == pdDef.LOD_SKIN and newLod == pdDef.LOD_SCATTER_SKIN:
   2246             self.buildDataManager.SetAllAsDirty()
   2247             return
   2248         if oldLod == pdDef.LOD_SCATTER_SKIN and newLod >= pdDef.LOD_SKIN:
   2249             self.skinLightmapRenderer = None
   2250             if newLod == pdDef.LOD_SKIN:
   2251                 return
   2252         if self.gender == pdDef.GENDER.MALE:
   2253             for bm in self.buildDataManager.GetModifiersByCategory(pdDef.HAIR_CATEGORIES.BEARD):
   2254                 bm.IsDirty = True
   2255 
   2256         for modifier in self.buildDataManager.GetModifiersAsList():
   2257             if modifier.IsMeshContainingModifier():
   2258                 modifier.IsDirty = True
   2259                 del modifier.meshes[:]
   2260                 modifier.meshGeometryResPaths = {}
   2261 
   2262         if affectTextureSize and newLod >= 0 and newLod < len(pdCfg.PerformanceOptions.lodTextureSizes):
   2263             self.SetTextureSize(*pdCfg.PerformanceOptions.lodTextureSizes[newLod])
   2264 
   2265     overrideLod = property(fget=lambda self: self.currentLOD, fset=setoverrideLod)
   2266     textureResolution = property(fget=lambda self: self.mapBundle.baseResolution, fset=SetTextureSize)
   2267 
   2268     def setusefastshader(self, value):
   2269         if self.__useFastShader != value:
   2270             self.__useFastShader = value
   2271             self.buildDataManager.SetAllAsDirty(clearMeshes=True)
   2272 
   2273     useFastShader = property(fget=lambda self: self.__useFastShader, fset=setusefastshader)