cosmeticsManager.py (23767B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\spaceObject\cosmeticsManager.py 2 from __future__ import print_function 3 from __future__ import absolute_import 4 import logging 5 import blue 6 import evegraphics.effects as gfx 7 import evegraphics.utils as gfxutils 8 import evetypes 9 import trinity 10 from cosmetics.common.ships.skins.static_data.component import ComponentsDataLoader, PatternProjectionType 11 from cosmetics.common.ships.skins.static_data.pattern_blend_mode import PatternBlendMode 12 from cosmetics.common.ships.skins.static_data.slot_name import SlotID, PATTERN_SLOT_IDS 13 from trinity import EveSOFDataAreaType 14 from .cosmeticsConst import AREA_MAPS, BLEND_MODE, DARKHULL_TEXTURE_MAP, PATTERN_INDEX_MAP 15 logger = logging.getLogger(__name__) 16 RESOURCE_NAME_BY_INDEX = {0: 'PatternMask1Map', 17 1: 'PatternMask2Map'} 18 INDEX_BY_SLOT_ID = {SlotID.PATTERN: 0, 19 SlotID.SECONDARY_PATTERN: 1} 20 21 class CosmeticsManager: 22 23 def __init__(self): 24 self.sofService = sm.GetService('sofService') 25 self.model = None 26 self.faction_id = None 27 self.pattern = None 28 self.targetMaterials = [1.0, 29 1.0, 30 1.0, 31 1.0] 32 self.hull_name = 'gc3_t1' 33 self.mat1 = str() 34 self.mat2 = str() 35 self.mat3 = str() 36 self.mat4 = str() 37 self.pmat1 = str() 38 self.pmat2 = str() 39 self.selected_materials = dict() 40 self.projection_texture = '' 41 self.projection_texture_secondary = '' 42 43 @property 44 def sofDB(self): 45 return self.sofService.spaceObjectFactory.GetSofDB() 46 47 def apply_design(self, skin_design): 48 if not skin_design: 49 return 50 for slot_id, component_instance in skin_design.slot_layout.slots.items(): 51 self.apply_component(slot_id, component_instance) 52 53 self.UpdatePatternProjectionParametersFromSkinState(skin_design, self.model) 54 blend_mode = skin_design.slot_layout.pattern_blend_mode 55 if blend_mode is not None: 56 CosmeticsManager.SetBlendMode(self.model, blendMode=blend_mode) 57 58 def apply_component(self, slot_id, component_instance): 59 if not component_instance: 60 self.selected_materials.pop(slot_id, None) 61 path = '' 62 else: 63 component_data = component_instance.get_component_data() 64 path = component_data.resource_file_path if component_data else None 65 if slot_id in PATTERN_SLOT_IDS: 66 self.SetProjectionTexture(path, slot_id) 67 if component_instance: 68 component_instance.ellipsoid_center = self.model.shapeEllipsoidCenter 69 component_instance.ellipsoid_radii = self.model.shapeEllipsoidRadius 70 CosmeticsManager.ConfigurePatternBorderSettings(self.model, component_instance.projection_type_u, component_instance.projection_type_v, slot_id) 71 self.ApplyScaling(slot_id, component_instance.scaling) 72 self.ApplyTranslation(slot_id, component_instance.position) 73 self.ApplyRotation(slot_id, component_instance.rotation) 74 self.SetMirroredState(slot_id, component_instance.mirrored) 75 elif slot_id == SlotID.SECONDARY_PATTERN_MATERIAL: 76 self.SetMaterialOnArea(path, slot_id) 77 else: 78 self.SetMaterialOnArea(path, slot_id) 79 80 def get_all_pattern_texture_names(self): 81 pass 82 83 def get_pattern_texture_name(self): 84 pass 85 86 def SetModel(self, model): 87 self.model = model 88 dna_parts = model.dna.split(':') 89 self.hull_name = dna_parts[0] 90 self.faction = dna_parts[1] 91 self.race = dna_parts[2] 92 factionID = evetypes.GetFactionID(self.typeID) 93 self.SetFaction(factionID) 94 hull = [ hull for hull in self.sofDB.hull if hull.name == self.hull_name ][0] 95 darkhullAreas = [ area for area in hull.opaqueAreas if area.areaType == EveSOFDataAreaType.Darkhull ] 96 if len(darkhullAreas) < 1: 97 return 98 darkuhull_indices = [ area.index for area in darkhullAreas ] 99 darkhull_effects = [ area.effect for area in self.model.mesh.opaqueAreas if area.index in darkuhull_indices ] 100 for fx in darkhull_effects: 101 areaMap = AREA_MAPS[self.faction_id] 102 targetIndex = areaMap[SlotID.TECH_AREA] 103 texturePath = DARKHULL_TEXTURE_MAP[targetIndex] 104 fxResource = [ res for res in fx.resources if res.name == 'MaterialMap' ][0] 105 fxResource.resourcePath = texturePath 106 107 def SetTypeID(self, type_id): 108 self.typeID = type_id 109 110 def SetFaction(self, factionID): 111 self.faction_id = factionID 112 113 def ApplyTranslation(self, slot_id, pos): 114 if not self.model: 115 return 116 self._get_custom_mask(slot_id).position = pos 117 118 def ApplyRotation(self, slot_id, rot): 119 if not self.model: 120 return 121 self._get_custom_mask(slot_id).rotation = rot 122 123 def _get_custom_mask(self, slot_id): 124 mask_index = INDEX_BY_SLOT_ID[slot_id] 125 return self.model.customMasks[mask_index] 126 127 def ApplyScaling(self, slot_id, scaling): 128 if not self.model: 129 return 130 self._get_custom_mask(slot_id).scaling = scaling 131 132 def SetTargetMaterialArea(self, slot_id, area_id, is_active): 133 if not self.model: 134 return 135 self.targetMaterials = list(self.targetMaterials) 136 remapped_slot_id = AREA_MAPS[self.faction_id][area_id] 137 self.targetMaterials[remapped_slot_id - 1] = float(is_active) 138 mask = self.model.customMasks[INDEX_BY_SLOT_ID[slot_id]] 139 mask.targetMaterials = self.targetMaterials 140 141 def SetProjectionTexture(self, texturePath, slot_id = SlotID.PATTERN): 142 attributeName = 'PatternMask1Map' 143 if slot_id == SlotID.PATTERN: 144 self.projection_texture = texturePath 145 elif slot_id == SlotID.SECONDARY_PATTERN: 146 attributeName = 'PatternMask2Map' 147 self.projection_texture_secondary = texturePath 148 for area in self.model.mesh.opaqueAreas: 149 for each in area.effect.resources: 150 if each.name == attributeName: 151 each.resourcePath = texturePath 152 153 @staticmethod 154 def SetBlendMode(model, blendMode = PatternBlendMode.OVERLAY): 155 if not model: 156 return 157 effects = [ area.effect for area in model.mesh.opaqueAreas ] 158 for each in effects: 159 gfx.SetEffectOption(each, 'BLEND_MODE', BLEND_MODE[blendMode]) 160 161 def SetMirroredState(self, slot_id, is_mirrored): 162 if self.model: 163 self._get_custom_mask(slot_id).isMirrored = is_mirrored 164 165 def GetDefaultMaterialForSlot(self, slot_id): 166 factionData = [ faction for faction in self.sofDB.faction if faction.name == self.faction ][0].areaTypes.Primary 167 try: 168 targetSlot = AREA_MAPS[self.faction_id][slot_id] 169 except KeyError: 170 return 171 172 if targetSlot == 1: 173 return factionData.material1 174 if targetSlot == 2: 175 return factionData.material2 176 if targetSlot == 3: 177 return factionData.material3 178 if targetSlot == 4: 179 return factionData.material4 180 181 @staticmethod 182 def GetDefaultMaterialForSlotStatic(slot_id, factionName, factionID): 183 sofDB = sm.GetService('sofService').spaceObjectFactory.GetSofDB() 184 if sofDB is None: 185 return 186 factionData = [ faction for faction in sofDB.faction if faction.name == factionName ][0].areaTypes.Primary 187 targetSlot = AREA_MAPS[factionID][slot_id] 188 if targetSlot == 1: 189 return factionData.material1 190 if targetSlot == 2: 191 return factionData.material2 192 if targetSlot == 3: 193 return factionData.material3 194 if targetSlot == 4: 195 return factionData.material4 196 197 def SetMaterialOnDarkhull(self, material, hull): 198 darkhullAreas = [ area for area in hull.opaqueAreas if area.areaType == EveSOFDataAreaType.Darkhull ] 199 if len(darkhullAreas) < 1: 200 return 201 prefix = '' 202 indices = ['1', 203 '2', 204 '3', 205 '4'] 206 for idx in indices: 207 index_string = prefix + 'Mtl' + idx 208 diffuseName = index_string + 'DiffuseColor' 209 dustDiffuseName = index_string + 'DustDiffuseColor' 210 fresnelName = index_string + 'FresnelColor' 211 glossName = index_string + 'Gloss' 212 darkuhull_indices = [ area.index for area in darkhullAreas ] 213 darkhull_effects = [ area.effect for area in self.model.mesh.opaqueAreas if area.index in darkuhull_indices ] 214 for fx in darkhull_effects: 215 gfx.SetParameterValue(fx, diffuseName, material.parameters[0].value) 216 gfx.SetParameterValue(fx, dustDiffuseName, material.parameters[1].value) 217 gfx.SetParameterValue(fx, fresnelName, material.parameters[2].value) 218 gfx.SetParameterValue(fx, glossName, material.parameters[3].value) 219 220 def SetMaterialOnArea(self, resPath, slot_id): 221 if not resPath: 222 material_name = self.GetDefaultMaterialForSlot(slot_id) 223 if material_name is None: 224 return 225 material_path = 'res:/dx9/model/SpaceObjectFactory/materials/' + material_name + '.red' 226 else: 227 material_path = resPath 228 material = blue.resMan.LoadObject(material_path) 229 self.selected_materials[slot_id] = resPath 230 dna = self.model.dna 231 hullName = dna.split(':')[0] 232 hull = [ hull for hull in self.sofDB.hull if hull.name == hullName ][0] 233 allowedShaders = ['quad/quaddetailv5.fx', 234 'quad/quadv5.fx', 235 'quad/skinned_quadheatv5.fx', 236 'skinned_quaddetailv5.fx'] 237 if slot_id == SlotID.PATTERN_MATERIAL or slot_id == SlotID.SECONDARY_PATTERN_MATERIAL: 238 primaryAreas = [ area for area in hull.opaqueAreas ] 239 decalAreas = [ area for area in hull.decalAreas ] 240 for each in decalAreas: 241 primaryAreas.append(each) 242 243 else: 244 primaryAreas = [ area for area in hull.opaqueAreas if area.areaType == EveSOFDataAreaType.Primary or area.areaType == EveSOFDataAreaType.SimplePrimary or area.areaType == EveSOFDataAreaType.Reactor or area.areaType == EveSOFDataAreaType.Ornament or 'area_hull' in area.name and area.shader in allowedShaders ] 245 prefix = '' 246 index_sting = str() 247 if self.faction_id in AREA_MAPS.keys(): 248 index_string = str(AREA_MAPS[self.faction_id][slot_id]) 249 else: 250 index_string = str(AREA_MAPS[500003]) 251 print('area map missing for faction_id') 252 print(self.faction_id) 253 if slot_id is SlotID.PATTERN_MATERIAL: 254 prefix = 'P' 255 index_string = '1' 256 if slot_id is SlotID.SECONDARY_PATTERN_MATERIAL: 257 prefix = 'P' 258 index_string = '2' 259 index_string = prefix + 'Mtl' + index_string 260 diffuseName = index_string + 'DiffuseColor' 261 dustDiffuseName = index_string + 'DustDiffuseColor' 262 fresnelName = index_string + 'FresnelColor' 263 glossName = index_string + 'Gloss' 264 indices = [ area.index for area in primaryAreas ] 265 effects = [ area.effect for area in self.model.mesh.opaqueAreas if area.index in indices ] 266 diffuseValue = filter(lambda obj: obj.name == 'DiffuseColor', material.parameters) 267 diffuseValue = diffuseValue[0].value if diffuseValue else material.parameters[0].value 268 dustDiffuseValue = filter(lambda obj: obj.name == 'DustDiffuseColor', material.parameters) 269 dustDiffuseValue = dustDiffuseValue[0].value if dustDiffuseValue else material.parameters[1].value 270 fresnelValue = filter(lambda obj: obj.name == 'FresnelColor', material.parameters) 271 fresnelValue = fresnelValue[0].value if fresnelValue else material.parameters[2].value 272 glossValue = filter(lambda obj: obj.name == 'Gloss', material.parameters) 273 glossValue = glossValue[0].value if glossValue else material.parameters[3].value 274 for fx in effects: 275 gfx.SetParameterValue(fx, fresnelName, fresnelValue) 276 gfx.SetParameterValue(fx, dustDiffuseName, dustDiffuseValue) 277 gfx.SetParameterValue(fx, diffuseName, diffuseValue) 278 gfx.SetParameterValue(fx, glossName, glossValue) 279 280 if slot_id is SlotID.TECH_AREA: 281 self.SetMaterialOnDarkhull(material, hull) 282 283 @staticmethod 284 def GetSlotComponentFromSkinState(skin_state, slotIndex): 285 slots, slot = (None, None) 286 if hasattr(skin_state, 'skin_data'): 287 if skin_state.skin_data is not None and hasattr(skin_state.skin_data, 'slot_layout'): 288 if skin_state.skin_data.slot_layout is not None and hasattr(skin_state.skin_data.slot_layout, 'slots'): 289 slots = skin_state.skin_data.slot_layout.slots 290 if skin_state is not None and hasattr(skin_state, 'slot_layout'): 291 if skin_state.slot_layout is not None and hasattr(skin_state.slot_layout, 'slots'): 292 slots = skin_state.slot_layout.slots 293 if slots is not None: 294 slot = slots.get(slotIndex) 295 return slot 296 297 @staticmethod 298 def CreateSOFDNAfromSkinState(skinState, typeID): 299 baseDNA = gfxutils.BuildSOFDNAFromTypeID(typeID) 300 try: 301 primaryMaterial = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.PRIMARY_NANOCOATING) 302 secondaryMaterial = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.SECONDARY_NANOCOATING) 303 tertiaryMaterial = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.TERTIARY_NANOCOATING) 304 techMaterial = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.TECH_AREA) 305 patternComponent = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.PATTERN) 306 patternMaterial = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.PATTERN_MATERIAL) 307 secondaryPatternComponent = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.SECONDARY_PATTERN) 308 secondaryPatternMaterial = CosmeticsManager.GetSlotComponentFromSkinState(skinState, SlotID.SECONDARY_PATTERN_MATERIAL) 309 dna_parts = baseDNA.split(':') 310 faction = dna_parts[1] 311 materialString = ':material?' 312 materialSlots = [SlotID.PRIMARY_NANOCOATING, 313 SlotID.SECONDARY_NANOCOATING, 314 SlotID.TERTIARY_NANOCOATING, 315 SlotID.TECH_AREA] 316 materialComponents = [primaryMaterial, 317 secondaryMaterial, 318 tertiaryMaterial, 319 techMaterial] 320 materials = ['', 321 '', 322 '', 323 ''] 324 for materialIndex in range(4): 325 component = materialComponents[materialIndex] 326 componentID = None 327 if component is not None: 328 if hasattr(component, 'component_id'): 329 componentID = component.component_id 330 if componentID is None: 331 defaultFactionID = evetypes.GetFactionID(typeID) 332 materialSOFName = CosmeticsManager.GetDefaultMaterialForSlotStatic(materialSlots[materialIndex], faction, defaultFactionID) 333 else: 334 component_data = ComponentsDataLoader.get_component_data(componentID) 335 filePath = component_data.resource_file_path 336 material = trinity.Load(filePath) 337 materialSOFName = material.name 338 defaultFactionID = evetypes.GetFactionID(typeID) 339 materialMappedIndex = AREA_MAPS[defaultFactionID][materialSlots[materialIndex]] 340 materials[materialMappedIndex - 1] = materialSOFName 341 342 for each in materials: 343 materialString += each + ';' 344 345 if None not in materials: 346 baseDNA += materialString[:-1] 347 if patternComponent is not None: 348 componentID = None 349 if patternComponent is not None: 350 if hasattr(patternComponent, 'component_id'): 351 componentID = patternComponent.component_id 352 if componentID is not None: 353 patternString = ':pattern?' 354 filePath = 'res:/dx9/model/SpaceObjectFactory/patterns/cosm_blank_projection.red' 355 pattern = trinity.Load(filePath) 356 patternName = pattern.name 357 patternString += patternName 358 materialComponentID = None 359 if patternMaterial is not None: 360 if hasattr(patternMaterial, 'component_id'): 361 materialComponentID = patternMaterial.component_id 362 if materialComponentID is not None: 363 component_data = ComponentsDataLoader.get_component_data(materialComponentID) 364 filePath = component_data.resource_file_path 365 patternMaterial = trinity.Load(filePath) 366 patternMaterialName = patternMaterial.name 367 patternString += ';' + patternMaterialName 368 secondaryMaterialComponentID = None 369 if secondaryPatternMaterial is not None: 370 if hasattr(secondaryPatternMaterial, 'component_id'): 371 secondaryMaterialComponentID = secondaryPatternMaterial.component_id 372 if secondaryMaterialComponentID is not None: 373 component_data = ComponentsDataLoader.get_component_data(secondaryMaterialComponentID) 374 filePath = component_data.resource_file_path 375 secondaryPatternMaterial = trinity.Load(filePath) 376 secondaryPatternMaterialName = secondaryPatternMaterial.name 377 patternString += ';' + secondaryPatternMaterialName 378 baseDNA += patternString 379 except Exception as exc: 380 logger.exception('Failed to create DNA from SKIN state: %s', exc) 381 382 return baseDNA 383 384 @staticmethod 385 def ClearPatternProjections(model): 386 for area in model.mesh.opaqueAreas: 387 for each in area.effect.resources: 388 if each.name == 'PatternMask1Map' or each.name == 'PatternMask2Map': 389 each.resourcePath = 'res:/texture/Global/black.dds' 390 391 @staticmethod 392 def UpdatePatternProjectionParametersFromSkinState(skin_state, model, typeID = None): 393 try: 394 CosmeticsManager.ClearPatternProjections(model) 395 if hasattr(skin_state, 'ship_type_id'): 396 typeID = skin_state.ship_type_id 397 for slot_id in PATTERN_SLOT_IDS: 398 CosmeticsManager._apply_pattern_projection_parameters(slot_id, skin_state, model, typeID) 399 400 sofDB = sm.GetService('sofService').spaceObjectFactory.GetSofDB() 401 hull_name = model.dna.split(':')[0] 402 hull = [ hull for hull in sofDB.hull if hull.name == hull_name ][0] 403 darkhullAreas = [ area for area in hull.opaqueAreas if area.areaType == EveSOFDataAreaType.Darkhull ] 404 if len(darkhullAreas) < 1: 405 return 406 darkuhull_indices = [ area.index for area in darkhullAreas ] 407 darkhull_effects = [ area.effect for area in model.mesh.opaqueAreas if area.index in darkuhull_indices ] 408 if typeID: 409 for fx in darkhull_effects: 410 faction_id = evetypes.GetFactionID(typeID) 411 areaMap = AREA_MAPS[faction_id] 412 targetIndex = areaMap[SlotID.TECH_AREA] 413 texturePath = DARKHULL_TEXTURE_MAP[targetIndex] 414 fxResource = [ res for res in fx.resources if res.name == 'MaterialMap' ][0] 415 fxResource.resourcePath = texturePath 416 417 except Exception as exc: 418 logger.exception('Failed to create DNA from SKIN state: %s', exc) 419 420 @staticmethod 421 def _apply_pattern_projection_parameters(slot_id, skin_state, model, typeID): 422 custom_mask_index = INDEX_BY_SLOT_ID[slot_id] 423 component_instance = CosmeticsManager.GetSlotComponentFromSkinState(skin_state, slot_id) 424 try: 425 if component_instance is not None: 426 patternMask = model.customMasks[custom_mask_index] 427 patternMask.position = component_instance.position 428 patternMask.rotation = component_instance.rotation 429 patternMask.scaling = component_instance.scaling 430 selectedSlots = (float(component_instance.projection_area1), 431 float(component_instance.projection_area2), 432 float(component_instance.projection_area3), 433 float(component_instance.projection_area4)) 434 materialSlots = [SlotID.PRIMARY_NANOCOATING, 435 SlotID.SECONDARY_NANOCOATING, 436 SlotID.TERTIARY_NANOCOATING, 437 SlotID.TECH_AREA] 438 if typeID is not None: 439 slots = [1.0, 440 1.0, 441 1.0, 442 1.0] 443 for index in range(4): 444 defaultFactionID = evetypes.GetFactionID(typeID) 445 materialMappedIndex = AREA_MAPS[defaultFactionID][materialSlots[index]] 446 slots[materialMappedIndex - 1] = selectedSlots[index] 447 448 selectedSlots = tuple(slots) 449 patternMask.targetMaterials = selectedSlots 450 patternMask.isMirrored = component_instance.mirrored 451 patternComponentID = component_instance.component_id 452 component_data = ComponentsDataLoader.get_component_data(patternComponentID) 453 CosmeticsManager.ConfigurePatternBorderSettings(model, component_instance.projection_type_u, component_instance.projection_type_v, slot_id) 454 texturePath = component_data.resource_file_path 455 for area in model.mesh.opaqueAreas: 456 for each in area.effect.resources: 457 if each.name == RESOURCE_NAME_BY_INDEX[custom_mask_index]: 458 each.resourcePath = texturePath 459 460 except Exception as exc: 461 logger.exception('Failed to apply projection parameters: %s', exc) 462 463 @staticmethod 464 def ConfigurePatternBorderSettings(model, projection_type_u, projection_type_v, slot_id): 465 projection_type_to_address = {PatternProjectionType.CLAMP: 4, 466 PatternProjectionType.REPEAT: 1, 467 PatternProjectionType.BORDER: 3} 468 pattern_index = PATTERN_INDEX_MAP[slot_id] 469 targetTextureName = 'PatternMask' + str(pattern_index) + 'MapSampler' 470 for area in model.mesh.opaqueAreas: 471 populateParameters = False 472 for i, each in enumerate(area.effect.samplerOverrides): 473 if len(each) < 3: 474 continue 475 if each[0] == targetTextureName: 476 mutableObj = list(each) 477 mutableObj[1] = projection_type_to_address.get(projection_type_u, 4) 478 mutableObj[2] = projection_type_to_address.get(projection_type_v, 4) 479 area.effect.samplerOverrides[i] = tuple(mutableObj) 480 populateParameters = True 481 482 if populateParameters: 483 area.effect.PopulateParameters()