shipInfoPreviewScene.py (16203B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\info\shipInfoPreviewScene.py 2 import math 3 import evetypes 4 import signals 5 import trinity 6 import blue 7 import mathext 8 import uthread2 9 import geo2 10 from carbon.client.script.environment.AudioUtil import PlaySound 11 from carbonui import uiconst 12 from carbonui.uianimations import animations 13 from eve.client.script.environment.sofService import GetSofService 14 from eve.client.script.ui.camera.sceneContainerCamera import SceneContainerCamera 15 from eve.client.script.ui.control.scenecontainer import SceneContainer 16 from eve.client.script.ui.shared.info.shipInfoConst import TALL_SHIP_POSITION_REPLACEMENTS, WIDE_SHIP_POSITION_REPLACEMENTS, SHIP_POSITIONS, FACTION_BACKGROUND_LOGOS_IDS, LIGHT_RIG_GRAPHIC_ID, get_faction_background 17 from eve.client.script.ui.shared.skins.controller import GetSkinnedShipModel 18 from fsdBuiltData.common.graphicIDs import GetGraphicFile 19 import evegraphics.utils as gfxutils 20 SCENE_GRAPHICID = 26811 21 ENVIRONMENT_VFX_GRAPHICID = 27580 22 SHIP_MATERIALIZATION_MULTIEFFECT_GRAPHICID = 27581 23 TRANSITION_ZOOM_DEFAULT = 0.2 24 MINIMUM_ZOOM_DISTANCE_DEFAULT = 5000 25 ZOOM_RANGE = [1.5, 3.5] 26 ZOOM_RANGES_BY_RATIO = [(1.0, [2.1, 4.5]), (1.35, [1.8, 3.5]), (1.5, [1.6, 3.5])] 27 28 class ShipInfoPreviewSceneContainer(SceneContainer): 29 30 def __init__(self, **kwargs): 31 super(ShipInfoPreviewSceneContainer, self).__init__(**kwargs) 32 self.environment = None 33 self.shipReplacementMultiEffect = None 34 self.model = None 35 self.oldModel = None 36 self.PrepareSpaceScene() 37 self._targetCenterOffset = 0.0 38 self._targetVerticalOffset = 0.0 39 self._targetZoom = 0.0 40 self._positionID = None 41 self.radius = 1.0 42 self._update_thread = uthread2.StartTasklet(self._update) 43 self.additionalBackgroundTexture = None 44 self.skin_state = None 45 self._loaded = False 46 self._lastBGType = -1.0 47 self._pendingLoadData = None 48 self._async_load_task = None 49 self.on_load_complete = signals.Signal('ShipInfoPreviewSceneContainer_LoadComplete') 50 51 def PrepareCamera(self): 52 self.camera = ShipInfoPreviewCamera() 53 self.camera.OnActivated() 54 55 def PrepareSpaceScene(self, scenePath = None): 56 if scenePath is None: 57 scenePath = GetGraphicFile(SCENE_GRAPHICID) 58 super(ShipInfoPreviewSceneContainer, self).PrepareSpaceScene(scenePath=scenePath) 59 if not self.scene: 60 return 61 if not self.scene.postprocess: 62 self.scene.postprocess = trinity.Tr2PostProcess2() 63 self.bgEnvironment = blue.resMan.LoadObject('res:/dx9/scene/showinfo/showinfo_logo_background_01a.red') 64 self.AddToScene(self.bgEnvironment, clear=0) 65 self.bgEnvironment.StartControllers() 66 67 def add_background_texture(self, texturePath): 68 if self.additionalBackgroundTexture: 69 self.remove_background_texture() 70 self.additionalBackgroundTexture = blue.resMan.LoadObject('res:/dx9/scene/showinfo/showinfo_logo_background_01a.red') 71 param = self.additionalBackgroundTexture.externalParameters.FindByName('Logo_resourcePath') 72 param.SetValue(texturePath) 73 child = self.additionalBackgroundTexture.effectChildren[0] 74 trans = child.transformModifiers[2] 75 trans.translation = (0, 0, -140000) 76 self.AddToScene(self.additionalBackgroundTexture, clear=0) 77 return self.additionalBackgroundTexture 78 79 def remove_background_texture(self): 80 if not self.additionalBackgroundTexture: 81 return 82 self.RemoveFromScene(self.additionalBackgroundTexture) 83 self.additionalBackgroundTexture = None 84 85 def _get_model_offset(self, model, typeID): 86 modelOffset = getattr(model, 'shapeEllipsoidCenter', (0.0, 0.0, 0.0)) 87 locatorSets = getattr(model, 'locatorSets', None) 88 if locatorSets: 89 lookAtLocator = locatorSets.FindByName('camera_look_at') 90 if lookAtLocator is not None and len(lookAtLocator.locators) > 0: 91 modelOffset = lookAtLocator.locators[0][0] 92 invertedModelOffset = geo2.Vec3Scale(modelOffset, -1.0) 93 return invertedModelOffset 94 95 def preview_ship(self, typeID, subSystems = [], skin_state = None, skip_repositioning = False, snap = True, skip_animation = False): 96 if self._async_load_task: 97 self._pendingLoadData = (self._load_ship, 98 typeID, 99 subSystems, 100 skin_state, 101 skip_repositioning, 102 snap, 103 skip_animation) 104 else: 105 self._async_load_task = uthread2.StartTasklet(self._load_ship, typeID, subSystems, skin_state, skip_repositioning, snap, skip_animation) 106 107 def preview_skin(self, typeID, subSystems = [], skin_state = None, skip_repositioning = False, snap = False, skip_animation = True): 108 if self._async_load_task: 109 self._pendingLoadData = (self._load_skin, 110 typeID, 111 subSystems, 112 skin_state, 113 skip_repositioning, 114 snap, 115 skip_animation) 116 else: 117 self._async_load_task = uthread2.StartTasklet(self._load_skin, typeID, subSystems, skin_state, skip_repositioning, snap, skip_animation) 118 119 def _load_complete(self, snap = True, skip_animation = False): 120 if self._pendingLoadData: 121 func, typeID, subSystems, skin_state, skip_repositioning, snap, skip_animation = self._pendingLoadData 122 self._pendingLoadData = None 123 func(typeID, subSystems, skin_state, skip_repositioning, snap, skip_animation) 124 else: 125 self.on_load_complete(snap, skip_animation) 126 self._async_load_task = None 127 128 def _load_ship(self, typeID, subSystems = [], skin_state = None, skip_repositioning = False, snap = True, skip_animation = False): 129 self.set_faction_background(evetypes.GetFactionID(typeID)) 130 self._load(typeID, subSystems, skin_state, skip_repositioning, snap, skip_animation) 131 132 def _load_skin(self, typeID, subSystems = [], skin_state = None, skip_repositioning = False, snap = False, skip_animation = True): 133 self._load(typeID, subSystems, skin_state, skip_repositioning, snap, skip_animation) 134 135 def _load(self, typeID, subSystems = [], skin_state = None, skip_repositioning = False, snap = False, skip_animation = False): 136 self._loaded = False 137 if self.model and self.model in self.scene.objects: 138 self.scene.objects.remove(self.model) 139 self.model = None 140 multiHullTypeIDList = None 141 if subSystems and len(subSystems) > 0: 142 multiHullTypeIDList = subSystems 143 if skin_state is not None: 144 self.model = GetSkinnedShipModel(skin=skin_state, typeID=typeID, multiHullTypeIDList=multiHullTypeIDList) 145 self.skin_state = skin_state 146 if not self.model: 147 modelDNA = gfxutils.BuildSOFDNAFromTypeID(typeID, multiHullTypeIDList=multiHullTypeIDList) 148 space_object_factory = GetSofService().spaceObjectFactory 149 self.model = space_object_factory.BuildFromDNA(modelDNA) 150 if self.destroyed: 151 return 152 self.model.rotationCurve = trinity.Tr2RotationAdapter() 153 self.model.translationCurve = trinity.Tr2TranslationAdapter() 154 self.model.translationCurve.value = self._get_model_offset(self.model, typeID) 155 if self.model.boosters: 156 self.model.boosters.alwaysOn = True 157 self.model.boosters.alwaysOnIntensity = 0.7 158 while len(self.model.observers) > 0: 159 self.model.observers.pop() 160 161 self.model.FreezeHighDetailMesh() 162 if hasattr(self.model, 'dirtLevel'): 163 self.model.dirtLevel = -1.5 164 self.scene.objects.append(self.model) 165 self.model.StartControllers() 166 lightRigAsset = GetGraphicFile(LIGHT_RIG_GRAPHIC_ID) 167 lightRig = trinity.Load(lightRigAsset) 168 self.model.effectChildren.append(lightRig) 169 trinity.WaitForResourceLoads() 170 maxDelay = 5.0 171 checkDelay = 50.0 172 currentDelay = 0.0 173 while not self.destroyed: 174 if not self.model: 175 return 176 if max(self.model.generatedShapeEllipsoidRadius) != -1: 177 break 178 if currentDelay > maxDelay: 179 break 180 blue.synchro.SleepWallclock(checkDelay) 181 currentDelay += checkDelay / 1000.0 182 183 self._loaded = True 184 if self.destroyed: 185 self._pendingLoadData = None 186 self._async_load_task = None 187 return 188 if not skip_repositioning: 189 self._fit_model_in_view(self.model) 190 self.camera.animate_camera_fov(0.8, 1.1) 191 self._load_complete(snap, skip_animation) 192 193 def _fit_model_in_view(self, model): 194 if model is None or not self.camera: 195 return 196 self.radius = model.GetBoundingSphereRadius() + 1 197 zoom_range = self._get_zoom_range_for_model() 198 self.SetZoomRange(zoom_range) 199 self.SetZoom(TRANSITION_ZOOM_DEFAULT) 200 self._targetZoom = TRANSITION_ZOOM_DEFAULT 201 self.camera.SetInnerBoundRadius(self.model.shapeEllipsoidRadius) 202 self.camera.centerOffset = self._targetCenterOffset = 0.5 203 self.camera.verticalOffset = self._targetVerticalOffset = 0 204 self.camera.atPosition = (0.0, 0.0, 0.0) 205 206 def _get_long_axis_ratio(self): 207 shield = self.model.generatedShapeEllipsoidRadius 208 longAxisIdx = shield.index(max(shield)) 209 longAxis = shield[longAxisIdx] 210 total = 0.0 211 for idx, axis in enumerate(shield): 212 if idx == longAxisIdx: 213 continue 214 total += longAxis / axis 215 216 return total / 2.0 217 218 def _get_zoom_range_for_model(self): 219 longAxisRatio = self._get_long_axis_ratio() 220 chosen_zoom_range = [1.0, 1.0] 221 for ratioStep, zoom_range in ZOOM_RANGES_BY_RATIO: 222 if ratioStep > longAxisRatio: 223 return chosen_zoom_range 224 chosen_zoom_range = zoom_range 225 226 return chosen_zoom_range 227 228 def SetZoomRange(self, zoomRange): 229 maxRenderDist = self.camera.farClip or 400000.0 230 min_zoom = zoomRange[0] * self.radius 231 max_zoom = min(zoomRange[1] * self.radius, maxRenderDist - 2 * self.radius) 232 self.SetMinMaxZoom(min_zoom, max_zoom) 233 234 def update_offset(self): 235 self.camera.centerOffset = mathext.lerp(self.camera.centerOffset, self._targetCenterOffset, 0.3) 236 self.camera.verticalOffset = mathext.lerp(self.camera.verticalOffset, self._targetVerticalOffset, 0.3) 237 238 @property 239 def loaded(self): 240 return self._loaded 241 242 @property 243 def centerOffset(self): 244 return self._targetCenterOffset 245 246 @centerOffset.setter 247 def centerOffset(self, value): 248 self._targetCenterOffset = value 249 250 def SetCenterOffset(self, value): 251 self.camera.centerOffset = self._targetCenterOffset = value 252 253 @property 254 def verticalOffset(self): 255 return self._targetVerticalOffset 256 257 @verticalOffset.setter 258 def verticalOffset(self, value): 259 self._targetVerticalOffset = value 260 261 def _update(self): 262 while not self.destroyed: 263 self.update_offset() 264 uthread2.Sleep(0.001) 265 266 def set_faction_background(self, factionID): 267 newBGType = get_faction_background(factionID) 268 if newBGType == self._lastBGType: 269 self.bgEnvironment.Start() 270 return 271 self._lastBGType = newBGType 272 self.bgEnvironment.SetControllerVariable('Faction', newBGType) 273 while len(self.bgEnvironment.effectChildren) == 0: 274 uthread2.Sleep(0.01) 275 276 while len(self.bgEnvironment.effectChildren[0].objects) == 0: 277 uthread2.Sleep(0.01) 278 279 backgroundLogo = self.bgEnvironment.effectChildren[0].objects[0] 280 trans = backgroundLogo.transformModifiers.Find('trinity.EveChildModifierSRT')[0] 281 trans.translation = (28000.0, -12000.0, -99000.0) 282 283 def set_ship_state(self, tabID): 284 pass 285 286 def position_ship(self, positionID, duration, initialize = False, sound = None, soundOffset = 0): 287 if not self._loaded: 288 return 289 if self.is_tall() and positionID in TALL_SHIP_POSITION_REPLACEMENTS: 290 positionID = TALL_SHIP_POSITION_REPLACEMENTS[positionID] 291 elif self.is_wide() and positionID in WIDE_SHIP_POSITION_REPLACEMENTS: 292 positionID = WIDE_SHIP_POSITION_REPLACEMENTS[positionID] 293 if positionID == self._positionID: 294 return 295 self._positionID = positionID 296 pitch, yaw = SHIP_POSITIONS[positionID] 297 if initialize: 298 self.set_camera_angle_deg(pitch, yaw) 299 else: 300 self.turn_camera_deg(pitch, yaw, duration) 301 if sound: 302 startSignal, stopSignal = sound 303 PlaySound(startSignal) 304 uthread2.StartTasklet(self._stop_sound, duration - soundOffset, stopSignal) 305 306 def zoom_to(self, targetZoom, duration): 307 animations.StopAllAnimations(self) 308 animations.MorphScalar(self, 'zoom', self.zoom, targetZoom, duration) 309 310 def _stop_sound(self, delay, signal): 311 uthread2.Sleep(delay) 312 PlaySound(signal) 313 314 def is_tall(self): 315 shield = self.model.generatedShapeEllipsoidRadius 316 longAxis = shield.index(max(shield)) 317 if longAxis == 1: 318 return True 319 return False 320 321 def is_wide(self): 322 shield = self.model.generatedShapeEllipsoidRadius 323 longAxis = shield.index(max(shield)) 324 if longAxis == 0: 325 return True 326 return False 327 328 def get_bounding_sphere_radius(self): 329 if self.model: 330 return self.model.GetBoundingSphereRadius() 331 return 0 332 333 def turn_camera_deg(self, pitch, yaw, duration): 334 pitchRad = math.radians(pitch) 335 yawRad = math.radians(yaw) 336 self.turn_camera(pitchRad, yawRad, duration) 337 338 def turn_camera(self, pitch, yaw, duration): 339 self.camera.turn_camera(pitch, yaw, duration, False) 340 341 def set_camera_angle_deg(self, pitch, yaw): 342 pitchRad = math.radians(pitch) 343 yawRad = math.radians(yaw) 344 self.set_camera_angle(pitchRad, yawRad) 345 346 def set_camera_angle(self, pitch, yaw): 347 self.camera.set_angle(pitch, yaw) 348 349 350 class ShipInfoPreviewCamera(SceneContainerCamera): 351 352 def __init__(self): 353 super(ShipInfoPreviewCamera, self).__init__() 354 self.kFovSpeed = 1.0 355 self.originalPitch = 0.0 356 self.originalYaw = 0.0 357 self.lastAnimDuration = 0.0 358 self.allowFreeInspection = True 359 360 def UpdateViewportSize(self, width, height): 361 if height: 362 self._aspectRatio = float(width) / height 363 364 def return_to_original_angle(self): 365 self._turn_camera(self.originalPitch, self.originalYaw, self.lastAnimDuration) 366 367 def turn_camera(self, pitch, yaw, duration, allowFreeInspection): 368 self.originalPitch = pitch 369 self.originalYaw = yaw 370 self.lastAnimDuration = duration 371 self.allowFreeInspection = allowFreeInspection 372 self._turn_camera(pitch, yaw, duration) 373 374 def _turn_camera(self, pitch, yaw, duration): 375 self.StopUpdateThreads(skipFov=True) 376 self.StopAnimations() 377 yawDist = self.yaw - yaw 378 if math.fabs(yawDist) > math.pi: 379 if yawDist < 0: 380 yaw = yaw - 2 * math.pi 381 else: 382 yaw = yaw + 2 * math.pi 383 if math.fabs(self.pitch - pitch) > math.pi: 384 pitch = pitch - 2 * math.pi 385 animations.MorphScalar(self, 'yaw', self.yaw, yaw, duration=duration) 386 animations.MorphScalar(self, 'pitch', self.pitch, pitch, duration=duration) 387 388 def set_angle(self, pitch, yaw): 389 self.StopUpdateThreads(skipFov=True) 390 self.StopAnimations() 391 self.pitch = pitch 392 self.yaw = yaw 393 394 def animate_camera_fov(self, finalValue, startValue = None): 395 if startValue is not None: 396 self.fov = startValue 397 self.SetFovTarget(finalValue)