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

avatardisplay.py (59865B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\avatardisplay\avatardisplay.py
      2 from __future__ import absolute_import
      3 from __future__ import print_function
      4 import logging
      5 import math
      6 import os
      7 import sys
      8 import time
      9 from brennivin.itertoolsext import first, first_or_default
     10 from eve.common.lib import appConst as const
     11 from eve.common.script.paperDoll.paperDollDefinitions import LOD_SKIN
     12 from eve.common.script.paperDoll.yamlPreloader import LoadYamlFileNicely
     13 from eveexceptions import UserError
     14 from mathext import clamp
     15 from eve.client.script.ui.login.charcreation.ccUtil import GenderIDToPaperDollGender
     16 from eve.client.script.ui.control.scenecontainer import SceneContainerBaseNavigation, SceneContainer
     17 from eve.client.script.ui.shared.preview import MergeBoundingBoxes
     18 import charactercreator.const as ccConst
     19 import evegraphics.settings as gfxsettings
     20 from carbonui.primitives.container import Container
     21 from carbonui.uianimations import animations
     22 import carbonui.const as uiconst
     23 from carbonui.uicore import uicore
     24 from charactercreator.client.grading import GetTexLUT, GetLUTIntensity
     25 import blue
     26 import evetypes
     27 import trinity
     28 import uthread
     29 import geo2
     30 import log
     31 from avatardisplay import cmd_avatarwindow
     32 import avatardisplay.avatar_command as ac
     33 from avatardisplay.errors import FailedToPlaybackScene
     34 from fsdBuiltData.common.character import GetAvatarBehaviorRows
     35 from functools import reduce
     36 AvatarDisplay_Next_CharID = None
     37 Prs_To_CharID_Dict = {}
     38 cache = {}
     39 preparing_scene = False
     40 logger = logging.getLogger()
     41 ARCHETYPES_TO_BLOODLINES = {'aashape': const.bloodlineAmarr,
     42  'akshape': const.bloodlineKhanid,
     43  'anshape': const.bloodlineNiKunni,
     44  'cashape': const.bloodlineAchura,
     45  'ccshape': const.bloodlineCivire,
     46  'cdshape': const.bloodlineDeteis,
     47  'drshape': const.bloodlineDrifter,
     48  'ggshape': const.bloodlineGallente,
     49  'gishape': const.bloodlineIntaki,
     50  'gjmshape': const.bloodlineJinMei,
     51  'mbshape': const.bloodlineBrutor,
     52  'msshape': const.bloodlineSebiestor,
     53  'mvshape': const.bloodlineVherokior}
     54 
     55 def PreloadScene(scene_dir, curve_list_yaml = '', per_scene_caching = False):
     56     uthread.new(AvatarDisplay.PreloadScene, scene_dir, curve_list_yaml=curve_list_yaml, per_scene_caching=per_scene_caching)
     57 
     58 
     59 def PlaybackScene(scene_dir, parent, anim_strs = None, audio_strs = None, hide = False, per_scene_caching = False, sound_start_time = 0):
     60     logger.info('avatardisplay:  Scene playback requested: %s', scene_dir)
     61     AvatarDisplay.PlaybackScene(scene_dir, parent, anim_strs, audio_strs, hide=hide, per_scene_caching=per_scene_caching, sound_start_time=sound_start_time)
     62 
     63 
     64 def PlaybackCharacterScene(male_scene_dir, female_scene_dir, parent, char_id, anim_strs = None, audio_strs = None, hide = False, per_scene_caching = False, sound_start_time = 0):
     65     AvatarDisplay.PlaybackCharacterScene(male_scene_dir, female_scene_dir, parent, char_id, anim_strs=anim_strs, audio_strs=audio_strs, hide=hide, per_scene_caching=per_scene_caching, sound_start_time=sound_start_time)
     66 
     67 
     68 def PlaybackCharacter(charID_str, parent, dna_str = None, anim_str = None, audio_str = None):
     69     AvatarDisplay.PlaybackCharacter(charID_str, parent, dna_str, anim_str, audio_str)
     70 
     71 
     72 GSTATE_CACHE = {}
     73 CACHE_GSF = 0
     74 CACHE_READY = 1
     75 CURVE_CACHE = {}
     76 CHARS_LOADING = []
     77 
     78 class AvatarDisplay(object):
     79 
     80     @staticmethod
     81     def ClearAvatarDisplayGlobals():
     82         global CHARS_LOADING
     83         global AvatarDisplay_Next_CharID
     84         global cache
     85         global Prs_To_CharID_Dict
     86         global CURVE_CACHE
     87         global GSTATE_CACHE
     88         logger.info('avatardisplay: CLEAR AVATARDISPLAY GLOBALS')
     89         logger.info('avatardisplay: GLOBALS: %s %s %s %s %s %s' % (str(GSTATE_CACHE),
     90          str(CURVE_CACHE),
     91          str(CHARS_LOADING),
     92          str(Prs_To_CharID_Dict),
     93          str(cache),
     94          str(AvatarDisplay_Next_CharID)))
     95         characterSvc = sm.GetService('character')
     96         GSTATE_CACHE = {}
     97         CURVE_CACHE = {}
     98         CHARS_LOADING = []
     99         Prs_To_CharID_Dict = {}
    100         for currkey in cache:
    101             if type(currkey) is int:
    102                 logger.info('avatardisplay: removing character %s' % currkey)
    103                 characterSvc.RemoveCharacter(currkey)
    104 
    105         cache = {}
    106         AvatarDisplay_Next_CharID = None
    107         logger.info('avatardisplay: GLOBALS: %s %s %s %s %s %s' % (str(GSTATE_CACHE),
    108          str(CURVE_CACHE),
    109          str(CHARS_LOADING),
    110          str(Prs_To_CharID_Dict),
    111          str(cache),
    112          str(AvatarDisplay_Next_CharID)))
    113 
    114     @staticmethod
    115     def DisplayCharacter(charID_str, dna_str = None, anim_str = None, audio_str = None):
    116         import time
    117         wnd = cmd_avatarwindow.MyWindow.Open(windowID='AvatarWindow %d' % time.time())
    118         AvatarDisplay.PlaybackCharacter(charID_str, wnd.sr.sceneContainer, dna_str, anim_str, audio_str)
    119         return 'Ok'
    120 
    121     @staticmethod
    122     def PlaybackCharacter(charID_str, parent, dna_str = None, anim_str = None, audio_str = None):
    123         avatarcontainer = AvatarPreviewContainer(parent=parent)
    124         characterSvc = sm.GetService('character')
    125         charID = AvatarDisplay.FindTempCharID(charID_str)
    126         if dna_str is None:
    127             dna = sm.RemoteSvc('paperDollServer').GetPaperDollData(charID)
    128         else:
    129             dna = characterSvc.factory.GetDNAFromYamlFile(blue.paths.ResolvePath(dna_str))
    130         if dna is None:
    131             raise UserError('CharacterHasNoDNA', {'charID': charID})
    132         uthread.new(avatarcontainer.PreviewCharacter, charID, dna=dna)
    133         avatarcontainer.sceneContainer.OrbitParent(0, -60)
    134         av = None
    135         while av is None:
    136             blue.synchro.Yield()
    137             av = characterSvc.GetSingleCharactersAvatar(charID)
    138 
    139         av.display = False
    140         doll = None
    141         while doll is None:
    142             blue.synchro.Yield()
    143             doll = characterSvc.GetSingleCharactersDoll(charID)
    144 
    145         while doll.IsBusyUpdating():
    146             blue.synchro.Yield()
    147 
    148         if anim_str is not None and '.gsf' in anim_str:
    149             AvatarDisplay.PlaybackSingleAvatarGState(anim_str, audio_str, av)
    150         elif anim_str is not None and '.gr2' in anim_str:
    151             AvatarDisplay.PlaybackSingleAvatarGr2(anim_str, audio_str, av)
    152         else:
    153             AvatarDisplay.PlaybackSingleAvatarDefaultIdle(audio_str, av)
    154 
    155     @staticmethod
    156     def PlaybackSingleAvatarDefaultIdle(audio_str, av):
    157         if 'female' in av.visualModel.geometryResPath.lower():
    158             gender_name = 'Female'
    159         else:
    160             gender_name = 'Male'
    161         anim_str = 'res:\\Animation\\CharacterCreatorV2\\GState\\CharacterCreator_Gstate_%s_Master.gsf' % gender_name
    162         AvatarDisplay.PlaybackSingleAvatarGState(anim_str, audio_str, av)
    163 
    164     @staticmethod
    165     def PlaybackSingleAvatarGr2(anim_str, audio_str, av):
    166         animation = trinity.Tr2GrannyAnimation()
    167         animation.resPath = anim_str
    168         trinity.WaitForResourceLoads()
    169         baseclip = animation.grannyRes.GetAnimationName(0)
    170         animation.PlayAnimationEx(baseclip, 0, 0, 1.0)
    171         if audio_str is not None:
    172             sm.GetService('audio').SendUIEvent(audio_str)
    173         av.animationUpdater = animation
    174         av.display = True
    175 
    176     @staticmethod
    177     def PlaybackSingleAvatarGState(anim_str, audio_str, av):
    178         animation = trinity.Tr2GStateAnimation()
    179         animation.gStateResPath = anim_str
    180         log.LogNotice('Resetting gStateResPath')
    181         trinity.WaitForResourceLoads()
    182         while not animation.IsFullyLoaded():
    183             blue.synchro.Yield()
    184 
    185         animation.InstantiateCharacter()
    186         if audio_str is not None:
    187             sm.GetService('audio').SendUIEvent(audio_str)
    188         av.animationUpdater = animation
    189         av.display = True
    190 
    191     @staticmethod
    192     def FindTempCharID(charID_str):
    193         global AvatarDisplay_Next_CharID
    194         charID = 0
    195         if int(charID_str) == 0:
    196             if AvatarDisplay_Next_CharID:
    197                 charID = AvatarDisplay_Next_CharID
    198                 AvatarDisplay_Next_CharID += 1
    199             else:
    200                 charID = 0
    201                 AvatarDisplay_Next_CharID = 1
    202         return charID
    203 
    204     @staticmethod
    205     def DisplayPreparedScene(scene_dir, anim_strs = None, audio_strs = None, sound_start_time = 0, sub_char = None):
    206         wnd = cmd_avatarwindow.MyWindow.Open(windowID='AvatarWindow %d' % time.time())
    207         wnd.height = 400
    208         wnd.width = 300
    209         if sub_char is None:
    210             PlaybackScene(scene_dir, wnd.sr.sceneContainer, anim_strs, audio_strs, sound_start_time=sound_start_time)
    211         else:
    212             PlaybackCharacterScene(scene_dir, scene_dir, wnd.sr.sceneContainer, sub_char)
    213         return 'Ok'
    214 
    215     @staticmethod
    216     def PreloadScene(scene_dirs, curve_list_yaml = '', per_scene_caching = False):
    217         if type(scene_dirs) == str:
    218             scene_dirs = [scene_dirs]
    219         dolls = []
    220         for scene_dir in scene_dirs:
    221             scene = trinity.Tr2InteriorScene()
    222             cache[scene_dir] = scene
    223             anim_strs, audio_strs, background, camera_dict, dna_list, lights, position_list, post, char_ids, saved_curveset_lists, saved_audio_delays = AvatarDisplay.ReadSceneData(scene_dir)
    224             charSvc = sm.GetService('character')
    225             if dna_list is not None:
    226                 bloodlineID_list = GetBloodlineIDListFromDNAList(dna_list)
    227                 gender_list = []
    228                 for dna in dna_list:
    229                     if dna[0] == 'male':
    230                         gender_list.append(1)
    231                     else:
    232                         gender_list.append(0)
    233 
    234                 for idx, dna in enumerate(dna_list):
    235                     if char_ids[idx] in cache or char_ids[idx] in CHARS_LOADING:
    236                         continue
    237                     CHARS_LOADING.append(char_ids[idx])
    238                     try:
    239                         character = charSvc.AddCharacterToScene(charID=char_ids[idx], scene=scene, gender=GenderIDToPaperDollGender(gender_list[idx]), bloodlineID=bloodlineID_list[idx], dna=dna, lod=LOD_SKIN, updateDoll=False, anim=False)
    240                         textureQuality = gfxsettings.Get(gfxsettings.GFX_CHAR_TEXTURE_QUALITY)
    241                         textureResolution = ccConst.DOLL_VIEWER_TEXTURE_RESOLUTIONS[textureQuality]
    242                         useFastShader = IsUsingLowQualityCharacters()
    243                         character.doll.textureResolution = textureResolution
    244                         character.doll.useFastShader = useFastShader
    245                         character.Update()
    246                         character.WaitForUpdate()
    247                         dolls.append(character.doll)
    248                         cache[char_ids[idx]] = character
    249                     except Exception as e:
    250                         raise e
    251                     finally:
    252                         if char_ids[idx] in CHARS_LOADING:
    253                             CHARS_LOADING.remove(char_ids[idx])
    254 
    255                 for idx, anim_str in enumerate(anim_strs):
    256                     if anim_str is not None and anim_str != 'no_anim':
    257                         if '.gsf' in anim_str:
    258                             gender_name = GenderIDToPaperDollGender(gender_list[idx])
    259                             if per_scene_caching:
    260                                 cacheKey = (anim_str, gender_name.lower(), scene)
    261                             else:
    262                                 cacheKey = (anim_str, gender_name.lower())
    263                             if cacheKey not in GSTATE_CACHE:
    264                                 logger.info('avatardisplay: GSTATE_CACHE PRELOAD START %s' % str(GSTATE_CACHE))
    265                                 GSTATE_CACHE[cacheKey] = [trinity.Tr2GStateAnimation(), False]
    266                                 logger.info('avatardisplay: GSTATE_CACHE %s' % str(GSTATE_CACHE))
    267                                 GSTATE_CACHE[cacheKey][CACHE_GSF].gStateResPath = str(anim_str)
    268                                 while not GSTATE_CACHE[cacheKey][CACHE_GSF].IsFullyLoaded():
    269                                     blue.synchro.Yield()
    270 
    271                                 GSTATE_CACHE[cacheKey][CACHE_GSF].InstantiateCharacter()
    272                                 GSTATE_CACHE[cacheKey][CACHE_READY] = True
    273                                 logger.info('avatardisplay: GSTATE_CACHE PRELOAD END %s' % str(GSTATE_CACHE))
    274                         elif '.gr2' in anim_str:
    275                             animation = trinity.Tr2GrannyAnimation()
    276                             animation.resPath = str(anim_str)
    277                     else:
    278                         gsf_anim = True
    279                         animation = trinity.Tr2GStateAnimation()
    280                         animation.gStateResPath = 'res:\\Animation\\CharacterCreatorV2\\GState\\CharacterCreator_Gstate_%s_Master.gsf' % dna_list[idx][0]
    281                         log.LogNotice('Resetting gStateResPath')
    282                         while not animation.IsFullyLoaded():
    283                             blue.synchro.Yield()
    284 
    285                         animation.InstantiateCharacter()
    286 
    287         if curve_list_yaml:
    288             curve_path_list = LoadYamlFileNicely(curve_list_yaml)
    289             for curve_path in curve_path_list:
    290                 curve_res = trinity.Load(curve_path)
    291                 if curve_path not in CURVE_CACHE:
    292                     CURVE_CACHE[curve_path] = curve_res
    293 
    294         while any(map(lambda doll: doll.IsBusyUpdating(), dolls)):
    295             blue.synchro.Yield()
    296 
    297         trinity.WaitForResourceLoads()
    298         blue.synchro.Sleep(100)
    299         trinity.WaitForResourceLoads()
    300 
    301     @staticmethod
    302     def PlaybackCharacterScene(male_scene_dir, female_scene_dir, parent, char_id, anim_strs = None, audio_strs = None, hide = False, per_scene_caching = False, sound_start_time = 0):
    303         dna = sm.RemoteSvc('paperDollServer').GetPaperDollData(char_id)
    304         dna_list = [dna]
    305         owner = cfg.eveowners.Get(char_id)
    306         gender = getattr(owner, 'gender', None)
    307         if gender is None:
    308             raise RuntimeError('{0.name} ({0.charID}) does not have a defined gender'.format(owner))
    309         if gender:
    310             scene_dir = male_scene_dir
    311         else:
    312             scene_dir = female_scene_dir
    313         AvatarDisplay.PlaybackScene(scene_dir, parent, anim_strs=anim_strs, audio_strs=audio_strs, hide=hide, per_scene_caching=per_scene_caching, sound_start_time=sound_start_time, dna_list=dna_list, gender_list=[gender], sub_char_id=char_id)
    314 
    315     @staticmethod
    316     def PlaybackScene(scene_dir, parent, anim_strs = None, audio_strs = None, hide = False, per_scene_caching = False, sound_start_time = 0, dna_list = None, gender_list = None, sub_char_id = None):
    317         parent.avatarscenecontainer = AvatarCommandReceiverContainer(parent=parent, name='avatarscenecontainer')
    318         anim_strs, audio_strs, background, camera_dict, scene_dna_list, lights, position_list, post, char_ids, saved_curveset_lists, saved_audio_delays = AvatarDisplay.ReadSceneData(scene_dir, anim_strs, audio_strs, sub_char_id=sub_char_id)
    319         if dna_list is None:
    320             dna_list = scene_dna_list
    321         uthread.new(parent.avatarscenecontainer.PreviewPreparedScene, char_ids=char_ids, dna_list=dna_list, position_list=position_list, lights=lights, camera=camera_dict, post=post, background=background, anim_strs=anim_strs, audio_strs=audio_strs, hide=hide, curveset_lists=saved_curveset_lists, audio_delays=saved_audio_delays, per_scene_caching=per_scene_caching, scene_dir=scene_dir, sound_start_time=sound_start_time, gender_list=gender_list, sub_char_id=sub_char_id)
    322 
    323     @staticmethod
    324     def ReadSceneData(scene_dir, anim_strs = None, audio_strs = None, sub_char_id = None):
    325         scene_dict = AvatarDisplay.ReadSceneYamlFile(scene_dir, 'scene.yaml')
    326         bg_dict = AvatarDisplay.ReadSceneYamlFile(scene_dir, 'background.yaml')
    327         camera_dict = AvatarDisplay.ReadSceneYamlFile(scene_dir, 'camera.yaml')
    328         background = bg_dict['path']
    329         width = 1000
    330         height = width / camera_dict['aspect_ratio']
    331         character_list = scene_dict['characters']
    332         dna_list, position_list, saved_anim_strs, saved_audio_strs, char_ids, saved_curveset_lists, saved_audio_delays = AvatarDisplay.ReadSceneCharacterData(character_list, scene_dir, sub_char_id=sub_char_id)
    333         if not anim_strs:
    334             anim_strs = saved_anim_strs
    335         if not audio_strs:
    336             audio_strs = saved_audio_strs
    337         lights = AvatarDisplay.ReadSceneLightsData(scene_dir)
    338         post = AvatarDisplay.ReadScenePostData(scene_dir)
    339         logger.info('avatardisplay: ReadSceneData complete %s', scene_dir)
    340         return (anim_strs,
    341          audio_strs,
    342          background,
    343          camera_dict,
    344          dna_list,
    345          lights,
    346          position_list,
    347          post,
    348          char_ids,
    349          saved_curveset_lists,
    350          saved_audio_delays)
    351 
    352     @staticmethod
    353     def ReadScenePostData(scene_dir):
    354         post = None
    355         post_path = os.path.join(scene_dir, 'post.yaml')
    356         resolved_post_path = blue.paths.ResolvePath(post_path)
    357         if resolved_post_path:
    358             post = LoadYamlFileNicely(post_path)
    359         return post
    360 
    361     @staticmethod
    362     def ReadSceneLightsData(scene_dir):
    363         lights = None
    364         lights_path = os.path.join(scene_dir, 'lights_client.red')
    365         resolved_lights_path = blue.paths.ResolvePath(lights_path)
    366         if resolved_lights_path:
    367             lights = trinity.Load(lights_path)
    368         return lights
    369 
    370     @staticmethod
    371     def ReadSceneCharacterData(character_list, scene_dir, sub_char_id = None):
    372         dna_list = []
    373         position_list = []
    374         saved_anim_strs = []
    375         saved_curveset_lists = []
    376         saved_audio_strs = []
    377         saved_audio_delays = []
    378         char_ids = []
    379         for char_idx, scene_char in enumerate(character_list):
    380             saved_anim_strs.append(scene_char.get('animation_path'))
    381             char_curveset_list = scene_char.get('curveset', [])
    382             for idx, curveset in enumerate(char_curveset_list):
    383                 if 'res:' not in curveset:
    384                     char_curveset_list[idx] = os.path.join(scene_dir, curveset)
    385 
    386             saved_curveset_lists.append(char_curveset_list)
    387             saved_audio_strs.append(scene_char.get('animation_lipsync_audio'))
    388             lipsync_audio_delay = scene_char.get('animation_lipsync_audio_delay', '0.0')
    389             if not lipsync_audio_delay:
    390                 lipsync_audio_delay = '0.0'
    391             saved_audio_delays.append(float(lipsync_audio_delay))
    392             if 'res:' not in scene_char['prs_path']:
    393                 prs_path = os.path.join(scene_dir, scene_char['prs_path'])
    394             else:
    395                 prs_path = scene_char['prs_path']
    396             if char_idx == 0 and sub_char_id is not None:
    397                 char_ids.append(AvatarDisplay.GetCharID(sub_char_id))
    398             else:
    399                 char_ids.append(AvatarDisplay.GetCharID(prs_path))
    400             dna_dict = LoadYamlFileNicely(prs_path)
    401             dna_list.append(dna_dict)
    402             position_path = os.path.join(scene_dir, scene_char['position_path'])
    403             position_dict = LoadYamlFileNicely(position_path)
    404             position_list.append(position_dict)
    405 
    406         return (dna_list,
    407          position_list,
    408          saved_anim_strs,
    409          saved_audio_strs,
    410          char_ids,
    411          saved_curveset_lists,
    412          saved_audio_delays)
    413 
    414     @staticmethod
    415     def GetSceneWindowID():
    416         import time
    417         timestr = str(int(time.time()))
    418         windowID = 'AvatarScene' + timestr
    419         return windowID
    420 
    421     @staticmethod
    422     def GetNextTempCharID():
    423         global AvatarDisplay_Next_CharID
    424         if AvatarDisplay_Next_CharID is None:
    425             AvatarDisplay_Next_CharID = 1
    426         char_id = AvatarDisplay_Next_CharID
    427         AvatarDisplay_Next_CharID += 1
    428         return char_id
    429 
    430     @staticmethod
    431     def GetCharID(prs_path):
    432         if type(prs_path) != str:
    433             prs_path = str(prs_path)
    434         prs_file = os.path.basename(prs_path)
    435         char_id = Prs_To_CharID_Dict.get(prs_file, None)
    436         if char_id is None:
    437             char_id = AvatarDisplay.GetNextTempCharID()
    438             Prs_To_CharID_Dict[prs_file] = char_id
    439         return char_id
    440 
    441     @staticmethod
    442     def ReadSceneYamlFile(scene_dir, filename):
    443         scene_path = os.path.join(scene_dir, filename)
    444         file_dict = LoadYamlFileNicely(scene_path)
    445         if not file_dict:
    446             raise FailedToPlaybackScene(scene_dir, file_missing=filename)
    447         return file_dict
    448 
    449 
    450 class AvatarPreviewContainer(Container):
    451     __notifyevents__ = ['OnGraphicSettingsChanged',
    452      'OnSetDevice',
    453      'OnSettingsCloseStarted',
    454      'OnUIScalingChange']
    455 
    456     @property
    457     def opacity(self):
    458         return self._opacity
    459 
    460     @opacity.setter
    461     def opacity(self, value):
    462         self._opacity = value
    463         if hasattr(self, 'sceneContainer') and self.sceneContainer.renderJob is not None:
    464             if hasattr(self.sceneContainer.renderJob, 'GetStep'):
    465                 step = self.sceneContainer.renderJob.GetStep('RENDER_BLEND')
    466                 if step is not None and hasattr(step, 'effect') and step.effect is not None:
    467                     for param in self.sceneContainer.renderJob.GetStep('RENDER_BLEND').effect.parameters:
    468                         if param.name == 'Opacity':
    469                             param.value = value
    470 
    471     @opacity.deleter
    472     def opacity(self):
    473         del self._opacity
    474 
    475     def FadeInOpacity(self, target_opacity = 1.0, duration = 1.0):
    476         self.fade_in_duration = duration
    477         if hasattr(self.sceneContainer.renderJob, 'enabled'):
    478             self.sceneContainer.renderJob.enabled = True
    479         animations.FadeIn(self, target_opacity, self.fade_in_duration)
    480 
    481     def FadeOutOpacity(self, duration = 1.0, timeOffset = 0.0):
    482         animations.FadeOut(self, duration, timeOffset)
    483         blue.synchro.SleepWallclock((duration + timeOffset) * 1000)
    484         if self.sceneContainer.renderJob is not None:
    485             self.sceneContainer.renderJob.enabled = False
    486 
    487     def ApplyAttributes(self, attributes):
    488         Container.ApplyAttributes(self, attributes)
    489         sm.RegisterNotify(self)
    490         self.context = None
    491         self.startLoadingCallback = attributes.get('OnStartLoading', None)
    492         self.stopLoadingCallback = attributes.get('OnStopLoading', None)
    493         self.skinController = None
    494         self.loading = False
    495         self.ConstructSceneContainer()
    496         self.sceneContainer.Startup()
    497         self._opacity = 1.0
    498         self.fade_in_duration = 1.0
    499         self.fade_out_duration = 1.0
    500         self.fade_out_offset = 0.0
    501         self.reload_thread = None
    502         self.scene_dir = ''
    503 
    504     def ConstructSceneContainer(self):
    505         self.sceneContainer = AvatarPreviewSceneContainer(parent=self, align=uiconst.TOALL)
    506 
    507     def Close(self):
    508         logger.info('avatardisplay: CLOSE AvatarPreviewContainer')
    509         self.FadeOutOpacity(self.fade_out_duration, self.fade_out_offset)
    510         super(AvatarPreviewContainer, self).Close()
    511         self._Cleanup()
    512 
    513     def _OnStartLoading(self):
    514         if self.startLoadingCallback:
    515             self.startLoadingCallback(self)
    516         else:
    517             self.loading = True
    518 
    519     def _OnStopLoading(self, success):
    520         if self.stopLoadingCallback:
    521             self.stopLoadingCallback(self, success)
    522         else:
    523             self.loading = False
    524 
    525     def PreviewCharacter(self, charID, dna = None, apparel = None, background = None):
    526         context = AvatarCharacterSceneContext(charID, dna=dna, apparel=apparel, background=background)
    527         return self.LoadScene(context)
    528 
    529     def PreviewPreparedScene(self, char_ids = [], dna_list = None, position_list = None, lights = None, camera = None, post = None, background = None, anim_strs = None, audio_strs = None, hide = False, curveset_lists = None, audio_delays = None, per_scene_caching = False, scene_dir = '', sound_start_time = 0, gender_list = None, sub_char_id = None):
    530         global preparing_scene
    531         logger.info('avatardisplay: PreviewPreparedScene started for %s' % str(scene_dir))
    532         while preparing_scene:
    533             logger.info('avatardisplay: Yielding on preparing_scene: %s' % str(preparing_scene))
    534             blue.synchro.Yield()
    535 
    536         preparing_scene = True
    537         try:
    538             logger.info('avatardisplay: Creating AvatarCharacterPreparedSceneContext for %s' % str(scene_dir))
    539             context = AvatarCharacterPreparedSceneContext(char_ids=char_ids, dna_list=dna_list, position_list=position_list, lights=lights, camera=camera, post=post, background=background, anim_strs=anim_strs, audio_strs=audio_strs, curveset_lists=curveset_lists, audio_delays=audio_delays, gender_list=gender_list, sub_char_id=sub_char_id)
    540             logger.info('avatardisplay: LoadScene started for %s' % str(scene_dir))
    541             scn = self.LoadScene(context, hide=hide, per_scene_caching=per_scene_caching, scene_dir=scene_dir, sound_start_time=sound_start_time)
    542         except Exception as e:
    543             raise e
    544         finally:
    545             preparing_scene = False
    546 
    547         return scn
    548 
    549     def LoadScene(self, context, force = False, hide = False, per_scene_caching = False, scene_dir = '', sound_start_time = 0):
    550         if context == self.context and not force:
    551             logger.info('avatardisplay: Multiple LoadScenes into the same context')
    552             return False
    553         if scene_dir:
    554             self.scene_dir = scene_dir
    555         success = True
    556         try:
    557             self._OnStartLoading()
    558             self._Cleanup()
    559             self.context = context
    560             self.context.LoadScene(self.sceneContainer, hide=hide, per_scene_caching=per_scene_caching, scene_dir=scene_dir, sound_start_time=sound_start_time)
    561             self.opacity = 0.0
    562             self.UpdateViewPort()
    563             logger.info('avatardisplay: Scene loading complete, ready for fade-in')
    564         except Exception:
    565             if not self.destroyed:
    566                 log.LogException('Exception raised while loading preview for {context}'.format(context=str(context)))
    567                 sys.exc_clear()
    568                 success = False
    569 
    570         self.FadeInOpacity(target_opacity=1.0, duration=self.fade_in_duration)
    571         self._OnStopLoading(success)
    572         return success
    573 
    574     def Reload(self, sound_start_time = 0):
    575         if self.reload_thread is not None:
    576             logger.info('avatardisplay: Skipping reload due to reload already pending')
    577             return
    578         if uicore.layer.systemmenu.isopen or uicore.layer.charactercreation.isopen:
    579             logger.info('avatardisplay: Skipping reload due to system menu or character creation being open.')
    580             return
    581         if not self.context or not hasattr(self.context, 'character_list'):
    582             logger.info('avatardisplay: Skipping reload called before context is set up.')
    583             return
    584         self.reload_thread = uthread.new(self.Reload_t, sound_start_time=sound_start_time)
    585 
    586     def Reload_t(self, sound_start_time = 0):
    587 
    588         def _cleanup_and_return():
    589             logger.info('avatardisplay: context destroyed before reload thread active')
    590             self.reload_thread = None
    591 
    592         blue.synchro.Sleep(30)
    593         if self.context is None:
    594             return _cleanup_and_return()
    595         update_list = [ character.doll.IsBusyUpdating() for character in self.context.character_list ]
    596         while any(update_list):
    597             blue.synchro.Yield()
    598             if self.context is None:
    599                 return _cleanup_and_return()
    600             update_list = [ character.doll.IsBusyUpdating() for character in self.context.character_list ]
    601 
    602         if self.sceneContainer.renderJob:
    603             if hasattr(self.sceneContainer.renderJob, 'SetStartOpacity'):
    604                 self.sceneContainer.renderJob.SetStartOpacity(0.0)
    605         if self.context:
    606             self.context.StopAudio()
    607             self.display = False
    608             self.context.ResetCharacterLoadLocks()
    609             self.LoadScene(self.context, force=True, scene_dir=self.scene_dir, sound_start_time=sound_start_time)
    610             self.isDirty = True
    611             self.display = True
    612         self.reload_thread = None
    613 
    614     def _Cleanup(self):
    615         try:
    616             preparing_scene = False
    617             if self.context:
    618                 self.context.StopAudio()
    619                 self.context.Cleanup()
    620                 self.context = None
    621         except Exception:
    622             log.LogException('Exception raised during preview container cleanup', severity=log.INFO)
    623             sys.exc_clear()
    624 
    625     def AnimEntry(self, yaw0 = 1.1 * math.pi, pitch0 = 0.0, yaw1 = 1.25 * math.pi, pitch1 = 0.3, duration = 2.0):
    626         self.sceneContainer.AnimEntry(yaw0, pitch0, yaw1, pitch1, duration)
    627 
    628     def UpdateViewPort(self):
    629         if uicore.layer.systemmenu.isopen:
    630             logger.info('avatardisplay: System menu is open and UpdateViewPort was called')
    631             return
    632         if self.sceneContainer:
    633             self.sceneContainer.UpdateViewPort()
    634 
    635     def OnSetDevice(self):
    636         if uicore.layer.systemmenu.isopen:
    637             logger.info('avatardisplay: System menu is open and SetDevice was called')
    638             return
    639         self.WaitForDollUpdates()
    640         logging.info('avatardisplay: Reloading in OnSetDevice')
    641         self.Reload()
    642 
    643     def WaitForDollUpdates(self):
    644         while self.context and any([ char.doll.getbusyUpdating() for char in self.context.character_list ]):
    645             blue.synchro.Yield()
    646 
    647     def OnGraphicSettingsChanged(self, changes):
    648         if uicore.layer.systemmenu.isopen:
    649             logger.info('avatardisplay: System menu is open and graphics settings were changed')
    650             return
    651         if self.context and any((setting in self.context.relevantSettings for setting in changes)):
    652             logging.info('avatardisplay: Reloading in OnGraphicsSettingsChanged')
    653             self.Reload()
    654 
    655     def OnSettingsCloseStarted(self):
    656         if hasattr(self.context, 'char_ids'):
    657             for char_id in self.context.char_ids:
    658                 sm.GetService('character').UpdateDoll(char_id)
    659 
    660         self.WaitForDollUpdates()
    661         logging.info('avatardisplay: Reloading in OnSettingsCloseStarted')
    662         self.Reload()
    663 
    664 
    665 class AvatarCommandReceiverContainer(AvatarPreviewContainer):
    666     AvatarPreviewContainer.__notifyevents__.append('OnAvatarCommand')
    667 
    668     def __init__(self, *args, **kwargs):
    669         super(AvatarCommandReceiverContainer, self).__init__(*args, **kwargs)
    670         self.COMMANDS = ['restart', 'play_scene_anim']
    671 
    672     def OnAvatarCommand(self, *args, **kwargs):
    673         self.process_command(args)
    674 
    675     def process_command(self, args):
    676         print(('got here', len(args), args[1:]))
    677         if len(args) >= 1:
    678             curr_command = args[0]
    679             print(curr_command)
    680             if curr_command in self.COMMANDS:
    681                 command_func = getattr(ac, 'avcmd_' + curr_command)
    682                 uthread.new(command_func, self, *args[1:])
    683 
    684     def PlaySceneBehavior(self, behavior_name):
    685         if self.context is not None:
    686             self.context.PlaySceneBehavior(behavior_name)
    687 
    688 
    689 class AvatarPreviewNavigation(SceneContainerBaseNavigation):
    690     default_cursor = uiconst.UICURSOR_CCALLDIRECTIONS
    691     default_state = uiconst.UI_NORMAL
    692 
    693     def UpdateCursor(self):
    694         if uicore.uilib.rightbtn and not uicore.uilib.leftbtn and self.sr.sceneContainer.verticalPanEnabled:
    695             self.cursor = uiconst.UICURSOR_CCUPDOWN
    696         else:
    697             self.cursor = uiconst.UICURSOR_CCALLDIRECTIONS
    698 
    699     def OnMouseDown(self, *args):
    700         SceneContainerBaseNavigation.OnMouseDown(self, *args)
    701         self.UpdateCursor()
    702 
    703     def OnMouseUp(self, *args):
    704         SceneContainerBaseNavigation.OnMouseUp(self, *args)
    705         self.UpdateCursor()
    706 
    707     def OnMouseMove(self, *args):
    708         if self.sr.sceneContainer.verticalPanEnabled and uicore.uilib.rightbtn:
    709             cameraDistance = self.sr.sceneContainer.camera.GetZoomDistance()
    710             delta = uicore.uilib.dy * 0.0006 * cameraDistance
    711             y = self.sr.sceneContainer.verticalPan
    712             self.sr.sceneContainer.verticalPan = y + delta
    713         else:
    714             SceneContainerBaseNavigation.OnMouseMove(self, *args)
    715 
    716 
    717 class AvatarPreviewSceneContainer(SceneContainer):
    718     default_state = uiconst.UI_DISABLED
    719 
    720     def ApplyAttributes(self, attributes):
    721         super(AvatarPreviewSceneContainer, self).ApplyAttributes(attributes)
    722         self._minY = None
    723         self._maxY = None
    724         self._opacity = 1.0
    725 
    726     @property
    727     def opacity(self):
    728         return self._opacity
    729 
    730     @opacity.setter
    731     def opacity(self, value):
    732         self._opacity = value
    733         self.renderObject.opacity = value
    734 
    735     @opacity.deleter
    736     def opacity(self):
    737         del self._opacity
    738 
    739     @property
    740     def verticalPanLimits(self):
    741         return (self._minY, self._maxY)
    742 
    743     @verticalPanLimits.setter
    744     def verticalPanLimits(self, limits):
    745         if limits is None:
    746             limits = (None, None)
    747         minY, maxY = limits
    748         if minY > maxY:
    749             minY, maxY = maxY, minY
    750         self._minY = minY
    751         self._maxY = maxY
    752 
    753     @property
    754     def verticalPanEnabled(self):
    755         return self._minY is not None and self._maxY is not None
    756 
    757     @property
    758     def verticalPan(self):
    759         if self.camera:
    760             return self.camera.atPosition[1]
    761 
    762     @verticalPan.setter
    763     def verticalPan(self, y):
    764         if self.verticalPanEnabled:
    765             y = clamp(y, self._minY, self._maxY)
    766             x, _, z = self.camera.atPosition
    767             self.camera.SetAtPosition((x, y, z))
    768 
    769     def Close(self):
    770         logger.info('avatardisplay: CLOSING AvatarPreviewSceneContainer')
    771         super(AvatarPreviewSceneContainer, self).Close()
    772 
    773 
    774 class AvatarSceneContext(object):
    775     relevantSettings = []
    776 
    777     def LoadScene(self, sceneContainer, hide = False):
    778         raise NotImplementedError('AvatarSceneContexts must override the LoadScene method')
    779 
    780     def Cleanup(self):
    781         pass
    782 
    783     def ResetCharacterLoadLocks(self):
    784         pass
    785 
    786 
    787 class AvatarCharacterPreparedSceneContext(AvatarSceneContext):
    788     relevantSettings = [gfxsettings.GFX_CHAR_TEXTURE_QUALITY, gfxsettings.UI_NCC_GREEN_SCREEN, gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION]
    789 
    790     def __init__(self, char_ids, dna_list, position_list, lights, camera, post, background = None, anim_strs = None, audio_strs = None, curveset_lists = None, audio_delays = None, gender_list = None, sub_char_id = None):
    791         self.char_ids = char_ids
    792         self.dna_list = dna_list
    793         self.position_list = position_list
    794         self.background = background
    795         self.boundingBox_list = []
    796         self.character_list = []
    797         self.lights = lights
    798         self.camera = camera
    799         self.post = post
    800         self.anim_strs = anim_strs
    801         self.audio_strs = audio_strs
    802         self.curveset_lists = curveset_lists
    803         self.audio_delays = audio_delays
    804         self.audio_playing_ID = 0
    805         self.audio_playing_event = ''
    806         self.anim_start_thread = None
    807         self.minimized = False
    808         self.lutObject = None
    809         self.gender_list = gender_list
    810         self.sub_char_id = sub_char_id
    811         self.granny_animation_list = None
    812         self.scene_container = None
    813         self.behavior_dict = None
    814 
    815     def __eq__(self, other):
    816         return isinstance(other, AvatarCharacterPreparedSceneContext) and self.dna_list == other.dna_list and self.background == other.background and self.char_ids == other.char_ids and self.position_list == other.position_list and self.lights == other.lights and self.camera == other.camera and self.post == other.post and self.anim_strs == other.anim_strs and self.audio_strs == other.audio_strs and self.curveset_lists == other.curveset_lists and self.audio_delays == other.audio_delays and self.gender_list == other.gender_list and self.sub_char_id == other.sub_char_id
    817 
    818     def LoadScene(self, sceneContainer, hide = False, per_scene_caching = False, scene_dir = '', sound_start_time = 0):
    819         logger.info('avatardisplay: LoadScene starting %s' % scene_dir)
    820         if uicore.layer.systemmenu.isopen:
    821             logger.info('avatardisplay: System menu is open')
    822             return
    823         background = self.GetBackground()
    824         logger.info('avatardisplay: LoadScene bg loaded %s' % scene_dir)
    825         sceneContainer.PrepareInteriorScene(useShadows=True, backgroundImage=background, startOpacity=0.0)
    826         logger.info('avatardisplay: LoadScene interior scene prepared %s' % scene_dir)
    827         sceneContainer.renderJob.enabled = False
    828         sceneContainer.UpdateAlignment()
    829         logger.info('avatardisplay: LoadScene UpdateAlignment called %s' % scene_dir)
    830         for curveSet in sceneContainer.scene.curveSets[:]:
    831             sceneContainer.scene.curveSets.remove(curveSet)
    832 
    833         if self.lights:
    834             lightList = sceneContainer.scene.lights[:]
    835             for light in lightList:
    836                 sceneContainer.scene.RemoveLightSource(light)
    837 
    838             for light in self.lights.lights:
    839                 sceneContainer.scene.AddLightSource(light)
    840 
    841             if self.lights.curveSets:
    842                 for curveSet in self.lights.curveSets:
    843                     sceneContainer.scene.curveSets.append(curveSet)
    844 
    845         logger.info('avatardisplay: LoadScene Lights added %s' % scene_dir)
    846         if self.camera:
    847             sceneContainer.camera.StopUpdateThreads()
    848             sceneContainer.camera.eyePosition = self.camera['position']
    849             sceneContainer.camera.atPosition = self.camera['point_of_interest']
    850             sceneContainer.camera.near_clip = self.camera.get('front_clip', 0.1)
    851             sceneContainer.camera.far_clip = self.camera.get('back_clip', 300)
    852             sceneContainer.camera.fov = self.camera.get('field_of_view', 0.3)
    853             aspect_ratio = self.camera.get('aspect_ratio', 1.778)
    854             sceneContainer.camera.UpdateViewportSize(aspect_ratio, 1.0)
    855         logger.info('avatardisplay: LoadScene camera set up %s' % scene_dir)
    856         avatar_list = []
    857         granny_animation_list = []
    858         charSvc = sm.GetService('character')
    859         if self.dna_list is not None:
    860             if self.sub_char_id is not None:
    861                 char_id_list = [self.sub_char_id]
    862             else:
    863                 char_id_list = None
    864             bloodlineID_list = GetBloodlineIDListFromDNAList(self.dna_list, char_id_list)
    865             tmp_gender_list = []
    866             for idx, dna in enumerate(self.dna_list):
    867                 if type(dna) == list:
    868                     if dna[0] == 'male':
    869                         tmp_gender_list.append(1)
    870                     else:
    871                         tmp_gender_list.append(0)
    872                 else:
    873                     tmp_gender_list.append(self.gender_list[idx])
    874 
    875             self.character_list = []
    876             try:
    877                 for idx, dna in enumerate(self.dna_list):
    878                     while self.char_ids[idx] in CHARS_LOADING:
    879                         logger.info('avatardisplay: Character loading for %s, yielding' % str(scene_dir))
    880                         blue.synchro.Yield()
    881 
    882                     CHARS_LOADING.append(self.char_ids[idx])
    883                     logger.info('avatardisplay: About to add character to scene %s' % str(scene_dir))
    884                     updateDoll = False
    885                     if self.sub_char_id is not None:
    886                         updateDoll = True
    887                     character = charSvc.AddCharacterToScene(charID=self.char_ids[idx], scene=sceneContainer.scene, gender=GenderIDToPaperDollGender(tmp_gender_list[idx]), bloodlineID=bloodlineID_list[idx], dna=dna, lod=LOD_SKIN, updateDoll=updateDoll, anim=False)
    888                     if self.char_ids[idx] not in cache:
    889                         cache[self.char_ids[idx]] = character
    890                     logger.info('avatardisplay: Character added %s' % str(scene_dir))
    891                     self.character_list.append(character)
    892                     pos = self.position_list[idx]
    893                     av = charSvc.GetSingleCharactersAvatar(self.char_ids[idx])
    894                     avatar_list.append(av)
    895                     av.display = False
    896                     av.translation = pos['translation']
    897                     av.rotation = pos['rotation']
    898 
    899                 for avatar in avatar_list:
    900                     avatar.display = True
    901 
    902                 logger.info('avatardisplay: Characters added %s' % scene_dir)
    903                 textureQuality = gfxsettings.Get(gfxsettings.GFX_CHAR_TEXTURE_QUALITY)
    904                 textureResolution = ccConst.DOLL_VIEWER_TEXTURE_RESOLUTIONS[textureQuality]
    905                 useFastShader = IsUsingLowQualityCharacters()
    906                 self.boundingBox_list = []
    907                 for idx, character in enumerate(self.character_list):
    908                     if textureResolution != character.doll.textureResolution or useFastShader != character.doll.useFastShader:
    909                         logger.info('TexRes: (%d, %d)' % (character.doll.textureResolution[0], character.doll.textureResolution[1]))
    910                         character.doll.textureResolution = textureResolution
    911                         character.doll.useFastShader = useFastShader
    912                     try:
    913                         self.boundingBox_list.append(character.visualModel.GetBoundingBoxInLocalSpace())
    914                     except RuntimeError:
    915                         self.boundingBox_list.append(((-0.5, -0.5, 0), (0.5, 0.5, 1.5)))
    916 
    917                 for character in self.character_list:
    918                     character.Update()
    919                     character.WaitForUpdate()
    920 
    921             except Exception as e:
    922                 raise e
    923             finally:
    924                 self.ResetCharacterLoadLocks()
    925 
    926         if useFastShader:
    927             sceneContainer.scene.ambientColor = (0.25, 0.25, 0.25)
    928         if not gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
    929             floor = trinity.Load(ccConst.CUSTOMIZATION_FLOOR)
    930             sceneContainer.scene.dynamics.append(floor)
    931         logger.info('avatardisplay: LoadScene character quality set %s' % scene_dir)
    932         if self.post:
    933             if self.post['lut']:
    934                 self.lutObject = blue.resMan.GetResource(self.post['lut_path'])
    935                 while not self.lutObject.isPrepared:
    936                     blue.synchro.Yield()
    937 
    938                 tex_lut_obj = GetTexLUT(sceneContainer.renderJob)
    939                 if tex_lut_obj is not None:
    940                     tex_lut_obj.SetResource(self.lutObject)
    941                     sceneContainer.renderJob.lut_res_path = self.post['lut_path']
    942                 influence_obj = GetLUTIntensity(sceneContainer.renderJob)
    943                 if influence_obj is not None:
    944                     influence_obj.value = self.post['lut_influence']
    945         logger.info('avatardisplay: LoadScene Postprocess done %s' % scene_dir)
    946         for idx, dna in enumerate(self.dna_list):
    947             av = charSvc.GetSingleCharactersAvatar(self.char_ids[idx])
    948             if 'female' in av.visualModel.geometryResPath.lower():
    949                 gender_name = 'Female'
    950             else:
    951                 gender_name = 'Male'
    952             gsf_anim = False
    953             animation = None
    954             if self.anim_strs is not None and self.anim_strs[idx] is not None and self.anim_strs[idx] != 'no_anim':
    955                 if '.gsf' in self.anim_strs[idx]:
    956                     gsf_anim = True
    957                     if per_scene_caching:
    958                         cacheKey = (self.anim_strs[idx], gender_name.lower(), scene_dir)
    959                     else:
    960                         cacheKey = (self.anim_strs[idx], gender_name.lower())
    961                     if cacheKey in GSTATE_CACHE:
    962                         while not GSTATE_CACHE[cacheKey][CACHE_READY]:
    963                             blue.synchro.Yield()
    964 
    965                         logger.info('avatardisplay: GSTATE_CACHE HIT %s' % str(GSTATE_CACHE))
    966                         animation = GSTATE_CACHE[cacheKey][CACHE_GSF]
    967                         animation.ResetParamsToDefault()
    968                     else:
    969                         logger.info('avatardisplay: GSTATE_CACHE MISS %s' % str(GSTATE_CACHE))
    970                         GSTATE_CACHE[cacheKey] = [trinity.Tr2GStateAnimation(), False]
    971                         GSTATE_CACHE[cacheKey][CACHE_GSF].gStateResPath = str(self.anim_strs[idx])
    972                         while not GSTATE_CACHE[cacheKey][CACHE_GSF].IsFullyLoaded():
    973                             blue.synchro.Yield()
    974 
    975                         GSTATE_CACHE[cacheKey][CACHE_GSF].InstantiateCharacter()
    976                         animation = GSTATE_CACHE[cacheKey][CACHE_GSF]
    977                         GSTATE_CACHE[cacheKey][CACHE_READY] = True
    978                 elif '.gr2' in self.anim_strs[idx]:
    979                     animation = trinity.Tr2GrannyAnimation()
    980                     animation.resPath = self.anim_strs[idx]
    981             else:
    982                 gsf_anim = True
    983                 animation = trinity.Tr2GStateAnimation()
    984                 animation.gStateResPath = 'res:\\Animation\\CharacterCreatorV2\\GState\\CharacterCreator_Gstate_%s_Master.gsf' % gender_name
    985                 log.LogNotice('Resetting gStateResPath')
    986                 trinity.WaitForResourceLoads()
    987                 if gsf_anim and animation is not None:
    988                     while not animation.IsFullyLoaded():
    989                         blue.synchro.Yield()
    990 
    991                     animation.InstantiateCharacter()
    992             if animation is not None:
    993                 av.animationUpdater = animation
    994             logger.info('avatardisplay: LoadScene character animation set up %s' % scene_dir)
    995             curveset_paths = self.curveset_lists[idx]
    996             if curveset_paths is not None:
    997                 self.BindCurveList(curveset_paths, av, sceneContainer)
    998             logger.info('avatardisplay: LoadScene character curves bound %s' % scene_dir)
    999             if not gsf_anim:
   1000                 granny_animation_list.append(animation)
   1001             else:
   1002                 granny_animation_list.append(None)
   1003 
   1004         if self.post:
   1005             if 'transmission_noise' in self.post:
   1006                 if self.post['transmission_noise']:
   1007                     sceneContainer.renderJob.PlaceTransmissionEffectInScene(sceneContainer.scene)
   1008         self.granny_animation_list = granny_animation_list
   1009         self.scene_container = sceneContainer
   1010         self.StartAnimation(sceneContainer.scene.curveSets, granny_animation_list, sound_start_time)
   1011         if not hide:
   1012             sceneContainer.renderJob.enabled = True
   1013 
   1014     def BindCurveList(self, curve_list, avatar, sceneContainer):
   1015         curvesets = []
   1016         for curveset_path in curve_list:
   1017             curveset_metadata_path = curveset_path[:-4] + '_metadata.yaml'
   1018             if curveset_path:
   1019                 if curveset_path.lower() in CURVE_CACHE:
   1020                     curveset = CURVE_CACHE[curveset_path.lower()]
   1021                 else:
   1022                     curveset = trinity.Load(curveset_path)
   1023                     if curveset_path.lower() not in CURVE_CACHE:
   1024                         CURVE_CACHE[curveset_path.lower()] = curveset
   1025                 curvesets.append(curveset)
   1026                 curveset_metadata = LoadYamlFileNicely(curveset_metadata_path)
   1027                 route = curveset_metadata['parameter_path']
   1028                 sceneContainer.scene.curveSets.append(curveset)
   1029                 for binding in curveset.bindings:
   1030                     if binding.destinationObject:
   1031                         if route:
   1032                             dest_param = self.GetTargetFromOwnerAndRoute(avatar, route)
   1033                             binding.destinationObject = dest_param
   1034                         else:
   1035                             gsf_param = avatar.animationUpdater.parameters.FindByName(binding.destinationObject.name)
   1036                             binding.destinationObject = gsf_param
   1037 
   1038         return curvesets
   1039 
   1040     def LoadBehaviors(self):
   1041         behavior_rows = GetAvatarBehaviorRows()
   1042         self.behavior_dict = {}
   1043         for behavior in behavior_rows:
   1044             self.behavior_dict[behavior.name, behavior.resGender] = behavior
   1045 
   1046     def PlaySceneBehavior(self, behavior_name, index = '0'):
   1047         if self.scene_container is None:
   1048             return
   1049         if self.behavior_dict is None:
   1050             self.LoadBehaviors()
   1051         charSvc = sm.GetService('character')
   1052         idx = int(index)
   1053         gender = charSvc.GetSingleCharactersMetadata(self.char_ids[idx])['genderID']
   1054         if (behavior_name, gender) in self.behavior_dict:
   1055             log.LogInfo('avatardisplay: playing %s for gender %s' % (behavior_name, gender))
   1056             curve_list = self.behavior_dict[behavior_name, gender].resPathList
   1057             avatar = charSvc.GetSingleCharactersAvatar(self.char_ids[idx])
   1058             self.PlayCurveListOnAvatar(curve_list, avatar, self.scene_container)
   1059         else:
   1060             log.logInfo('avatardisplay: playing nothing, requested behavior not in table %s %s' % (behavior_name, gender))
   1061 
   1062     def PlayCurveListOnAvatar(self, curve_list, avatar, sceneContainer):
   1063         if self.granny_animation_list is not None:
   1064             curvesets = self.BindCurveList(curve_list, avatar, sceneContainer)
   1065             self.StartAnimation(curvesets, self.granny_animation_list, sound_start_time=0, silence_sound=True)
   1066 
   1067     def ResetCharacterLoadLocks(self):
   1068         for charid in self.char_ids:
   1069             if charid in CHARS_LOADING:
   1070                 CHARS_LOADING.remove(charid)
   1071 
   1072     def StartAnimation(self, curve_sets, granny_animation_list, sound_start_time = 0, silence_sound = False):
   1073         if self.anim_start_thread:
   1074             return
   1075         self.anim_start_thread = uthread.new(self.StartAnimation_t, curve_sets, granny_animation_list, sound_start_time, silence_sound)
   1076 
   1077     def IsSupportedVOLanguage(self):
   1078         return session.languageID == 'EN'
   1079 
   1080     def StartAnimation_t(self, curve_sets, granny_animation_list, sound_start_time = 0, silence_sound = False):
   1081         logger.info('avatardisplay: animation started')
   1082         delay_index = zip(self.audio_delays, range(len(self.audio_delays)))
   1083         delay_index.sort(key=lambda x: x[0])
   1084         curr_offset = 0
   1085         play_sound = False
   1086         curr_time = time.time()
   1087         if not sound_start_time:
   1088             play_sound = True and not silence_sound
   1089             sound_start_time = curr_time
   1090         offset = curr_time - sound_start_time
   1091         if curve_sets:
   1092             for curveSet in curve_sets:
   1093                 if 'lipsync' in curveSet.name:
   1094                     if self.IsSupportedVOLanguage():
   1095                         curveSet.PlayFrom(offset)
   1096                     else:
   1097                         curveSet.Stop()
   1098                 else:
   1099                     curveSet.PlayFrom(offset)
   1100 
   1101         for idx in range(len(self.dna_list)):
   1102             if granny_animation_list[idx] is not None:
   1103                 animation = granny_animation_list[idx]
   1104                 baseclip = animation.grannyRes.GetAnimationName(0)
   1105                 animation.PlayAnimationEx(baseclip, 1, 0, 1.0)
   1106 
   1107         if play_sound:
   1108             for delay, idx in delay_index:
   1109                 if self.audio_strs is not None and self.audio_strs[idx] != 'no_audio':
   1110                     self.StartAudio(str(self.audio_strs[idx]), delay - curr_offset)
   1111                     curr_offset = delay
   1112 
   1113         self.anim_start_thread = None
   1114 
   1115     def StartAudio(self, audio_str, delay):
   1116         if self.minimized:
   1117             return
   1118         if not self.IsSupportedVOLanguage():
   1119             return
   1120         logger.info('avatardisplay: start audio event=%s delay=%s sec', audio_str, delay)
   1121         blue.synchro.Sleep(delay * 1000)
   1122         self.audio_playing_event = audio_str
   1123         self.audio_playing_ID = sm.GetService('audio').SendUIEventWithCallback(audio_str)
   1124         logger.info('avatardisplay: audio UI event sent %s', audio_str)
   1125 
   1126     def StopAudio(self):
   1127         self.audio_playing_event = ''
   1128         if self.anim_start_thread:
   1129             logger.info('avatardisplay: killing anim start thread')
   1130             self.anim_start_thread.kill()
   1131         if self.audio_playing_ID > 0:
   1132             sm.GetService('audio').StopUIEvent(self.audio_playing_ID)
   1133             logger.info('avatardisplay: stop audio request sent for %s', self.audio_playing_ID)
   1134 
   1135     def SetMinimized(self):
   1136         self.minimized = True
   1137 
   1138     def SetMaximized(self):
   1139         self.minimized = False
   1140 
   1141     def GetSceneAudioPlayingEvent(self):
   1142         return self.audio_playing_event
   1143 
   1144     @staticmethod
   1145     def GetTargetFromOwnerAndRoute(owner, route):
   1146         import re
   1147         steps = route.split('.')
   1148         current = owner
   1149         for step in steps:
   1150             if step.lower() == 'owner':
   1151                 continue
   1152             if hasattr(current, step):
   1153                 current = getattr(current, step)
   1154                 continue
   1155             regex = u'(?P<name>^.+)\\[\\"(?P<key>.+)\\"\\]'
   1156             match = re.match(regex, step)
   1157             if match is not None and match.groups():
   1158                 name = match.group('name')
   1159                 key = match.group('key')
   1160                 if hasattr(current, name):
   1161                     tmp = getattr(current, name)
   1162                     if tmp.FindByName(key):
   1163                         current = tmp.FindByName(key)
   1164 
   1165         return current
   1166 
   1167     def GetBackground(self):
   1168         background = self.background
   1169         if gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
   1170             background = 'res:/UI/Texture/CharacterCreation/backdrops/Background_1001.dds'
   1171         return background
   1172 
   1173     def SetupCamera(self, sceneContainer):
   1174         if self.boundingBox_list:
   1175             SetupInteriourCamera(sceneContainer, reduce(MergeBoundingBoxes, self.boundingBox_list))
   1176         else:
   1177             SetupInteriourCamera(sceneContainer, ((-0.5, -0.5, 0), (0.5, 0.5, 1.5)))
   1178 
   1179 
   1180 class AvatarCharacterSceneContext(AvatarSceneContext):
   1181     relevantSettings = [gfxsettings.GFX_CHAR_TEXTURE_QUALITY, gfxsettings.UI_NCC_GREEN_SCREEN, gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION]
   1182 
   1183     def __init__(self, charID, dna = None, apparel = None, background = None):
   1184         dna = dna or sm.RemoteSvc('paperDollServer').GetPaperDollData(charID)
   1185         if dna is None:
   1186             raise UserError('CharacterHasNoDNA', {'charID': charID})
   1187         self.charID = charID
   1188         self.dna = dna
   1189         self.apparel = apparel or []
   1190         self.background = background
   1191         self.boundingBox = None
   1192         self.character = None
   1193 
   1194     def __eq__(self, other):
   1195         return isinstance(other, AvatarCharacterSceneContext) and self.charID == other.charID and self.dna == other.dna and self.apparel == other.apparel and self.background == other.background
   1196 
   1197     def LoadScene(self, sceneContainer, hide = False):
   1198         useFastShader = False
   1199         background = self.GetBackground()
   1200         sceneContainer.PrepareInteriorScene(useShadows=True, backgroundImage=background)
   1201         if self.charID > 65535:
   1202             owner = cfg.eveowners.Get(self.charID)
   1203             charInfo = sm.RemoteSvc('charMgr').GetPublicInfo(self.charID)
   1204             bloodlineID = charInfo.bloodlineID
   1205             gender = getattr(owner, 'gender', None)
   1206             if gender is None:
   1207                 raise RuntimeError('{0.name} ({0.charID}) does not have a defined gender'.format(owner))
   1208         else:
   1209             bloodlineID = const.bloodlineGallente
   1210             gender = 0
   1211             if self.dna[0] == 'male':
   1212                 gender = 1
   1213         charSvc = sm.GetService('character')
   1214         char_loaded = charSvc.IsSingleCharacterLoaded(self.charID)
   1215         character = charSvc.AddCharacterToScene(charID=self.charID, scene=sceneContainer.scene, gender=GenderIDToPaperDollGender(gender), bloodlineID=bloodlineID, dna=self.dna, lod=LOD_SKIN, updateDoll=not char_loaded, anim=False)
   1216         self.character = character
   1217         if not char_loaded:
   1218             textureQuality = gfxsettings.Get(gfxsettings.GFX_CHAR_TEXTURE_QUALITY)
   1219             textureResolution = ccConst.DOLL_VIEWER_TEXTURE_RESOLUTIONS[textureQuality]
   1220             character.doll.textureResolution = textureResolution
   1221             useFastShader = IsUsingLowQualityCharacters()
   1222             character.doll.useFastShader = useFastShader
   1223             for typeID in self.apparel:
   1224                 apparel = GetPaperDollResource(typeID, gender=gender)
   1225                 if apparel is None:
   1226                     log.LogError('Unable to preview %s (%s) since it has no associated resource' % (evetypes.GetName(typeID), typeID))
   1227                     continue
   1228                 charSvc.ApplyTypeToDoll(self.charID, apparel.resPath, doUpdate=False)
   1229 
   1230             character.Update()
   1231             character.WaitForUpdate()
   1232         try:
   1233             self.boundingBox = character.visualModel.GetBoundingBoxInLocalSpace()
   1234         except RuntimeError:
   1235             self.boundingBox = ((-0.5, -0.5, 0), (0.5, 0.5, 1.5))
   1236 
   1237         if useFastShader:
   1238             sceneContainer.scene.ambientColor = (0.25, 0.25, 0.25)
   1239         if not gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
   1240             floor = trinity.Load(ccConst.CUSTOMIZATION_FLOOR)
   1241             sceneContainer.scene.dynamics.append(floor)
   1242         self.SetupCamera(sceneContainer)
   1243 
   1244     def GetBackground(self):
   1245         background = self.background
   1246         if gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
   1247             background = 'res:/UI/Texture/CharacterCreation/backdrops/Background_1001.dds'
   1248         return background
   1249 
   1250     def SetupCamera(self, sceneContainer):
   1251         SetupInteriourCamera(sceneContainer, self.boundingBox)
   1252 
   1253     def Cleanup(self):
   1254         pass
   1255 
   1256 
   1257 def SetupInteriourCamera(sceneContainer, boundingBox):
   1258     p0, p1 = geo2.Vector(boundingBox[0]), geo2.Vector(boundingBox[1])
   1259     center = 0.5 * (p1 - p0) + p0
   1260     camera = sceneContainer.camera
   1261     camera.SetAtPosition(center)
   1262     sceneContainer.verticalPanLimits = (p0.y, p1.y)
   1263     rad = max(geo2.Vec3Length(p0 - p1), 0.3)
   1264     alpha = sceneContainer.fov * 1.5 / 2.0
   1265     maxZoom = min(rad * (1 / math.tan(alpha)), 9.0)
   1266     minZoom = rad + sceneContainer.frontClip
   1267     sceneContainer.SetMinMaxZoom(minZoom, maxZoom)
   1268     camera.SetZoomLinear(0.6)
   1269     camera.kMinPitch = 0.0
   1270     camera.kMaxPitch = math.pi / 2.0
   1271     camera.kOrbitSpeed = 30.0
   1272     camera.farClip = 100.0
   1273 
   1274 
   1275 def GetPaperDollResource(typeID, gender = None):
   1276     assets = filter(lambda a: a.typeID == typeID, cfg.paperdollResources)
   1277     if len(assets) == 0:
   1278         log.LogWarn('PreviewWnd::PreviewType - No asset matched the typeID {}'.format(typeID))
   1279         return None
   1280     default_asset = first(assets)
   1281     unisex_asset = first_or_default(assets, lambda a: a.resGender is None, default_asset)
   1282     return first_or_default(assets, lambda a: a.resGender == gender, unisex_asset)
   1283 
   1284 
   1285 def GetBloodlineFromDNA(dna, charID = None):
   1286     if type(dna) == list:
   1287         bloodlineArchetypeString = ''
   1288         for modifier in dna:
   1289             if type(modifier) is dict:
   1290                 cat = modifier.get('category', '')
   1291                 if cat == 'archetypes':
   1292                     bloodlineArchetypeString = modifier['path'].split('/')[1]
   1293 
   1294         if bloodlineArchetypeString in ARCHETYPES_TO_BLOODLINES:
   1295             return ARCHETYPES_TO_BLOODLINES[bloodlineArchetypeString]
   1296         else:
   1297             return None
   1298     else:
   1299         charInfo = sm.RemoteSvc('charMgr').GetPublicInfo(charID)
   1300         return charInfo.bloodlineID
   1301 
   1302 
   1303 def GetBloodlineIDListFromDNAList(dna_list, char_id_list = None):
   1304     bloodlineID_list = []
   1305     for idx, dna in enumerate(dna_list):
   1306         if type(char_id_list) == list:
   1307             char_id = char_id_list[idx]
   1308         else:
   1309             char_id = None
   1310         bloodline = GetBloodlineFromDNA(dna, charID=char_id)
   1311         if bloodline is None:
   1312             bloodline = const.bloodlineGallente
   1313             logging.warning('avatardisplay: No valid archetype in .prs file')
   1314         bloodlineID_list.append(bloodline)
   1315 
   1316     return bloodlineID_list
   1317 
   1318 
   1319 def IsUsingLowQualityCharacters():
   1320     return gfxsettings.Get(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION)