using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.Events; namespace Bozo.AnimeCharacters { public class Outfit : OutfitBase { public bool Initalized { get; set; } [SerializeField] bool AttachInEditMode; private OutfitSystem system; //[Header("Character Creator Settings")] public string OutfitName; public Sprite OutfitIcon; public string[] ColorChannels = new string[] { "Base" }; public string TextureCatagory; public bool supportDecals; public bool supportPatterns; public bool showCharacterCreator = true; public SkinnedMeshRenderer skinnedRenderer { get { if (skinnedRenderers.Length > 0) return skinnedRenderers[0]; else return null; } } public SkinnedMeshRenderer[] skinnedRenderers { get; private set; } public Renderer outfitRenderer { get; private set; } //[field: Header("Outfit Settings")] [SerializeField] public OutfitType Type; public string AttachPoint; [Range(-1,11)]public int materialIndex = -1; [Range(0,100)]public int materialPriority = 0; public Color[] defaultColors; public string[] tags; public GameObject[] optionalPieces; public Transform[] additionalBones; private Dictionary tagShapes = new Dictionary(); private Dictionary shapes = new Dictionary(); public LinkedColorSets[] LinkedColorSets; public OutfitType[] IncompatibleSets; public OutfitType[] HideTypes; //[Header("User Settings")] public int currentSwatch; public List outfitSwatches = new List(); public Transform[] originalBones; public Transform originalRootBone; public Transform editorAttachPoint; //Materials public bool customShader; public Material material; public Renderer controlMaterial; public int controlIndex; public Dictionary editMaterials = new Dictionary(); private MaterialPropertyBlock block; public List decalDatas; //Events public UnityAction OnColorChanged; // public bool editMode; public Color[] colors = new Color[9]; private void OnValidate() { if (Application.isPlaying && gameObject.scene.isLoaded) { if (system == null) system = GetComponentInParent(); SetColorInital(); } #if UNITY_EDITOR if (AttachInEditMode && !Application.isPlaying) SoftAttach(); #endif } private void Awake() { outfitRenderer = GetComponentInChildren(); skinnedRenderers = GetComponentsInChildren(true); if (outfitRenderer) material = outfitRenderer.sharedMaterial; block = new MaterialPropertyBlock(); foreach (var smr in skinnedRenderers) { smr.sharedMaterial = material; } InitSetUpShapes(); } private void OnEnable() { Attach(); } private void OnDisable() { if (!system) return; system.OnOutfitChanged -= OnOutfitUpdate; system.OnShapeChanged -= SetShape; system.RemoveOutfit(this, false); } private void OnDestroy() { if (!system) return; system.OnOutfitChanged -= OnOutfitUpdate; } private void Start() { if (!Initalized) Attach(); SetColorInital(); } public void Attach(Transform parent) { transform.parent = parent; Attach(); } public void Attach(OutfitSystem system) { transform.parent = system.transform; Attach(); } public void Attach(bool force = false) { system = GetComponentInParent(); outfitRenderer = GetComponentInChildren(); if (system == null) return; if (!system.initalized) return; RemoveIncompatible(); CheckTags(); CopySystemShapes(); system.OnOutfitChanged += OnOutfitUpdate; system.OnShapeChanged += SetShape; if (Initalized && !force) { system.ReattachOutfit(this); return; } var extensions = GetComponentsInChildren(); foreach (var item in extensions) { item.Initalize(system, this); } //Reassigning original bones incase it was attached in editor if (originalBones.Length > 0 && skinnedRenderer) { skinnedRenderer.bones = originalBones; skinnedRenderer.rootBone = originalRootBone; } system.AttachOutfit(this); foreach (var item in extensions) { item.Execute(system, this); } } public void ReturnBones() { foreach (var item in additionalBones) { if (item == null) continue; item.parent = transform; } } #region OnUpdate Methods private void OnOutfitUpdate(Outfit newOutfit) { if (RemoveIfIncompatible(newOutfit)) return; CheckTags(); } private void CheckTags() { var shapes = new List(tagShapes.Keys); foreach (var smr in skinnedRenderers) { for (int i = 0; i < shapes.Count; i++) { var yes = system.ContainsTag(shapes[i]); if (yes) { smr.SetBlendShapeWeight(tagShapes[shapes[i]], 100); } else { smr.SetBlendShapeWeight(tagShapes[shapes[i]], 0); } } } } #endregion #region Shapes Methods private void CopySystemShapes() { if (system) { var shapesKeys = shapes.Keys.ToArray(); for (int i = 0; i < shapesKeys.Length; i++) { var systemValue = system.GetShape(shapesKeys[i]); if (systemValue == -10000) continue; SetShape(shapesKeys[i], systemValue); } } } private void InitSetUpShapes() { if (!skinnedRenderer) return; var mesh = skinnedRenderer.sharedMesh; var blendShapeCount = skinnedRenderer.sharedMesh.blendShapeCount; for (int i = 0; i < blendShapeCount; i++) { var blendFullName = mesh.GetBlendShapeName(i); var blendName = mesh.GetBlendShapeName(i); //removing nameshape that maya gives var sort = blendName.Split("."); if (sort.Length > 1) { blendName = sort[1]; } sort = blendName.Split("_"); if (sort.Length > 1) { if (sort[0] == "Tag") { tagShapes.Add(sort[1], i); } if (sort[0] == "Shape") { shapes.Add(sort[1], i); } } } } public void SetShape(string key, float value) { if (value <= -10000) return; if (!skinnedRenderer) return; var sort = key.Split("."); if (sort.Length > 1) { key = sort[1]; } if (!shapes.TryGetValue(key, out int index)) { return; } foreach (var smr in skinnedRenderers) { if (smr.sharedMesh.blendShapeCount <= index) continue; smr.SetBlendShapeWeight(index, value); } } #endregion #region Incompatible Outfit Methods private void RemoveIncompatible() { foreach (var item in IncompatibleSets) { system.RemoveOutfit(item, true); } } private bool RemoveIfIncompatible(Outfit outfit) { if (outfit == null) return false; foreach (var item in IncompatibleSets) { if (outfit.Type == item) { system.RemoveOutfit(this, true); return true; } } return false; } #endregion #region Color Methods public void SetControlMaterial(Renderer mat, int index) { //This is for controlling a proxy material so the outfit can use another shader entirely controlMaterial = mat; controlIndex = index; } public void SetMaterial(Material mat) { if(outfitRenderer) outfitRenderer.sharedMaterial = mat; foreach (var smr in skinnedRenderers) { smr.sharedMaterial = mat; } } #region colors private void SetColorInital() { if (!outfitRenderer) return; for (int i = 0; i < defaultColors.Length; i++) { SetColor(defaultColors[i], i + 1); } } public void SetColor(Color color, int index, bool linkedChanged = false) { colors[index - 1] = color; if (system == null) { system = GetComponentInParent(); } if (outfitRenderer == null) { outfitRenderer = GetComponentInChildren(); } var material = SelectBlock(); try { if (customShader) { SetColor(color); } else { material.SetColor("_Color_" + index, color); } } catch { Debug.LogError(name + " Failed to load colors. Is it missing its material or renderer?"); } foreach (var item in LinkedColorSets) { if (!linkedChanged) { var linkedOutfit = system.GetOutfit(item.linkedType); if (linkedOutfit == null) continue; if (index > item.linkedChannelRange) continue; linkedOutfit.SetColor(color, index, true); } } UpdateMaterialBlock(); OnColorChanged?.Invoke(this); } public virtual void SetColor(Color color) { var material = SelectBlock(); if (material.HasProperty("_Color_1")) { material.SetColor("_Color_1", color); } else if (material.HasProperty("_Color")) { material.SetColor("_Color", color); } else if (material.HasProperty("_MainColor")) { material.SetColor("_MainColor", color); } UpdateMaterialBlock(); } public virtual List GetColors() { var colors = new List(); var material = SelectMaterial(); if (!material) return colors; for (int i = 1; i <= 9; i++) { if(block.HasProperty("_Color_" + i)) { colors.Add(SelectBlock().GetColor("_Color_" + i)); } else { colors.Add(material.GetColor("_Color_" + i)); } } return colors; } public virtual Color GetColor(int channel) { var material = SelectMaterial(); if (customShader) { if (material.HasProperty("_Color_1")) { return material.GetColor("_Color_1"); } else if (material.HasProperty("_Color")) { return material.GetColor("_Color"); } else if (material.HasProperty("_MainColor")) { return material.GetColor("_MainColor"); } else { return Color.white; } } if(block.HasProperty("_Color_" + channel)) return SelectBlock().GetColor("_Color_" + channel); return material.GetColor("_Color_" + channel); } public virtual void SetDecal(Texture texture) { var material = SelectBlock(); if (texture == null) return; material.SetTexture("_DecalMap", texture); UpdateMaterialBlock(); } public virtual void SetDecalSize(Vector4 size) { var material = SelectBlock(); if (!material.HasProperty("_DecalScale")) return; material.SetVector("_DecalScale", size); UpdateMaterialBlock(); } public virtual Vector4 GetDecalSize() { var material = SelectMaterial(); if (!material.HasProperty("_DecalScale")) return new Vector4(0, 0, 0, 0); var v = material.GetVector("_DecalScale"); return new Vector4(v.x, v.y, 0, 0); } public virtual Texture GetDecal() { var material = SelectMaterial(); if (!material.HasProperty("_DecalMap")) return null; return material.GetTexture("_DecalMap"); } public virtual void SetDecalColor(Color color, int index) { var material = SelectBlock(); material.SetColor("_DecalColor_" + index, color); UpdateMaterialBlock(); } public virtual Color GetDecalColor(int index) { var material = SelectMaterial(); return material.GetColor("_DecalColor_" + index); } public virtual List GetDecalColors() { var material = SelectMaterial(); var colors = new List(); for (int i = 1; i < 4; i++) { colors.Add(material.GetColor("_DecalColor_" + i)); } return colors; } public virtual void SetPattern(Texture texture) { var material = SelectBlock(); if (texture == null) texture = Texture2D.blackTexture; material.SetTexture("_PatternMap", texture); UpdateMaterialBlock(); OnColorChanged?.Invoke(this); } public virtual Texture GetPattern() { var material = SelectMaterial(); if (!material.HasProperty("_PatternMap")) return null; return material.GetTexture("_PatternMap"); } public virtual void SetPatternColor(Color color, int index) { var material = SelectBlock(); material.SetColor("_PatternColor_" + index, color); UpdateMaterialBlock(); OnColorChanged?.Invoke(this); } public virtual Color GetPatternColor(int index) { var material = SelectMaterial(); return material.GetColor("_PatternColor_" + index); } public virtual List GetPatternColors() { var material = SelectMaterial(); var colors = new List(); for (int i = 1; i < 4; i++) { colors.Add(material.GetColor("_PatternColor_" + i)); } return colors; } public virtual void SetPatternSize(Vector2 size) { var material = SelectBlock(); material.SetVector("_PatternScale", size); UpdateMaterialBlock(); OnColorChanged?.Invoke(this); } public virtual Vector4 GetPatternSize() { var material = SelectMaterial(); if (!material.HasProperty("_PatternScale")) return new Vector4(0, 0, 0, 0); var v = material.GetVector("_PatternScale"); return new Vector4(v.x, v.y, 0, 0); } public virtual void SetBaseTexture(Texture texture, Texture normalTexture = null) { } protected MaterialPropertyBlock SelectBlock() { if (controlMaterial) controlMaterial.GetPropertyBlock(block, controlIndex); else if (outfitRenderer) outfitRenderer.GetPropertyBlock(block); return block; } protected Material SelectMaterial() { Material mat = null; if (controlMaterial) mat = controlMaterial.sharedMaterials[controlIndex]; else if (outfitRenderer) mat = outfitRenderer.sharedMaterial; else if (material) mat = material; return mat; } public void UpdateMaterialBlock() { if (controlMaterial) controlMaterial.SetPropertyBlock(block, controlIndex); else { if (outfitRenderer) { outfitRenderer.SetPropertyBlock(block); foreach (var item in skinnedRenderers) { item.SetPropertyBlock(block, controlIndex); } } } } #endregion public override void SetSwatch(int swatchIndex, bool linkedChanged = false) { if (!customShader) return; if (!material) { material = GetComponentInChildren().material; } if (swatchIndex + 1 > outfitSwatches.Count) return; var swatchID = outfitSwatches[swatchIndex].swatchID; var tex = Resources.Load(swatchID); material.mainTexture = tex; currentSwatch = swatchIndex; foreach (var item in LinkedColorSets) { if (!linkedChanged) { var linkedOutfit = system.GetOutfit(item.linkedType); if (linkedOutfit == null) continue; linkedOutfit.SetSwatch(swatchIndex, true); } } } public OutfitData GetOutfitData() { var outfitData = new OutfitData(); var path = Type.name + "/" + name; path = path.Replace("(Clone)", ""); outfitData.outfit = path; if (customShader) { outfitData.color = GetColor(1); outfitData.swatch = currentSwatch; } else { outfitData.colors = GetColors(); var decal = GetDecal(); if (decal != null) { outfitData.decal = "Decal/" + decal.name; outfitData.decalColors = GetDecalColors(); outfitData.decalScale = GetDecalSize(); } else { outfitData.decal = ""; } var pattern = GetPattern(); if (pattern != null) { outfitData.pattern = "Pattern/" + pattern.name; outfitData.patternColors = GetPatternColors(); outfitData.patternScale = GetPatternSize(); } else { outfitData.pattern = ""; } } var vis = new bool[optionalPieces.Length]; for (int i = 0; i < optionalPieces.Length; i++) { if (optionalPieces[i] == null) continue; vis[i] = optionalPieces[i].activeSelf; } outfitData.partVisibility = vis; outfitData.decalDatas = decalDatas; return outfitData; } #endregion #region Soft Attach #if UNITY_EDITOR public void SoftAttach() { var system = GetComponentInParent(); if (system == null) { //return bones if no longer attach to system SoftDetach(); return; } system.SoftAttach(this); } public void SoftDetach() { editorAttachPoint = null; if (originalBones.Length > 0) { var skinnedRenderer = GetComponentInChildren(); if (skinnedRenderer == null) return; skinnedRenderer.bones = originalBones; skinnedRenderer.rootBone = originalRootBone; originalBones = new Transform[0]; originalRootBone = null; } } #endif #endregion #region Magicka Cloth2 private void InitCloth() { /* #if MAGICACLOTH2 var cloth = GetComponentInChildren(); if (!cloth) return; cloth.enabled = false; cloth.Initialize(); cloth.DisableAutoBuild(); #endif */ } public void ActivateCloth(Dictionary boneMap) { /* #if MAGICACLOTH2 var cloth = GetComponentInChildren(); if (!cloth) return; cloth.ReplaceTransform(boneMap); var col = system.GetClothColliders(); List ClothColliders = col .Where(m => m != null) .Select(m => m.GetComponent()) .ToList(); cloth.SerializeData.colliderCollisionConstraint.colliderList = ClothColliders; cloth.enabled = true; #endif */ } #endregion #region Utility Scripts [ContextMenu("QuickName")] private void QuickName() { int underscoreIndex = name.IndexOf('_'); string trimmed = underscoreIndex >= 0 ? name.Substring(underscoreIndex + 1) : name; string spaced = Regex.Replace(trimmed, "(?