projectedDecals.py (22618B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\paperDoll\projectedDecals.py 2 import trinity 3 import uthread 4 import blue 5 import copy 6 import yaml 7 import eve.common.script.paperDoll.paperDollDefinitions as pdDef 8 import eve.common.script.paperDoll.paperDollCommonFunctions as PD 9 import eve.client.script.paperDoll.commonClientFunctions as pdCcF 10 import eve.common.script.paperDoll.paperDollDataManagement as pdDm 11 ST_SHADERRES = 'res:/Graphics/Effect/Managed/Interior/Avatar/SkinnedAvatarBRDF.fx' 12 RT_SHADERRES = 'res:/Graphics/Effect/Managed/Interior/Avatar/SkinnedAvatarTattooBRDF.fx' 13 BK_SHADERRES = 'res:/Graphics/Effect/Managed/Interior/Avatar/SkinnedAvatarTattooBaking.fx' 14 INV_SHADERRES = 'res:/Graphics/Effect/Managed/Interior/Avatar/InvisibleAvatar.fx' 15 EO_SHADERRES = 'res:/Graphics/Effect/Utility/Compositing/ExpandOpaque.fx' 16 MASK_SHADERRES = 'res:/Graphics/Effect/Managed/Interior/Avatar/SkinnedAvatarTattooMaskBaking.fx' 17 DECAL_PROJECTION_DISABLED = -1.0 18 DECAL_PROJECTION_PLANAR = 0.0 19 DECAL_PROJECTION_CYLINDRICAL = 1.0 20 DECAL_PROJECTION_CAMERA = 2.0 21 DECAL_PROJECTION_EO_LOD_THRESHOLD = 1 22 23 def ShowTexture(texture, showAlpha = False): 24 from carbon.tools.jessica.app.preview import Preview 25 Preview(texture) 26 27 28 class DecalBaker(object): 29 30 def __init__(self, factory): 31 self.factory = factory 32 self.__avatar = None 33 self._gender = None 34 self.bakeScene = None 35 self.size = None 36 self.isReady = False 37 self.doingAvatar = False 38 self.doingDecal = False 39 self.bakingTasklet = None 40 self.avatarShaderSettingTasklet = None 41 self.decalSettingTasklet = None 42 if pdDef.GENDER_ROOT: 43 self.femalePath = 'res:\\Graphics\\Character\\Female\\Paperdoll' 44 self.malePath = 'res:\\Graphics\\Character\\Male\\Paperdoll' 45 else: 46 self.femalePath = 'res:\\Graphics\\Character\\Modular\\Female' 47 self.malePath = 'res:\\Graphics\\Character\\Modular\\Male' 48 self.femaleBindPose = self.GetBindPose(pdDef.FEMALE_DECAL_BINDPOSE) 49 self.maleBindPose = self.GetBindPose(pdDef.MALE_DECAL_BINDPOSE) 50 51 def Initialize(self): 52 self.doingAvatar = False 53 self.doingDecal = False 54 55 def HasAvatar(self): 56 return self.__avatar is not None and type(self.__avatar) is trinity.Tr2IntSkinnedObject 57 58 def GetBindPose(self, resPath): 59 updater = trinity.Tr2GrannyAnimation() 60 updater.resPath = resPath 61 return updater 62 63 def SetSize(self, size): 64 if not (type(size) is tuple or type(size) is list) and len(size) == 2: 65 raise TypeError('DecalBaker::SetSize - Size is not a tuple or list of length 2!') 66 self.size = size 67 68 def __DoBlendShapes(self, doll): 69 self.factory.ApplyMorphTargetsToMeshes(self.__avatar.visualModel.meshes, doll.buildDataManager.GetMorphTargets()) 70 71 def __CreateNudeAvatar(self, doll): 72 if doll.gender == pdDef.GENDER.FEMALE: 73 genderPath = self.femalePath 74 else: 75 genderPath = self.malePath 76 self._gender = doll.gender 77 avatar = trinity.Tr2IntSkinnedObject() 78 avatar.visualModel = self.factory.CreateVisualModel(doll.gender) 79 avatar.name = 'TattooBakingAvatar' 80 bodyParts = [] 81 for nudePartModularPath in pdDef.DEFAULT_NUDE_PARTS: 82 bodyPartTuple = None 83 if pdDef.DOLL_PARTS.HEAD in nudePartModularPath: 84 headMods = doll.buildDataManager.GetModifiersByCategory(pdDef.DOLL_PARTS.HEAD) 85 if headMods: 86 headMod = doll.buildDataManager.GetModifiersByCategory(pdDef.DOLL_PARTS.HEAD)[0] 87 if headMod.redfile: 88 projectionHead = blue.resMan.LoadObject(headMod.redfile) 89 for mesh in projectionHead.meshes: 90 for decalArea in mesh.decalAreas: 91 f = decalArea.effect 92 if f is None: 93 continue 94 for p in f.parameters: 95 if p.name == 'CutMaskInfluence': 96 p.value = 0.0 97 elif p.name == 'DecalClip': 98 p.value = 0.0 99 100 mesh.opaqueAreas.append(decalArea) 101 mesh.decalAreas.remove(decalArea) 102 103 bodyPartTuple = (headMod.name, projectionHead) 104 if not bodyPartTuple: 105 mod = self.factory.CollectBuildData(self._gender, nudePartModularPath) 106 bodyPartTuple = (mod.name, blue.resMan.LoadObject(mod.redfile)) 107 for mesh in bodyPartTuple[1].meshes: 108 for opaqueArea in mesh.opaqueAreas: 109 f = opaqueArea.effect 110 if f is None: 111 continue 112 for p in f.parameters: 113 if p.name == 'CutMaskInfluence': 114 p.value = 0.0 115 elif p.name == 'DecalClip': 116 p.value = 0.0 117 118 bodyParts.append(bodyPartTuple) 119 120 PD.BeFrameNice() 121 for bodyPartTuple in bodyParts: 122 modname, bodyPart = bodyPartTuple 123 if bodyPart: 124 for mesh in bodyPart.meshes: 125 mesh.name = modname 126 if len(mesh.opaqueAreas) > 0: 127 areasCount = mesh.GetAreasCount() 128 for i in xrange(areasCount): 129 areas = mesh.GetAreas(i) 130 if areas and areas != mesh.opaqueAreas: 131 del areas[:] 132 133 avatar.visualModel.meshes.append(mesh) 134 135 self.__avatar = avatar 136 self.__DoBlendShapes(doll) 137 if doll.gender == pdDef.GENDER.FEMALE: 138 bindPose = self.femaleBindPose 139 else: 140 bindPose = self.maleBindPose 141 avatar.animationUpdater = bindPose 142 clip = bindPose.grannyRes.GetAnimationName(0) 143 bindPose.PlayAnimationEx(clip, 0, 0, 0) 144 avatar.ResetAnimationBindings() 145 bindPose.EndAnimation() 146 147 def CreateTargetAvatarFromDoll(self, doll): 148 self.doingAvatar = True 149 self.__CreateNudeAvatar(doll) 150 151 def PrepareAvatar_t(): 152 try: 153 self.SetShader_t(self.__avatar, [], BK_SHADERRES, False) 154 finally: 155 self.doingAvatar = False 156 157 self.avatarShaderSettingTasklet = uthread.new(PrepareAvatar_t) 158 uthread.schedule(self.avatarShaderSettingTasklet) 159 160 def CreateTargetsOnModifier(self, modifier, asRenderTargets = False): 161 textureFormat = trinity.PIXEL_FORMAT.B8G8R8A8_UNORM 162 163 def validateTarget(target, width, height): 164 if not (target and target.isGood and target.depth > 0): 165 return False 166 if target.width != width or target.height != height: 167 return False 168 return True 169 170 if modifier.decalData.bodyEnabled: 171 modifier.mapD[pdDef.DOLL_PARTS.BODY] = trinity.TriTextureRes(trinity.Tr2RenderTarget(self.size[0] / 2, self.size[1], 1, textureFormat)) 172 if modifier.decalData.headEnabled: 173 modifier.mapD[pdDef.DOLL_PARTS.HEAD] = trinity.TriTextureRes(trinity.Tr2RenderTarget(self.size[0] / 2, self.size[1] / 2, 1, textureFormat)) 174 175 def BakeDecalToModifier(self, decal, projectedDecalModifier): 176 while self.doingAvatar: 177 PD.Yield(frameNice=False) 178 179 if type(self.__avatar) is not trinity.Tr2IntSkinnedObject: 180 raise TypeError('DecalBaker::DoPrepare - Avatar is not set!') 181 if type(decal) is not ProjectedDecal: 182 raise TypeError('DecalBaker::BakeDecalToModifier - decal is not an instance of PaperDoll::ProjectedDecal!') 183 self.isReady = False 184 self.doingDecal = False 185 if projectedDecalModifier.decalData != decal: 186 projectedDecalModifier.decalData = decal 187 188 def PrepareDecal_t(): 189 self.doingDecal = True 190 self.SetDecal_t(self.__avatar, decal, True, True) 191 self.doingDecal = False 192 193 self.decalSettingTasklet = uthread.new(PrepareDecal_t) 194 self.CreateTargetsOnModifier(projectedDecalModifier) 195 self.bakingTasklet = uthread.new(self.DoBake_t, projectedDecalModifier) 196 197 def _ExpandOpaque(self, bodyPart, targetTex): 198 eoMaskPath = 'res:/graphics/character/global/tattoomask/{0}_opaque_mask_{1}.dds'.format(self._gender, bodyPart) 199 eoMaskRes = blue.resMan.GetResource(eoMaskPath) 200 fx = trinity.Tr2Effect() 201 fx.effectFilePath = EO_SHADERRES 202 while not eoMaskRes.isPrepared or not fx.effectResource.isPrepared: 203 PD.Yield() 204 205 if not eoMaskRes.isGood: 206 raise ValueError('Paperdoll::Opaque mask resource failed to load.') 207 if not fx.effectResource.isGood: 208 raise ValueError('Paperdoll::Effect resource failed to load.') 209 tex = trinity.TriTextureParameter() 210 tex.name = 'Mask' 211 tex.SetResource(eoMaskRes) 212 fx.resources.append(tex) 213 v = trinity.Tr2Vector2Parameter() 214 v.name = 'gMaskSize' 215 v.value = (eoMaskRes.width, eoMaskRes.height) 216 fx.parameters.append(v) 217 tex = trinity.TriTextureParameter() 218 tex.name = 'Texture' 219 tex.SetResource(targetTex) 220 fx.resources.append(tex) 221 v = trinity.Tr2Vector2Parameter() 222 v.name = 'gTextureSize' 223 v.value = (targetTex.width, targetTex.height) 224 fx.parameters.append(v) 225 fx.RebuildCachedData() 226 vp = trinity.TriViewport() 227 vp.width = targetTex.width 228 vp.height = targetTex.height 229 expandedRT = trinity.Tr2RenderTarget(vp.width, vp.height, 1, trinity.PIXEL_FORMAT.B8G8R8A8_UNORM) 230 rj = trinity.CreateRenderJob('Expanding Opaque') 231 rj.PushRenderTarget(expandedRT) 232 rj.SetProjection(trinity.TriProjection()) 233 rj.SetView(trinity.TriView()) 234 rj.SetViewport(vp) 235 rj.PushDepthStencil(None) 236 rj.Clear((0.0, 0.0, 0.0, 0.0)) 237 rj.SetStdRndStates(trinity.RM_FULLSCREEN) 238 rj.SetRenderState(trinity.D3DRS_SEPARATEALPHABLENDENABLE, 1) 239 rj.SetRenderState(trinity.D3DRS_SRCBLENDALPHA, trinity.TRIBLEND_ONE) 240 rj.SetRenderState(trinity.D3DRS_DESTBLENDALPHA, trinity.TRIBLEND_ZERO) 241 rj.RenderEffect(fx) 242 rj.SetRenderTarget(targetTex.wrappedRenderTarget) 243 rj.RenderTexture(expandedRT) 244 rj.PopRenderTarget() 245 rj.PopDepthStencil() 246 rj.ScheduleChained() 247 rj.WaitForFinish() 248 249 def _Bake(self, targetTex, transform): 250 self.SetShaderValue(self.__avatar, 'TattooVSUVTransform', transform) 251 size = (targetTex.width, targetTex.height) 252 renderTarget = targetTex.wrappedRenderTarget 253 sourceVP = trinity.TriViewport() 254 sourceVP.width = size[0] 255 sourceVP.height = size[1] 256 rj = trinity.CreateRenderJob('Baking out source decal texture') 257 rj.PushRenderTarget(renderTarget) 258 rj.SetProjection(trinity.TriProjection()) 259 rj.SetView(trinity.TriView()) 260 rj.SetViewport(sourceVP) 261 rj.PushDepthStencil(None) 262 rj.Clear((0.0, 0.0, 0.0, 0.0)) 263 rj.SetStdRndStates(trinity.RM_FULLSCREEN) 264 rj.Update(self.bakeScene) 265 rj.RenderScene(self.bakeScene) 266 rj.PopRenderTarget() 267 rj.PopDepthStencil() 268 rj.ScheduleChained() 269 rj.WaitForFinish() 270 271 def DoBake_t(self, projectedDecalModifier): 272 while self.doingDecal or self.doingAvatar: 273 PD.Yield() 274 275 self.bakeScene = trinity.WodBakingScene() 276 self.bakeScene.Avatar = self.__avatar 277 if projectedDecalModifier.decalData.bodyEnabled: 278 while not projectedDecalModifier.mapD[pdDef.DOLL_PARTS.BODY].isPrepared: 279 PD.Yield(frameNice=False) 280 281 if not projectedDecalModifier.mapD[pdDef.DOLL_PARTS.BODY].isGood: 282 raise ValueError('Paperdoll::Decal modifier body diffuse map failed to load.') 283 bodyTex = projectedDecalModifier.mapD[pdDef.DOLL_PARTS.BODY] 284 self._Bake(bodyTex, (0.0, 0.0, 2.0, 1.0)) 285 self._ExpandOpaque(pdDef.DOLL_PARTS.BODY, bodyTex) 286 elif projectedDecalModifier.mapD.get(pdDef.DOLL_PARTS.BODY): 287 del projectedDecalModifier.mapD[pdDef.DOLL_PARTS.BODY] 288 if projectedDecalModifier.decalData.headEnabled: 289 while not projectedDecalModifier.mapD[pdDef.DOLL_PARTS.HEAD].isPrepared: 290 PD.Yield(frameNice=False) 291 292 if not projectedDecalModifier.mapD[pdDef.DOLL_PARTS.HEAD].isGood: 293 raise ValueError('Paperdoll::Decal modifier head diffuse map failed to load.') 294 headTex = projectedDecalModifier.mapD[pdDef.DOLL_PARTS.HEAD] 295 self._Bake(headTex, (-0.5, 0.0, 2.0, 2.0)) 296 self._ExpandOpaque(pdDef.DOLL_PARTS.HEAD, headTex) 297 elif projectedDecalModifier.mapD.get(pdDef.DOLL_PARTS.HEAD): 298 del projectedDecalModifier.mapD[pdDef.DOLL_PARTS.HEAD] 299 self.bakeScene = None 300 self.isReady = True 301 302 def SetShader_t(self, avatar, targetsToIgnore, shaderres, allTargets = False): 303 try: 304 effect = trinity.Tr2Effect() 305 effect.effectFilePath = shaderres 306 while not effect.effectResource.isPrepared: 307 PD.Yield() 308 309 if not effect.effectResource.isGood: 310 raise TaskletExit('Paperdoll::Effect resource failed to load for shader %s' % shaderres) 311 for mesh in avatar.visualModel.meshes: 312 areasList = [mesh.opaqueAreas, mesh.decalAreas, mesh.transparentAreas] 313 if allTargets or mesh.name not in targetsToIgnore: 314 for areas in areasList: 315 for area in areas: 316 transformUV = None 317 for p in area.effect.parameters: 318 if p.name == 'TransformUV0': 319 transformUV = p.value 320 break 321 322 area.effect.effectFilePath = shaderres 323 area.effect.PopulateParameters() 324 area.effect.RebuildCachedData() 325 for p in area.effect.parameters: 326 if p.name == 'TransformUV0': 327 p.value = transformUV 328 break 329 330 except TaskletExit: 331 raise 332 333 def SetShaderValue(self, avatar, name, value): 334 for mesh in avatar.visualModel.meshes: 335 for effect in pdCcF.GetEffectsFromMesh(mesh): 336 effect.StartUpdate() 337 for p in effect.parameters: 338 if p.name == name: 339 p.value = value 340 break 341 342 effect.RebuildCachedData() 343 effect.EndUpdate() 344 345 def SetDecal_t(self, avatar, decal, setTexture, setMask): 346 if decal is None: 347 return 348 while self.doingAvatar or decal.BusyLoading(): 349 PD.Yield(frameNice=False) 350 351 if not decal.maskResource.isGood: 352 raise ValueError('Paperdoll::Decal mask resource failed to load.') 353 if not decal.textureResource.isGood: 354 raise ValueError('Paperdoll::Decal texture resource failed to load.') 355 for mesh in iter(avatar.visualModel.meshes): 356 for effect in pdCcF.GetEffectsFromMesh(mesh): 357 effect.StartUpdate() 358 for p in effect.parameters: 359 valuesToSet = None 360 if p.name == 'TattooYawPitchRoll': 361 valuesToSet = decal.GetYPR() 362 elif p.name == 'TattooPosition': 363 valuesToSet = decal.GetPositionAndScale() 364 elif p.name == 'TattooOptions': 365 valuesToSet = decal.GetOptions() 366 elif p.name == 'TattooDimensions': 367 valuesToSet = decal.GetDimensions() 368 elif p.name == 'TattooAspectRatio' and hasattr(decal, 'aspectRatio'): 369 valuesToSet = decal.aspectRatio 370 elif p.name == 'TattooPickingLayer': 371 valuesToSet = decal.layer 372 if type(valuesToSet) == tuple: 373 valLen = len(valuesToSet) 374 if valLen < len(p.value): 375 valuesToSet = valuesToSet + p.value[valLen:] 376 if valuesToSet: 377 p.value = valuesToSet 378 379 resourceNameFound = False 380 if setTexture: 381 resourceNameFound = self.SetTexture_t('TattooTextureMap', decal.textureResource, effect) 382 if setMask and resourceNameFound: 383 resourceNameFound = self.SetTexture_t('TattooTextureMask', decal.maskResource, effect) 384 effect.RebuildCachedData() 385 effect.EndUpdate() 386 387 def SetTexture_t(self, name, texture, effect): 388 for i, r in enumerate(effect.resources): 389 if r.name == name: 390 r.SetResource(texture) 391 return True 392 393 return False 394 395 396 class ProjectedDecal(object): 397 398 def __init__(self): 399 self.layer = 0 400 self.azimuth = 0.0 401 self.incline = 1.5707963267948966 402 self.bodyEnabled = True 403 self.headEnabled = True 404 self.posx = 0.0 405 self.posy = 0.0 406 self.posz = 0.0 407 self.scale = 0.0 408 self.aspectRatio = 1.0 409 self.angleRotation = 180.0 410 self.mode = DECAL_PROJECTION_DISABLED 411 self.flipx = False 412 self.flipy = False 413 self.offsety = 0.0 414 self.offsetx = 0.0 415 self.radius = 2.0 416 self.height = 4.0 417 self.yaw = 0.0 418 self.pitch = 0.0 419 self.roll = 0.0 420 self.planarBeta = 0.0 421 self.planarScale = 0.0 422 self.maskPathEnabled = False 423 self.texturePath = '' 424 self.maskPath = '' 425 self.label = '' 426 self.textureResource = None 427 self.maskResource = None 428 429 def __str__(self): 430 return yaml.dump(self.__getstate__(), default_flow_style=False, Dumper=yaml.CDumper) 431 432 def __getstate__(self): 433 state = dict() 434 for key, value in self.__dict__.iteritems(): 435 if type(value) is not trinity.TriTextureRes: 436 state[key] = copy.copy(value) 437 else: 438 state[key] = None 439 440 return state 441 442 def __eq__(self, other): 443 if not other: 444 return False 445 typesThatMatter = (float, 446 bool, 447 str, 448 int) 449 for key, val in other.__dict__.iteritems(): 450 if key not in self.__dict__: 451 return False 452 if type(self.__dict__[key]) in typesThatMatter: 453 if self.__dict__[key] != val: 454 return False 455 456 return True 457 458 def __ne__(self, other): 459 return not self.__eq__(other) 460 461 def BusyLoading(self): 462 if self.texturePath and not self.textureResource: 463 raise AttributeError('PaperDoll::ProjectedDecal - Decal has texturepath defined but no texture resource and is not loading it!') 464 if self.maskPath and not self.maskResource: 465 raise AttributeError('PaperDoll::ProjectedDecal - Decal has maskpath defined but no texture resource and is not loading it!') 466 loading = self.textureResource and not self.textureResource.isPrepared or self.maskResource and not self.maskResource.isPrepared 467 return loading 468 469 def __GetResourceSetPath(self, path, pathMember): 470 if path: 471 setattr(self, pathMember, path) 472 return blue.resMan.GetResource(str(path)) 473 else: 474 return None 475 476 def SetTexturePath(self, path): 477 self.textureResource = self.__GetResourceSetPath(path, 'texturePath') 478 479 def SetMaskPath(self, path): 480 self.maskResource = self.__GetResourceSetPath(path, 'maskPath') 481 482 def SetCylindricalProjection(self): 483 self.mode = DECAL_PROJECTION_CYLINDRICAL 484 485 def SetPlanarProjection(self): 486 self.mode = DECAL_PROJECTION_PLANAR 487 488 def SetDisabledProjection(self): 489 self.mode = DECAL_PROJECTION_DISABLED 490 491 def SetPosition(self, posx, posy, posz): 492 self.posx = posx 493 self.posy = posy 494 self.posz = posz 495 496 def GetPositionAndScale(self): 497 return (self.posx, 498 self.posy, 499 self.posz, 500 self.scale) 501 502 def SetOptions(self, angleRotation, flipX, flipY, offsetY = 0.0, offsetX = 0.0): 503 self.angleRotation = angleRotation 504 self.flipx = flipX 505 self.flipy = flipY 506 self.offsety = offsetY 507 self.offsetx = offsetX 508 509 def GetOptions(self): 510 flipPack = 10.0 * float(self.flipy) + float(self.flipx) 511 return (self.angleRotation, 512 flipPack, 513 self.offsety, 514 self.offsetx) 515 516 def SetDimensions(self, radius, height): 517 self.radius = radius 518 self.height = height 519 520 def GetDimensions(self): 521 if self.mode == DECAL_PROJECTION_PLANAR: 522 return (self.planarBeta, self.planarScale, float(self.maskPathEnabled)) 523 else: 524 return (self.radius, self.height, float(self.maskPathEnabled)) 525 526 def SetYPR(self, yaw, pitch, roll): 527 self.yaw = yaw 528 self.pitch = pitch 529 self.roll = roll 530 531 def GetYPR(self): 532 return (self.yaw, 533 self.pitch, 534 self.roll, 535 self.mode) 536 537 @staticmethod 538 def Load(source): 539 inst = source 540 if isinstance(source, basestring): 541 inst = pdDm.LoadYamlFileNicely(source) 542 projectedDecal = ProjectedDecal() 543 for key, val in inst.__dict__.iteritems(): 544 if key in projectedDecal.__dict__: 545 projectedDecal.__dict__[key] = val 546 547 if inst.texturePath: 548 projectedDecal.SetTexturePath(inst.texturePath) 549 if inst.maskPath: 550 projectedDecal.SetMaskPath(inst.maskPath) 551 return projectedDecal 552 553 @staticmethod 554 def Save(source, resPath): 555 f = file(resPath, 'w') 556 yaml.dump(source, f, default_flow_style=False, Dumper=yaml.CDumper) 557 f.flush() 558 f.close()