preview.py (50888B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\preview.py 2 import uthread2 3 from brennivin.itertoolsext import first, first_or_default 4 from carbon.common.script.util.format import FmtDist 5 from carbonui.control.scrollContainer import ScrollContainer 6 from carbonui.primitives.container import Container 7 from carbonui.primitives.containerAutoSize import ContainerAutoSize 8 from carbonui.primitives.sprite import Sprite 9 from characterdata.npccharacters import get_npc_character 10 from characterdata.races import get_race_name 11 from cosmetics.client.shipSkinLicensesSvc import get_ship_skin_license_svc 12 from cosmetics.common.ships.skins.static_data.skin_type import ShipSkinType 13 from eve.client.script.environment.model.turretSet import TurretSet 14 from eve.client.script.paperDoll.paperDollImpl import Doll, Factory 15 from eve.client.script.paperDoll.paperDollSpawnWrappers import PaperDollCharacter 16 from eve.client.script.ui.camera.cameraUtil import GetModelLookAtOffset 17 from carbonui.button.group import ButtonGroup 18 from eve.client.script.ui.control.eveLabel import EveCaptionMedium, EveHeaderSmall, Label 19 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel 20 from carbonui.control.window import Window 21 from player_messaging.client.ui_message import message_player 22 from eve.client.script.ui.control.scenecontainer import SceneContainerBaseNavigation, SceneContainer 23 from eve.client.script.ui.control.themeColored import FillThemeColored 24 from eve.client.script.ui.login.charcreation.ccUtil import GenderIDToPaperDollGender 25 from eve.client.script.ui.shared.skins.controller import SkinPanelController, ThirdPartySkinWrapper, GetSkinnedShipModel 26 from eve.client.script.ui.shared.skins.skinPanel import SkinPanel 27 from eve.client.script.ui.station.assembleModularShip import AssembleShip 28 from eve.client.script.ui.util.uiComponents import Component, ButtonEffect 29 from eve.common.script.paperDoll.paperDollDefinitions import ACCESSORIES_CATEGORIES, BODY_CATEGORIES, DOLL_PARTS, GENDER, HEAD_CATEGORIES, LOD_SKIN 30 from eve.common.script.sys import idCheckers 31 from eve.common.script.sys.eveCfg import IsPreviewable 32 from eve.common.script.sys.idCheckers import IsShipType 33 import evetypes 34 from eveexceptions import UserError 35 from fsdBuiltData.common.graphicIDs import GetGraphicFile, GetSofFactionName 36 from inventorycommon.util import IsModularShip 37 from localization import GetByLabel 38 from publicGateway.grpc.exceptions import GenericException 39 from stackless_response_router.exceptions import TimeoutException 40 from utillib import KeyVal 41 import mathext 42 import carbon.common.script.sys.service as service 43 import carbonui.const as uiconst 44 import charactercreator.const as ccConst 45 import eve.common.lib.appConst as appConst 46 import evegraphics.settings as gfxsettings 47 import evegraphics.utils as gfxutils 48 import eveSpaceObject 49 import eveSpaceObject.spaceobjanimation as soanimation 50 import geo2 51 import inventorycommon.const as invconst 52 import localization 53 import log 54 import math 55 import re 56 import sys 57 import trinity 58 import uthread 59 import inventorycommon.typeHelpers 60 import charactercreator.client.animparams as animparams 61 from eve.client.script.environment.sofService import GetSofService 62 from carbonui.uicore import uicore 63 from eveservices.menu import GetMenuService 64 from functools import reduce 65 MESH_NAMES_BY_GROUPID = {invconst.groupApparelEyewear: [ACCESSORIES_CATEGORIES.ACCESSORIES], 66 invconst.groupApparelProsthetics: [ACCESSORIES_CATEGORIES.SLEEVESLOWER], 67 invconst.groupApparelTattoos: [ACCESSORIES_CATEGORIES.SLEEVESLOWER], 68 invconst.groupApparelPiercings: [], 69 invconst.groupApparelScars: [BODY_CATEGORIES.SCARS], 70 invconst.groupApparelMidLayer: [BODY_CATEGORIES.TOPINNER], 71 invconst.groupApparelOuter: [BODY_CATEGORIES.OUTER], 72 invconst.groupApparelTops: [BODY_CATEGORIES.TOPINNER], 73 invconst.groupApparelBottoms: [BODY_CATEGORIES.BOTTOMOUTER], 74 invconst.groupApparelFootwear: [BODY_CATEGORIES.FEET], 75 invconst.groupApparelHairStyles: [DOLL_PARTS.HAIR, DOLL_PARTS.HEAD], 76 invconst.groupApparelMakeup: [HEAD_CATEGORIES.MAKEUP], 77 invconst.groupApparelAugmentations: [DOLL_PARTS.HEAD], 78 invconst.groupApparelMasks: [ACCESSORIES_CATEGORIES.ACCESSORIES]} 79 MANNEQUIN_RES_BY_GENDER = {GENDER.MALE: 'res:/Graphics/Character/DNAFiles/Mannequin/MaleMannequin.prs', 80 GENDER.FEMALE: 'res:/Graphics/Character/DNAFiles/Mannequin/FemaleMannequin.prs'} 81 PAPERDOLL_CATEGORIES_COVERING = {ccConst.bottommiddle: [ccConst.bottomouter]} 82 83 def GetPaperDollResource(typeID, gender = None): 84 assets = filter(lambda a: a.typeID == typeID, cfg.paperdollResources) 85 if len(assets) == 0: 86 log.LogWarn('PreviewWnd::PreviewType - No asset matched the typeID {}'.format(typeID)) 87 return None 88 default_asset = first(assets) 89 unisex_asset = first_or_default(assets, lambda a: a.resGender is None, default_asset) 90 return first_or_default(assets, lambda a: a.resGender == gender, unisex_asset) 91 92 93 def SetupAnimations(model): 94 if not model.controllers: 95 model.ChainAnimationEx('NormalLoop', 0, 0, 1.0) 96 soanimation.TriggerDefaultStates(model) 97 98 99 class Preview(service.Service): 100 __guid__ = 'svc.preview' 101 __servicename__ = 'preview' 102 __displayname__ = 'Preview Service' 103 __exportedcalls__ = {'PreviewType': [], 104 'PreviewCharacter': [], 105 'DisplayCharacter': []} 106 __dependencies__ = [] 107 108 def Run(self, memStream = None): 109 service.Service.Run(self, memStream=memStream) 110 self.state = service.SERVICE_RUNNING 111 112 def PreviewType(self, typeID, subsystems = None, itemID = None, animate = True, otherTypeID = None, skin_state = None, **kwargs): 113 categoryID = evetypes.GetCategoryID(typeID) 114 shipTypeID = None 115 skin = None 116 if categoryID == invconst.categoryShip: 117 shipTypeID = typeID 118 elif categoryID == invconst.categoryShipSkin: 119 skin = sm.GetService('cosmeticsSvc').GetSkinByLicenseType(typeID) 120 shipTypeID = otherTypeID if otherTypeID and otherTypeID in skin.types else skin.types[0] 121 if shipTypeID: 122 params = {'skin': skin} 123 from eve.client.script.ui.shared.info.shipInfoConst import TAB_SKINS 124 sm.GetService('info').ShowInfo(typeID=shipTypeID, itemID=itemID, selectTabType=TAB_SKINS, params=params) 125 return 126 wnd = PreviewWnd.Open(typeID=typeID) 127 if wnd: 128 wnd.PreviewType(typeID=typeID, subsystems=subsystems, itemID=itemID, animate=animate, otherTypeID=otherTypeID, skin_state=skin_state, **kwargs) 129 return wnd 130 131 def PreviewCharacter(self, charID): 132 if charID in appConst.auraAgentIDs: 133 return 134 dna = sm.RemoteSvc('paperDollServer').GetPaperDollData(charID) 135 if dna is None: 136 raise UserError('CharacterHasNoDNA', {'charID': charID}) 137 wnd = PreviewCharacterWnd.GetIfOpen() 138 if wnd: 139 wnd.PreviewCharacter(charID=charID, dna=dna) 140 else: 141 wnd = PreviewCharacterWnd.Open(charID=charID, dna=dna) 142 return wnd 143 144 def DisplayCharacter(self, charID): 145 dna = sm.RemoteSvc('paperDollServer').GetPaperDollData(charID) 146 if dna is None: 147 raise UserError('CharacterHasNoDNA', {'charID': charID}) 148 wnd = PreviewCharacterWnd.GetIfOpen() 149 if wnd: 150 wnd.PreviewCharacter(charID=charID, dna=dna) 151 else: 152 PreviewCharacterWnd.Open(charID=charID, dna=dna) 153 154 155 @Component(ButtonEffect(opacityIdle=0.0, opacityHover=0.5, opacityMouseDown=1.0, bgElementFunc=lambda parent, _: parent.hilite, audioOnEntry=uiconst.SOUND_ENTRY_HOVER, audioOnClick=uiconst.SOUND_ENTRY_SELECT)) 156 157 class SidePanelButton(ContainerAutoSize): 158 default_state = uiconst.UI_NORMAL 159 160 def ApplyAttributes(self, attributes): 161 super(SidePanelButton, self).ApplyAttributes(attributes) 162 self.onClick = attributes.onClick 163 self.expanded = attributes.get('expanded', False) 164 Sprite(parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/preview/tab.png', width=20, height=80) 165 self.hilite = Sprite(parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/preview/tab-hilite.png', width=20, height=80, opacity=0.0) 166 rotation = 0 if self.expanded else math.pi 167 self.tabIcon = Sprite(parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, left=2, top=32, texturePath='res:/UI/Texture/classes/preview/tab-icon.png', width=16, height=16, rotation=rotation) 168 169 def OnClick(self, *args): 170 self.expanded = not self.expanded 171 self.AnimClick() 172 self.onClick() 173 174 def AnimClick(self): 175 rotation = 0 if self.expanded else math.pi 176 uicore.animations.MorphScalar(self.tabIcon, 'rotation', startVal=self.tabIcon.rotation, endVal=rotation, duration=0.3) 177 178 179 class PreviewWnd(Window): 180 __guid__ = 'form.PreviewWnd' 181 default_windowID = 'previewWnd' 182 default_minSize = (420, 330) 183 default_caption = localization.GetByLabel('UI/Preview/PreviewCaption') 184 PANEL_WIDTH = 416 185 186 def ApplyAttributes(self, attributes): 187 Window.ApplyAttributes(self, attributes) 188 self.typeID = attributes.typeID 189 width = self.PANEL_WIDTH if self.IsPanelExpanded() else 0 190 self.sidePanelWrapper = Container(name='sidePanelWrapper', parent=self.sr.main, align=uiconst.TOLEFT, width=width, bgColor=(1.0, 1.0, 1.0, 0.05), padLeft=-16, padBottom=-16, clipChildren=True) 191 self.sidePanel = Container(name='SidePanel', parent=self.sidePanelWrapper, align=uiconst.TOLEFT, width=self.PANEL_WIDTH, padding=8) 192 self.skinController = SkinPanelController(hullTypeID=None) 193 self.skinPanel = SkinPanel(parent=self.sidePanel, align=uiconst.TOLEFT, width=self.PANEL_WIDTH - 16, controller=self.skinController, settingsPrefix='Preview_SkinPanel', logContext='PreviewWindow') 194 mainCont = Container(name='MainContainer', parent=self.sr.main, align=uiconst.TOALL, padding=(16, 0, -16, -16)) 195 opacity = 1.0 if self.IsPanelEnabled() else 0.0 196 self.sidePanelButton = SidePanelButton(parent=mainCont, align=uiconst.CENTERLEFT, opacity=opacity, onClick=self.ToggleSidePanel, expanded=self.IsPanelExpanded()) 197 self.loadingWheel = LoadingWheel(parent=mainCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED) 198 self.previewContFill = FillThemeColored(parent=mainCont, align=uiconst.TOALL) 199 overlayCont = Container(name='overlayCont', parent=mainCont, padding=2, clipChildren=1) 200 self.title = EveCaptionMedium(text='', parent=overlayCont, align=uiconst.TOTOP, padding=(17, 4, 17, 0), state=uiconst.UI_NORMAL) 201 self.title.GetMenu = self.GetShipMenu 202 self.title.expandOnLeft = 1 203 self.subtitle = EveHeaderSmall(text='', parent=overlayCont, align=uiconst.TOTOP, padding=(19, 0, 17, 0), state=uiconst.UI_DISABLED) 204 self.descCont = ScrollContainer(name='DescriptionContainer', parent=mainCont, align=uiconst.TOBOTTOM_PROP, height=0.3, bgColor=(0.0, 0.0, 0.0, 0.3), state=uiconst.UI_HIDDEN) 205 self.previewContainer = PreviewContainer(parent=mainCont, align=uiconst.TOALL, OnStartLoading=self.OnStartLoading, OnStopLoading=self.OnStopLoading) 206 self.previewContainer.navigation.OnDropData = self.OnDropData 207 self.desc = Label(parent=self.descCont, padding=12, fontsize=12, align=uiconst.TOTOP) 208 209 def IsPanelExpanded(self): 210 return self.IsPanelEnabled() and settings.user.ui.Get('previewPanel', 1) 211 212 def IsPanelEnabled(self): 213 categoryID = evetypes.GetCategoryID(self.typeID) 214 if categoryID in (invconst.categoryShip, invconst.categoryShipSkin): 215 self.UpdateSidePanelButton(isEnabled=True) 216 return True 217 self.UpdateSidePanelButton(isEnabled=False) 218 return False 219 220 def UpdateSidePanelButton(self, isEnabled): 221 if not hasattr(self, 'sidePanelButton'): 222 return 223 if not self.sidePanelButton: 224 return 225 if isEnabled: 226 self.sidePanelButton.Enable() 227 else: 228 self.sidePanelButton.Disable() 229 230 def ToggleSidePanel(self): 231 isExpanded = not self.IsPanelExpanded() 232 settings.user.ui.Set('previewPanel', isExpanded) 233 self.UpdateSidePanel(isExpanded) 234 235 def UpdateSidePanel(self, expanded = None): 236 if expanded is None: 237 expanded = settings.user.ui.Get('previewPanel', 1) 238 width = self.PANEL_WIDTH if expanded else 0 239 uicore.animations.MorphScalar(self.sidePanelWrapper, 'width', startVal=self.sidePanelWrapper.width, endVal=width, duration=0.3) 240 241 def OnStartLoading(self, previewCont): 242 uicore.animations.FadeIn(self.loadingWheel, duration=0.4) 243 uicore.animations.FadeIn(self.previewContFill, duration=0.05, sleep=True) 244 self.ClearText() 245 246 def OnStopLoading(self, previewCont, success): 247 uicore.animations.FadeOut(self.loadingWheel, duration=0.2) 248 if success: 249 uicore.animations.FadeOut(self.previewContFill, duration=0.4) 250 self.UpdateText() 251 252 def UpdateText(self): 253 context = self.previewContainer.context 254 if not hasattr(context, 'typeID'): 255 return 256 groupID = evetypes.GetGroupID(context.typeID) 257 categoryID = evetypes.GetCategoryID(context.typeID) 258 title = evetypes.GetName(context.typeID) 259 if hasattr(context, 'itemID'): 260 bp = sm.GetService('michelle').GetBallpark() 261 if bp: 262 slim = bp.GetInvItem(context.itemID) 263 if slim: 264 title = slim.name 265 self.title.text = title 266 subtitle = '' 267 if categoryID != invconst.categoryApparel: 268 scene = self.previewContainer.sceneContainer.scene 269 model = first_or_default(getattr(scene, 'objects', []), None) 270 if model: 271 radius = round(model.GetBoundingSphereRadius() * 2, 0) 272 if groupID in invconst.turretModuleGroups: 273 subtitle = localization.GetByLabel('UI/Preview/ShipSubLabelNoRace', groupName=evetypes.GetGroupName(context.typeID), length=FmtDist(radius)) 274 else: 275 raceID = evetypes.GetRaceID(context.typeID) 276 try: 277 subtitle = localization.GetByLabel('UI/Preview/ShipSubLabel', raceName=get_race_name(raceID), groupName=evetypes.GetGroupName(context.typeID), length=FmtDist(radius)) 278 except KeyError: 279 subtitle = localization.GetByLabel('UI/Preview/ShipSubLabelNoRace', groupName=evetypes.GetGroupName(context.typeID), length=FmtDist(radius)) 280 281 self.subtitle.text = subtitle 282 if categoryID == invconst.categoryApparel: 283 self.descCont.Show() 284 description = evetypes.GetDescription(context.typeID) or '' 285 description = re.sub('<b>|</b>|\\r', '', description) 286 description = re.sub('\\n', '<br>', description) 287 self.desc.text = description 288 289 def GetShipMenu(self, *args): 290 return GetMenuService().GetMenuFromItemIDTypeID(None, self.typeID, includeMarketDetails=True).filter([localization.GetByLabel('UI/Preview/Preview')]) 291 292 def OnDropData(self, dragObj, nodes): 293 super(PreviewWnd, self).OnDropData(dragObj, nodes) 294 node = first(nodes) 295 typeID = None 296 if hasattr(node, 'item') and hasattr(node.item, 'typeID'): 297 typeID = node.item.typeID 298 elif hasattr(node, 'typeID'): 299 typeID = node.typeID 300 itemID = None 301 if hasattr(node, 'item') and hasattr(node.item, 'itemID'): 302 itemID = node.item.itemID 303 elif hasattr(node, 'itemID'): 304 itemID = node.itemID 305 if typeID: 306 if IsPreviewable(typeID): 307 self.PreviewType(typeID, itemID=itemID) 308 else: 309 raise UserError('PreviewNoModel') 310 311 def PreviewType(self, typeID, subsystems = None, itemID = None, animate = True, otherTypeID = None, skin_state = None, **kwargs): 312 uthread.new(self._PreviewType, typeID, subsystems, itemID, animate, otherTypeID, skin_state, **kwargs) 313 314 def _PreviewType(self, typeID, subsystems, itemID, animate, otherTypeID = None, skin_state = None, **kwargs): 315 self.typeID = typeID 316 self.BringToFront() 317 if evetypes.GetCategoryID(typeID) == invconst.categoryApparel: 318 self.SetMinSize([320, 470]) 319 self.SetMaxSize([800, 950]) 320 else: 321 self.SetMinSize([660, 320]) 322 self.SetMaxSize([None, None]) 323 if not self.IsPanelEnabled(): 324 uicore.animations.FadeOut(self.sidePanelButton, duration=0.3) 325 self.UpdateSidePanel(expanded=False) 326 if self.IsPanelEnabled(): 327 uicore.animations.FadeIn(self.sidePanelButton, duration=0.3) 328 self.UpdateSidePanel() 329 if IsModularShip(typeID): 330 kv = KeyVal(typeID=typeID) 331 wnd = AssembleShip.GetIfOpen('PreviewSubSystems') 332 if wnd: 333 wnd.UpdateShip(kv, subsystems) 334 else: 335 wnd = AssembleShip.Open(windowID='PreviewSubSystems', ship=kv, groupIDs=None, isPreview=True, setselected=subsystems) 336 subsystems = wnd.getSubSystems() 337 else: 338 self.CloseSubSystemWnd() 339 newScene = self.previewContainer.PreviewType(typeID, subsystems=subsystems, itemID=itemID, controller=getattr(self, 'skinController', None), otherTypeID=otherTypeID, skin_state=skin_state, **kwargs) 340 groupID = evetypes.GetGroupID(typeID) 341 categoryID = evetypes.GetCategoryIDByGroup(groupID) 342 if categoryID == invconst.categoryShip: 343 self.skinPanel.SetPreviewType(typeID) 344 elif categoryID == invconst.categoryShipSkin: 345 skin = sm.GetService('cosmeticsSvc').GetSkinByLicenseType(typeID) 346 shipTypeID = otherTypeID if otherTypeID and otherTypeID in skin.types else skin.types[0] 347 self.skinPanel.SetPreviewType(shipTypeID) 348 if newScene and animate: 349 self.previewContainer.AnimEntry() 350 351 def ClearText(self): 352 self.title.text = '' 353 self.subtitle.text = '' 354 self.desc.SetText('') 355 self.descCont.Hide() 356 357 def BringToFront(self): 358 self.Maximize() 359 wnd = AssembleShip.GetIfOpen(windowID='PreviewSubSystems') 360 if wnd and wnd.parent.children.index(wnd) > 1: 361 wnd.Maximize() 362 363 def _OnResize(self, *args, **kw): 364 self.previewContainer.UpdateViewPort() 365 366 def CloseSubSystemWnd(self): 367 AssembleShip.CloseIfOpen(windowID='PreviewSubSystems') 368 369 def Close(self, setClosed = False, *args, **kwds): 370 Window.Close(self, setClosed, *args, **kwds) 371 self.CloseSubSystemWnd() 372 373 def OnStartMinimize_(self, *args): 374 self.previewContainer.Hide() 375 376 def OnStartMaximize_(self, *args): 377 self.previewContainer.Show() 378 379 380 class PreviewCharacterWnd(Window): 381 __guid__ = 'form.PreviewCharacterWnd' 382 default_windowID = 'previewCharacterWnd' 383 default_minSize = (420, 320) 384 385 def ApplyAttributes(self, attributes): 386 Window.ApplyAttributes(self, attributes) 387 from eve.client.script.ui.shared.neocom.characterSheetWindow import CharacterSheetWindow 388 CharacterSheetWindow.CloseIfOpen() 389 self.charID = attributes.charID 390 self.dna = attributes.dna 391 self.btnGroup = ButtonGroup(parent=self.sr.main, idx=0) 392 self.btnGroup.AddButton(localization.GetByLabel('UI/Preview/ViewPortrait'), self.SwitchToPortrait, (self.charID,)) 393 self.previewContainer = PreviewContainer(parent=self.sr.main) 394 self.PreviewCharacter(self.charID, dna=self.dna) 395 396 def PreviewCharacter(self, charID, dna): 397 self.charID = charID 398 self.dna = dna 399 caption = localization.GetByLabel('UI/InfoWindow/PortraitCaption', character=charID) 400 self.SetCaption(caption) 401 uthread.new(self.previewContainer.PreviewCharacter, charID, dna=dna) 402 self.Maximize() 403 404 def SwitchToPortrait(self, charID): 405 from eve.client.script.ui.shared.portraitWindow.portraitWindow import PortraitWindow 406 PortraitWindow.CloseIfOpen() 407 portraitWnd = PortraitWindow.Open(charID=charID) 408 portraitWnd.Maximize() 409 self.CloseByUser() 410 411 def _OnResize(self, *args, **kw): 412 self.previewContainer.UpdateViewPort() 413 414 415 class InvalidPreviewType(Exception): 416 pass 417 418 419 class PreviewContainerClosing(Exception): 420 pass 421 422 423 class PreviewNavigation(SceneContainerBaseNavigation): 424 default_cursor = uiconst.UICURSOR_CCALLDIRECTIONS 425 default_state = uiconst.UI_NORMAL 426 427 def UpdateCursor(self): 428 if uicore.uilib.rightbtn and not uicore.uilib.leftbtn and self.sr.sceneContainer.verticalPanEnabled: 429 self.cursor = uiconst.UICURSOR_CCUPDOWN 430 else: 431 self.cursor = uiconst.UICURSOR_CCALLDIRECTIONS 432 433 def OnMouseDown(self, *args): 434 SceneContainerBaseNavigation.OnMouseDown(self, *args) 435 self.UpdateCursor() 436 437 def OnMouseUp(self, *args): 438 SceneContainerBaseNavigation.OnMouseUp(self, *args) 439 self.UpdateCursor() 440 441 def OnMouseMove(self, *args): 442 if self.sr.sceneContainer.verticalPanEnabled and uicore.uilib.rightbtn: 443 cameraDistance = self.sr.sceneContainer.camera.GetZoomDistance() 444 delta = uicore.uilib.dy * 0.0006 * cameraDistance 445 y = self.sr.sceneContainer.verticalPan 446 self.sr.sceneContainer.verticalPan = y + delta 447 else: 448 SceneContainerBaseNavigation.OnMouseMove(self, *args) 449 450 451 class PreviewSceneContainer(SceneContainer): 452 default_state = uiconst.UI_DISABLED 453 454 def ApplyAttributes(self, attributes): 455 super(PreviewSceneContainer, self).ApplyAttributes(attributes) 456 self._minY = None 457 self._maxY = None 458 459 @property 460 def verticalPanLimits(self): 461 return (self._minY, self._maxY) 462 463 @verticalPanLimits.setter 464 def verticalPanLimits(self, limits): 465 if limits is None: 466 limits = (None, None) 467 minY, maxY = limits 468 if minY > maxY: 469 minY, maxY = maxY, minY 470 self._minY = minY 471 self._maxY = maxY 472 473 @property 474 def verticalPanEnabled(self): 475 return self._minY is not None and self._maxY is not None 476 477 @property 478 def verticalPan(self): 479 if self.camera: 480 return self.camera.atPosition[1] 481 482 @verticalPan.setter 483 def verticalPan(self, y): 484 if self.verticalPanEnabled: 485 y = mathext.clamp(y, self._minY, self._maxY) 486 x, _, z = self.camera.atPosition 487 self.camera.SetAtPosition((x, y, z)) 488 489 490 class PreviewContainer(Container): 491 __notifyevents__ = ['OnGraphicSettingsChanged', 'OnSetDevice'] 492 493 def ApplyAttributes(self, attributes): 494 Container.ApplyAttributes(self, attributes) 495 sm.RegisterNotify(self) 496 self.context = None 497 self.startLoadingCallback = attributes.get('OnStartLoading', None) 498 self.stopLoadingCallback = attributes.get('OnStopLoading', None) 499 self.skinController = None 500 self.loadingWheel = LoadingWheel(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED) 501 self.loadingWheel.opacity = 0.0 502 self.ConstructSceneContainer() 503 self.sceneContainer.Startup() 504 self.navigation = PreviewNavigation(parent=self) 505 self.navigation.Startup(self.sceneContainer) 506 507 def ConstructSceneContainer(self): 508 self.sceneContainer = PreviewSceneContainer(parent=self, align=uiconst.TOALL) 509 510 def Close(self): 511 super(PreviewContainer, self).Close() 512 self._Cleanup() 513 514 def _OnStartLoading(self): 515 if self.startLoadingCallback: 516 self.startLoadingCallback(self) 517 else: 518 uicore.animations.FadeIn(self.loadingWheel, duration=0.4) 519 520 def _OnStopLoading(self, success): 521 if self.stopLoadingCallback: 522 self.stopLoadingCallback(self, success) 523 else: 524 uicore.animations.FadeOut(self.loadingWheel, duration=0.4) 525 526 def PreviewType(self, typeID, **kwargs): 527 if not IsPreviewable(typeID): 528 raise InvalidPreviewType('%s (%s) is not previewable' % (evetypes.GetName(typeID), typeID)) 529 groupID = evetypes.GetGroupID(typeID) 530 categoryID = evetypes.GetCategoryIDByGroup(groupID) 531 subsystems = kwargs.get('subsystems', None) 532 if categoryID == invconst.categoryApparel: 533 return self.PreviewApparel(typeID, gender=kwargs.get('gender'), background=kwargs.get('background')) 534 elif groupID in invconst.turretModuleGroups: 535 return self.PreviewTurret(typeID, scenePath=kwargs.get('scenePath')) 536 elif categoryID == invconst.categoryShip: 537 return self.PreviewShip(typeID, **kwargs) 538 elif categoryID == invconst.categoryShipSkin: 539 return self.PreviewSkin(typeID, **kwargs) 540 else: 541 return self.PreviewSpaceEntity(typeID, itemID=kwargs.get('itemID', None), subsystems=subsystems, scenePath=kwargs.get('scenePath', None)) 542 543 def PreviewApparel(self, typeID, gender = None, background = None): 544 from eve.client.script.ui.shared.neocom.characterSheetWindow import CharacterSheetWindow 545 CharacterSheetWindow.CloseIfOpen() 546 context = ApparelSceneContext(typeID, gender=gender, background=background) 547 return self.LoadScene(context) 548 549 def PreviewCharacter(self, charID, dna = None, apparel = None, background = None): 550 context = CharacterSceneContext(charID, dna=dna, apparel=apparel, background=background) 551 return self.LoadScene(context) 552 553 def PreviewShip(self, typeID, controller = None, skin_state = None, scenePath = None, subsystems = None, **kwargs): 554 skin = skin_state 555 if skin_state: 556 if skin_state.skin_type == ShipSkinType.FIRST_PARTY_SKIN: 557 skin = sm.GetService('cosmeticsSvc').GetFirstPartySkinObject(licenseeID=skin_state.character_id, skinID=skin_state.skin_data.skin_id) 558 elif skin_state.skin_type == ShipSkinType.THIRD_PARTY_SKIN: 559 if skin_state.character_id == session.charid: 560 try: 561 skin_license = get_ship_skin_license_svc().get_license(license_id=skin_state.skin_data.skin_id, owner_character_id=skin_state.character_id) 562 if skin_license: 563 skin = ThirdPartySkinWrapper(skin_license) 564 except (GenericException, TimeoutException): 565 message_player(GetByLabel('UI/Common/CannotConnectToServer')) 566 567 if controller: 568 controller.onChange.disconnect(self.UpdateShipEntity) 569 if controller.typeID != typeID: 570 controller.typeID = typeID 571 if skin: 572 for s in controller.skins: 573 if s == skin: 574 controller.PickSkinAndMoveToIt(s, deselectIfSame=False) 575 break 576 577 self.skinController = controller 578 controller.onChange.connect(self.UpdateShipEntity) 579 self.scenePath = scenePath 580 return self.PreviewShipEntity(typeID, skin=skin_state, scenePath=scenePath, subsystems=subsystems) 581 582 def PreviewSkin(self, typeID, controller = None, scenePath = None, subsystems = None, otherTypeID = None, **kwargs): 583 skin = sm.GetService('cosmeticsSvc').GetSkinByLicenseType(typeID) 584 entityTypeID = otherTypeID if otherTypeID and otherTypeID in skin.types else skin.types[0] 585 if controller: 586 controller.onChange.disconnect(self.UpdateSkinnedEntity) 587 if controller.typeID != entityTypeID: 588 controller.typeID = entityTypeID 589 for s in controller.skins: 590 if s.skinID == skin.skinID: 591 controller.PickSkinAndMoveToIt(s, deselectIfSame=False) 592 break 593 594 self.skinController = controller 595 controller.onChange.connect(self.UpdateSkinnedEntity) 596 self.scenePath = scenePath 597 return self.PreviewShipEntity(entityTypeID, skin=skin, scenePath=scenePath, subsystems=subsystems) 598 599 def UpdateSkinnedEntity(self): 600 typeID = self.skinController.typeID 601 if IsShipType(typeID): 602 self.UpdateShipEntity() 603 return 604 materialSetID = None if self.skinController.previewed is None else self.skinController.previewed.materialSetID 605 uthread.new(self.PreviewSpaceEntity, typeID, materialSetID=materialSetID, scenePath=self.scenePath, subsystems=getattr(self.context, 'subsystems', None), resetZoom=False) 606 607 def UpdateShipEntity(self): 608 uthread.new(self.PreviewShipEntity, self.skinController.typeID, skin=self.skinController.previewed, scenePath=self.scenePath, subsystems=getattr(self.context, 'subsystems', None), resetZoom=False) 609 610 def PreviewSpaceEntity(self, typeID, itemID = None, materialSetID = None, subsystems = None, scenePath = None, resetZoom = True): 611 context = SpaceEntitySceneContext(typeID, itemID=itemID, materialSetID=materialSetID, subsystems=subsystems, scenePath=scenePath, resetZoom=resetZoom) 612 return self.LoadScene(context) 613 614 def PreviewShipEntity(self, typeID, skin = None, scenePath = None, subsystems = None, resetZoom = True): 615 context = ShipEntitySceneContext(typeID, skin=skin, subsystems=subsystems, scenePath=scenePath, resetZoom=resetZoom) 616 return self.LoadScene(context) 617 618 def PreviewSofDna(self, dna, dirt = None, scenePath = None): 619 context = SofDnaSceneContext(dna, dirt=dirt, scenePath=scenePath) 620 return self.LoadScene(context) 621 622 def PreviewTurret(self, typeID, scenePath = None): 623 context = TurretSceneContext(typeID, scenePath=scenePath) 624 return self.LoadScene(context) 625 626 def LoadScene(self, context, force = False): 627 if context == self.context and not force: 628 return False 629 success = True 630 try: 631 self._OnStartLoading() 632 self._Cleanup() 633 self.context = context 634 self.context.LoadScene(self.sceneContainer) 635 self.UpdateViewPort() 636 except Exception: 637 if not self.destroyed: 638 log.LogException('Exception raised while loading preview for {context}'.format(context=str(context))) 639 sys.exc_clear() 640 success = False 641 642 self._OnStopLoading(success) 643 return success 644 645 def Reload(self): 646 if self.context: 647 self.LoadScene(self.context, force=True) 648 649 def _Cleanup(self): 650 try: 651 if self.context: 652 self.context.Cleanup() 653 self.context = None 654 except Exception: 655 log.LogException('Exception raised during preview container cleanup', severity=log.INFO) 656 sys.exc_clear() 657 658 def AnimEntry(self, yaw0 = 1.1 * math.pi, pitch0 = 0.0, yaw1 = 1.25 * math.pi, pitch1 = 0.3, duration = 2.0): 659 self.sceneContainer.AnimEntry(yaw0, pitch0, yaw1, pitch1, duration) 660 661 def UpdateViewPort(self): 662 if self.sceneContainer: 663 self.sceneContainer.UpdateViewPort() 664 665 def OnSetDevice(self): 666 uthread.new(self.Reload) 667 668 def OnGraphicSettingsChanged(self, changes): 669 if self.context and any((setting in self.context.relevantSettings for setting in changes)): 670 uthread.new(self.Reload) 671 672 673 class SceneContext(object): 674 relevantSettings = [] 675 676 def LoadScene(self, sceneContainer): 677 raise NotImplementedError('SceneContexts must override the LoadScene method') 678 679 def Cleanup(self): 680 pass 681 682 683 class ApparelSceneContext(SceneContext): 684 relevantSettings = [gfxsettings.GFX_CHAR_TEXTURE_QUALITY] 685 686 def __init__(self, typeID, gender = None, background = None): 687 if evetypes.GetCategoryID(typeID) != invconst.categoryApparel: 688 raise InvalidPreviewType('%s (%s) is not an apparel item' % (evetypes.GetName(typeID), typeID)) 689 self.typeID = typeID 690 self.gender = gender 691 self.background = background 692 self.mannequin = None 693 694 def __eq__(self, other): 695 return isinstance(other, ApparelSceneContext) and self.typeID == other.typeID and self.gender == other.gender and self.background == other.background 696 697 def LoadScene(self, sceneContainer): 698 sceneContainer.PrepareInteriorScene(backgroundImage=self.background, useShadows=True) 699 apparel = GetPaperDollResource(self.typeID, gender=self.gender) 700 if apparel is None: 701 raise InvalidPreviewType('%s (%s) does not have an associated paper doll resource' % (evetypes.GetName(self.typeID), self.typeID)) 702 factory = Factory() 703 mannequin = PaperDollCharacter(factory) 704 self.mannequin = mannequin 705 dollGender = GenderIDToPaperDollGender(apparel.resGender) 706 mannequin.doll = Doll('mannequin', gender=dollGender) 707 mannequin.doll.Load(MANNEQUIN_RES_BY_GENDER[dollGender], factory) 708 mannequin.WaitForUpdate() 709 textureQuality = gfxsettings.Get(gfxsettings.GFX_CHAR_TEXTURE_QUALITY) 710 resolution = ccConst.TEXTURE_RESOLUTIONS[textureQuality] 711 mannequin.doll.textureResolution = resolution 712 mannequin.doll.overrideLod = LOD_SKIN 713 mannequin.Spawn(sceneContainer.scene) 714 mannequin.WaitForUpdate() 715 with CaptureDollMeshChanges(mannequin.doll) as meshes: 716 modifier = mannequin.doll.SetItemType(factory, apparel.resPath) 717 if 'tattoo/head' in apparel.resPath.lower(): 718 modifier.SetColorVariation('default_a') 719 typeData = factory.GetItemType(apparel.resPath, gender=dollGender) 720 apparelCategory = sm.GetService('character').GetCategoryFromResPath(typeData[0]) 721 coveringCategories = PAPERDOLL_CATEGORIES_COVERING.get(apparelCategory, []) 722 for category in coveringCategories: 723 mannequin.doll.buildDataManager.RemoveMeshContainingModifiers(category) 724 725 mannequin.Update() 726 mannequin.WaitForUpdate() 727 newMeshes = set(filter(lambda m: m not in meshes.before, meshes.after)) 728 assetGroupID = evetypes.GetGroupID(self.typeID) 729 if apparelCategory == 'makeup/bodyaugmentations': 730 assetGroupID = invconst.groupApparelOuter 731 if apparelCategory == 'tattoo/head': 732 assetGroupID = invconst.groupApparelAugmentations 733 meshNameCheck = lambda mesh: any(map(lambda name: mesh.name.startswith(name), MESH_NAMES_BY_GROUPID[assetGroupID])) 734 groupMeshes = set(filter(meshNameCheck, meshes.after)) 735 boundingBoxes = map(lambda m: m.geometry.GetBoundingBox(0), newMeshes | groupMeshes) 736 if len(boundingBoxes) == 0: 737 aabb = mannequin.visualModel.GetBoundingBoxInLocalSpace() 738 else: 739 aabb = reduce(MergeBoundingBoxes, boundingBoxes) 740 if dollGender == GENDER.FEMALE: 741 animationRes = 'res:/Animation/PortraitCreator/Female/CharacterCreation/Mannequin.gr2' 742 else: 743 animationRes = 'res:/Animation/PortraitCreator/Male/CharacterCreation/Mannequin.gr2' 744 animationUpdater = trinity.Tr2GrannyAnimation() 745 animationUpdater.resPath = animationRes 746 if animationUpdater is not None: 747 mannequin.avatar.animationUpdater = animationUpdater 748 floorShadow = trinity.Load(ccConst.CUSTOMIZATION_FLOOR) 749 sceneContainer.scene.dynamics.append(floorShadow) 750 SetupInteriourCamera(sceneContainer, aabb) 751 752 def Cleanup(self): 753 self.mannequin = None 754 755 756 class CharacterSceneContext(SceneContext): 757 relevantSettings = [gfxsettings.GFX_CHAR_TEXTURE_QUALITY, gfxsettings.UI_NCC_GREEN_SCREEN, gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION] 758 759 def __init__(self, charID, dna = None, apparel = None, background = None): 760 dna = dna or sm.RemoteSvc('paperDollServer').GetPaperDollData(charID) 761 self.charID = charID 762 self.dna = dna 763 self.apparel = apparel or [] 764 self.background = background 765 self.boundingBox = None 766 self.character = None 767 768 def __eq__(self, other): 769 return isinstance(other, CharacterSceneContext) and self.charID == other.charID and self.dna == other.dna and self.apparel == other.apparel and self.background == other.background 770 771 def LoadScene(self, sceneContainer): 772 if idCheckers.IsNPCCharacter(self.charID): 773 charInfo = get_npc_character(self.charID) 774 else: 775 charInfo = sm.RemoteSvc('charMgr').GetPublicInfo(self.charID) 776 background = self.GetBackground() 777 sceneFile = self.GetSceneFile(charInfo) 778 sceneContainer.PrepareInteriorScene(useShadows=True, backgroundImage=background, sceneFile=sceneFile) 779 if charInfo.gender is None: 780 owner = cfg.eveowners.Get(self.charID) 781 raise RuntimeError('{0.name} ({0.charID}) does not have a defined gender'.format(owner)) 782 scene = sceneContainer.scene 783 self.character = self.AddCharacterToScene(charInfo, scene) 784 self.PrepareCharacterAnimation(self.character.avatar, charInfo.gender) 785 try: 786 self.boundingBox = self.character.visualModel.GetBoundingBoxInLocalSpace() 787 except RuntimeError: 788 self.boundingBox = ((-0.5, -0.5, 0), (0.5, 0.5, 1.5)) 789 790 useFastShader = gfxsettings.Get(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION) 791 if useFastShader: 792 sceneContainer.scene.ambientColor = (0.25, 0.25, 0.25) 793 if not gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN): 794 floor = trinity.Load(ccConst.CUSTOMIZATION_FLOOR) 795 sceneContainer.scene.dynamics.append(floor) 796 self.SetupCamera(sceneContainer) 797 798 def AddCharacterToScene(self, charInfo, scene): 799 character = sm.GetService('character').AddCharacterToScene(charID=self.charID, scene=scene, gender=GenderIDToPaperDollGender(charInfo.gender), bloodlineID=charInfo.bloodlineID, raceID=None, dna=self.dna, lod=LOD_SKIN, updateDoll=False) 800 textureQuality = gfxsettings.Get(gfxsettings.GFX_CHAR_TEXTURE_QUALITY) 801 textureResolution = ccConst.DOLL_VIEWER_TEXTURE_RESOLUTIONS[textureQuality] 802 character.doll.textureResolution = textureResolution 803 useFastShader = gfxsettings.Get(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION) 804 character.doll.useFastShader = useFastShader 805 for typeID in self.apparel: 806 apparel = GetPaperDollResource(typeID, gender=charInfo.gender) 807 if apparel is None: 808 log.LogError('Unable to preview %s (%s) since it has no associated resource' % (evetypes.GetName(typeID), typeID)) 809 continue 810 sm.GetService('character').ApplyTypeToDoll(self.charID, apparel.resPath, doUpdate=False) 811 812 character.Update() 813 character.WaitForUpdate() 814 return character 815 816 def PrepareCharacterAnimation(self, avatar, gender): 817 pass 818 819 def GetBackground(self): 820 background = self.background 821 if gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN): 822 background = 'res:/UI/Texture/CharacterCreation/backdrops/Background_1001.dds' 823 return background 824 825 def GetSceneFile(self, charInfo): 826 return 'res:/Graphics/Interior/characterCreation/Preview.red' 827 828 def SetupCamera(self, sceneContainer): 829 SetupInteriourCamera(sceneContainer, self.boundingBox) 830 831 def Cleanup(self): 832 sm.GetService('character').RemoveCharacter(self.charID) 833 834 835 def _UpdateLighting(scene, camera, ship, alive): 836 scene.nebulaIntensity = 1 837 scene.sunDiffuseColor = (3, 3, 3) 838 while alive: 839 direction = geo2.Vec3Normalize(geo2.Vec3Subtract(camera.atPosition, camera.eyePosition)) 840 scene.sunDirection = direction 841 uthread2.Yield() 842 843 844 class SpaceEntitySceneContext(SceneContext): 845 846 def __init__(self, typeID, itemID = None, materialSetID = None, subsystems = None, scenePath = None, resetZoom = True): 847 self.typeID = typeID 848 self.itemID = itemID 849 self.materialSetID = materialSetID 850 self.subsystems = subsystems 851 self.alive = [] 852 self.resetZoom = resetZoom 853 if scenePath is None: 854 hullTypeIDList = self.subsystems 855 if subsystems is not None: 856 hullTypeIDList = self.subsystems.values() 857 modelDNA = gfxutils.BuildSOFDNAFromTypeID(self.typeID, materialSetID=self.materialSetID, multiHullTypeIDList=hullTypeIDList) 858 if modelDNA is not None: 859 raceName = self.GetRaceFromDNAString(modelDNA) 860 scenePath = eveSpaceObject.GetPreviewScenePathByRaceName(raceName) 861 else: 862 scenePath = eveSpaceObject.GetPreviewScenePathByRaceName('') 863 self.scenePath = scenePath 864 865 def __eq__(self, other): 866 return isinstance(other, SpaceEntitySceneContext) and self.typeID == other.typeID and self.itemID == other.itemID and self.materialSetID == other.materialSetID and self.subsystems == other.subsystems and self.scenePath == other.scenePath 867 868 @staticmethod 869 def GetRaceFromDNAString(dnaString): 870 parts = dnaString.split(':') 871 return parts[2] 872 873 def LoadScene(self, sceneContainer): 874 sceneContainer.PrepareSpaceScene(scenePath=self.scenePath, resetZoom=self.resetZoom) 875 resFile = inventorycommon.typeHelpers.GetGraphicFile(self.typeID) 876 modelDNA = None 877 if evetypes.GetCategoryID(self.typeID) == invconst.categoryStation and self.itemID: 878 stations = cfg.mapSolarSystemContentCache.npcStations 879 npcStation = stations.get(self.itemID, None) 880 if npcStation: 881 graphicID = npcStation.graphicID 882 modelDNA = gfxutils.BuildSOFDNAFromGraphicID(graphicID) 883 subsystems = None 884 if self.subsystems is not None and len(self.subsystems) > 0: 885 subsystems = self.subsystems.values() 886 if modelDNA is None: 887 modelDNA = gfxutils.BuildSOFDNAFromTypeID(self.typeID, materialSetID=self.materialSetID, multiHullTypeIDList=subsystems) 888 if modelDNA is not None: 889 spaceObjectFactory = GetSofService().spaceObjectFactory 890 model = spaceObjectFactory.BuildFromDNA(modelDNA) 891 else: 892 model = trinity.Load(resFile) 893 if model is None: 894 raise InvalidPreviewType('%s (%s) failed to load associated model' % (evetypes.GetName(self.typeID), self.typeID)) 895 if getattr(model, 'boosters', None) is not None: 896 model.boosters = None 897 if getattr(model, 'modelRotationCurve', None) is not None: 898 model.modelRotationCurve = None 899 if getattr(model, 'modelTranslationCurve', None) is not None: 900 model.modelTranslationCurve = None 901 SetupAnimations(model) 902 trinity.WaitForResourceLoads() 903 model.FreezeHighDetailMesh() 904 sceneContainer.AddToScene(model) 905 SetupSpaceCamera(sceneContainer, model, resetZoom=self.resetZoom) 906 self.alive = [True] 907 uthread2.StartTasklet(_UpdateLighting, sceneContainer.scene, sceneContainer.camera, model, self.alive) 908 909 def Cleanup(self): 910 del self.alive[:] 911 912 913 class ShipEntitySceneContext(SceneContext): 914 915 def __init__(self, typeID, skin = None, subsystems = None, scenePath = None, resetZoom = True): 916 self.typeID = typeID 917 self.skin = skin 918 self.subsystems = subsystems 919 self.alive = [] 920 self.resetZoom = resetZoom 921 self.scenePath = scenePath or self._GetDefaultScenePath() 922 923 def __eq__(self, other): 924 return isinstance(other, SpaceEntitySceneContext) and self.typeID == other.typeID and self.skin == other.skin and self.subsystems == other.subsystems and self.scenePath == other.scenePath 925 926 def _GetDefaultScenePath(self): 927 hullTypeIDList = self.subsystems.values() if self.subsystems else [] 928 modelDNA = gfxutils.BuildSOFDNAFromTypeID(self.typeID, multiHullTypeIDList=hullTypeIDList) 929 if modelDNA is not None: 930 raceName = self.GetRaceFromDNAString(modelDNA) 931 scenePath = eveSpaceObject.GetPreviewScenePathByRaceName(raceName) 932 else: 933 scenePath = eveSpaceObject.GetPreviewScenePathByRaceName('') 934 return scenePath 935 936 @staticmethod 937 def GetRaceFromDNAString(dnaString): 938 parts = dnaString.split(':') 939 return parts[2] 940 941 def LoadScene(self, sceneContainer): 942 sceneContainer.PrepareSpaceScene(scenePath=self.scenePath, resetZoom=self.resetZoom) 943 subsystems = self.subsystems.values() if self.subsystems else None 944 model = GetSkinnedShipModel(skin=self.skin, typeID=self.typeID, multiHullTypeIDList=subsystems) 945 if model is None: 946 resFile = inventorycommon.typeHelpers.GetGraphicFile(self.typeID) 947 model = trinity.Load(resFile) 948 if model is None: 949 raise InvalidPreviewType('%s (%s) failed to load associated model' % (evetypes.GetName(self.typeID), self.typeID)) 950 if getattr(model, 'boosters', None) is not None: 951 model.boosters = None 952 if getattr(model, 'modelRotationCurve', None) is not None: 953 model.modelRotationCurve = None 954 if getattr(model, 'modelTranslationCurve', None) is not None: 955 model.modelTranslationCurve = None 956 SetupAnimations(model) 957 trinity.WaitForResourceLoads() 958 model.FreezeHighDetailMesh() 959 sceneContainer.AddToScene(model) 960 SetupSpaceCamera(sceneContainer, model, resetZoom=self.resetZoom) 961 self.alive = [True] 962 uthread2.StartTasklet(_UpdateLighting, sceneContainer.scene, sceneContainer.camera, model, self.alive) 963 964 def Cleanup(self): 965 del self.alive[:] 966 967 968 class SofDnaSceneContext(SceneContext): 969 970 def __init__(self, dna, dirt = None, scenePath = None): 971 if scenePath is None: 972 scenePath = GetGraphicFile(20413) 973 self.dna = dna 974 self.dirt = dirt 975 self.scenePath = scenePath 976 self.model = None 977 978 def __eq__(self, other): 979 return isinstance(other, SofDnaSceneContext) and self.dna == other.dna and self.scenePath == other.scenePath and self.dirt == other.dirt 980 981 def LoadScene(self, sceneContainer): 982 try: 983 sceneContainer.PrepareSpaceScene(scenePath=self.scenePath) 984 except RuntimeError: 985 return 986 987 spaceObjectFactory = GetSofService().spaceObjectFactory 988 model = spaceObjectFactory.BuildFromDNA(self.dna) 989 self.model = model 990 if model is None: 991 raise InvalidPreviewType('failed to load associated model: ' + self.dna) 992 if self.dirt is not None: 993 model.dirtLevel = self.dirt 994 sceneContainer.AddToScene(model) 995 SetupSpaceCamera(sceneContainer, model) 996 997 def GetModel(self): 998 return self.model 999 1000 1001 class TurretSceneContext(SceneContext): 1002 1003 def __init__(self, typeID, scenePath = None): 1004 if evetypes.GetGroupID(typeID) not in invconst.turretModuleGroups: 1005 raise InvalidPreviewType('%s (%s) is not a turret module' % (evetypes.GetName(typeID), typeID)) 1006 self.typeID = typeID 1007 self.scenePath = scenePath or 'res:/dx9/scene/fitting/previewTurrets.red' 1008 1009 def __eq__(self, other): 1010 return isinstance(other, TurretSceneContext) and self.typeID == other.typeID and self.scenePath == other.scenePath 1011 1012 def LoadScene(self, sceneContainer): 1013 sceneContainer.PrepareSpaceScene(maxPitch=math.pi / 2 - 0.1, scenePath=self.scenePath) 1014 model = trinity.Load('res:/dx9/model/ship/IconPreview/PreviewTurretShip.red') 1015 graphicID = evetypes.GetGraphicID(self.typeID) 1016 turretSet = TurretSet.FitTurret(model, self.typeID, 1, GetSofFactionName(graphicID), checkSettings=False) 1017 if turretSet is None: 1018 raise RuntimeError('Failed to load preview for %s (%s)' % (evetypes.GetName(self.typeID), self.typeID)) 1019 boundingSphere = turretSet.turretSets[0].boundingSphere 1020 model.boundingSphereRadius = boundingSphere[3] 1021 model.boundingSphereCenter = boundingSphere[:3] 1022 if model.boundingSphereCenter[1] < 2.0: 1023 model.boundingSphereCenter = (boundingSphere[0], 2.0, boundingSphere[2]) 1024 for turret in turretSet.turretSets: 1025 if 'moonharvester' not in turret.locatorName: 1026 turret.bottomClipHeight = 0.0 1027 else: 1028 turret.display = True 1029 turret.FreezeHighDetailLOD() 1030 turret.ForceStateDeactive() 1031 turret.EnterStateIdle() 1032 1033 sceneContainer.AddToScene(model) 1034 SetupSpaceCamera(sceneContainer, model) 1035 1036 1037 def SetupSpaceCamera(sceneContainer, model, resetZoom = True): 1038 sceneContainer.verticalPanLimits = None 1039 lookatOffset = GetModelLookAtOffset(model) 1040 camera = sceneContainer.camera 1041 camera.SetAtPosition(lookatOffset) 1042 alpha = sceneContainer.fov / 2.0 1043 radius = model.GetBoundingSphereRadius() + geo2.Vec3Length(lookatOffset) 1044 maxZoom = min(sceneContainer.backClip - radius, max(radius * (1 / math.tan(alpha)) * 2, 1.0)) 1045 minZoom = radius + sceneContainer.frontClip 1046 sceneContainer.SetMinMaxZoom(minZoom, maxZoom) 1047 if resetZoom: 1048 sceneContainer.SetZoom(0.5) 1049 1050 1051 def SetupInteriourCamera(sceneContainer, boundingBox): 1052 p0, p1 = geo2.Vector(boundingBox[0]), geo2.Vector(boundingBox[1]) 1053 center = 0.5 * (p1 - p0) + p0 1054 camera = sceneContainer.camera 1055 camera.SetAtPosition(center) 1056 sceneContainer.verticalPanLimits = (p0.y, p1.y) 1057 rad = max(geo2.Vec3Length(p0 - p1), 0.3) 1058 alpha = sceneContainer.fov * 1.5 / 2.0 1059 maxZoom = min(rad * (1 / math.tan(alpha)), 9.0) 1060 minZoom = rad + sceneContainer.frontClip 1061 sceneContainer.SetMinMaxZoom(minZoom, maxZoom) 1062 camera.SetZoomLinear(0.6) 1063 camera.kMinPitch = 0.0 1064 camera.kMaxPitch = math.pi / 2.0 1065 camera.kOrbitSpeed = 30.0 1066 camera.farClip = 100.0 1067 1068 1069 class CaptureDollMeshChanges(object): 1070 1071 def __init__(self, doll): 1072 self.doll = doll 1073 1074 def __enter__(self): 1075 self.before = self.doll.buildDataManager.GetMeshes(includeClothMeshes=True) 1076 return self 1077 1078 def __exit__(self, type, value, traceback): 1079 self.after = self.doll.buildDataManager.GetMeshes(includeClothMeshes=True) 1080 1081 1082 def MergeBoundingBoxes(a, b): 1083 merged = ((min(a[0][0], b[0][0]), min(a[0][1], b[0][1]), min(a[0][2], b[0][2])), (max(a[1][0], b[1][0]), max(a[1][1], b[1][1]), max(a[1][2], b[1][2]))) 1084 return merged 1085 1086 1087 SCHEME = 'preview' 1088 1089 def register_link_handlers(registry): 1090 registry.register(SCHEME, handle_preview_link, hint='UI/Preview/Preview') 1091 1092 1093 def handle_preview_link(url): 1094 type_id = parse_preview_url(url) 1095 sm.GetService('preview').PreviewType(type_id) 1096 1097 1098 def parse_preview_url(url): 1099 return int(url[url.index(':') + 1:])