public static async Task<ITextureResource> GenerateHeadSprite( CharacterHeadSpriteData data, ProceduralTextureRequest request, bool isMale, HeadSpriteType headSpriteType, bool isPreview, Vector2Ushort? customTextureSize = null, sbyte spriteQualityOffset = 0) { var isFrontFace = headSpriteType == HeadSpriteType.Front; var renderingTag = request.TextureName; var side = isFrontFace ? "Front" : "Back"; var style = data.FaceStyle; var faceStylesProvider = SharedCharacterFaceStylesProvider.GetForGender(isMale); var facePath = $"{faceStylesProvider.FacesFolderPath}{style.FaceId}/{side}"; var faceShapePath = facePath + ".png"; if (!IsFileExists(faceShapePath)) { Api.Logger.Error("Face sprite not found: " + faceShapePath); // try fallback facePath = faceStylesProvider.FacesFolderPath + "Face01/" + side; faceShapePath = facePath; if (!IsFileExists(faceShapePath)) { // no fallback return TextureResource.NoTexture; } } var faceTopPath = $"{facePath}Top{style.TopId}.png"; var faceBottomPath = $"{facePath}Bottom{style.BottomId}.png"; if (isFrontFace) { if (!IsFileExists(faceTopPath)) { Api.Logger.Error("Face top sprite not found: " + faceTopPath); // try fallback faceTopPath = $"{facePath}Top01.png"; if (!IsFileExists(faceTopPath)) { // no fallback return TextureResource.NoTexture; } } if (!IsFileExists(faceBottomPath)) { Api.Logger.Error("Face bottom sprite not found: " + faceBottomPath); // try fallback faceBottomPath = $"{facePath}Bottom01.png"; if (!IsFileExists(faceBottomPath)) { // no fallback return TextureResource.NoTexture; } } } var protoItemHeadEquipment = data.HeadEquipmentItemProto; var isHairVisible = protoItemHeadEquipment?.IsHairVisible ?? true; isHairVisible &= style.HairId is not null; string hair = null, hairBehind = null; if (isHairVisible && !string.IsNullOrEmpty(style.HairId)) { var hairBase = faceStylesProvider.HairFolderPath + $"{style.HairId}/{side}"; hair = hairBase + ".png"; hairBehind = hairBase + "Behind.png"; } string skinTone = null; if (!string.IsNullOrEmpty(style.SkinToneId)) { skinTone = SharedCharacterFaceStylesProvider.GetSkinToneFilePath(style.SkinToneId); } string hairColor = null; if (!string.IsNullOrEmpty(style.HairColorId)) { hairColor = SharedCharacterFaceStylesProvider.HairColorRootFolderPath + $"{style.HairColorId}" + ".png"; } string helmetFront = null, helmetBehind = null; TextureResource helmetFrontMaskTextureResource = null; if (protoItemHeadEquipment is not null) { protoItemHeadEquipment.ClientGetHeadSlotSprites(data.HeadEquipmentItem, isMale, data.SkeletonResource, isFrontFace, isPreview, out helmetFront, out helmetBehind); if (helmetFront is null) { throw new Exception("Helmet attachment is not available for " + protoItemHeadEquipment); } if (isFrontFace) { helmetFrontMaskTextureResource = new TextureResource( helmetFront.Substring(0, helmetFront.Length - ".png".Length) + "Mask.png", qualityOffset: spriteQualityOffset); if (!Api.Shared.IsFileExists(helmetFrontMaskTextureResource.FullPath)) { helmetFrontMaskTextureResource = null; } } } // let's combine all the layers (if some elements are null - they will not be rendered) List<ComposeLayer> layers; if (protoItemHeadEquipment is null || protoItemHeadEquipment.IsHeadVisible) { var faceLayer = await CreateFaceTexture( request, renderingTag, customTextureSize, new List<ComposeLayer>() { new(faceShapePath, spriteQualityOffset), new(faceTopPath, spriteQualityOffset), new(faceBottomPath, spriteQualityOffset) },
public static async Task <ITextureResource> GenerateHeadSprite( CharacterHeadSpriteData data, ProceduralTextureRequest request, bool isMale, HeadSpriteType headSpriteType, Vector2Ushort?customTextureSize = null, sbyte spriteQualityOffset = 0) { var isFrontFace = headSpriteType == HeadSpriteType.Front; var renderingTag = request.TextureName; var side = isFrontFace ? "Front" : "Back"; var style = data.FaceStyle; var faceStylesProvider = SharedCharacterFaceStylesProvider.GetForGender(isMale); var facePath = $"{faceStylesProvider.FacesFolderPath}{style.FaceId}/{side}"; var faceShapePath = facePath + ".png"; if (!IsFileExists(faceShapePath)) { Api.Logger.Error("Face sprite not found: " + faceShapePath); // try fallback facePath = faceStylesProvider.FacesFolderPath + "Face01/" + side; faceShapePath = facePath; if (!IsFileExists(faceShapePath)) { // no fallback return(TextureResource.NoTexture); } } var faceTopPath = $"{facePath}Top{style.TopId}.png"; var faceBottomPath = $"{facePath}Bottom{style.BottomId}.png"; if (isFrontFace) { if (!IsFileExists(faceTopPath)) { Api.Logger.Error("Face top sprite not found: " + faceTopPath); // try fallback faceTopPath = $"{facePath}Top01.png"; if (!IsFileExists(faceTopPath)) { // no fallback return(TextureResource.NoTexture); } } if (!IsFileExists(faceBottomPath)) { Api.Logger.Error("Face bottom sprite not found: " + faceBottomPath); // try fallback faceBottomPath = $"{facePath}Bottom01.png"; if (!IsFileExists(faceBottomPath)) { // no fallback return(TextureResource.NoTexture); } } } var itemHeadEquipment = data.HeadEquipment; var protoItemHeadEquipment = (IProtoItemEquipmentHead)itemHeadEquipment?.ProtoItem; var isHairVisible = protoItemHeadEquipment?.IsHairVisible ?? true; isHairVisible &= style.HairId != null; string hair = null, hairBehind = null; if (isHairVisible && !string.IsNullOrEmpty(style.HairId)) { var hairBase = faceStylesProvider.HairFolderPath + $"{style.HairId}/{side}"; hair = hairBase + ".png"; hairBehind = hairBase + "Behind.png"; } string skinTone = null; if (!string.IsNullOrEmpty(style.SkinToneId)) { skinTone = SharedCharacterFaceStylesProvider.GetSkinToneFilePath(style.SkinToneId); } string hairColor = null; if (!string.IsNullOrEmpty(style.HairColorId)) { hairColor = SharedCharacterFaceStylesProvider.HairColorRootFolderPath + $"{style.HairColorId}" + ".png"; } string helmetFront = null, helmetBehind = null; if (protoItemHeadEquipment != null) { protoItemHeadEquipment.ClientGetHeadSlotSprites( itemHeadEquipment, isMale, data.SkeletonResource, isFrontFace, out helmetFront, out helmetBehind); if (helmetFront is null) { throw new Exception("Helmet attachment is not available for " + protoItemHeadEquipment); } } // let's combine all the layers (if some elements are null - they will not be rendered) List <ComposeLayer> layers; if (protoItemHeadEquipment is null || protoItemHeadEquipment.IsHeadVisible) { var faceLayer = await CreateFaceTexture( request, renderingTag, customTextureSize, new List <ComposeLayer>() { new ComposeLayer(faceShapePath, spriteQualityOffset), new ComposeLayer(faceTopPath, spriteQualityOffset), new ComposeLayer(faceBottomPath, spriteQualityOffset) }, skinTone); layers = new List <ComposeLayer>(); if (isHairVisible) { var(layerHair, layerHairBehind) = await GetHairLayers(request, hair, hairBehind, hairColor, spriteQualityOffset); if (headSpriteType != HeadSpriteType.BackOverlay) { layers.Add(new ComposeLayer(helmetBehind, spriteQualityOffset)); layers.Add(layerHairBehind); layers.Add(faceLayer); } if (headSpriteType != HeadSpriteType.Back) { if (layerHair.TextureResource is null && helmetFront is null && layers.Count == 0) { return(TransparentTexturePlaceholder); } layers.Add(layerHair); layers.Add(new ComposeLayer(helmetFront, spriteQualityOffset)); } } else // hair is not visible { if (headSpriteType == HeadSpriteType.BackOverlay) { return(TransparentTexturePlaceholder); } layers.Add(new ComposeLayer(helmetBehind, spriteQualityOffset)); layers.Add(faceLayer); layers.Add(new ComposeLayer(helmetFront, spriteQualityOffset)); } }