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)