/* * File generated by SourceCombiner.exe using 31 source files. * Created On: 6/14/2023 8:59:18 PM */ using MeshVR; using MVR.FileManagementSecure; using Random = System.Random; using SimpleJSON; using static VAM_Decal_Maker.PathHelper; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using uFileBrowser; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using UnityEngine.Video; //*** SourceCombiner -> original file VAM_Decal_Maker.cs *** namespace VAM_Decal_Maker { public class Decal_Maker : MVRScript { private const string pluginName = "DecalMaker"; private const string pluginVersion = "RC 9"; //person script is attatched too private Atom _parentAtom; //VAM parent private bool _SetupFinished = false; private bool _ShadersLoaded = false; private bool _SetupError = false; private bool _processingCharacterChange = false; private Texture[] gpuDecalArray; private Texture[] gpuSpecArray; private Texture[] gpuGlossArray; private Texture[] gpuDiffuseArray; private Texture[] gpuNormalArray; public DAZCharacter _dazCharacter { get; private set; } private DAZCharacterSelector _dazCharacterSelector; public Shader _customBulkDecalShader { get; private set; } public Shader _customBulkDecalShaderUI { get; private set; } public Shader _customSpecGlossShader { get; private set; } public Shader _customNormShader { get; private set; } public Shader _customPackedNormShader { get; private set; } public Shader _customNormalMapShaderUI { get; private set; } public Shader _customSpecularMapShaderUI { get; private set; } public Shader _customGenitalMakerShaderUI { get; private set; } public JSONClass _savedData { get; set; } public static Dictionary resourceTextures = new Dictionary(); public DAZMergedSkinV2 _dazSkin { get; private set; } //Events public Action OnUpDateAction; public Action OnDestroyAction; //A public event for listeners to subscribe to //Note that by using the generic EventHandler event type //we do not need to declare a separate delegate type. public event EventHandler CoreEvent; //delegate to get saveJSON from DecalManager public delegate JSONArray GetJSONSaveDelegate(string MaterialSlot, string TextureSlot); public GetJSONSaveDelegate GetJSONDelegate; public bool _isMale; public string _uvSetName; //UI public JSONStorableBool _toggleNippleCutout; public JSONStorableBool _toggleGenitalCutout; private UIDynamicButton _resetButton; private UIDynamicButton _editButton; private UIDynamicButton _genButton; private UIDynamicButton _clearAllButton; private UIDynamicButton _prefabButton; private UIDynamicButton _savePresetButton; private UIDynamicButton _loadPresetButton; private UIDynamicToggle _toggleNipple; private UIDynamicToggle _toggleGenital; public HeaderPanel _headerPanel; //GenitalMaker _genitalMakerPanel; private PresetManager _appearancePresetManager; private JSONClass _lastLoadedAppearanceJSON = new JSONClass(); private UserConfirmPanel userConfirmPanel; private Canvas UserConfirmCanvas; private AdjustmentPanel adjustmentPanel; private GenitalPanel genitalPanel; private readonly bool _Debug = false; public void ToggleGensCallBack(bool value) { OnCoreChange(this, new PanelEventArgs(EventEnum.ToggleGenitalCutout, value)); } public void ToggleNipsCallBack(bool value) { OnCoreChange(this, new PanelEventArgs(EventEnum.ToggleNippleCutout, value)); } public override void Init() { try { if (containingAtom.mainController == null) { SuperController.LogError("Please add this plugin to a PERSON atom."); return; } _parentAtom = containingAtom; _parentAtom.mainController.interactableInPlayMode = true; _appearancePresetManager = _parentAtom.mainPresetControl.GetComponentsInChildren().Where(x => x.name == "AppearancePresets").FirstOrDefault(); //registers DM to store with appearances List spStore = _appearancePresetManager.specificStorables.ToList(); PresetManager.SpecificStorable storable = new PresetManager.SpecificStorable { specificStorableBucket = this.transform, specificKey = null, storeId = string.Empty, isSpecificKeyAnObject = false, includeChildren = false }; spStore.Add(storable); _appearancePresetManager.specificStorables = spStore.ToArray(); _appearancePresetManager.RefreshStorables(); //setup all values before finishing UI StartCoroutine(CharacterChanged()); StartCoroutine(MonitorLastLoadedAppearance()); //use vam's built in loader for assets then load shader from bundle instead of smuggling them in as a unity Atom AssetLoader.AssetBundleFromFileRequest req = new AssetLoader.AssetBundleFromFileRequest() { path = GetPackagePath(this) + "Custom/Scripts/Chokaphi/VAM_Decal_Maker/Icons/chokpahi-decal.assetbundle", callback = LoadAssetBundle, }; AssetLoader.QueueLoadAssetBundleFromFile(req); _clearAllButton = CreateButton("Clear Everything"); _prefabButton = CreateButton("USE Prefab", true); PrefabPanel prefabPanel = new PrefabPanel(this); _resetButton = CreateButton("Reset Texture"); UIDynamic spacer = CreateSpacer(true); spacer.height = 50; GridLayoutGroup glg = spacer.gameObject.AddComponent(); glg.constraint = GridLayoutGroup.Constraint.FixedColumnCount; glg.constraintCount = 2; glg.cellSize = new Vector2(250, 50); glg.spacing = new Vector2(20, 0); _editButton = UIWidgets.CreateVAMButtom(this, spacer.transform, "EDIT"); _editButton.transform.localPosition = Vector3.zero; _genButton = UIWidgets.CreateVAMButtom(this, spacer.transform, "Genital"); _genButton.transform.localPosition = Vector3.zero; _loadPresetButton = CreateButton("Load Preset"); _savePresetButton = CreateButton("Save Preset", true); _toggleNippleCutout = new JSONStorableBool("Nipple Cutouts ON", true, new JSONStorableBool.SetBoolCallback(ToggleNipsCallBack)); _toggleNipple = CreateToggle(_toggleNippleCutout); RegisterBool(_toggleNippleCutout); _toggleGenitalCutout = new JSONStorableBool("Genital Cutouts ON", true, new JSONStorableBool.SetBoolCallback(ToggleGensCallBack)); _toggleGenital = CreateToggle(_toggleGenitalCutout, true); RegisterBool(_toggleGenitalCutout); JSONStorableUrl lastImagePath = new JSONStorableUrl("LastImageDir", "Custom/Atom/Person/Textures"); RegisterUrl(lastImagePath); lastImagePath.isStorable = false; //assign listners _clearAllButton.button.onClick.AddListener(ResetAll); _prefabButton.button.onClick.AddListener(prefabPanel.OnClick); string presetsPath = "Custom/Scripts/Chokaphi/VAM_Decal_Maker/Presets"; if (!FileManagerSecure.DirectoryExists(presetsPath)) { FileManagerSecure.CreateDirectory(presetsPath); } if (!FileManagerSecure.DirectoryExists(presetsPath + "/PreFabs")) { FileManagerSecure.CreateDirectory(presetsPath + "/PreFabs"); } _savePresetButton.button.onClick.AddListener(() => { SuperController.singleton.fileBrowserUI.defaultPath = presetsPath; SuperController.singleton.fileBrowserUI.showFiles = true; SuperController.singleton.fileBrowserUI.showDirs = true; SuperController.singleton.fileBrowserUI.SetTextEntry(true); SuperController.singleton.fileBrowserUI.hideExtension = true; SuperController.singleton.fileBrowserUI.shortCuts = new List(); SuperController.singleton.fileBrowserUI.Show(SavePresetCallback); }); _loadPresetButton.button.onClick.AddListener(() => { //update for 1.21 to use the existing preset dialog system List shortCuts = FileManagerSecure.GetShortCutsForDirectory(presetsPath, false, true, true, false); SuperController.singleton.GetMediaPathDialog(PresetLoadCallBack, "json", presetsPath, false, true, false, null, true, shortCuts, true, true); }); //legacy storables some addon may use JSONStorableAction resetAllAction = new JSONStorableAction("ClearAll", ResetAll); RegisterAction(resetAllAction); JSONStorableAction performLoadAction = new JSONStorableAction("PerformLoad", PerformLoad); RegisterAction(performLoadAction); JSONStorableAction resetTexturesAction = new JSONStorableAction("Reset To Original Textures", ResetOriginalGPUTexturesAction); RegisterAction(resetTexturesAction); //load a preset from path JSONStorableUrl loadPresetFile = new JSONStorableUrl("Load Preset File", string.Empty, LoadJSONRequest, "json", true); RegisterUrl(loadPresetFile); loadPresetFile.isStorable = false; } catch (Exception e) { SuperController.LogError("Decal Maker: Error during Init" + e); } } private void LoadJSONRequest(string path) { if (string.IsNullOrEmpty(path)) return; JSONNode jn = LoadJSON(path); if (jn != null) { _savedData = (JSONClass)jn; PerformLoad(); } } public Texture2D GetResource(string path, bool linear = false) { string cachepath = path + linear.ToString(); Texture2D texture; if (resourceTextures.TryGetValue(cachepath, out texture)) { return texture; } //not in dictionary byte[] tmppng = FileManagerSecure.ReadAllBytes(GetPackagePath(this) + path); texture = new Texture2D(1, 1, TextureFormat.DXT5, linear); texture.LoadImage(tmppng); resourceTextures.Add(cachepath, texture); return texture; } private void LogError(string err) { if (_Debug) { Debug.LogError(err); } } public void SaveImageToFile(Texture2D tempTex, string path) { //string path = "Custom/PluginData/Chokaphi/VAM_Decal_Maker/" + name; try { byte[] bytes = tempTex.EncodeToPNG(); Debug.Log("Save to " + path); FileManagerSecure.WriteAllBytes(path + ".png", bytes); } catch (Exception ex) { LogError("This error is most likly due to trying to save a readonly texture./n" + ex.Message); } finally { GameObject.Destroy(tempTex); //Resources.UnloadAsset(tempTex); } } public void DebugSave(Texture2D tempTex, string name) { //string path = "Custom/PluginData/Chokaphi/VAM_Decal_Maker/" + name; //byte[] bytes = tempTex.EncodeToPNG(); //FileManagerSecure.WriteAllBytes(path, bytes); } private void LoadAssetBundle(MeshVR.AssetLoader.AssetBundleFromFileRequest request) { if (request.assetBundle == null) { SuperController.LogError("Decal Maker was unable to load chokpahi-decal.assetbundle"); return; } string path = "assets/_shaders/"; _customSpecGlossShader = request.assetBundle.LoadAsset(path + "SpecGloss/unlit-specular2.shader"); _customNormShader = request.assetBundle.LoadAsset(path + "Normal/unlit-normalmap2.shader"); _customPackedNormShader = request.assetBundle.LoadAsset(path + "Normal/unlit-packednormalmap.shader"); _customNormalMapShaderUI = request.assetBundle.LoadAsset(path + "Normal/custom-ui-normalmapoverlay.shader"); _customGenitalMakerShaderUI = request.assetBundle.LoadAsset(path + "unlit-genitalmaker2.shader"); _customBulkDecalShader = request.assetBundle.LoadAsset(path + "Decal/unlit-decal5.shader"); _customBulkDecalShaderUI = request.assetBundle.LoadAsset(path + "Decal/unlit-decal5UI.shader"); if (_customSpecGlossShader && _customNormShader && _customPackedNormShader && _customBulkDecalShader && _customNormalMapShaderUI && _customGenitalMakerShaderUI) { _ShadersLoaded = true; } } // Update is called with each rendered frame by Unity private void Update() { try { if (_SetupError) return; //Fire UpdateEvents to subscribers if (_SetupFinished == true && OnUpDateAction != null) { OnUpDateAction(); } if (_dazCharacter != null && !_processingCharacterChange) { if (_dazCharacter != _dazCharacterSelector.selectedCharacter) { _processingCharacterChange = true; LogError("StartCoroutine(CharacterChanged()"); StartCoroutine(CharacterChanged()); } } if (_ShadersLoaded == false) { LogError("On update call waiting on shaders to load"); } if (_SetupFinished == false && _ShadersLoaded == true) { LogError("On update call waiting on SetUp"); Setup(); LogError("setup should be finished " + _SetupFinished); } } catch (Exception e) { SuperController.LogError("Exception caught: " + e); } } public void OnCoreChange(object o, PanelEventArgs e) { //Fire the event - notifying all subscribers CoreEvent?.Invoke(o, e); } //called on reload or removal of plugin public void OnDestroy() { StopCoroutine(CharacterChanged()); StopCoroutine(MonitorLastLoadedAppearance()); ResetOriginalGPUTextures(_dazCharacter); AssetLoader.DoneWithAssetBundleFromFile(GetPackagePath(this) + "Custom/Scripts/Chokaphi/VAM_Decal_Maker/Icons/chokpahi-decal.assetbundle"); DeregisterBool(_toggleNippleCutout); DeregisterBool(_toggleNippleCutout); _parentAtom.RemoveCanvas(UserConfirmCanvas); OnDestroyAction?.Invoke(); } private void Setup() { try { //create Header Panel _headerPanel = new HeaderPanel(this); _resetButton.button.onClick.AddListener(() => { ResetOriginalGPUTextures(_dazCharacter); }); _editButton.button.onClick.AddListener(() => { if (adjustmentPanel == null) { adjustmentPanel = new AdjustmentPanel(this); } else { adjustmentPanel.gameObject.SetActive(true); } }); //UIDynamicButton NewPanelButton = CreateButton(" LAST LEFT ELEMENT"); //NewPanelButton.button.onClick.AddListener(() => //{ //}); //UIDynamicButton NewPanelButtonBack = CreateButton(" Back"); //NewPanelButtonBack.button.onClick.AddListener(() => //{ //}); //UIDynamicButton UnusedButton2 = CreateButton("LAST RIGHT ELEMENT", true); //UnusedButton2.button.onClick.AddListener(() => //{ // ImageLoaderThreaded.singleton.ReportOnTextures(); //}); _genButton.button.onClick.AddListener(() => { if (genitalPanel == null) { genitalPanel = new GenitalPanel(this); } else { genitalPanel.Show(); //genitalPanel.gameObject.SetActive(true); } }); //ui for Appearnce link prompt //move into my own canvas Some may watch "UserConfirmCanvas" for child windows GameObject originalCanvasGo = GameObject.Find("UserConfirmCanvas"); Canvas originalCanvas = originalCanvasGo.GetComponent(); UserConfirmCanvas = Instantiate(originalCanvas); UserConfirmCanvas.transform.SetParent(originalCanvas.transform.parent); UserConfirmCanvas.transform.localPosition = originalCanvas.transform.localPosition; UserConfirmCanvas.transform.localRotation = originalCanvas.transform.localRotation; UserConfirmCanvas.transform.localScale = originalCanvas.transform.localScale; userConfirmPanel = new UserConfirmPanel(UserConfirmCanvas.transform, this); _parentAtom.AddCanvas(UserConfirmCanvas); userConfirmPanel.SetActive(false); //this is hacky but sets the UI to look nice on start. _prefabButton.button.onClick.Invoke(); _SetupFinished = true; OnCoreChange(this, new PanelEventArgs(EventEnum.CoreSetupFinished)); } catch (Exception e) { SuperController.LogError("Exception caught: " + e.Message); SuperController.LogError(e.StackTrace); _SetupError = true; } } private void StoreGPUMats() { gpuDecalArray = new Texture[_dazSkin.GPUmaterials.Length]; gpuSpecArray = new Texture[_dazSkin.GPUmaterials.Length]; gpuGlossArray = new Texture[_dazSkin.GPUmaterials.Length]; gpuDiffuseArray = new Texture[_dazSkin.GPUmaterials.Length]; gpuNormalArray = new Texture[_dazSkin.GPUmaterials.Length]; for (int i = 0; i < _dazSkin.GPUmaterials.Length; i++) { if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.MainTex)) gpuDiffuseArray[i] = _dazSkin.GPUmaterials[i].GetTexture(MatSlotEnum.MainTex); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.DecalTex)) gpuDecalArray[i] = _dazSkin.GPUmaterials[i].GetTexture(MatSlotEnum.DecalTex); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.SpecTex)) gpuSpecArray[i] = _dazSkin.GPUmaterials[i].GetTexture(MatSlotEnum.SpecTex); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.GlossTex)) gpuGlossArray[i] = _dazSkin.GPUmaterials[i].GetTexture(MatSlotEnum.GlossTex); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.BumpMap)) gpuNormalArray[i] = _dazSkin.GPUmaterials[i].GetTexture(MatSlotEnum.BumpMap); } } private void RestoreGPUMats() { //why not use .GPUmaterials[i] = DazSkinv2.dazMesh.materials[i]; for (int i = 0; i < _dazSkin.GPUmaterials.Length; i++) { if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.MainTex)) _dazSkin.GPUmaterials[i].SetTexture(MatSlotEnum.MainTex, gpuDiffuseArray[i]); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.DecalTex)) _dazSkin.GPUmaterials[i].SetTexture(MatSlotEnum.DecalTex, gpuDecalArray[i]); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.SpecTex)) _dazSkin.GPUmaterials[i].SetTexture(MatSlotEnum.SpecTex, gpuSpecArray[i]); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.GlossTex)) _dazSkin.GPUmaterials[i].SetTexture(MatSlotEnum.GlossTex, gpuGlossArray[i]); if (_dazSkin.GPUmaterials[i].HasProperty(MatSlotEnum.BumpMap)) _dazSkin.GPUmaterials[i].SetTexture(MatSlotEnum.BumpMap, gpuNormalArray[i]); } } public Texture2D GetOriginalGPUTexture(int id, string MaterialSlot) { switch (MaterialSlot) { case MatSlotEnum.DecalTex: return (Texture2D)gpuDiffuseArray[id]; case MatSlotEnum.SpecTex: return (Texture2D)gpuSpecArray[id]; case MatSlotEnum.GlossTex: return (Texture2D)gpuGlossArray[id]; case MatSlotEnum.BumpMap: return (Texture2D)gpuNormalArray[id]; } return null; } public Texture2D GetCurrentGPUTexture(int id, string MaterialSlot) { return (Texture2D)_dazSkin.GPUmaterials[id].GetTexture(MaterialSlot); } public void RestoreGPUMatbyID(int id, string MaterialSlot) { switch (MaterialSlot) { case MatSlotEnum.DecalTex: if (_dazSkin.GPUmaterials[id].HasProperty(MatSlotEnum.DecalTex)) _dazSkin.GPUmaterials[id].SetTexture(MaterialSlot, gpuDecalArray[id]); break; case MatSlotEnum.SpecTex: if (_dazSkin.GPUmaterials[id].HasProperty(MatSlotEnum.SpecTex)) _dazSkin.GPUmaterials[id].SetTexture(MaterialSlot, gpuSpecArray[id]); break; case MatSlotEnum.GlossTex: if (_dazSkin.GPUmaterials[id].HasProperty(MatSlotEnum.GlossTex)) _dazSkin.GPUmaterials[id].SetTexture(MaterialSlot, gpuGlossArray[id]); break; case MatSlotEnum.BumpMap: if (_dazSkin.GPUmaterials[id].HasProperty(MatSlotEnum.BumpMap)) _dazSkin.GPUmaterials[id].SetTexture(MaterialSlot, gpuNormalArray[id]); break; } } public void QueueCharacterChanged() { if (_processingCharacterChange) { return; } _processingCharacterChange = true; StartCoroutine(CharacterUpdated(false)); } private bool CheckForAppeancePresetSave() { JSONArray storables = _appearancePresetManager.lastLoadedJSON["storables"] as JSONArray; if (storables != null) { JSONNode node = storables.Childs.Where(x => x["id"].Value.EndsWith("_VAM_Decal_Maker.Decal_Maker")).FirstOrDefault(); if (node != null) { _savedData = node as JSONClass; userConfirmPanel.SetActive(true); } } return false; } private IEnumerator MonitorLastLoadedAppearance() { //starts as null while (true) { LogError("_lastLoaded waiting till not null"); yield return new WaitWhile(() => _appearancePresetManager.lastLoadedJSON == null); LogError("_lastLoaded IS not null"); yield return new WaitWhile(() => _lastLoadedAppearanceJSON == _appearancePresetManager.lastLoadedJSON); yield return new WaitWhile(() => _processingCharacterChange); LogError("Set _lastLoaded"); //store the current value of PresetManager lastLoadedJSON to restore DM prests on appearance changes _lastLoadedAppearanceJSON = _appearancePresetManager.lastLoadedJSON; //check and load decal maker preset if exists yield return new WaitWhile(() => CheckForAppeancePresetSave()); } } private IEnumerator CharacterUpdated(bool newCharacter = false) { if (!newCharacter) yield return new WaitForSeconds(1); LogError("CharacterUpdated"); //loadingIcon.gameObject stays true until all texture/cloathing load process is finished LogError("yielding till all textures load " + SuperController.singleton.loadingIcon.gameObject.activeSelf); yield return new WaitWhile(() => SuperController.singleton.loadingIcon.gameObject.activeSelf); LogError("textures load finished continue coroutine"); //should store the current skin textures. LogError("STORE Current Skin Textures"); StoreGPUMats(); OnCoreChange(this, new PanelEventArgs(EventEnum.CoreNewCharacterSelected, newCharacter)); _processingCharacterChange = false; } private IEnumerator CharacterChanged() { LogError("CharacterChanged"); //used to know which character is being used or changed _dazCharacterSelector = _parentAtom.GetComponentInChildren(); DAZCharacter previousDazCharacter = _dazCharacter; ResetOriginalGPUTextures(previousDazCharacter); _dazCharacter = _dazCharacterSelector.selectedCharacter; yield return new WaitUntil(() => _dazCharacter.ready); _dazSkin = (DAZMergedSkinV2)_dazCharacter.skin; _isMale = _dazCharacter.isMale; _uvSetName = _dazCharacter.UVname.Replace("UV: ", ""); LogError("Waiting on UI"); yield return new WaitUntil(() => _SetupFinished == true); LogError("UI should be ready"); yield return StartCoroutine(CharacterUpdated(true)); _processingCharacterChange = true; if (_savedData != null) { LogError("Restore save state"); PerformLoad(_savedData); _savedData = null; } _processingCharacterChange = false; } //called on character change or UI private void ResetOriginalGPUTexturesAction() { ResetOriginalGPUTextures(_dazCharacter); } private void ResetOriginalGPUTextures(DAZCharacter dAZCharacter) { try { if (dAZCharacter == null || _dazSkin == null) return; RestoreGPUMats(); } catch (Exception e) { SuperController.LogError("Decal Maker: Error resetting custom textures " + e); } } private void ResetAll() { ResetOriginalGPUTextures(_dazCharacter); OnCoreChange(this, new PanelEventArgs(EventEnum.CoreResetAll)); } private void PresetLoadCallBack(string path) { PresetLoad(path); } #region LOAD/SAVE public void PresetLoad(string path, bool temp = false, string package = null) { if (string.IsNullOrEmpty(path)) return; JSONClass jc = this.LoadJSON(path) as JSONClass; PerformLoad(jc, temp, package, path); } private void SavePresetCallback(string path, bool autoScreenShot = false) { if (string.IsNullOrEmpty(path)) return; JSONClass json = GetJSON(); path = path.Replace(".dsgn.DecalMakerPreset", ""); path = path.Replace(".DecalMakerPreset", ""); path += ".DecalMakerPreset.json"; this.SaveJSON(json, path); SuperController.singleton.DoSaveScreenshot(path); //if(autoScreenShot) // SuperController.singleton.SetLeftSelect(); //SuperController.singleton.fileBrowserUI.ClearCacheImage(path); } //SAVE public override JSONClass GetJSON(bool includePhysical = true, bool includeAppearance = true, bool forceStore = false) { JSONClass jc = base.GetJSON(includePhysical, includeAppearance, true); jc["SaveVersion"] = "2"; foreach (string matSlot in MatSlotEnum.Values) { foreach (string region in BodyRegionEnum.Values) { //each manager has subscribed to this delegate. Each will only respond when they match the passed arguments //since only the last subscriber gets val return normally. Use a custom invoke to record each var subs = GetJSONDelegate.GetInvocationList(); foreach (var sub in subs) { JSONArray dc = (JSONArray)sub.DynamicInvoke(matSlot, region); if (dc != null) { jc[matSlot][region] = dc; } } } } jc["Nipple Cutouts ON"] = _toggleNippleCutout.val.ToString(); jc["Genital Cutouts ON"] = _toggleGenitalCutout.val.ToString(); return jc; } //LOAD public override void RestoreFromJSON(JSONClass jc, bool restorePhysical = true, bool restoreAppearance = true, JSONArray presetAtoms = null, bool setMissingToDefault = true) { base.RestoreFromJSON(jc, restorePhysical, restoreAppearance, presetAtoms); //store and call once we have everything loaded. RestorefromJSON is too early _savedData = jc; } //used for JSONStorableAction public void PerformLoad() { PerformLoad(_savedData); } private void PerformLoad(JSONClass jc, bool IsTemp = false, string package = null, string selectedFile = null) { if (jc == null) return; //convert to new file version if save is old try { if (jc["SaveVersion"] == null) jc = ConvertSaveToV1(jc); if (jc["SaveVersion"].AsInt == 1) jc = ConvertSaveToV2(jc); } catch { SuperController.LogError("Decal Maker: Unable to convert save"); return; } // Load Save Data try { _toggleNippleCutout.RestoreFromJSON(jc); _toggleGenitalCutout.RestoreFromJSON(jc); foreach (string matSlot in MatSlotEnum.Values) { foreach (string region in BodyRegionEnum.Values) { if (jc[matSlot] != null && jc[matSlot][region] != null) { JSONArray array = jc[matSlot][region] as JSONArray; foreach (JSONClass saveJSON in array) { OnCoreChange(this, new PanelEventArgs(EventEnum.CoreRestoreFromJSON, saveJSON) { materialSlot = matSlot, bodyRegion = region, Bool = IsTemp }); } } } } } catch (Exception e) { SuperController.LogError("Decal Maker: Unable to Load save"); SuperController.LogError(e.Message); SuperController.LogError(e.StackTrace); return; } finally { _savedData = null; } } #endregion #region Upgrade Save Version //upgrades old saves to new format private JSONClass ConvertSaveToV1(JSONClass jc) { List keys = new List() { BodyRegionEnum.Torso, BodyRegionEnum.Genitals, BodyRegionEnum.Face, BodyRegionEnum.Limbs }; JSONClass dc = new JSONClass(); foreach (string key in keys) { if (jc[key] != null) { dc.Add(key, jc[key]); } jc.Remove(key); } jc["Decal"] = dc; return jc; } private JSONClass ConvertSaveToV2(JSONClass jc) { try { //old keys Dictionary conversion = new Dictionary() { { "Decal", MatSlotEnum.DecalTex }, { "Specular", MatSlotEnum.SpecTex }, { "Gloss", MatSlotEnum.GlossTex }, { "Normal", MatSlotEnum.BumpMap } }; //SuperController.singleton.SaveJSON(jc, "OUT 1.txt"); foreach (var keyValue in conversion) { JSONClass n = new JSONClass(); if (jc[keyValue.Key] != null) { n.Add(keyValue.Value, jc[keyValue.Key]); jc.Remove(keyValue.Key); jc.Add(keyValue.Value, n[keyValue.Value]); } } jc["SaveVersion"].AsInt = 2; return jc; } catch (Exception e) { SuperController.LogError("Convert to save version 2 error " + e.Message); return null; } } } #endregion } //*** SourceCombiner -> original file AdjustmentPanel.cs *** namespace VAM_Decal_Maker { public class AdjustmentPanel : PanelBase { private MyButton centerpoint; private ImagePanelBase bottomPanel; private JSONStorableStringChooser regionJSSC; private JSONStorableStringChooser decalJSSC; private JSONStorableFloat rotationJSF; private JSONStorableFloat scaleJSF; private List DecalChoices = new List(); private DecalPanel currentDecal; private List DecalPanels; private bool IsMouseDown = false; private bool IsMouseIn = false; private bool IsMouseDownCE = false; private bool IsMouseInCE = false; //lazy load since it will not be used often private RenderPanelCPU _render; private RenderPanelCPU RenderPanel { get { if (_render == null) _render = new RenderPanelCPU(DM, MatSlotEnum.DecalTex, BodyRegionEnum.Torso); return _render; } } private TextPanel locationTextPanel; private string LocationText { set { locationTextPanel.text = value; } } private Vector2 lastMouseUVPosition; public AdjustmentPanel(Decal_Maker DM) : base(DM) { CreatePanelBackground(1078, 1300, Converters.RGBIntToFloat(105, 19, 134)); gameObject.transform.SetParent(DM.UITransform, false); gameObject.transform.localPosition = new Vector3(0, 0, 0); bottomPanel = new ImagePanelBase(DM, MatSlotEnum.MainTex, BodyRegionEnum.Torso, false, false); bottomPanel.transform.SetParent(transform, false); bottomPanel.transform.localPosition = new Vector3(0, -50, 0); bottomPanel.material = new Material(DM._customBulkDecalShaderUI); RectTransform rct = bottomPanel.gameObject.GetComponent(); rct.sizeDelta = new Vector2(1078, 1000); //15,2,0,28 bottomPanel.material.SetTexture("_MainTex", DM._dazSkin.GPUmaterials[15].GetTexture(MatSlotEnum.MainTex)); //SetLayout(100, 100); MouseEvent me = bottomPanel.Button.gameObject.AddComponent(); me.MouseDown = (e) => { IsMouseDown = true; lastMouseUVPosition = CalculateUVTranslate(); }; me.MouseUp = (e) => { IsMouseDown = false; }; me.MouseEnter = (e) => { IsMouseIn = true; }; me.MouseExit = (e) => { IsMouseIn = false; }; me.MouseDrag = UpdateTextureTranslate; //piviot icon centerpoint = new MyButton("X", Color.cyan, bottomPanel.transform, Vector3.zero, new Vector2(20, 20)); centerpoint.button.onClick.AddListener(() => { }); centerpoint.button.OnPointerUpAction = UpdateCenterpoint; MouseEvent ce = centerpoint.button.gameObject.AddComponent(); ce.MouseDown = (e) => { IsMouseDownCE = true; }; ce.MouseUp = (e) => { IsMouseDownCE = false; }; ce.MouseEnter = (e) => { IsMouseInCE = true; }; ce.MouseExit = (e) => { IsMouseInCE = false; }; ce.MouseDrag = MoveCenterpointButton; MyButton resetButton = new MyButton("Reset", Color.red, transform, new Vector3(-450, -600, 0), new Vector2(100, 50)); resetButton.button.onClick.AddListener(Reset); MyButton saveButton = new MyButton("Export", Color.green, transform, new Vector3(300, -600, 0), new Vector2(100, 50)); saveButton.button.onClick.AddListener(() => { SuperController.singleton.fileBrowserUI.defaultPath = "Custom/Atom/Person/Textures"; SuperController.singleton.fileBrowserUI.showFiles = true; SuperController.singleton.fileBrowserUI.showDirs = true; SuperController.singleton.fileBrowserUI.SetTextEntry(true); SuperController.singleton.fileBrowserUI.hideExtension = true; SuperController.singleton.fileBrowserUI.shortCuts = new List(); SuperController.singleton.fileBrowserUI.Show(SaveImage); }); MyButton closeButton = new MyButton("Close", Color.yellow, transform, new Vector3(450, -600, 0), new Vector2(100, 50)); closeButton.button.onClick.AddListener(() => { gameObject.SetActive(false); }); locationTextPanel = new TextPanel(gameObject, new Vector3(0, -600, 0), "UV location") { text = "0,0", color = Converters.RGBIntToFloat(236, 236, 19), fontSize = 30, fontStyle = FontStyle.Bold, alignment = TextAnchor.MiddleCenter, sizeDelta = new Vector2(600, 50) }; PanelBase topPanel = new PanelBase(DM); topPanel.transform.SetParent(transform, false); topPanel.transform.localPosition = new Vector3(0, 550, 0); topPanel.SetLayout(1048 / 2, 95); topPanel.CreatePanelBackground(1078, 200, Converters.RGBIntToFloat(105, 19, 134)); GridLayoutGroup glg = topPanel.gameObject.GetComponent(); glg.constraintCount = 2; regionJSSC = new JSONStorableStringChooser("Body Part", BodyRegionEnum.Values, string.Empty, "Select Body Part", RegionSelectionChange); UIDynamicPopup dynamicPopupType = UIWidgets.CreatePopup(DM, topPanel.transform, regionJSSC); decalJSSC = new JSONStorableStringChooser("Decal", DecalChoices, string.Empty, "Select Decal To Edit", DecalSelectionChange); UIDynamicPopup dynamicDecalType = UIWidgets.CreatePopup(DM, topPanel.transform, decalJSSC); rotationJSF = new JSONStorableFloat("Rotation", 0, RotationChanged, -360, 360); UIDynamicSlider UISliderRot = UIWidgets.CreateSlider(DM, topPanel.transform, rotationJSF, -360, 360, 0, "Rotation", 1); UISliderRot.labelText.transform.localPosition = new Vector3(0, -20, 0); scaleJSF = new JSONStorableFloat("Scale", 1, ScaleChanged, -10, 10, false); UIDynamicSlider UISliderScale = UIWidgets.CreateSlider(DM, topPanel.transform, scaleJSF, -10, 10, 1, "Scale", 1); UISliderScale.labelText.transform.localPosition = new Vector3(0, -20, 0); RegisterForCoreEvents(CoreEvents); regionJSSC.SetVal(BodyRegionEnum.Torso); } private void Reset() { Vector4 m = new Vector4(0, 0, 0.5f, 0.5f); if (currentDecal != null) { currentDecal.TextureScaleJSF.SetValToDefault(); currentDecal.TextureRotationJSF.SetValToDefault(); currentDecal.TextureRotationJSF.SetValToDefault(); currentDecal.TextureScaleJSF.SetValToDefault(); currentDecal.TextureTranAndCenter = m; LocationText = "0,0"; centerpoint.transform.localPosition = Vector2.zero; } else { bottomPanel.material.SetFloat("_DecalScale0", 1); bottomPanel.material.SetFloat("_DecalRot0", 0); bottomPanel.material.SetVector("_DecalTrans0", m); LocationText = "0,0"; rotationJSF.SetValToDefault(); scaleJSF.SetValToDefault(); centerpoint.transform.localPosition = Vector2.zero; } } private void Sync() { if (currentDecal != null) { bottomPanel.material.SetTexture("_DecalTex0", currentDecal.ImagePanel.mainTexture); bottomPanel.material.SetColor("_DecalColor0", currentDecal.ImagePanel.color); bottomPanel.material.SetFloat("_DecalScale0", currentDecal.TextureScaleJSF.val); bottomPanel.material.SetFloat("_DecalRot0", currentDecal.TextureRotationJSF.val); Vector4 m = currentDecal.TextureTranAndCenter; bottomPanel.material.SetVector("_DecalTrans0", m); LocationText = string.Format("{0:f3},{1:f3}", m.x, m.y); } else { bottomPanel.material.SetTexture("_DecalTex0", null); bottomPanel.material.SetFloat("_DecalScale0", 1); bottomPanel.material.SetFloat("_DecalRot0", 0); Vector4 m = new Vector4(0, 0, 0.5f, 0.5f); bottomPanel.material.SetVector("_DecalTrans0", m); LocationText = "0,0"; rotationJSF.SetValToDefault(); scaleJSF.SetValToDefault(); centerpoint.transform.localPosition = Vector2.zero; } } private void ScaleChanged(float value) { if (currentDecal != null) { currentDecal.TextureScaleJSF.SetVal(value); } } private void RotationChanged(float value) { if (currentDecal != null) { currentDecal.TextureRotationJSF.SetVal(value); } } private void UpdateDecalChooser(string s = BodyRegionEnum.Torso) { ManagerPanel manager; DM._headerPanel.SelectionPanelDecal.ManagerPanels.TryGetValue(s, out manager); if (manager == null) DM._headerPanel.SelectionPanelDecalEye.ManagerPanels.TryGetValue(s, out manager); if (manager != null) { DecalPanels = manager.DecalPanels; DecalChoices = new List(); foreach (DecalPanel d in manager.DecalPanels) { //Debug.Log("Panels added " + d.RandomName); DecalChoices.Add(d.RandomName); } decalJSSC.choices = null; decalJSSC.choices = DecalChoices; Sync(); } } private void RegionSelectionChange(string s) { UpdateDecalChooser(s); //select first decal if available string start = decalJSSC.choices.FirstOrDefault(); decalJSSC.SetVal(start); int id = TextureIndex.GetFirstTextureID(s, DM._isMale); Texture texture = DM._dazSkin.GPUmaterials[id].GetTexture(MatSlotEnum.MainTex); bottomPanel.material.SetTexture("_MainTex", texture); bottomPanel.gameObject.SetActive(false); bottomPanel.gameObject.SetActive(true); //update the DecalSelection } private void DecalSelectionChange(string s) { currentDecal = null; for (int i = 0; i < DecalChoices.Count; i++) { if (DecalChoices[i] == s) { currentDecal = DecalPanels[i]; break; } } if (currentDecal != null) { Sync(); rotationJSF.SetVal(currentDecal.TextureRotationJSF.val); scaleJSF.SetVal(currentDecal.TextureScaleJSF.val); } else { Reset(); //reset panel bottomPanel.material.SetTexture("_DecalTex0", null); bottomPanel.material.SetColor("_DecalColor0", Color.clear); currentDecal = null; } } private Vector2 CalculateUVTranslate() { Vector2 vector2 = CalculateUVPos(); //apply offset to move 0,0 lower left to 0,0 in center vector2.x = vector2.x - 0.5f; vector2.y = vector2.y - 0.5f; return vector2; } private Vector2 CalculateUVPos() { Texture2D tex = (Texture2D)bottomPanel.material.mainTexture; RectTransform rct = bottomPanel.gameObject.GetComponent(); Rect r = rct.rect; Vector2 pos; RectTransformUtility.ScreenPointToLocalPointInRectangle(rct, Input.mousePosition, Camera.main, out pos); int px = Mathf.Clamp(0, (int)(((pos.x - r.x) * tex.width) / r.width), tex.width); int py = Mathf.Clamp(0, (int)(((pos.y - r.y) * tex.height) / r.height), tex.height); float U = px / (float)tex.width; float V = py / (float)tex.height; return new Vector2(U, V); } private void UpdateTextureTranslate(PointerEventData e) { if (IsMouseDown && IsMouseIn && !IsMouseDownCE && !IsMouseInCE) { //Get how far mouse has moved and add that to the UV translation Vector2 currentUV = CalculateUVTranslate(); Vector2 mouseDelta = currentUV - lastMouseUVPosition; lastMouseUVPosition = currentUV; if (currentDecal != null) { float rotation = currentDecal.TextureRotationJSF.val; float scale = currentDecal.TextureScaleJSF.val; Vector2 initial = currentDecal.TextureTranslate; //ChatGPT Keeps mouse 1:1 whith the scaled UV // Calculate the inverse of the transformation matrix Matrix4x4 matrix = Matrix4x4.TRS(initial, Quaternion.AngleAxis(rotation, Vector3.forward), Vector3.one / scale); // Apply the inverse matrix to the mouse delta to make it proportional to the original texture size Vector3 TRSDelta = matrix * new Vector3(mouseDelta.x, mouseDelta.y, 0); // Calculate the new texture position using the transformed mouse delta Vector2 newPosition = initial - new Vector2(TRSDelta.x, TRSDelta.y); currentDecal.TextureTranslate = newPosition; } } } private void MoveCenterpointButton(PointerEventData e) { if (IsMouseDownCE && (IsMouseInCE || IsMouseIn)) { Vector2 pos; RectTransformUtility.ScreenPointToLocalPointInRectangle(bottomPanel.transform as RectTransform, Input.mousePosition, SuperController.singleton.MonitorCenterCamera, out pos); centerpoint.transform.localPosition = pos; } } private void UpdateCenterpoint() { if (IsMouseDownCE && (IsMouseInCE || IsMouseIn)) { if (currentDecal != null) { Vector4 m = currentDecal.TextureTranAndCenter; Vector2 UV = CalculateUVPos(); m.z = UV.x; m.w = UV.y; // Debug.Log(string.Format("x={0:f3} y={1:f3} z={2:f3} w={3:f3} Dx{4:f3} Dy{5:f3}", m.x, m.y, UV.x, UV.y, TRSDelta.x, TRSDelta.y)); currentDecal.TextureTranAndCenter = m; } } } private void SaveGPUtex(Texture2D tempTex, string path) { DM.ToggleNipsCallBack(true); DM.ToggleGensCallBack(true); DM.SaveImageToFile(tempTex, path); } private void SaveImage(string path) { if (currentDecal == null) return; RenderPanel.SetGPUTexDelegate = ((Texture2D tempTex, string MaterialSlot, string TextureSlot) => { if (!string.IsNullOrEmpty(path)) { SaveGPUtex(tempTex, path); } }); RenderPanel.DecalPanels = new List { //HINT: If you add the other from the DecalPanels list you would get the blended output. currentDecal }; DM.ToggleNipsCallBack(false); DM.ToggleGensCallBack(false); DM.StartCoroutine(RenderPanel.ApplyChanges()); } protected override void Update() { } private void CoreEvents(object sender, PanelEventArgs e) { //Debug.Log(DateTime.Now + " " + e.EventName); switch (e.EventName) { case EventEnum.DecalPanelTextureMove: if (currentDecal == e.DecalPanel) { Sync(); } break; case EventEnum.ManagerPanelButtonCLOSE: case EventEnum.ManagerPanelButtonADD: case EventEnum.DecalPanelMove: case EventEnum.DecalPanelDELETE: case EventEnum.DecalPanelButtonAdd: case EventEnum.DecalPanelButtonCLOSE: case EventEnum.DecalPanelButtonCOPY: case EventEnum.DecalPanelButtonDOWN: if (e.DecalPanel != null && !string.IsNullOrEmpty(e.DecalPanel.TextureSlot)) { UpdateDecalChooser(e.DecalPanel.TextureSlot); } break; } } public override void OnDestroy() { UnRegisterForCoreEvents(CoreEvents); base.OnDestroy(); } } public class MouseEvent : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler { public Action MouseDown; public Action MouseUp; public Action MouseEnter; public Action MouseExit; public Action MouseDrag; public void OnDrag(PointerEventData eventData) { MouseDrag?.Invoke(eventData); } public void OnPointerDown(PointerEventData eventData) { //Debug.Log("StartDrag"); MouseDown?.Invoke(eventData); } public void OnPointerEnter(PointerEventData eventData) { //Debug.Log("Enter"); MouseEnter?.Invoke(eventData); } public void OnPointerExit(PointerEventData eventData) { //Debug.Log("Exit"); MouseExit?.Invoke(eventData); } public void OnPointerUp(PointerEventData eventData) { //Debug.Log("StopDrag"); MouseUp?.Invoke(eventData); } } } //*** SourceCombiner -> original file GenitalPanel.cs *** namespace VAM_Decal_Maker { public class GenitalPanel : PanelBase { private ImagePanelBase bottomPanel; private JSONStorableStringChooser regionJSSC; private JSONStorableStringChooser decalJSSC; private JSONStorableFloat hsvHueJSF; private JSONStorableFloat hsvSatJSF; private JSONStorableFloat hsvValJSF; private RenderPanelGenital _renderCPU; private bool Initialized = false; private RenderPanelGenital RenderCPU { get { if (_renderCPU == null) _renderCPU = new RenderPanelGenital(DM, MatSlotEnum.DecalTex, BodyRegionEnum.Genitals, bottomPanel.material); return _renderCPU; } } private List DecalChoices = new List(); private List GenitalAreas = new List() { "base", "labia", "anus", "hood" }; private DecalPanel currentDecal; public GenitalPanel(Decal_Maker DM) : base(DM) { CreatePanelBackground(1078, 1300, Converters.RGBIntToFloat(105, 19, 134)); gameObject.transform.SetParent(DM.UITransform, false); gameObject.transform.localPosition = new Vector3(0, 0, 0); bottomPanel = new ImagePanelBase(DM, MatSlotEnum.MainTex, BodyRegionEnum.Torso, false, false); bottomPanel.transform.SetParent(transform, false); bottomPanel.transform.localPosition = new Vector3(0, -50, 0); bottomPanel.material = new Material(DM._customGenitalMakerShaderUI); RectTransform rct = bottomPanel.gameObject.GetComponent(); rct.sizeDelta = new Vector2(1078, 1000); //15,2,0,28 bottomPanel.material.SetTexture("_TorsoTex", DM._dazSkin.GPUmaterials[15].GetTexture(MatSlotEnum.MainTex)); MyButton saveButton = new MyButton("Export", Color.green, transform, new Vector3(300, -600, 0), new Vector2(100, 50)); saveButton.button.onClick.AddListener(() => { SuperController.singleton.fileBrowserUI.defaultPath = "Custom/Atom/Person/Textures"; SuperController.singleton.fileBrowserUI.showFiles = true; SuperController.singleton.fileBrowserUI.showDirs = true; SuperController.singleton.fileBrowserUI.SetTextEntry(true); SuperController.singleton.fileBrowserUI.hideExtension = true; SuperController.singleton.fileBrowserUI.shortCuts = new List(); SuperController.singleton.fileBrowserUI.Show(SaveImage); }); MyButton closeButton = new MyButton("Close", Color.yellow, transform, new Vector3(450, -600, 0), new Vector2(100, 50)); closeButton.button.onClick.AddListener(() => { gameObject.SetActive(false); }); PanelBase topPanel = new PanelBase(DM); topPanel.transform.SetParent(transform, false); topPanel.transform.localPosition = new Vector3(0, 550, 0); topPanel.SetLayout(1048 / 2, 95); topPanel.CreatePanelBackground(1078, 200, Converters.RGBIntToFloat(105, 19, 134)); GridLayoutGroup glg = topPanel.gameObject.GetComponent(); glg.constraintCount = 3; glg.startCorner = GridLayoutGroup.Corner.UpperRight; hsvHueJSF = new JSONStorableFloat("Hue", 0, BaseColor, 0, 1); UIDynamicSlider HueSlider = UIWidgets.CreateSlider(DM, topPanel.transform, hsvHueJSF, 0, 1, 0, "Hue", 1); HueSlider.labelText.transform.localPosition = new Vector3(0, -20, 0); regionJSSC = new JSONStorableStringChooser("Body Part", GenitalAreas, "base", "Select Part", RegionSelectionChange); UIDynamicPopup dynamicPopupType = UIWidgets.CreatePopup(DM, topPanel.transform, regionJSSC); hsvSatJSF = new JSONStorableFloat("Sat", 0, BaseColor, 0, 1); UIDynamicSlider SatSlider = UIWidgets.CreateSlider(DM, topPanel.transform, hsvSatJSF, 0, 1, 0, "Sat", 1); SatSlider.labelText.transform.localPosition = new Vector3(0, -20, 0); decalJSSC = new JSONStorableStringChooser("Decal", DecalChoices, DecalChoices, string.Empty, "Select Decal To Edit", DecalSelectionChange); UIDynamicPopup dynamicDecalType = UIWidgets.CreatePopup(DM, topPanel.transform, decalJSSC); hsvValJSF = new JSONStorableFloat("Val", 1, BaseColor, 0, 1); UIDynamicSlider ValSlider = UIWidgets.CreateSlider(DM, topPanel.transform, hsvValJSF, 0, 1, 1, "Val", 1); ValSlider.labelText.transform.localPosition = new Vector3(0, -20, 0); Init(); } private void Init() { if (DM._isMale) { bottomPanel.material.SetTexture("_GenitalTex", DM.GetResource("Custom/Atom/Person/Textures/MaleBase/genitalsD.png")); } else { bottomPanel.material.SetTexture("_GenitalTex", DM.GetResource("Custom/Atom/Person/Textures/GenitalMaker/base/base1.png")); bottomPanel.material.SetTexture("_HoodTex", DM.GetResource("Custom/Atom/Person/Textures/GenitalMaker/hood/Genital_Base_Hood.png")); bottomPanel.material.SetTexture("_LabiaTex", DM.GetResource("Custom/Atom/Person/Textures/GenitalMaker/labia/MonsterShinkai_1_KayGensD_Labia.png")); bottomPanel.material.SetTexture("_AnusTex", DM.GetResource("Custom/Atom/Person/Textures/GenitalMaker/anus/Asian_Big.png")); Initialized = true; } } public void Show() { int id = TextureIndex.GetFirstTextureID(BodyRegionEnum.Torso, DM._isMale); bottomPanel.material.SetTexture("_MainTex", DM.GetCurrentGPUTexture(id, MatSlotEnum.MainTex)); if (!Initialized) { Init(); } gameObject.SetActive(true); } private void BaseColor(float f) { float H = hsvHueJSF.val; float S = hsvSatJSF.val; float V = hsvValJSF.val; Color color = Color.HSVToRGB(H, S, V); bottomPanel.material.SetColor("_GenitalColor", color); RenderCPU.IsDirty = true; } private void DecalSelectionChange(string decal) { string region = regionJSSC.val; //Debug.Log("Que inage " + decal + " " + region); LoadImage(decal, region); } private void RegionSelectionChange(string region) { UpdateTexList(region); //select first decal if available string start = decalJSSC.choices.FirstOrDefault(); decalJSSC.SetVal(start); bottomPanel.gameObject.SetActive(false); bottomPanel.gameObject.SetActive(true); //update the DecalSelection } private void UpdateTexList(string choice) { string path = PathHelper.GetPackagePath(DM) + "Custom/Atom/Person/Textures/GenitalMaker/" + choice; //Debug.Log(path + " " + FileManagerSecure.DirectoryExists(path)); if (FileManagerSecure.DirectoryExists(path)) { string[] files = FileManagerSecure.GetFiles(path); decalJSSC.choices = files.ToList(); List list = new List(); foreach (string file in files) { list.Add(FileManagerSecure.GetFileName(file)); } decalJSSC.displayChoices = list; } } private void LoadImage(string filePath, string region) { GenQueuedImage queuedImage = new GenQueuedImage(); queuedImage.region = region; queuedImage.imgPath = filePath; queuedImage.forceReload = false; queuedImage.createMipMaps = false; queuedImage.isNormalMap = IsNormalMap; queuedImage.isThumbnail = false; queuedImage.linear = linear; queuedImage.createAlphaFromGrayscale = false; queuedImage.compress = false; if (!IsNormalMap) { queuedImage.compress = true; } queuedImage.callback = new ImageLoaderThreaded.ImageLoaderCallback(OnImageLoaded); // = new ImageLoaderThreaded.ImageLoaderCallback(OnImageLoaded); //start image load ImageLoaderThreaded.singleton.QueueImage(queuedImage); } private void OnImageLoaded(ImageLoaderThreaded.QueuedImage qi) { GenQueuedImage gqi = (GenQueuedImage)qi; string region = gqi.region; switch (region) { case "base": bottomPanel.material.SetTexture("_GenitalTex", gqi.tex); break; case "anus": bottomPanel.material.SetTexture("_AnusTex", gqi.tex); bottomPanel.material.SetColor("_AnusColor", Color.white); break; case "hood": bottomPanel.material.SetTexture("_HoodTex", gqi.tex); bottomPanel.material.SetColor("_HoodColor", Color.white); break; case "labia": bottomPanel.material.SetTexture("_LabiaTex", gqi.tex); bottomPanel.material.SetColor("_LabiaColor", Color.white); break; } RenderCPU.IsDirty = true; bottomPanel.gameObject.SetActive(false); bottomPanel.gameObject.SetActive(true); } public override void OnDestroy() { base.OnDestroy(); } private void SaveGPUtex(Texture2D tempTex, string path) { DM.ToggleGensCallBack(true); DM.SaveImageToFile(tempTex, path); RenderCPU.ResetDelegate(); } private void SaveImage(string path) { RenderCPU.SetGPUTexDelegate = ((Texture2D tempTex, string MaterialSlot, string TextureSlot) => { if (!string.IsNullOrEmpty(path)) { SaveGPUtex(tempTex, path); } }); RenderCPU.DecalPanels = new List { //HINT: If you add the DecalPanels list you would get the blended output. currentDecal }; DM.ToggleNipsCallBack(false); DM.ToggleGensCallBack(false); DM.StartCoroutine(RenderCPU.ApplyChanges()); } } public class GenQueuedImage : ImageLoaderThreaded.QueuedImage { public string region; public string decal; } } //*** SourceCombiner -> original file Converters.cs *** namespace VAM_Decal_Maker { public static class Converters { public static Color RGBIntToFloat(int r, int g, int b) { float R = (float)r / 255; float G = (float)g / 255; float B = (float)b / 255; return new Color(R, G, B, 1); } public static Color HSVToRGB(HSVColor hsv) { return Color.HSVToRGB(hsv.H, hsv.S, hsv.V); } public static Color HSVToRGB(JSONStorableColor HSVToRGB) { HSVColor hsv = HSVToRGB.val; return Color.HSVToRGB(hsv.H, hsv.S, hsv.V); } //need to turn our texture and mat enums into VAM equiv strings public static string FakeEnumToVAMName(string fakeEnum) { switch (fakeEnum) { default: case MatSlotEnum.DecalTex: return "Decal"; case MatSlotEnum.MainTex: return "Diffuse"; case MatSlotEnum.SpecTex: return "Specular"; case MatSlotEnum.GlossTex: return "Gloss"; case MatSlotEnum.BumpMap: return "Normal"; case BodyRegionEnum.Face: return "face"; case BodyRegionEnum.Limbs: return "limbs"; case BodyRegionEnum.Torso: return "torso"; case BodyRegionEnum.Genitals: return "genitals"; } } } } //*** SourceCombiner -> original file Locaization.cs *** namespace VAM_Decal_Maker.Helpers { public static class Localization { private static CultureInfo ci = CultureInfo.InstalledUICulture; public static string Lcs(string eng) { string locale = ci.Name; Dictionary strings; locales.TryGetValue(locale, out strings); strings?.TryGetValue(eng, out eng); return eng; } private readonly static Dictionary> locales = new Dictionary> { { "en-US", en_EN } }; private static readonly Dictionary en_EN = new Dictionary() { { "Please add this plugin to a PERSON atom.", "Please add this plugin to a PERSON atom." }, { "Clear Everything", "Clear Everything"}, { "USE Prefab", "USE Prefab"}, { "Reset Texture", "Reset Texture"}, { "Edit Textures","Edit Textures"}, { "Load Preset", "Load Preset"}, { "Save Preset", "Save Preset"}, }; } } //*** SourceCombiner -> original file RenderPanelCPU.cs *** namespace VAM_Decal_Maker { public class RenderPanelCPU : RenderPanelDecal { public RenderPanelCPU(Decal_Maker DM, string MaterialSlot, string TextureSlot) : base(DM, MaterialSlot, TextureSlot) { } //Use Slow CPU blend to allow texture saving. public override bool GpuCombine(Texture baseTex, Material material, bool linear = false) { int w = baseTex.width; int h = baseTex.height; RenderTextureReadWrite renderTexConversion = RenderTextureReadWrite.sRGB; if (linear) { renderTexConversion = RenderTextureReadWrite.Linear; } RenderTexture tmp = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGB32, renderTexConversion); //tmp.antiAliasing = 8; //Backup the currently set RenderTexture RenderTexture previous = RenderTexture.active; Graphics.Blit(baseTex, tmp, material); RenderTexture.active = tmp; tempTexture.ReadPixels(new Rect(0, 0, w, h), 0, 0, false); tempTexture.Apply(); // Reset the active RenderTexture RenderTexture.active = previous; RenderTexture.ReleaseTemporary(tmp); return true; } } } //*** SourceCombiner -> original file TextureIndex.cs *** namespace VAM_Decal_Maker { internal static class TextureIndex { private static readonly Dictionary femaleTextureIndex = new Dictionary() { {BodyRegionEnum.Torso, new int[]{15,18,19,20,21,27,29}}, {BodyRegionEnum.Face, new int[]{2,5,11} }, {BodyRegionEnum.Limbs, new int[]{0,12,14,16,17,22,23}}, {BodyRegionEnum.Genitals, new int[]{28}}, {BodyRegionEnum.EyeReflection, new int[]{1}}, {BodyRegionEnum.EyePupil, new int[]{4}}, {BodyRegionEnum.EyeIris, new int[]{8}}, {BodyRegionEnum.EyeCornea, new int[]{10}}, {BodyRegionEnum.EyeSclera, new int[]{13}} }; private static readonly Dictionary maleTextureIndex = new Dictionary() { {BodyRegionEnum.Torso, new int[]{15,16,19,20,21,22,30,32}}, {BodyRegionEnum.Face, new int[]{2,5,11}}, {BodyRegionEnum.Limbs, new int[]{0,12,14,17,18,23,24}}, {BodyRegionEnum.Genitals, new int[]{28,29}}, {BodyRegionEnum.EyeReflection, new int[]{1}}, {BodyRegionEnum.EyePupil, new int[]{4}}, {BodyRegionEnum.EyeIris, new int[]{8}}, {BodyRegionEnum.EyeCornea, new int[]{10}}, {BodyRegionEnum.EyeSclera, new int[]{13}} }; public static Dictionary GetTexureDictionary(bool IsMale) { if (IsMale) return maleTextureIndex; return femaleTextureIndex; } public static int[] GetTextureIDs(string region, bool IsMale) { if (IsMale) return maleTextureIndex[region]; return femaleTextureIndex[region]; } public static int GetFirstTextureID(string region, bool IsMale) { if (IsMale) return maleTextureIndex[region][0]; return femaleTextureIndex[region][0]; } } } //*** SourceCombiner -> original file RenderPanelGenital.cs *** namespace VAM_Decal_Maker { public class RenderPanelGenital : RenderPanelCPU { private Material SourceMaterial; public RenderPanelGenital(Decal_Maker DM, string MaterialSlot, string TextureSlot, Material mat) : base(DM, MaterialSlot, TextureSlot) { material = mat; } public void ResetDelegate() { SetGPUTexDelegate = SetGPUTexture; } public override IEnumerator ApplyChanges() { //ZeroMaterial(); bool linear = false; yield return GpuCombine(clearTex, material, linear); SetGPUTexDelegate(tempTexture, MaterialSlot, TextureSlot); //Only un-flag IsDirty if no updates happened while processing. if (dirtyTimeStart == dirtyTime) { IsDirty = false; } Processing = false; } } } //*** SourceCombiner -> original file UIWidgets.cs *** namespace VAM_Decal_Maker { public static class UIWidgets { public static UIDynamicPopup CreatePopup(Decal_Maker DM, Transform parent, JSONStorableStringChooser jsc = null) { Transform popupPrefabType = GameObject.Instantiate(DM.manager.configurableScrollablePopupPrefab); popupPrefabType.SetParent(parent, false); popupPrefabType.localPosition = Vector3.zero; UIDynamicPopup dynamicPopupType = popupPrefabType.GetComponent(); dynamicPopupType.name = "dynamicPop"; if (jsc != null) { jsc.popup = dynamicPopupType.popup; } return dynamicPopupType; } public static UIDynamicColorPicker CreateColorPicker(Decal_Maker DM, Transform parent, JSONStorableColor jcolor, bool isStorable = false) { Transform colorPickerPrefab = GameObject.Instantiate(DM.manager.configurableColorPickerPrefab); colorPickerPrefab.SetParent(parent, false); colorPickerPrefab.localPosition = Vector3.zero; UIDynamicColorPicker colorPickerDynamic = colorPickerPrefab.GetComponent(); colorPickerDynamic.showLabel = false; jcolor.colorPicker = colorPickerDynamic.colorPicker; if (isStorable) jcolor.isStorable = false; return colorPickerDynamic; } public static UIDynamicSlider CreateSlider(Decal_Maker DM, Transform parent, JSONStorableFloat action, int min = 0, int max = 1, int startValue = 1, string title = "", float sliderAlpha = 0.1f) { Transform sliderPrefab = GameObject.Instantiate(DM.manager.configurableSliderPrefab); sliderPrefab.SetParent(parent, false); sliderPrefab.localPosition = Vector3.zero; UIDynamicSlider sliderDynamic = sliderPrefab.GetComponent(); sliderDynamic.transform.localPosition = new Vector2(0, -80); sliderDynamic.quickButtonsEnabled = false; sliderDynamic.rangeAdjustEnabled = false; sliderDynamic.defaultButtonEnabled = false; Image sliderBackground = sliderDynamic.GetComponentsInChildren().Where(x => x.name == "Panel").First(); Color c = sliderBackground.color; c.a = sliderAlpha; sliderBackground.color = c; action.slider = sliderDynamic.slider; //sliderDynamic.slider.onValueChanged.AddListener(action); sliderDynamic.slider.minValue = min; sliderDynamic.slider.maxValue = max; sliderDynamic.slider.value = startValue; sliderDynamic.label = title; sliderDynamic.labelText.transform.localPosition += new Vector3(400, 0, 0); RectTransform fill = sliderDynamic.slider.fillRect; fill.sizeDelta += new Vector2(0, 20); return sliderDynamic; } public static UIDynamicButton CreateVAMButtom(Decal_Maker DM, Transform parent, string label) { Transform buttonPrefab = GameObject.Instantiate(DM.manager.configurableButtonPrefab.transform); UIDynamicButton uIDynamicButton = buttonPrefab.GetComponent(); if (uIDynamicButton != null) { uIDynamicButton.label = label; } uIDynamicButton.transform.SetParent(parent, false); return uIDynamicButton; } } } //*** SourceCombiner -> original file UserConfirmPanel.cs *** namespace VAM_Decal_Maker { public class UserConfirmPanel : PanelBase { private Text promptText; private Image BackGround; private MyButton confirmButton; private MyButton confirmCleanButton; private MyButton cancelButton; public UserConfirmPanel(Transform parent, Decal_Maker DM) : base(DM) { gameObject.transform.SetParent(parent, false); SetLayout(500, 200); BackGround = CreatePanelBackground(500, 200, Color.white); TextPanel textPanel = new TextPanel(gameObject, new Vector2(40, 40), "User choice Panel") { alignment = TextAnchor.UpperLeft, color = Color.black, fontSize = 30, fontStyle = FontStyle.Bold, text = "The Appearance you just loaded also has DecalMaker settings.\n\nDo you want to apply these Settings to DecalMaker", blocksRaycasts = false, sizeDelta = new Vector2(400, 200), }; confirmButton = new MyButton("Add to Existing\nDecals", Color.green, gameObject.transform); confirmCleanButton = new MyButton("Clear Existing Decals\nThen Add this Preset", Color.yellow, gameObject.transform); cancelButton = new MyButton("Do not Add", Color.red, gameObject.transform); confirmButton.button.onClick.AddListener(confirmButtonClick); confirmCleanButton.button.onClick.AddListener(confirmCleanButtonClick); cancelButton.button.onClick.AddListener(cancelButtonClick); SetActive(false); } private void confirmButtonClick() { DM.CallAction("PerformLoad"); SetActive(false); } private void confirmCleanButtonClick() { DM.CallAction("ClearAll"); DM.CallAction("PerformLoad"); SetActive(false); } private void cancelButtonClick() { DM._savedData = null; SetActive(false); } public void SetPrompt(string prompt) { if (this.promptText != null) { this.promptText.text = prompt; } } public void SetActive(bool active) { gameObject.SetActive(active); } public override void OnDestroy() { base.OnDestroy(); GameObject.Destroy(gameObject); } } } //*** SourceCombiner -> original file DecalPanel.cs *** namespace VAM_Decal_Maker { public class DecalPanel : PanelBase { public ImagePanel ImagePanel { get; set; } public MyButton Up { get; private set; } public MyButton Down { get; private set; } public MyButton Open { get; private set; } public MyButton Close { get; private set; } public MyButton Copy { get; private set; } public JSONStorableColor jcolor { get; private set; } public JSONStorableFloat sliderJSF { get; private set; } public UIDynamicSlider sliderDynamic { get; private set; } public string linkedPanelID { get { return linkJSSC.val; } set { linkJSSC.val = value; } } public Action LastDir; private string _imagePath = ""; public string ImagePathText { get { return _imagePath; } set { _imagePath = value; texturePathTextPanel.text = FormattPathText(value); } } private string _randomName; public string RandomName { get { if (_randomName == null) { return "ERROR!!!"; } return _randomName; } set { if (randomNameTextPanel != null) randomNameTextPanel.text = value; _randomName = value; } } private TextPanel texturePathTextPanel; private TextPanel randomNameTextPanel; private Image panelImage; private static List linkedLayerChoices = new List() { "*", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" }; private JSONStorableAction videoPlayPauseJSA; private JSONStorableFloat videoFrameJSF; private JSONStorableFloat videoTimeJSF; private JSONStorableStringChooser linkJSSC; private JSONStorableUrl imageJSURL; public JSONStorableFloat TextureScaleJSF; public JSONStorableFloat TextureRotationJSF; private JSONStorableFloat TextureTranslateXJSF; private JSONStorableFloat TextureTranslateYJSF; private JSONStorableFloat TextureCenterXJSF; private JSONStorableFloat TextureCenterYJSF; Action SliderAction; public Vector4 TextureTranAndCenter { get { return GetTextureTranslation(); } set { SetTextureTranslation(value); } } public Vector2 TextureCenter { get { return new Vector2(TextureCenterXJSF.val, TextureCenterYJSF.val); } set { SetTextureCenter(value); } } public Vector2 TextureTranslate { get { return new Vector2(TextureTranslateXJSF.val, TextureTranslateYJSF.val); } set { SetTextureTranslate(value); } } private void SetTextureTranslate(Vector2 value) { TextureTranslateXJSF.valNoCallback = value.x; TextureTranslateYJSF.valNoCallback = value.y; JSONStorablesUpdate(0); } private void SetTextureCenter(Vector2 center) { TextureCenterXJSF.valNoCallback = center.x; TextureCenterYJSF.valNoCallback = center.y; JSONStorablesUpdate(0); } private Vector4 GetTextureTranslation() { float x = TextureTranslateXJSF.val; float y = TextureTranslateYJSF.val; float z = TextureCenterXJSF.val; float w = TextureCenterYJSF.val; return new Vector4(x, y, z, w); } private void SetTextureTranslation(Vector4 translation) { TextureTranslateXJSF.valNoCallback = translation.x; TextureTranslateYJSF.valNoCallback = translation.y; TextureCenterXJSF.valNoCallback = translation.z; TextureCenterYJSF.valNoCallback = translation.w; //trigger update JSONStorablesUpdate(0); } private void CoreEvents(object o, PanelEventArgs e) { } //events from image panel public void ImagePanelEventProcessor(object o, PanelEventArgs e) { switch (e.EventName) { case EventEnum.ImagePanelVidePrepared: VideoLoaded(e.videoPlayer); break; case EventEnum.ImagePanelImageLoad: //Deregister video controls if (DM.GetAction(videoPlayPauseJSA.name) != null) { DeregisterAction(videoPlayPauseJSA); DeregisterFloat(videoFrameJSF); DeregisterFloat(videoTimeJSF); } break; case EventEnum.ImagePanelVideoLoad: //register video controls if (DM.GetAction(videoPlayPauseJSA.name) == null) { RegisterAction(videoPlayPauseJSA); RegisterFloat(videoFrameJSF, false); RegisterFloat(videoTimeJSF, false); } break; } if (e.EventName == EventEnum.ImagePanelVidePrepared) { } //add reference to this panel and pass on. Manager Panel handles operations e.DecalPanel = this; RaiseCoreEvent(this, e); } private void JSONStorablesUpdate(float f) { RaiseCoreEvent(this, new PanelEventArgs(EventEnum.DecalPanelTextureMove, this)); } public override void OnDestroy() { UnRegisterForCoreEvents(CoreEvents); ImagePanel.ImagePanelUpdate -= ImagePanelEventProcessor; //ImagePanel.OnDestroy(); base.OnDestroy(); } public DecalPanel(Transform anchor, float width, float height, Color? color, Decal_Maker DM, string RandomName, string MaterialSlot, string TextureSlot, bool IsNormalMap = false, bool linear = false) : base(DM) { gameObject.transform.SetParent(anchor, false); this.MaterialSlot = MaterialSlot; this.TextureSlot = TextureSlot; this.IsNormalMap = IsNormalMap; this.linear = linear; this.RandomName = RandomName; //basePanel panelImage = gameObject.AddComponent(); panelImage.material = new Material(Shader.Find("UI/Default-Overlay")); panelImage.color = color ?? new Color(0, 0, 0, 0f); panelImage.rectTransform.sizeDelta = new Vector2(width, height); //Image panel GameObject imageObject = new GameObject("ImagePanel"); imageObject.transform.SetParent(gameObject.transform, false); ImagePanel = new ImagePanel(DM, null, TextureSlot, MaterialSlot, IsNormalMap, linear); ImagePanel.gameObject.transform.SetParent(gameObject.transform, false); ImagePanel.gameObject.transform.localPosition = new Vector3(panelImage.rectTransform.rect.xMax - ImagePanel.rectTransform.rect.xMax - 10, 0, 0); //register for event, this is the local image panels events ImagePanel.ImagePanelUpdate += ImagePanelEventProcessor; RegisterForCoreEvents(CoreEvents); //create name and slider //must be before text and color selector so it layers properly AddSlider(); GameObject colorObject = new GameObject("ColorPanel"); colorObject.transform.SetParent(gameObject.transform, false); colorObject.transform.localScale = new Vector3(.55f, .55f, .55f); colorObject.transform.localPosition = new Vector2(-350, 50); Image colorImage = colorObject.AddComponent(); colorImage.material = new Material(Shader.Find("UI/Default-Overlay")); colorImage.color = new Color(0, 1, 1, 0f); colorImage.rectTransform.sizeDelta = new Vector2(600, 300); colorImage.transform.localPosition = new Vector2(-350, 50); HSVColor hsvc = HSVColorPicker.RGBToHSV(1f, 1f, 1f); jcolor = new JSONStorableColor(CreateJSN("Color"), hsvc, UnifiedImagePanelColor); UIDynamicColorPicker colorPickerDynamic = UIWidgets.CreateColorPicker(DM, colorImage.transform, jcolor); RegisterColor(jcolor, false); videoPlayPauseJSA = new JSONStorableAction(CreateJSN("Play-Pause"), VideoPlayPause); videoFrameJSF = new JSONStorableFloat(CreateJSN("Step to Frame"), 0, VideoPlaySeekFrame, 0, 0); videoTimeJSF = new JSONStorableFloat(CreateJSN("Step to Time"), 0, VideoPlaySeekTime, 0, 0); //buttons can not parent one another, child buttons set hit detection to parent buttons leading to all being lit //https://answers.unity.com/questions/1783936/a-button-inside-of-a-button-is-highlighting-parent.html Vector2 buttonSize = new Vector2(60, 60); Copy = new MyButton("", new Color(.156f, .746f, .176f), gameObject.transform, new Vector3(-165, 50, 0), buttonSize); Texture2D CopyIcon = DM.GetResource("Custom/Scripts/Chokaphi/VAM_Decal_Maker/Icons/Copy-Icon.jpg"); Copy.SetIcon(CopyIcon); Open = new MyButton("", new Color(.7f, .7f, .7f), gameObject.transform, new Vector3(-100, 50, 0), buttonSize); Texture2D OpenIcon = DM.GetResource("Custom/Scripts/Chokaphi/VAM_Decal_Maker/Icons/Open-Icon.jpg"); Open.SetIcon(OpenIcon); Up = new MyButton("", new Color(0.9f, 0.6f, 0.1f), gameObject.transform, new Vector3(-35, 50, 0), buttonSize); Texture2D UpIcon = DM.GetResource("Custom/Scripts/Chokaphi/VAM_Decal_Maker/Icons/Up-Icon.jpg"); Up.SetIcon(UpIcon); Down = new MyButton("", new Color(0.1f, 0.6f, 0.8f), gameObject.transform, new Vector3(30, 50, 0), buttonSize); Texture2D DownIcon = DM.GetResource("Custom/Scripts/Chokaphi/VAM_Decal_Maker/Icons/Down-Icon.jpg"); Down.SetIcon(DownIcon); Close = new MyButton("", new Color(0.6f, 0, 0, 1), gameObject.transform, new Vector3(180, 50, 0), buttonSize); Texture2D CloseIcon = DM.GetResource("Custom/Scripts/Chokaphi/VAM_Decal_Maker/Icons/Close-Icon.jpg"); Close.SetIcon(CloseIcon); //Panel Name Text randomNameTextPanel = new TextPanel(gameObject, Vector3.zero, "") { text = RandomName, color = new Color(1 - panelImage.color.r, 1 - panelImage.color.g, 1 - panelImage.color.b), fontSize = 60, fontStyle = FontStyle.Bold, alignment = TextAnchor.UpperCenter, sizeDelta = new Vector2(width, height), }; //Texture Path Text texturePathTextPanel = new TextPanel(gameObject, new Vector2(40, 40), "Path Panel") { alignment = TextAnchor.LowerLeft, color = Color.black, fontSize = 30, fontStyle = FontStyle.Bold, text = "Click White Square to select image.-->", blocksRaycasts = false, sizeDelta = new Vector2(width, height), }; Up.button.onClick.AddListener(() => { RaiseCoreEvent(this, new PanelEventArgs(EventEnum.DecalPanelButtonUP, this)); }); Down.button.onClick.AddListener(() => { RaiseCoreEvent(this, new PanelEventArgs(EventEnum.DecalPanelButtonDOWN, this)); }); Close.button.onClick.AddListener(() => { RaiseCoreEvent(this, new PanelEventArgs(EventEnum.DecalPanelButtonCLOSE, this)); }); Open.button.onClick.AddListener(() => { RaiseCoreEvent(this, new PanelEventArgs(EventEnum.DecalPanelButtonAdd, this)); }); Copy.button.onClick.AddListener(() => { RaiseCoreEvent(this, new PanelEventArgs(EventEnum.DecalPanelButtonCOPY, this)); }); linkJSSC = new JSONStorableStringChooser("LinkpanelSelection", linkedLayerChoices, "*", "", (string x) => { RaiseCoreEvent(this, new PanelEventArgs(EventEnum.DecalPanelLinkChanged, this)); }); PopupPanel popupPanel = new PopupPanel(DM, linkJSSC); popupPanel.transform.SetParent(transform, false); popupPanel.transform.localPosition = new Vector2(95, 50); imageJSURL = new JSONStorableUrl(CreateJSN("ReplaceImage"), string.Empty, (string path) => { RestoreImageFromJSON(path); }); TextureScaleJSF = new JSONStorableFloat(CreateJSN("texScale"), 1, JSONStorablesUpdate, -10, 10, false); TextureRotationJSF = new JSONStorableFloat(CreateJSN("texRot"), 0, JSONStorablesUpdate, -360, 360, false); TextureTranslateXJSF = new JSONStorableFloat(CreateJSN("textTransX"), 0, JSONStorablesUpdate, -2, 2, false); TextureTranslateYJSF = new JSONStorableFloat(CreateJSN("textTransY"), 0, JSONStorablesUpdate, -2, 2, false); TextureCenterXJSF = new JSONStorableFloat(CreateJSN("textCnterX"), 0.5f, JSONStorablesUpdate, 0, 1, false); TextureCenterYJSF = new JSONStorableFloat(CreateJSN("textCnterY"), 0.5f, JSONStorablesUpdate, 0, 1, false); RegisterUrl(imageJSURL, false); RegisterFloat(TextureScaleJSF, false); RegisterFloat(TextureRotationJSF, false); RegisterFloat(TextureTranslateXJSF, false); RegisterFloat(TextureTranslateYJSF, false); } //copy source to target, name remains unique public void CopyDataToTargetPanel(DecalPanel target, bool fullCopy = true) { target.linkJSSC.SetVal(this.linkJSSC.val); target.jcolor.valNoCallback = this.jcolor.val; target.sliderDynamic.slider.value = this.sliderDynamic.slider.value; if (fullCopy) { target.BrowserCallBack(this.ImagePathText); target.TextureScaleJSF.valNoCallback = TextureScaleJSF.val; target.TextureRotationJSF.valNoCallback = TextureRotationJSF.val; target.TextureTranslateXJSF.valNoCallback = TextureTranslateXJSF.val; target.TextureTranslateYJSF.valNoCallback = TextureTranslateYJSF.val; target.TextureCenterXJSF.valNoCallback = TextureCenterXJSF.val; target.TextureCenterYJSF.valNoCallback = TextureCenterYJSF.val; } } public void CopyDataFromTargetPanel(DecalPanel target, bool fullCopy = true) { this.linkJSSC.SetVal(target.linkJSSC.val); this.jcolor.valNoCallback = target.jcolor.val; this.sliderJSF.valNoCallback = target.sliderJSF.val; ImagePanel.SetMaterialColor(Converters.HSVToRGB(jcolor.val), false); SliderAction(target.sliderJSF.val, false); if (fullCopy) { BrowserCallBack(target.ImagePathText); TextureScaleJSF.valNoCallback = target.TextureScaleJSF.val; TextureRotationJSF.valNoCallback = target.TextureRotationJSF.val; TextureTranslateXJSF.valNoCallback = target.TextureTranslateXJSF.val; TextureTranslateYJSF.valNoCallback = target.TextureTranslateYJSF.val; TextureCenterXJSF.valNoCallback = target.TextureCenterXJSF.val; TextureCenterYJSF.valNoCallback = target.TextureCenterYJSF.val; } } #region Video public void VideoLoaded(VideoPlayer videoPlayer) { try { ulong frames = videoPlayer.frameCount; double time = frames / videoPlayer.frameRate; float seconds = (float)time; videoFrameJSF.max = frames; videoTimeJSF.max = seconds; } catch (Exception e) { SuperController.LogError(e.Message); } } private void VideoPlaySeekTime(float seconds) { ImagePanel.videoPlayer.time = seconds; } private void VideoPlaySeekFrame(float frame) { int f = Mathf.RoundToInt(frame); ImagePanel.videoPlayer.frame = f; } private void VideoPlayPause() { if (ImagePanel.videoPlayer.isPlaying) { ImagePanel.videoPlayer.Pause(); } else { ImagePanel.videoPlayer.Play(); } } private string CreateJSN(string prefix = null) { if (string.IsNullOrEmpty(prefix)) return string.Format("{0}{1}{2}", TextureSlot, MaterialSlot, RandomName); return string.Format("{0}_{1}{2}{3}", prefix, TextureSlot, MaterialSlot, RandomName); } #endregion private void AddSlider() { if (IsNormalMap) { SliderAction = UnifiedImagePanelNormal; sliderJSF = new JSONStorableFloat(CreateJSN("Strength"), 1, (float f)=> { SliderAction(f, true); }, -10, 10); sliderDynamic = UIWidgets.CreateSlider(DM, gameObject.transform, sliderJSF, -10, 10, 1, "Normal Strength"); } else if (linear) { SliderAction = UnifiedImagePanelSpec; sliderJSF = new JSONStorableFloat(CreateJSN("Strength"), 1, (float f) => { SliderAction(f, true); }, -10, 10); sliderDynamic = UIWidgets.CreateSlider(DM, gameObject.transform, sliderJSF, -10, 10, 1, "Spec/Gloss Strength"); } else { SliderAction = UnifiedImagePanelColor; sliderJSF = new JSONStorableFloat(CreateJSN("Alpha"), 1, (float f) => { SliderAction(f, true); }, 0, 1); sliderDynamic = UIWidgets.CreateSlider(DM, gameObject.transform, sliderJSF, 0, 1, 1, "Alpha value"); } RegisterFloat(sliderJSF, false); } //Merge callbacks from slider and color picker to a color value private void UnifiedImagePanelColor(JSONStorableColor jColor) { UnifiedImagePanelColor(sliderJSF.val, true); } public void UnifiedImagePanelColor(float f, bool sendEvent) { Color newColor = Converters.HSVToRGB(jcolor); newColor.a = f; ImagePanel.SetMaterialColor(newColor, sendEvent); } private void UnifiedImagePanelNormal(float f, bool sendEvent) { ImagePanel.SetNormalScale(f, sendEvent); } private void UnifiedImagePanelSpec(float f, bool sendEvent) { ImagePanel.SetSpecularScale(f, sendEvent); } public string FormattPathText(string text) { //count char back to first slash int lastIndex = text.LastIndexOf("/"); if (text.Length > 55 && lastIndex >= 0) { string endText = text.Substring(lastIndex); //if file name is super long clip it if (endText.Length > 40) endText = text.Substring(lastIndex, 40); int leftover = 55 - endText.Length + 3; string startText = text.Substring(0, leftover); text = startText + "..." + endText; } return text; } //Used to correct paths in JSON to available VAR files until VAM expands FileManagerSecure //this is a nasty fucking mess //path = image path package public void RestoreImageFromJSON(string path, string package = null, string selectedFile = null) { try { path = FileManagerSecure.NormalizePath(path); Match m; //VAR Path for textures with direct var ref if ((m = Regex.Match(path, "^(.+\\..+)\\.(.+):/(.+/)(.+)")).Success) { //this is a packagedVAR reference use latest version string varName = m.Groups[1].Value; string filePath = m.Groups[3].Value; string fileName = m.Groups[4].Value; ShortCut sc = FileManagerSecure.GetShortCutsForDirectory(filePath).Where(x => x.package.Contains(varName) && x.isLatest).FirstOrDefault(); //if nothing found with this path if (sc == null) { SuperController.LogError("Decal Maker: Texture not found: " + path); return; } path = Regex.Replace(sc.path, "^.+/(.+\\..+\\..+)\\..+(:.+)", "$1$2/" + fileName); //removes AddonPackages/ path prefix and the .var from shortcut path } //if texture and presets are in same VAR VAM changes all paths to SELF: //prefabs UI passes in a package ref else if ((m = Regex.Match(path, "^SELF:.+")).Success && package != null) { //SELF:/Custom/Atom/Person/Textures/DecalMaker - mofmes power goo/Face/Cheeks/face04/D.png path = Regex.Replace(path, "^(SELF)(:.+)", package + "$2"); //replace SELF:/ with source package } else if ((m = Regex.Match(path, "^SELF:.+")).Success && selectedFile != null) { if ((m = Regex.Match(selectedFile, "^(.+\\..+\\..+):.+")).Success) { string varName = m.Groups[1].Value; path = Regex.Replace(path, "^(SELF)(:.+)", varName + "$2"); //replace SELF:/ with source package } } //no fing clue. We have to find it else if ((m = Regex.Match(path, "^SELF:.+")).Success) { //Find the first package with this path path = Regex.Replace(path, "^SELF:/(.+/)", "$1"); string pathWithoutFileName = Regex.Replace(path, "^(.+/).+", "$1"); List shortCuts = FileManagerSecure.GetShortCutsForDirectory(pathWithoutFileName); foreach (ShortCut s in shortCuts) { if (s.isLatest && s.package != "" && s.package != "AddonPackages Flattened" && s.package != "AddonPackages Filtered") { List files = GetFilesAtPathRecursive(s.path, null); foreach (string f in files) { string t = FileManagerSecure.NormalizePath(f); if (t.Contains(path)) { path = t; break; } } } } } BrowserCallBack(path); } catch (Exception) { SuperController.LogError("Decal Maker: Unable to Resove Texture Path"); //SuperController.LogError(e.InnerException.StackTrace); } } public void BrowserCallBack(string path) { if (string.IsNullOrEmpty(path)) { ImagePathText = ""; } else { string dir = FileManagerSecure.GetDirectoryName(path); DM.SetUrlParamValue("LastImageDir", dir); ImagePathText = path; ImagePanel.LoadResourceFile(path); } } public JSONClass Save() { JSONClass decalSaveJSON = new JSONClass(); decalSaveJSON["H"].AsFloat = jcolor.val.H; decalSaveJSON["S"].AsFloat = jcolor.val.S; decalSaveJSON["V"].AsFloat = jcolor.val.V; decalSaveJSON["Alpha"].AsFloat = sliderJSF.val; decalSaveJSON["RandomName"] = RandomName; decalSaveJSON["LinkID"] = linkedPanelID; decalSaveJSON["Scale"].AsFloat = TextureScaleJSF.val; decalSaveJSON["Rotation"].AsFloat = TextureRotationJSF.val; decalSaveJSON["T_CPT-X"].AsFloat = TextureTranAndCenter.x; decalSaveJSON["T_CPT-Y"].AsFloat = TextureTranAndCenter.y; decalSaveJSON["T_CPT-Z"].AsFloat = TextureTranAndCenter.z; decalSaveJSON["T_CPT-W"].AsFloat = TextureTranAndCenter.w; //SuperController.LogError("ALPHA IS " + ImagePanel.sliderValue); //jcolor.StoreJSON(decalSaveJSON, true, true, true); if (!string.IsNullOrEmpty(ImagePanel.Path)) { string relativePath = FileManagerSecure.NormalizePath(ImagePanel.Path); decalSaveJSON["Path"] = relativePath; } return decalSaveJSON; } public void RestorePanelFromJSON(JSONClass saveJSON) { if (saveJSON["Alpha"] != null) sliderJSF.SetVal(saveJSON["Alpha"].AsFloat); if (saveJSON["H"] != null) { float h = saveJSON["H"].AsFloat; float s = saveJSON["S"].AsFloat; float v = saveJSON["V"].AsFloat; jcolor.SetVal(h, s, v); } if (saveJSON["Path"] != null) RestoreImageFromJSON(saveJSON["Path"]); if (!string.IsNullOrEmpty(saveJSON["LinkID"])) linkedPanelID = saveJSON["LinkID"]; if (saveJSON["Scale"] != null) { TextureScaleJSF.valNoCallback = saveJSON["Scale"].AsFloat; TextureRotationJSF.valNoCallback = saveJSON["Rotation"].AsFloat; float x = saveJSON["T_CPT-X"].AsFloat; float y = saveJSON["T_CPT-Y"].AsFloat; float z = saveJSON["T_CPT-Z"].AsFloat; float w = saveJSON["T_CPT-W"].AsFloat; TextureTranAndCenter = new Vector4(x, y, z, w); } } } } //*** SourceCombiner -> original file FakeEnums.cs *** //lets fake enum since compiler issue namespace VAM_Decal_Maker { public static class EventEnum { //Core Panel public const string CoreSetupFinished = "Core-SETUP FINISHED"; public const string CoreTempDecalToPerm = "Core Button:Panel Temp to Perm"; public const string CoreRemoveTempPanels = "Remove Decals flagged with temp"; public const string CoreNewCharacterSelected = "New Skin Selected"; public const string ToggleGenitalCutout = "_toggleGenitalCutout"; public const string ToggleNippleCutout = "_toggleNippleCutout"; public const string CoreRestoreFromJSON = "Restore data from json"; public const string CoreResetAll = "Reset and clear all panels"; //ImagePanel public const string ImagePanelPathChanged = "ImagePanel-PathChange"; public const string ImagePanelMaterialChanged = "ImagePanel Material Changed"; public const string ImagePanelNormalSliderChange = "ImagePanel Normal Scale Changed"; public const string ImagePanelSpecSliderChange = "ImagePanelSpecChanged"; public const string ImagePanelTextureCleared = "ImagePanelTextureCleared"; public const string ImagePanelTextureLoaded = "ImagePanelTextureLoaded"; public const string ImagePanelVideoFrameUpdate = "ImagePanelVideoFrameUpdate"; public const string ImagePanelVidePrepared = "ImagePanel Vide Prepared"; public const string ImagePanelImageLoad = "Image selected for loading"; public const string ImagePanelVideoLoad = "Video selected for loading"; //DecalPanel public const string DecalPanelMaterialChange = "MatChange"; public const string DecalPanelMove = "DecalPanelMove"; public const string DecalPanelButtonUP = "DecalPanel-Move-UP"; public const string DecalPanelButtonDOWN = "DecalPanel-Move-DOWN"; public const string DecalPanelButtonCLOSE = "DecalPanel Close Button"; public const string DecalPanelButtonAdd = "DecalPanel-CREATE"; public const string DecalPanelButtonCOPY = "DecalPanel-Duplicate"; public const string DecalPanelDELETE = "DecalPanel-DELETE"; public const string DecalPanelLinkChanged = "DecalPanel-LinkChanged"; public const string DecalPanelTextureMove = "DecalPanel Texture Move"; //ManagerPanel public const string ManagerPanelSelection = "New Texture Region Selected"; public const string ManagerPanelRestoreActive = "Restore last active panel"; public const string ManagerPanelButtonADD = "Add new panel after last panel"; public const string ManagerPanelButtonCLOSE = "Remove last panel"; //HeaderPanel public const string HeaderPanelSelection = "New Material Region Selected"; //preview Panel //public const string Scale = "Scale"; //public const string Create = "Create"; //public const string Delete = "Delete"; //public const string Sort = "Sort"; } public static class MatSlotEnum { public const string MainTex = "_MainTex"; public const string DecalTex = "_DecalTex"; public const string BumpMap = "_BumpMap"; public const string SpecTex = "_SpecTex"; public const string GlossTex = "_GlossTex"; public static readonly List Values = new List() { MainTex, DecalTex, BumpMap, SpecTex, GlossTex }; } public static class BodyRegionEnum { public const string Torso = "torso"; public const string Face = "face"; public const string Limbs = "limbs"; public const string Genitals = "genitals"; public const string EyeReflection = "relflection"; public const string EyePupil = "pupil"; public const string EyeIris = "iris"; public const string EyeCornea = "cornea"; public const string EyeSclera = "whites"; public static readonly List Values = new List() { Torso, Genitals, Face, Limbs, EyeIris, EyeSclera, EyeCornea, EyeReflection, EyePupil }; } } //*** SourceCombiner -> original file HeaderPanel.cs *** namespace VAM_Decal_Maker { public class HeaderPanel : UIPanelBase { public SelectionPanel ActivePanel { get; set; } public SelectionPanel SelectionPanelDecal { get; private set; } public SelectionPanel SelectionPanelSpec { get; private set; } public SelectionPanel SelectionPanelGloss { get; private set; } public SelectionPanel SelectionPanelNorm { get; private set; } public SelectionPanel SelectionPanelDecalEye { get; private set; } public SelectionPanel SelectionPanelSpecEye { get; private set; } public SelectionPanel SelectionPanelGlossEye { get; private set; } public SelectionPanel SelectionPanelNormEye { get; private set; } public MyButton decalButton { get; private set; } public MyButton specButton { get; private set; } public MyButton glossButton { get; private set; } public MyButton normButton { get; private set; } public MyButton eyedecalButton { get; private set; } public MyButton eyespecButton { get; private set; } public MyButton eyeglossButton { get; private set; } public MyButton eyenormButton { get; private set; } public MyButton eyesButton { get; private set; } public Dictionary selectionPanels { get; private set; } private List buttons; //events from Core private void CorePanelEvent(object o, PanelEventArgs e) { switch (e.EventName) { case EventEnum.HeaderPanelSelection: UpdateSelection(o); break; //once setup is finished set Decal as active panel case EventEnum.CoreSetupFinished: RaiseCoreEvent(decalButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelDecal)); break; } } private void UpdateSelection(object o) { if (o.GetType() != typeof(MyButton)) return; foreach (MyButton button in buttons) { if (button == (MyButton)o) { button.Selected(true); } else { button.Selected(false); } } } public HeaderPanel(Decal_Maker DM) : base(DM, 50) { //Image i = spacerLeft.gameObject.AddComponent(); //i.material = new Material(Shader.Find("UI/Default-Overlay")); //i.material.color = Color.green; gameObject.transform.SetParent(spacerLeft.transform, false); //center panel in UI gameObject.transform.localPosition += new Vector3(270, 10, 0); //basePanel CreatePanelBackground(1050, 70, new Color(.5f, .5f, .5f, 1)); SetLayout(200, 50); SelectionPanelDecal = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.DecalTex, IsNormalMap = false, linear = false, PanelName = MaterialSlot }; SelectionPanelSpec = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.SpecTex, IsNormalMap = false, linear = true, PanelName = MaterialSlot }; SelectionPanelGloss = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.GlossTex, IsNormalMap = false, linear = true, PanelName = MaterialSlot }; SelectionPanelNorm = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.BumpMap, IsNormalMap = true, linear = true, PanelName = MaterialSlot }; //EYES SelectionPanelDecalEye = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.DecalTex, IsNormalMap = false, linear = false, PanelName = MaterialSlot }; SelectionPanelSpecEye = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.SpecTex, IsNormalMap = false, linear = true, PanelName = MaterialSlot }; SelectionPanelGlossEye = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.GlossTex, IsNormalMap = false, linear = true, PanelName = MaterialSlot }; SelectionPanelNormEye = new SelectionPanel(1050, 300, new Color(1, 1, 1, 1f), DM) { MaterialSlot = MatSlotEnum.BumpMap, IsNormalMap = true, linear = true, PanelName = MaterialSlot }; decalButton = new MyButton("Decal", new Color(1, 0.3f, 0.4f), transform); decalButton.button.onClick.AddListener(() => RaiseCoreEvent(decalButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelDecal))); specButton = new MyButton("Specular", new Color(0.95f, 0.25f, 0.91f), transform); specButton.button.onClick.AddListener(() => RaiseCoreEvent(specButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelSpec))); glossButton = new MyButton("Gloss", new Color(0.2f, .98f, 0.2f), transform); glossButton.button.onClick.AddListener(() => RaiseCoreEvent(glossButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelGloss))); normButton = new MyButton("Normal", new Color(0.2f, 0.9f, .9f), transform); normButton.button.onClick.AddListener(() => RaiseCoreEvent(normButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelNorm))); eyedecalButton = new MyButton("EyeDecal", new Color(1, 0.3f, 0.4f), transform); eyedecalButton.button.onClick.AddListener(() => RaiseCoreEvent(decalButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelDecalEye))); eyedecalButton.gameObject.SetActive(false); eyespecButton = new MyButton("EyeSpecular", new Color(0.95f, 0.25f, 0.91f), transform); eyespecButton.button.onClick.AddListener(() => RaiseCoreEvent(specButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelSpecEye))); eyespecButton.gameObject.SetActive(false); eyeglossButton = new MyButton("EyeGloss", new Color(0.2f, .98f, 0.2f), transform); eyeglossButton.button.onClick.AddListener(() => RaiseCoreEvent(glossButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelGlossEye))); eyeglossButton.gameObject.SetActive(false); eyenormButton = new MyButton("EyeNormal", new Color(0.2f, 0.9f, .9f), transform); eyenormButton.button.onClick.AddListener(() => RaiseCoreEvent(normButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelNormEye))); eyenormButton.gameObject.SetActive(false); buttons = new List() { decalButton, specButton, glossButton, normButton, eyedecalButton, eyeglossButton, eyenormButton, eyespecButton }; eyesButton = new MyButton("Eyes Toggle", new Color(0.2f, 0.8f, .6f), transform); bool eyesActive = false; eyesButton.button.onClick.AddListener(() => { foreach (MyButton button in buttons) { button.gameObject.SetActive(!button.gameObject.activeSelf); } eyesActive = !eyesActive; if (eyesActive) { RaiseCoreEvent(eyedecalButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelDecalEye)); } else { RaiseCoreEvent(decalButton, new PanelEventArgs(EventEnum.HeaderPanelSelection, SelectionPanelDecal)); } }); //now finalize panels SelectionPanelDecal.CreateManagerPanels(); SelectionPanelSpec.CreateManagerPanels(); SelectionPanelGloss.CreateManagerPanels(); SelectionPanelNorm.CreateManagerPanels(); SelectionPanelDecal.PanelName = "Decal"; SelectionPanelSpec.PanelName = "Specular"; SelectionPanelGloss.PanelName = "Gloss"; SelectionPanelNorm.PanelName = "Normal"; SelectionPanelDecalEye.CreateManagerEyePanels(); SelectionPanelSpecEye.CreateManagerEyePanels(); SelectionPanelGlossEye.CreateManagerEyePanels(); SelectionPanelNormEye.CreateManagerEyePanels(); ActivePanel = SelectionPanelDecal; selectionPanels = new Dictionary { { "Decal", SelectionPanelDecal }, { "Specular", SelectionPanelSpec }, { "Gloss", SelectionPanelGloss }, { "Normal", SelectionPanelNorm } }; DM.CoreEvent += CorePanelEvent; } } } //*** SourceCombiner -> original file ImagePanel.cs *** namespace VAM_Decal_Maker { public class ImagePanel : ImagePanelBase { public bool ImageLoading { get; private set; } public string Path { get; set; } //isolate image from direct access since imagepanel sometimes modifies the image shader or image so use imagePanels properties not the image's public Color color { get; private set; } = Color.white; public Texture mainTexture { get { return base.material.mainTexture; } set { base.material.mainTexture = value; } } public RectTransform rectTransform { get { return image.GetComponent(); } } private GameObject video; public VideoPlayer videoPlayer { get; private set; } //lazy load/create since it wont be used a lot of times private RenderTexture _videoTexture; private RenderTexture videoTexture { get { if (_videoTexture == null) { if (linear) _videoTexture = new RenderTexture(4096, 4096, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); else _videoTexture = new RenderTexture(4096, 4096, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); _videoTexture.Create(); } return _videoTexture; } } public event EventHandler ImagePanelUpdate; private void OnImagePanelChange(PanelEventArgs e) { if (ImagePanelUpdate != null) { //store a ref to this imagepanel e.ImagePanel = this; ImagePanelUpdate(this, e); } } public ImagePanel(Decal_Maker DM, Color? color = null, string TextureSlot = null, string MaterialSlot = null, bool IsNormalMap = false, bool linear = false) : base(DM, TextureSlot, MaterialSlot, IsNormalMap, linear) { base.TextureSlot = TextureSlot; base.MaterialSlot = MaterialSlot; base.IsNormalMap = IsNormalMap; base.linear = linear; //don't parent video player to object that activates/inactivates or it stops video = new GameObject(); videoPlayer = video.AddComponent(); videoPlayer.playOnAwake = false; videoPlayer.renderMode = VideoRenderMode.RenderTexture; videoPlayer.frameReady += FrameUpdateEvent; videoPlayer.prepareCompleted += VideoPreparedEvent; videoPlayer.sendFrameReadyEvents = true; videoPlayer.isLooping = true; //image = gameObject.AddComponent(); // image.material = new Material(Shader.Find("UI/Default-Overlay")); SetMaterialColor(color ?? new Color(1, 1, 1, 1f), true); RectTransform imageRect = image.rectTransform; imageRect.sizeDelta = new Vector2(300, 300); //Button = gameObject.AddComponent