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

renderer_apparel.py (35805B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\iconrendering2\renderers\renderer_apparel.py
      2 from __future__ import print_function
      3 import os
      4 import wx
      5 import dogma.data as dogma_data
      6 import trinity
      7 import blue
      8 import imageutils
      9 import PIL.Image as PILImage
     10 import charactercreator.client.animparams as animparams
     11 import charactercreator.const as ccConst
     12 import eve.client.script.ui.login.charcreation.ccUtil as ccUtil
     13 import eve.common.script.paperDoll.paperDollDataManagement as pdDm
     14 from eve.client.script.paperDoll.paperDollImpl import Doll
     15 from eve.client.script.paperDoll.paperDollSpawnWrappers import PaperDollCharacter
     16 from eve.common.script.paperDoll.paperDollDefinitions import LOD_SKIN
     17 from eve.common.script.util.paperDollBloodLineAssets import bloodlineAssets
     18 import evegraphics.settings as settings
     19 import types
     20 import log
     21 from charactercreator.client.characterDollUtilities import CharacterDollUtilities
     22 from eve.client.script.ui.camera.charCreationCamera import CharCreationCamera
     23 from eve.client.script.ui.login.charcreation.assetRendererConst import DRESSCODE, SETUP, TUCKINDEX, EXAGGERATE, SCARGROUPS, SCARCAMERASETINGS, MALE_MANNEQUIN, FEMALE_MANNEQUIN, LIGHTLOCATION, PIERCINGRIGHTGROUPS, PIERCINGLEFTGROUPS, PIERCINGPAIRGROUPS, NORMAL_LIGHT_SETTINGS, DRESSCODE_CUSTOM_BY_CATEGORY, DRESSCODE_FEMALE_DEFAULT, DRESSCODE_MALE_DEFAULT, DRESSCODE_CATEGORIES, DOLL_MODIFIERS
     24 
     25 def GetNESDirectory():
     26     try:
     27         up = os.path.join(os.environ['USERPROFILE'])
     28         return os.path.join(up, 'Desktop', 'CharacterIconOutput')
     29     except:
     30         return ''
     31 
     32 
     33 def GetNESIconPath(path, genderID):
     34     directory = GetNESDirectory()
     35     if not os.path.exists(directory):
     36         os.mkdir(directory)
     37     directory += '\\Male' if genderID == ccConst.GENDERID_MALE else '\\Female'
     38     if not os.path.exists(directory):
     39         os.mkdir(directory)
     40     return directory + '\\' + os.path.basename(path)
     41 
     42 
     43 CLIENT_OUTPUT_ROOT = 'res:\\UI\\Asset\\'
     44 CLEAR_COLOR = (0.9,
     45  0.9,
     46  0.9,
     47  0.0)
     48 BACKGROUND_PATH = 'res:/UI/Texture/CharacterCreation/assetBackground.png'
     49 RENDER_SIZE = 2048
     50 NES_ICON_SIZE = 512
     51 CLIENT_ICON_SIZE = 128
     52 
     53 class ApparelRenderer(object):
     54 
     55     def __init__(self, outputSuffix = ''):
     56         self.scene = None
     57         self.lightScene = None
     58         self.camera = None
     59         self.characterSvc = sm.GetService('character')
     60         self.paperDollClient = sm.GetService('paperDollClient')
     61         self.sceneMgr = sm.GetService('sceneManager')
     62         self.factory = self.characterSvc.factory
     63         self.resolution = ccConst.TEXTURE_RESOLUTIONS[0]
     64         self.genClientIcon = True
     65         self.genNESIcon = True
     66         self.charID = 0
     67         self.renderJob = None
     68         self.doll = None
     69         self.avatar = None
     70         self.cdu = CharacterDollUtilities()
     71         self.mannequin = None
     72         self.backBuffer = None
     73         self.floor = None
     74         self.rt = None
     75         self.clear_step = None
     76         self.outputSuffix = outputSuffix
     77 
     78     def SetupScene(self, gender = False, bloodlineID = None):
     79         settings.Set(settings.GFX_ANTI_ALIASING, settings.AA_QUALITY_DISABLED)
     80         self.scene = trinity.Load(ccConst.SCENE_PATH_CUSTOMIZATION)
     81         self.lightScene = trinity.Load(NORMAL_LIGHT_SETTINGS)
     82         ccUtil.SetupLighting(self.scene, self.lightScene, self.lightScene)
     83         self.camera = CharCreationCamera(None)
     84         self.camera.fieldOfView = 0.5
     85         self.camera.distance = 7.0
     86         self.camera.frontClip = 0.1
     87         self.camera.backclip = 100.0
     88         if bloodlineID is not None:
     89             gender_name = 'female'
     90             if gender:
     91                 gender_name = 'male'
     92             self.mannequin = self.PrepareBloodlineDoll(bloodlineID, gender_name, self.scene)
     93             self.avatar = self.mannequin.avatar
     94         else:
     95             self.avatar, self.mannequin = self.CreateAndLoadMannequin(gender, self.scene)
     96         self.renderJob = self.CreateCharacterRenderJob('testing')
     97         self.SetShadow(self.avatar, self.scene)
     98         self.scene.UpdateScene(blue.os.GetSimTime())
     99         blue.resMan.Wait()
    100 
    101     def CreateCharacterRenderJob(self, name):
    102         size = RENDER_SIZE
    103         rt = trinity.Tr2RenderTarget(size, size, 1, trinity.PIXEL_FORMAT.B8G8R8A8_UNORM)
    104         rt.name = 'MyRT'
    105         ds = trinity.Tr2DepthStencil(size, size, trinity.DEPTH_STENCIL_FORMAT.AUTO)
    106         ds.name = 'MyDS'
    107         self.rt = rt
    108         rj = trinity.CreateRenderJob('TakeSnapShot')
    109         rj.PushRenderTarget(rt)
    110         rj.PushDepthStencil(ds)
    111         self.clear_step = rj.Clear((0.0, 0.0, 0.0, 0.0), 1.0)
    112         rj.SetView(self.camera.viewMatrix)
    113         rj.SetProjection(self.camera.projectionMatrix)
    114         rj.RenderScene(self.scene)
    115         trinity.WaitForResourceLoads()
    116         rj.PopDepthStencil()
    117         rj.PopRenderTarget()
    118         return rj
    119 
    120     def CreateAndLoadMannequin(self, genderID, scene):
    121         mannequin = PaperDollCharacter(self.factory)
    122         mannequin.doll = Doll('mannequin', gender=ccUtil.GenderIDToPaperDollGender(genderID))
    123         doll = mannequin.doll
    124         mannequin.avatar = trinity.Tr2IntSkinnedObject()
    125         self.doll = doll
    126         if genderID == ccConst.GENDERID_MALE:
    127             doll.Load(MALE_MANNEQUIN, self.factory)
    128         else:
    129             doll.Load(FEMALE_MANNEQUIN, self.factory)
    130         self.WaitForDoll(doll)
    131         doll.overrideLod = LOD_SKIN
    132         doll.textureResolution = self.resolution
    133         grannyPath = 'res:/Animation/PortraitCreator/Male/CharacterCreation/Mannequin.gr2'
    134         if doll.gender == 'female':
    135             grannyPath = 'res:/Animation/PortraitCreator/Female/CharacterCreation/Mannequin.gr2'
    136         self.factory.CreateGrannyAnimation(mannequin.avatar, grannyPath)
    137         mannequin.Spawn(scene, updateDoll=True)
    138         mannequin.WaitForUpdate()
    139         return (mannequin.avatar, mannequin)
    140 
    141     def WaitForDoll(self, doll):
    142         while doll.busyUpdating:
    143             blue.synchro.Yield()
    144 
    145     def WaitForDollAndScene(self, doll, scene):
    146         while len(scene.dynamics) < 1:
    147             blue.synchro.Yield()
    148 
    149         blue.synchro.Yield()
    150         self.WaitForDoll(doll)
    151 
    152     def FreezeCharacter(self, avatar):
    153         network = animparams.GetParamsPerAvatar(avatar)
    154         network.SetControlParameter('ControlParameters|isAlive', 0)
    155         network.update = False
    156         blue.synchro.Yield()
    157 
    158     def SetCameraAndLightPiercings(self, category, typeInfo, character, camera, scene):
    159         typeName, a, b = typeInfo
    160         if typeName.endswith('left', 0, -1):
    161             dictToUse = PIERCINGLEFTGROUPS
    162         elif typeName.endswith('right', 0, -1):
    163             dictToUse = PIERCINGRIGHTGROUPS
    164         else:
    165             dictToUse = PIERCINGPAIRGROUPS
    166         self.SetUpCamera(camera, category, character, dictToUse, scene)
    167 
    168     def SetCameraForScar(self, typeInfo, character, camera, scene):
    169         group = SCARGROUPS.get(typeInfo, None)
    170         if group is None:
    171             print('couldnt find the group, return')
    172             return
    173         self.SetUpCamera(camera, group, character, SCARCAMERASETINGS, scene)
    174 
    175     def SetCamera(self, camera, poi, distance, yaw, pitch):
    176         camera.SetPointOfInterest(poi)
    177         camera.distance = distance
    178         camera.SetFieldOfView(0.3)
    179         camera.SetYaw(yaw)
    180         camera.SetPitch(pitch)
    181         camera.Update()
    182 
    183     def SetUpCamera(self, camera, category, character, categoryList = SETUP, scene = None, genderID = None):
    184         if (category, genderID) in categoryList:
    185             options = categoryList[category, genderID]
    186         else:
    187             options = categoryList.get(category, None)
    188         if options:
    189             log.LogNotice('+ category = %s' % category)
    190             boneName, offset, lightSetting = options
    191             if lightSetting:
    192                 path = '%s%s.red' % (LIGHTLOCATION, lightSetting)
    193                 lightScene = trinity.Load(path)
    194                 ccUtil.SetupLighting(scene, lightScene, lightScene)
    195             log.LogNotice('before joint')
    196             joint = 4294967295L
    197             while joint == 4294967295L:
    198                 log.LogNotice('joint = %s' % joint)
    199                 log.LogNotice('boneName = %s' % boneName)
    200                 joint = character.avatar.GetBoneIndex(boneName)
    201                 log.LogNotice('j = %s' % joint)
    202                 blue.synchro.Yield()
    203                 log.LogNotice('done waiting')
    204 
    205             log.LogNotice('-- joint = %s' % joint)
    206             poi = character.avatar.GetBonePosition(joint)
    207             distance, yOffset, xOffset, yaw, pitch = offset
    208             x, y, z = poi
    209             if yOffset:
    210                 y += yOffset
    211             if xOffset:
    212                 x += xOffset
    213             poi = (x, y, z)
    214             log.LogNotice('before poi')
    215             if category in (ccConst.bottomouter, ccConst.feet):
    216                 poi = (0.0, y, z)
    217             log.LogNotice('before setting camera')
    218             self.SetCamera(camera, poi, distance, yaw, pitch)
    219             log.LogNotice('after setting camera')
    220             return (distance,
    221              yaw,
    222              pitch,
    223              poi)
    224         else:
    225             return
    226 
    227     def GetCategoriesFromPath(self, typeID, genderID):
    228         asset = GetPaperDollResource(typeID, genderID)
    229         path = '/' + asset.resPath
    230         rv = []
    231         categs = SETUP.keys()
    232         categs.sort()
    233         categs.reverse()
    234         for each in categs:
    235             if isinstance(each, types.StringTypes):
    236                 pathstr = '/%s/' % each
    237                 if pathstr in path.lower():
    238                     rv.append(each)
    239 
    240         return rv
    241 
    242     def PrepareBloodlineDoll(self, bloodlineID, paperdollGender, scene):
    243         self.characterSvc.RemoveCharacter(self.charID)
    244         self.SetupCharacter(bloodlineID, scene, paperdollGender)
    245         character = self.characterSvc.GetSingleCharacter(self.charID)
    246         character.avatar.translation = (0.0, 0.0, 0.0)
    247         grannyPath = 'res:/Animation/PortraitCreator/Male/CharacterCreation/Mannequin.gr2'
    248         if character.doll.gender == 'female':
    249             grannyPath = 'res:/Animation/PortraitCreator/Female/CharacterCreation/Mannequin.gr2'
    250         self.factory.CreateGrannyAnimation(character.avatar, grannyPath)
    251         self.WaitForDollAndScene(character.doll, scene)
    252         trinity.WaitForResourceLoads()
    253         return character
    254 
    255     def SetupCharacter(self, bloodlineID, scene, paperdollGender):
    256         self.characterSvc.AddCharacterToScene(self.charID, scene, paperdollGender, bloodlineID=bloodlineID, raceID=None, noRandomize=True)
    257         doll = self.characterSvc.GetSingleCharactersDoll(self.charID)
    258         self.WaitForDoll(doll)
    259         doll.overrideLod = LOD_SKIN
    260         doll.textureResolution = self.resolution
    261         self.characterSvc.SetDollBloodline(self.charID, bloodlineID)
    262         self.characterSvc.ApplyItemToDoll(self.charID, 'head', bloodlineAssets[bloodlineID], doUpdate=False)
    263         if paperdollGender == 'female':
    264             modifier = doll.AddResource('hair/hair_shortcut_03', 1.0, self.factory)
    265             modifier.SetColorVariation('20_a')
    266             doll.AddResource('makeup/eyebrows/eyebrows_01', 1.0, self.factory)
    267         else:
    268             modifier = doll.AddResource('hair/hair_stubble_01', 1.0, self.factory)
    269             modifier.SetColorVariation('20_a')
    270             doll.AddResource('makeup/eyebrows/eyebrows_01', 1.0, self.factory)
    271         self.characterSvc.UpdateDoll(self.charID, fromWhere='RenderLoop')
    272 
    273     def DoRenderNormalAssetType(self, typeID, genderID, bloodlineID = 7, genClientIcon = False, genNESIcon = False):
    274         camera = self.camera
    275         character = self.mannequin
    276         scene = self.scene
    277         doll = character.doll
    278         for category in self.GetCategoriesFromPath(typeID, genderID):
    279             typeData = [typeID]
    280             log.LogNotice('before dresscode')
    281             if category in DRESSCODE:
    282                 removeDcModifers = self.EnforceDresscode(bloodlineID, category, doll, genderID, character)
    283             log.LogNotice('go render type')
    284             for itemType in typeData:
    285                 wasRendered = self.RenderNormalType(bloodlineID, camera, category, character, genderID, itemType, scene, genClientIcon=genClientIcon, genNESIcon=genNESIcon)
    286 
    287     def DoRenderMannequinAssetType(self, typeID, genderID, category, avatar = None, mannequin = None, scene = None, genNESIcon = False, genClientIcon = False):
    288         if avatar is None:
    289             avatar = self.avatar
    290         if mannequin is None:
    291             mannequin = self.mannequin
    292         if scene is None:
    293             scene = self.scene
    294         self.SetUpCamera(self.camera, category, mannequin, scene=scene)
    295         mannequin.doll.buildDataManager.SetAllAsDirty()
    296         if category == ccConst.bottommiddle:
    297             pantsModifiers = mannequin.doll.buildDataManager.GetModifiersByCategory(ccConst.bottomouter)
    298             for pm in pantsModifiers:
    299                 mannequin.doll.RemoveResource(pm.GetResPath(), self.factory)
    300 
    301         modifierList = []
    302         asset = GetPaperDollResource(typeID, genderID)
    303         doll = mannequin.doll
    304         path = asset.resPath
    305         modifier = doll.SetItemType(self.factory, path, weight=1.0)
    306         if modifier:
    307             modifierList.append(modifier)
    308         mannequin.Update()
    309         blue.pyos.synchro.SleepWallclock(500)
    310         mannequin.WaitForUpdate()
    311         if not modifierList:
    312             return False
    313         self.SetShadow(avatar, scene)
    314         outputPath = self.GetOutputPath(assetPath=path, genderID=genderID, useMannequin=True, category=category, typeID=typeID)
    315         scene.UpdateScene(blue.os.GetSimTime())
    316         blue.resMan.Wait()
    317         if genClientIcon:
    318             self.CheckoutFile(outputPath)
    319             self.SaveScreenShot(outputPath, CLIENT_ICON_SIZE, True)
    320         if genNESIcon:
    321             outputPath = GetNESIconPath(outputPath, genderID)
    322             self.SaveScreenShot(outputPath, NES_ICON_SIZE, False)
    323         for modifier in modifierList:
    324             doll.RemoveResource(modifier.GetResPath(), self.factory)
    325 
    326         mannequin.Update()
    327         mannequin.WaitForUpdate()
    328         return True
    329 
    330     def RenderNormalType(self, bloodlineID, camera, category, character, genderID, itemType, scene, genClientIcon = False, genNESIcon = False):
    331         type_res_path = None
    332         for resource in cfg.paperdollResources:
    333             if resource.typeID == itemType and (resource.resGender == genderID or resource.resGender is None):
    334                 type_res_path = resource.resPath
    335 
    336         if type_res_path is None:
    337             raise ValueError('Type ID not found! %d' % itemType)
    338         doll = character.doll
    339         doll.buildDataManager.SetAllAsDirty()
    340         modifier = doll.SetItemType(self.factory, type_res_path, weight=1.0)
    341         if 'tattoo/head' in type_res_path.lower() and modifier:
    342             modifier.SetColorVariation('default_a')
    343         if not modifier:
    344             return False
    345         typeInfo = self.GetCategoriesFromPath(itemType, genderID)
    346         self.ApplyTuckingIfNeeded(category)
    347         self.TrySetColor(bloodlineID, category, genderID, typeInfo)
    348         if (category, genderID) in EXAGGERATE:
    349             if getattr(modifier, 'weight', None) is not None:
    350                 modifier.weight = 1.5 * modifier.weight
    351         character.Update()
    352         blue.pyos.synchro.SleepWallclock(500)
    353         character.WaitForUpdate()
    354         blue.resMan.Wait()
    355         trinity.WaitForResourceLoads()
    356         self.SetShadow(character.avatar, scene)
    357         outputPath = self.GetOutputPath(assetPath=type_res_path, genderID=genderID, category=category, bloodlineID=bloodlineID, typeID=itemType)
    358         if genderID == ccConst.GENDERID_FEMALE:
    359             character.avatar.animationUpdater.boneOffset.ClearTransforms()
    360         scene.UpdateScene(blue.os.GetSimTime())
    361         blue.resMan.Wait()
    362         if genClientIcon:
    363             self.CheckoutFile(outputPath)
    364             self.SaveScreenShot(outputPath, CLIENT_ICON_SIZE, True)
    365         if genNESIcon:
    366             outputPath = GetNESIconPath(outputPath, genderID)
    367             self.SaveScreenShot(outputPath, NES_ICON_SIZE, False)
    368         return True
    369 
    370     def EnforceDresscode(self, bloodlineID, category, doll, genderID, character):
    371         if category in DRESSCODE_CUSTOM_BY_CATEGORY:
    372             dressCode = DRESSCODE_CUSTOM_BY_CATEGORY[category][genderID]
    373         elif genderID == ccConst.GENDERID_FEMALE:
    374             dressCode = DRESSCODE_FEMALE_DEFAULT
    375         else:
    376             dressCode = DRESSCODE_MALE_DEFAULT
    377         removeDcModifers = []
    378         for dcCategory in DRESSCODE_CATEGORIES:
    379             if dcCategory == category:
    380                 continue
    381             dcTypeData = self.characterSvc.GetAvailableTypesByCategory(dcCategory, genderID, bloodlineID, None, forceAllTypes=True)
    382             if not dcTypeData:
    383                 continue
    384             for itemType in dcTypeData:
    385                 assetID = itemType[0]
    386                 if assetID in dressCode:
    387                     if dcCategory == ccConst.hair:
    388                         var = self.GetHairColor(genderID, bloodlineID)
    389                     else:
    390                         var = None
    391                     asset = GetPaperDollResource(itemType, genderID)
    392                     type_res_path = asset.resPath
    393                     dcModifier = doll.SetItemType(self.factory, type_res_path, weight=1.0)
    394                     character.Update()
    395                     character.WaitForUpdate()
    396                     if dcModifier:
    397                         removeDcModifers.append(dcModifier.GetResPath())
    398                     self.WaitForDoll(doll)
    399                     blue.resMan.Wait()
    400                     break
    401 
    402         return removeDcModifers
    403 
    404     def ApplyTuckingIfNeeded(self, category):
    405         if category not in TUCKINDEX:
    406             return
    407         tuckPath, requiredModifier, subKey = ccConst.TUCKMAPPING[category]
    408         tuckModifier = sm.GetService('character').GetModifierByCategory(self.charID, tuckPath)
    409         if tuckModifier:
    410             tuckVariations = tuckModifier.GetVariations()
    411             tuckStyle = tuckModifier.GetResPath().split('/')[-1]
    412             self.characterSvc.ApplyItemToDoll(self.charID, category, tuckStyle, variation=tuckVariations[TUCKINDEX[category]])
    413 
    414     def TrySetColor(self, bloodlineID, category, genderID, typeInfo):
    415         if category in (ccConst.beard, ccConst.hair, ccConst.eyebrows):
    416             category = ccConst.hair
    417         try:
    418             if typeInfo[1] or typeInfo[2]:
    419                 return
    420             categoryColors = self.characterSvc.GetAvailableColorsForCategory(category, genderID, None)
    421             if not categoryColors:
    422                 return
    423             primary, secondary = categoryColors
    424             primaryVal = (primary[1][0], primary[1][2])
    425             if primary and secondary:
    426                 secondaryVal = (secondary[1][0], secondary[1][2])
    427                 self.characterSvc.SetColorValueByCategory(self.charID, category, primaryVal, secondaryVal)
    428             else:
    429                 self.characterSvc.SetColorValueByCategory(self.charID, category, primaryVal, None)
    430         except:
    431             pass
    432         finally:
    433             if category == ccConst.hair:
    434                 sm.GetService('character').SetHairDarkness(0, 0.5)
    435 
    436     def SetShadow(self, avatar, scene):
    437         scene.shadowSize = 2048
    438 
    439     def GetHairColor(self, genderID, bloodlineID):
    440         colorsA, colorsB = sm.GetService('character').GetAvailableColorsForCategory(ccConst.hair, genderID, None)
    441         colorA = []
    442         colorB = []
    443         var = None
    444         color1Value, color1Name, color2Name, variation = (None, None, None, None)
    445         if len(colorsA) > 0:
    446             indexA = int(len(colorsA) * 0.3)
    447             colorA = colorsA[indexA]
    448             colorB = None
    449             if len(colorsB) > 0:
    450                 colorB = colorsB[0]
    451             color1Value, color1Name, color2Name, variation = sm.GetService('character').GetColorsToUse(colorA, colorB)
    452         if color1Value:
    453             return var
    454         if colorB:
    455             var = variation
    456         elif len(colorA) > 0:
    457             var = colorA[1]
    458         return var
    459 
    460     @staticmethod
    461     def CheckoutFile(path):
    462         import ccpp4
    463         p4 = ccpp4.P4Init()
    464         try:
    465             if p4.EditOrAdd(path, False):
    466                 return True
    467         except:
    468             pass
    469 
    470         return False
    471 
    472     def GetScreenshotRenderTarget(self, rt):
    473         readable = trinity.Tr2RenderTarget(rt.width, rt.height, 1, rt.format)
    474         if not readable.isValid:
    475             raise RuntimeError('out of video memory')
    476         rt.Resolve(readable)
    477         return readable
    478 
    479     def Tr2HostBitmapToPILImage(self, hostBitmap):
    480         import tempfile
    481         if not hostBitmap:
    482             return
    483         fd, path = tempfile.mkstemp(suffix='.png')
    484         hostBitmap.Save(path)
    485         bitmap = PILImage.open(path)
    486         bitmap.load()
    487         os.close(fd)
    488         os.remove(path)
    489         return bitmap
    490 
    491     def SaveScreenShot(self, outputPath, targetRes = 512, applyBackground = False):
    492         import imageutils
    493         from PIL import Image as PILImage
    494         print('SaveScreenShot', outputPath)
    495         blue.resMan.Wait()
    496         self.render_frame(self.renderJob)
    497         backbuffer = self.rt
    498         image = self.CreateAlphaPreservingScreenShot(backbuffer, self.renderJob, clearStep=self.clear_step)
    499         if applyBackground:
    500             background = PILImage.open(blue.paths.ResolvePath(BACKGROUND_PATH))
    501             background.load()
    502             if background.size[0] != RENDER_SIZE:
    503                 background = background.resize((RENDER_SIZE, RENDER_SIZE), PILImage.ANTIALIAS)
    504             image = PILImage.composite(image, background, image)
    505         if targetRes != RENDER_SIZE:
    506             image = image.resize((targetRes, targetRes), PILImage.ANTIALIAS)
    507         imageutils.from_pil(image).save(outputPath)
    508 
    509     def GetOutputPath(self, assetPath, genderID, useMannequin = False, category = None, bloodlineID = -1, typeID = None):
    510         assetResPath = self.ReformatAssetPath(assetPath)
    511         categoryPath = self.ReformatAssetPath(category)
    512         subFolder = 'mannequin/' + categoryPath + '/'
    513         ccUtil.CreateCategoryFolderIfNeeded(blue.paths.ResolvePath(CLIENT_OUTPUT_ROOT), subFolder)
    514         outputPath = blue.paths.ResolvePath(CLIENT_OUTPUT_ROOT) + subFolder
    515         suffix = 'png'
    516         if self.outputSuffix != '':
    517             suffix = '%s.' % self.outputSuffix + suffix
    518         if typeID:
    519             if genderID == ccConst.GENDERID_MALE:
    520                 gender = 'male'
    521             else:
    522                 gender = 'female'
    523             outputPath = outputPath + '%s_%s_%s.%s' % (typeID,
    524              gender,
    525              assetResPath,
    526              suffix)
    527         elif bloodlineID < 0:
    528             outputPath = outputPath + '%s_g%s.%s' % (assetResPath, genderID, suffix)
    529         else:
    530             outputPath = outputPath + '%s_g%s_b%s.%s' % (assetResPath,
    531              genderID,
    532              bloodlineID,
    533              suffix)
    534         return outputPath
    535 
    536     @staticmethod
    537     def ReformatAssetPath(path):
    538         assetResPath = path.replace('/', '_').replace('.type', '')
    539         return assetResPath
    540 
    541     def GetClearColor(self):
    542         return self.clear_step.color
    543 
    544     def SetClearColor(self, newColor):
    545         self.clear_step.color = newColor
    546 
    547     def FreezeRenderJob(self, rj):
    548         try:
    549             rj.scene.object.update = False
    550             rj.scene.object.backgroundRenderingEnabled = False
    551         except:
    552             pass
    553 
    554         trinity.device.animationTimeScale = 0
    555 
    556     def ThawRenderJob(self, rj):
    557         try:
    558             rj.scene.object.update = True
    559             rj.scene.object.backgroundRenderingEnabled = True
    560         except:
    561             pass
    562 
    563     def CreateRGPCompositeEffect(self, shotA, shotAColor, shotB, shotBColor, finalImage):
    564         effect = trinity.Tr2Effect()
    565         effect.effectFilePath = 'res:/graphics/Effect/Managed/Space/System/BlueScreenMatting.fx'
    566         param = trinity.TriTextureParameter()
    567         param.name = 'ShotA'
    568         res = trinity.TriTextureRes()
    569         res.SetFromRenderTarget(shotA)
    570         param.SetResource(res)
    571         effect.resources.append(param)
    572         param = trinity.Tr2Vector4Parameter()
    573         param.name = 'shotABackgroundColor'
    574         param.value = shotAColor
    575         effect.parameters.append(param)
    576         param = trinity.TriTextureParameter()
    577         param.name = 'ShotB'
    578         res = trinity.TriTextureRes()
    579         res.SetFromRenderTarget(shotB)
    580         param.SetResource(res)
    581         effect.resources.append(param)
    582         param = trinity.Tr2Vector4Parameter()
    583         param.name = 'shotBBackgroundColor'
    584         param.value = shotBColor
    585         effect.parameters.append(param)
    586         param = trinity.TriTextureParameter()
    587         param.name = 'FinalImage'
    588         res = trinity.TriTextureRes()
    589         res.SetFromRenderTarget(finalImage)
    590         param.SetResource(res)
    591         effect.resources.append(param)
    592         return effect
    593 
    594     def RunRGBCompositeEffect(self, effect, width, height):
    595         rt = trinity.Tr2RenderTarget(width, height, 1, trinity.PIXEL_FORMAT.B8G8R8A8_UNORM)
    596         rj = trinity.CreateRenderJob('rj')
    597         rj.steps.append(trinity.TriStepPushRenderTarget(rt))
    598         rj.steps.append(trinity.TriStepClear((0, 0, 0, 0)))
    599         rj.steps.append(trinity.TriStepSetStdRndStates(trinity.RM_FULLSCREEN))
    600         rj.steps.append(trinity.TriStepPushDepthStencil(None))
    601         rj.steps.append(trinity.TriStepRenderEffect(effect))
    602         rj.steps.append(trinity.TriStepPopRenderTarget())
    603         rj.steps.append(trinity.TriStepPopDepthStencil())
    604         trinity.WaitForResourceLoads()
    605         rj.ScheduleOnce()
    606         rj.WaitForFinish()
    607         return trinity.Tr2HostBitmap(rt)
    608 
    609     def CreateAlphaPreservingScreenShot(self, backBuffer, rj, clearStep = None):
    610         originalClearColor = self.GetClearColor()
    611         try:
    612             bloom = True
    613             try:
    614                 bloom = rj.postProcess.Bloom
    615                 rj.postProcess.Bloom = False
    616             except:
    617                 pass
    618 
    619             self.FreezeRenderJob(rj)
    620             colorA = (1.0, 0.0, 1.0, 1.0)
    621             self.SetClearColor(colorA)
    622             self.render_frame(rj)
    623             shotA = self.GetScreenshotRenderTarget(backBuffer)
    624             imA = trinity.Tr2HostBitmap(shotA)
    625             colorB = (0.0, 1.0, 0.0, 0.0)
    626             self.SetClearColor(colorB)
    627             self.render_frame(rj)
    628             shotB = self.GetScreenshotRenderTarget(backBuffer)
    629             imB = trinity.Tr2HostBitmap(shotB)
    630             try:
    631                 rj.postProcess.Bloom = bloom
    632             except:
    633                 pass
    634 
    635             self.SetClearColor((0, 0, 0, 0))
    636             self.render_frame(rj)
    637             final = self.GetScreenshotRenderTarget(backBuffer)
    638             imFinal = trinity.Tr2HostBitmap(final)
    639             effect = self.CreateRGPCompositeEffect(shotA, colorA, shotB, colorB, final)
    640             bmp = self.RunRGBCompositeEffect(effect, backBuffer.width, backBuffer.height)
    641             return self.Tr2HostBitmapToPILImage(bmp)
    642         except:
    643             import traceback
    644             traceback.print_exc()
    645             return
    646         finally:
    647             self.SetClearColor(originalClearColor)
    648             self.ThawRenderJob(rj)
    649             trinity.device.animationTimeScale = 1.0
    650 
    651     def render_frame(self, rj):
    652         rj.ScheduleOnce()
    653         rj.WaitForFinish()
    654 
    655 
    656 def GetValidPaperDollGenders(typeID):
    657     genders = []
    658     for each in cfg.paperdollResources:
    659         if each.typeID == typeID:
    660             if each.resGender is not None:
    661                 genders.append(each.resGender)
    662             else:
    663                 genders.append(0)
    664                 genders.append(1)
    665 
    666     return genders
    667 
    668 
    669 def GetPaperDollCategory(typeID, genderID):
    670     for each in cfg.paperdollResources:
    671         if each.typeID == typeID:
    672             asset = GetPaperDollResource(typeID, genderID)
    673             path = '/' + asset.resPath
    674             rv = []
    675             categs = SETUP.keys()
    676             categs.sort()
    677             categs.reverse()
    678             for each in categs:
    679                 if isinstance(each, types.StringTypes):
    680                     pathstr = '/%s/' % each
    681                     if pathstr in path.lower():
    682                         rv.append(each)
    683 
    684             return rv
    685 
    686 
    687 def GetPaperDollResource(typeID, genderID):
    688     assets = []
    689     for each in cfg.paperdollResources:
    690         if type(typeID) is int and each.typeID == typeID:
    691             assets.append(each)
    692         elif type(typeID) is tuple and typeID[1][0] in each.resPath.lower():
    693             assets.append(each)
    694 
    695     if len(assets) == 1:
    696         return assets[0]
    697     if len(assets) > 1:
    698         for asset in assets:
    699             if genderID == asset.resGender:
    700                 return asset
    701 
    702     log.LogError('PreviewWnd::PreviewType - No asset matched the typeID: %d' % typeID)
    703 
    704 
    705 def IsValidType(typeID):
    706     for each in cfg.paperdollResources:
    707         if each.typeID == typeID:
    708             return True
    709 
    710     return False
    711 
    712 
    713 def OpenTrinityWindow():
    714     mainFrame = wx.GetApp().GetTopWindow()
    715     tp = mainFrame.windowManager.GetWindow('Trinity Panel')
    716 
    717 
    718 class RenderCharacter(object):
    719 
    720     def __init__(self, doll, avatar):
    721         self.doll = doll
    722         self.avatar = avatar
    723 
    724 
    725 def setup_cfg():
    726     import fsdBuiltData.common.character as fsdchar
    727     charSvc = sm.GetService('character')
    728     charSvc.assetsToIDs = {'male': None,
    729      'female': None}
    730     paperdollModifierLocations = fsdchar.GetModifierLocationRows()
    731     paperdollResources = fsdchar.GetResourceRows()
    732     paperdollSculptingLocations = fsdchar.GetSculptingLocationRows()
    733     paperdollColors = fsdchar.GetColorLocationRows()
    734     paperdollColorNames = fsdchar.GetColorNameRows()
    735     paperdollPortraitResources = fsdchar.GetPortraitResourceRows()
    736     cfg.paperdollModifierLocations = paperdollModifierLocations
    737     cfg.paperdollResources = paperdollResources
    738     cfg.paperdollSculptingLocations = paperdollSculptingLocations
    739     cfg.paperdollColors = paperdollColors
    740     cfg.paperdollColorNames = paperdollColorNames
    741     cfg.paperdollPortraitResources = paperdollPortraitResources
    742     dogma_data.get_all_attributes = lambda : []
    743     dogma_data.get_all_effects = lambda : []
    744     charSvc.PopulateModifierLocationDicts(True)
    745 
    746 
    747 def RenderClientIcon(typenum, gender, outputSuffix = ''):
    748     if outputSuffix == '' or outputSuffix == 'EN':
    749         blue.os.languageID = 'EN'
    750     elif outputSuffix == 'ZH':
    751         blue.os.languageID = 'ZH'
    752     if not IsValidType(typenum):
    753         print('*****  Type %d does not match a paperdoll resource. *****' % typenum)
    754         return False
    755     pdDm.ClearAllCachedMaps()
    756     aprenderer = ApparelRenderer(outputSuffix)
    757     OpenTrinityWindow()
    758     if GetPaperDollCategory(typenum, gender)[0] in DOLL_MODIFIERS:
    759         curr_bloodline = 1
    760         aprenderer.SetupScene(gender, bloodlineID=curr_bloodline)
    761         aprenderer.mannequin.doll.buildDataManager.SetAllAsDirty()
    762         aprenderer.SetUpCamera(aprenderer.camera, GetPaperDollCategory(typenum, gender)[0], aprenderer.mannequin, scene=aprenderer.scene)
    763         aprenderer.DoRenderNormalAssetType(typenum, gender, bloodlineID=curr_bloodline, genClientIcon=True)
    764     else:
    765         aprenderer.SetupScene(gender)
    766         aprenderer.SetUpCamera(aprenderer.camera, GetPaperDollCategory(typenum, gender)[0], aprenderer.mannequin, scene=aprenderer.scene)
    767         aprenderer.DoRenderMannequinAssetType(typenum, gender, GetPaperDollCategory(typenum, gender)[0], genClientIcon=True)
    768 
    769 
    770 def RenderNESIcon(typenum, gender, outputSuffix = ''):
    771     if outputSuffix == '' or outputSuffix == 'EN':
    772         blue.os.languageID = 'EN'
    773     elif outputSuffix == 'ZH':
    774         blue.os.languageID = 'ZH'
    775     if not IsValidType(typenum):
    776         print('*****  Type %d does not match a paperdoll resource. *****' % typenum)
    777         return False
    778     pdDm.ClearAllCachedMaps()
    779     aprenderer = ApparelRenderer(outputSuffix)
    780     OpenTrinityWindow()
    781     if GetPaperDollCategory(typenum, gender)[0] in DOLL_MODIFIERS:
    782         curr_bloodline = 1
    783         aprenderer.SetupScene(gender, bloodlineID=curr_bloodline)
    784         aprenderer.SetUpCamera(aprenderer.camera, GetPaperDollCategory(typenum, gender)[0], aprenderer.mannequin, scene=aprenderer.scene)
    785         aprenderer.DoRenderNormalAssetType(typenum, gender, bloodlineID=curr_bloodline, genNESIcon=True)
    786     else:
    787         aprenderer.SetupScene(gender)
    788         aprenderer.SetUpCamera(aprenderer.camera, GetPaperDollCategory(typenum, gender)[0], aprenderer.mannequin, scene=aprenderer.scene)
    789         aprenderer.DoRenderMannequinAssetType(typenum, gender, GetPaperDollCategory(typenum, gender)[0], genNESIcon=True)
    790 
    791 
    792 def ProcessTypes(typeslist, genClientIconCB, genNESIconCB, outputSuffix = ''):
    793     print('**************  STARTING APPAREL RENDER  *************************')
    794     types_string = str(typeslist)
    795     types = [ int(t) for t in types_string.split(',') ]
    796     for typenum in types:
    797         genders = GetValidPaperDollGenders(typenum)
    798         for gender in genders:
    799             if genClientIconCB:
    800                 print('Rendering %s Client Icon for type %d gender %d' % (outputSuffix, typenum, gender))
    801                 RenderClientIcon(typenum, gender, outputSuffix)
    802             if genNESIconCB:
    803                 print('Rendering %s NES Icon for type %d gender %d' % (outputSuffix, typenum, gender))
    804                 RenderNESIcon(typenum, gender, outputSuffix)
    805 
    806 
    807 TYPES_TEXT_CONTROL = None
    808 GEN_CLIENT_ICON_CB = None
    809 GEN_NES_ICON_CB = None
    810 RESOURCE_LANGUAGE_DROPDOWN = False
    811 
    812 class RenderIconDialog(wx.Dialog):
    813 
    814     def __init__(self, *args, **kw):
    815         super(RenderIconDialog, self).__init__(*args, **kw)
    816         self.InitUI()
    817         self.SetSize((400, 300))
    818         self.SetTitle('Apparel Icon Render')
    819 
    820     def InitUI(self):
    821         global TYPES_TEXT_CONTROL
    822         global GEN_NES_ICON_CB
    823         global RESOURCE_LANGUAGE_DROPDOWN
    824         global GEN_CLIENT_ICON_CB
    825         pnl = wx.Panel(self)
    826         vbox = wx.BoxSizer(wx.VERTICAL)
    827         sb = wx.StaticBox(pnl, label='Render Parameters')
    828         sbs = wx.StaticBoxSizer(sb, orient=wx.VERTICAL)
    829         GEN_CLIENT_ICON_CB = wx.CheckBox(pnl, label='Generate Client Icon')
    830         sbs.Add(GEN_CLIENT_ICON_CB)
    831         GEN_NES_ICON_CB = wx.CheckBox(pnl, label='Generate NES Icon')
    832         sbs.Add(GEN_NES_ICON_CB)
    833         sbs.Add(wx.StaticText(pnl, -1, 'Resource Language'))
    834         RESOURCE_LANGUAGE_DROPDOWN = wx.ComboBox(pnl, value='EN', choices=['EN', 'ZH'], style=wx.CB_READONLY)
    835         sbs.Add(RESOURCE_LANGUAGE_DROPDOWN)
    836         sbs.Add(wx.StaticText(pnl, -1, 'Types'))
    837         TYPES_TEXT_CONTROL = wx.TextCtrl(pnl, size=(300, -1))
    838         sbs.Add(TYPES_TEXT_CONTROL, flag=wx.LEFT, border=5)
    839         pnl.SetSizer(sbs)
    840         hbox2 = wx.BoxSizer(wx.HORIZONTAL)
    841         okButton = wx.Button(self, label='Ok')
    842         closeButton = wx.Button(self, label='Close')
    843         hbox2.Add(okButton)
    844         hbox2.Add(closeButton, flag=wx.LEFT, border=5)
    845         vbox.Add(pnl, flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, border=10)
    846         vbox.Add(hbox2, flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, border=10)
    847         self.SetSizer(vbox)
    848         okButton.Bind(wx.EVT_BUTTON, self.OnOk)
    849         closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
    850 
    851     def OnClose(self, e):
    852         self.Destroy()
    853 
    854     def OnOk(self, e):
    855         typeslist = TYPES_TEXT_CONTROL.GetValue()
    856         genClientIconCB = GEN_CLIENT_ICON_CB.GetValue()
    857         genNESIconCB = GEN_NES_ICON_CB.GetValue()
    858         self.Destroy()
    859         ProcessTypes(typeslist, genClientIconCB, genNESIconCB, outputSuffix=RESOURCE_LANGUAGE_DROPDOWN.GetValue().replace('EN', ''))
    860 
    861 
    862 def start():
    863     setup_cfg()
    864     cdDialog = RenderIconDialog(None, title='Apparel Icon Render')
    865     cdDialog.ShowModal()