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)