public static PushupController GetCharaController(ChaControl character) => character == null ? null : character.gameObject.GetComponent <PushupController>();
protected override void EnablePov() { if (!currentChara) { if (isStudio) { var selectedCharas = GuideObjectManager.Instance.selectObjectKey.Select(x => Studio.Studio.GetCtrlInfo(x) as OCIChar).Where(x => x != null).ToList(); if (selectedCharas.Count > 0) { var ociChar = selectedCharas.First(); currentChara = ociChar.charInfo; currentCharaId = ociChar.objectInfo.dicKey; currentCharaGo = currentChara.gameObject; } else { Logger.LogMessage("Select a character in workspace to enter its POV"); } } else { Queue <ChaControl> CreateQueue() { return(new Queue <ChaControl>(FindObjectsOfType <ChaControl>())); } ChaControl GetCurrentChara() { for (int i = 0; i < charaQueue.Count; i++) { var chaControl = charaQueue.Dequeue(); // Remove destroyed if (!chaControl) { continue; } // Rotate the queue charaQueue.Enqueue(chaControl); if (chaControl.sex == 0 && hFlag && (hFlag.mode == HFlag.EMode.aibu || hFlag.mode == HFlag.EMode.lesbian || hFlag.mode == HFlag.EMode.masturbation)) { continue; } // Found a valid character, otherwise skip (needed for story mode H because roam mode characters are in the queue too, just disabled) if (chaControl.objTop.activeInHierarchy) { return(chaControl); } } return(null); } if (charaQueue == null) { charaQueue = CreateQueue(); } currentChara = GetCurrentChara(); if (!currentChara) { charaQueue = CreateQueue(); currentChara = GetCurrentChara(); } currentCharaGo = currentChara?.gameObject; } } if (currentChara) { prevVisibleHeadAlways = currentChara.fileStatus.visibleHeadAlways; if (HideHead.Value) { currentChara.fileStatus.visibleHeadAlways = false; } GameCamera = Camera.main; var cc = (MonoBehaviour)GameCamera.GetComponent <CameraControl_Ver2>() ?? GameCamera.GetComponent <Studio.CameraControl>(); if (cc) { cc.enabled = false; } // Fix depth of field being completely out of focus var depthOfField = GameCamera.GetComponent <UnityStandardAssets.ImageEffects.DepthOfField>(); dofOrigSize = depthOfField.focalSize; dofOrigAperature = depthOfField.aperture; if (depthOfField.enabled) { depthOfField.focalTransform.localPosition = new Vector3(0, 0, 0.25f); depthOfField.focalSize = 0.9f; depthOfField.aperture = 0.6f; } // only use head rotation if there is no existing rotation if (!LookRotation.TryGetValue(currentCharaGo, out _)) { LookRotation[currentCharaGo] = currentChara.objHeadBone.transform.rotation.eulerAngles; } else { // always get z axis from head var rot = LookRotation[currentCharaGo]; LookRotation[currentCharaGo] = new Vector3(rot.x, rot.y, currentChara.objHeadBone.transform.rotation.eulerAngles.z); } base.EnablePov(); backupLayer = GameCamera.gameObject.layer; GameCamera.gameObject.layer = 0; } }
/// <summary> /// 將Controller內之Material Editor Data儲存至ChaControl ExtendedData內 /// </summary> /// <param name="chaCtrl">對象ChaControl</param> public static void SetExtDataFromController(ChaControl chaCtrl) { MonoBehaviour MaterialEditorController = chaCtrl.GetComponents <MonoBehaviour>().FirstOrDefault(x => Equals(x.GetType().Name, "MaterialEditorCharaController")); MaterialEditorController.Invoke("OnCardBeingSaved", new object[] { 1 }); }
internal void HSceneEnd() { ChaControl.ChangeEyesShaking(false); IsInit = false; }
/// <summary> /// Get the controller for the character /// </summary> public static AnimationControllerCharaController GetController(ChaControl character) => character?.gameObject?.GetComponent <AnimationControllerCharaController>();
private static void SetShapeBodyValueSliderFixPre(ChaControl __instance, out bool __state) { __state = __instance.hiPoly; AccessTools.Property(typeof(ChaControl), nameof(ChaControl.hiPoly)).SetValue(__instance, true, null); }
public void Merge(ChaControl chaControl) { StartCoroutine(TryMerge(chaControl)); }
/// <summary> /// Returns the UncensorSelectorController for the specified character or null if it does not exist /// </summary> public static UncensorSelectorController GetController(ChaControl character) => character?.gameObject?.GetComponent <UncensorSelectorController>();
internal static void SetClothesStatePostfix(ChaControl __instance, int clothesKind) { if (clothesKind == 0 || clothesKind == 2) //tops and bras GetCharaController(__instance)?.ClothesStateChangeEvent(); }
/// <summary> /// Set the skin line visibility for every color matching object configured in the manifest.xml /// </summary> internal static void SetLineVisibility(ChaControl chaControl, BodyData bodyData, PenisData penisData, BallsData ballsData) { SetLineVisibility(chaControl, bodyData); SetLineVisibility(chaControl, penisData); SetLineVisibility(chaControl, ballsData); }
/// <summary> /// Set the skin gloss for every color matching object configured in the manifest.xml /// </summary> internal static void SetSkinGloss(ChaControl chaControl, BodyData bodyData, PenisData penisData, BallsData ballsData) { SetSkinGloss(chaControl, bodyData); SetSkinGloss(chaControl, penisData); SetSkinGloss(chaControl, ballsData); }
/// <summary> /// Load the body asset, copy its mesh, and delete it /// </summary> internal static void ReloadCharacterBody(ChaControl chaControl, BodyData bodyData) { string OOBase = bodyData?.OOBase ?? Defaults.OOBase; string Asset = bodyData?.Asset ?? (chaControl.sex == 0 ? Defaults.AssetMale : Defaults.AssetFemale); if (chaControl.hiPoly == false) { Asset += "_low"; } GameObject uncensorCopy = CommonLib.LoadAsset <GameObject>(OOBase, Asset, true); SkinnedMeshRenderer o_body_a = chaControl.objBody.GetComponentsInChildren <SkinnedMeshRenderer>(true).FirstOrDefault(x => x.name == "o_body_a"); //Copy any additional parts to the character if (bodyData != null && o_body_a != null && bodyData.AdditionalParts.Count > 0) { foreach (var mesh in uncensorCopy.gameObject.GetComponentsInChildren <SkinnedMeshRenderer>(true)) { if (bodyData.AdditionalParts.Contains(mesh.name)) { SkinnedMeshRenderer part = chaControl.objBody.GetComponentsInChildren <SkinnedMeshRenderer>(true).FirstOrDefault(x => x.name == mesh.name); Transform parent = o_body_a.gameObject.GetComponentsInChildren <Transform>(true).FirstOrDefault(c => c.name == mesh.transform.parent.name); if (part == null && parent != null) { var copy = Instantiate(mesh); copy.name = mesh.name; copy.transform.parent = parent; copy.bones = o_body_a.bones.Where(b => b != null && copy.bones.Any(t => t.name.Equals(b.name))).ToArray(); } } } } foreach (var mesh in chaControl.objBody.GetComponentsInChildren <SkinnedMeshRenderer>(true)) { if (mesh.name == "o_body_a") { UpdateMeshRenderer(uncensorCopy.gameObject.GetComponentsInChildren <SkinnedMeshRenderer>(true).FirstOrDefault(x => x.name == mesh.name), mesh); } else if (BodyParts.Contains(mesh.name)) { UpdateMeshRenderer(uncensorCopy.gameObject.GetComponentsInChildren <SkinnedMeshRenderer>(true).FirstOrDefault(x => x.name == mesh.name), mesh, true); } else if (bodyData != null) { foreach (var part in bodyData.ColorMatchList) { if (mesh.name == part.Object) { UpdateMeshRenderer(uncensorCopy.gameObject.GetComponentsInChildren <SkinnedMeshRenderer>().FirstOrDefault(x => x.name == part.Object), mesh, true); } } } //Destroy all additional parts attached to the current body that shouldn't be there if (AllAdditionalParts.Contains(mesh.name)) { if (bodyData == null || !bodyData.AdditionalParts.Contains(mesh.name)) { Destroy(mesh); } else { UpdateMeshRenderer(uncensorCopy.gameObject.GetComponentsInChildren <SkinnedMeshRenderer>(true).FirstOrDefault(x => x.name == mesh.name), mesh, true); } } } Destroy(uncensorCopy); }
/// <summary> /// Reload this character's uncensor /// </summary> public void UpdateUncensor() => ChaControl.StartCoroutine(UncensorUpdate.ReloadCharacterUncensor(ChaControl, BodyData, PenisData, PenisVisible, BallsData, BallsVisible));
private static OCICharMale Add( ChaControl _male, OICharInfo _info, ObjectCtrlInfo _parent, TreeNodeObject _parentNode, bool _addInfo, int _initialPosition) { OCICharMale ociCharMale = new OCICharMale(); ChaFileStatus _status = new ChaFileStatus(); _status.Copy(_male.fileStatus); _male.ChangeNowCoordinate(false, true); _male.Load(true); _male.InitializeExpression(1, true); ociCharMale.charInfo = _male; ociCharMale.charReference = (ChaReference)_male; ociCharMale.preparation = (Preparation)_male.objAnim.GetComponent <Preparation>(); ociCharMale.finalIK = _male.fullBodyIK; for (int index = 0; index < 2; ++index) { GameObject gameObject = _male.objHair.SafeGet <GameObject>(index); if (Object.op_Inequality((Object)gameObject, (Object)null)) { AddObjectAssist.ArrangeNames(gameObject.get_transform()); } } AddObjectAssist.SetupAccessoryDynamicBones((OCIChar)ociCharMale); AddObjectAssist.DisableComponent((OCIChar)ociCharMale); ociCharMale.objectInfo = (ObjectInfo)_info; GuideObject guideObject = Singleton <GuideObjectManager> .Instance.Add(((Component)_male).get_transform(), _info.dicKey); guideObject.scaleSelect = 0.1f; guideObject.scaleRot = 0.05f; guideObject.isActiveFunc += new GuideObject.IsActiveFunc(((ObjectCtrlInfo)ociCharMale).OnSelect); guideObject.SetVisibleCenter(true); ociCharMale.guideObject = guideObject; ociCharMale.optionItemCtrl = (OptionItemCtrl)((Component)_male).get_gameObject().AddComponent <OptionItemCtrl>(); ociCharMale.optionItemCtrl.animator = _male.animBody; ociCharMale.optionItemCtrl.oiCharInfo = _info; _info.changeAmount.onChangeScale += new Action <Vector3>(ociCharMale.optionItemCtrl.ChangeScale); ociCharMale.charAnimeCtrl = ociCharMale.preparation.CharAnimeCtrl; ociCharMale.charAnimeCtrl.oiCharInfo = _info; ociCharMale.yureCtrl = ociCharMale.preparation.YureCtrl; ociCharMale.yureCtrl.Init((OCIChar)ociCharMale); int group = _info.animeInfo.group; int category = _info.animeInfo.category; int no = _info.animeInfo.no; float animeNormalizedTime = _info.animeNormalizedTime; ociCharMale.LoadAnime(0, 0, 1, 0.0f); _male.animBody.Update(0.0f); _info.animeInfo.group = group; _info.animeInfo.category = category; _info.animeInfo.no = no; _info.animeNormalizedTime = animeNormalizedTime; IKSolver ikSolver = ((IK)ociCharMale.finalIK).GetIKSolver(); if (!ikSolver.get_initiated()) { ikSolver.Initiate(((Component)ociCharMale.finalIK).get_transform()); } if (_addInfo) { Studio.Studio.AddInfo((ObjectInfo)_info, (ObjectCtrlInfo)ociCharMale); } else { Studio.Studio.AddObjectCtrlInfo((ObjectCtrlInfo)ociCharMale); } TreeNodeObject _parent1 = !Object.op_Inequality((Object)_parentNode, (Object)null) ? (_parent == null ? (TreeNodeObject)null : _parent.treeNodeObject) : _parentNode; TreeNodeObject treeNodeObject = Studio.Studio.AddNode(_info.charFile.parameter.fullname, _parent1); treeNodeObject.enableChangeParent = true; treeNodeObject.treeState = _info.treeState; treeNodeObject.onVisible += new TreeNodeObject.OnVisibleFunc(((ObjectCtrlInfo)ociCharMale).OnVisible); treeNodeObject.enableVisible = true; treeNodeObject.visible = _info.visible; guideObject.guideSelect.treeNodeObject = treeNodeObject; ociCharMale.treeNodeObject = treeNodeObject; AddObjectAssist.InitBone((OCIChar)ociCharMale, _male.objBodyBone.get_transform(), Singleton <Info> .Instance.dicBoneInfo); AddObjectAssist.InitIKTarget((OCIChar)ociCharMale, _addInfo); AddObjectAssist.InitLookAt((OCIChar)ociCharMale); AddObjectAssist.InitAccessoryPoint((OCIChar)ociCharMale); ociCharMale.voiceCtrl.ociChar = (OCIChar)ociCharMale; List <DynamicBone> source = new List <DynamicBone>(); foreach (GameObject gameObject in _male.objHair) { source.AddRange((IEnumerable <DynamicBone>)gameObject.GetComponents <DynamicBone>()); } ociCharMale.InitKinematic(((Component)_male).get_gameObject(), ociCharMale.finalIK, _male.neckLookCtrl, source.Where <DynamicBone>((Func <DynamicBone, bool>)(v => Object.op_Inequality((Object)v, (Object)null))).ToArray <DynamicBone>(), (DynamicBone[])null); treeNodeObject.enableAddChild = false; if (_initialPosition == 1) { _info.changeAmount.pos = Singleton <Studio.Studio> .Instance.cameraCtrl.targetPos; } _info.changeAmount.OnChange(); treeNodeObject.treeState = TreeNodeObject.TreeState.Close; Studio.Studio.AddCtrlInfo((ObjectCtrlInfo)ociCharMale); _parent?.OnLoadAttach(!Object.op_Inequality((Object)_parentNode, (Object)null) ? _parent.treeNodeObject : _parentNode, (ObjectCtrlInfo)ociCharMale); ociCharMale.LoadAnime(_info.animeInfo.group, _info.animeInfo.category, _info.animeInfo.no, _info.animeNormalizedTime); ociCharMale.ActiveKinematicMode(OICharInfo.KinematicMode.IK, _info.enableIK, true); for (int index = 0; index < 5; ++index) { ociCharMale.ActiveIK((OIBoneInfo.BoneGroup)(1 << index), _info.activeIK[index], false); } // ISSUE: object of a compiler-generated type is created foreach (\u003C\u003E__AnonType18 <OIBoneInfo.BoneGroup, int> anonType18 in ((IEnumerable <OIBoneInfo.BoneGroup>)FKCtrl.parts).Select <OIBoneInfo.BoneGroup, \u003C\u003E__AnonType18 <OIBoneInfo.BoneGroup, int> >((Func <OIBoneInfo.BoneGroup, int, \u003C\u003E__AnonType18 <OIBoneInfo.BoneGroup, int> >)((p, i) => new \u003C\u003E__AnonType18 <OIBoneInfo.BoneGroup, int>(p, i)))) { ociCharMale.ActiveFK(anonType18.p, ociCharMale.oiCharInfo.activeFK[anonType18.i], ociCharMale.oiCharInfo.activeFK[anonType18.i]); } ociCharMale.ActiveKinematicMode(OICharInfo.KinematicMode.FK, _info.enableFK, true); for (int categoryNo = 0; categoryNo < _info.expression.Length; ++categoryNo) { ociCharMale.charInfo.EnableExpressionCategory(categoryNo, _info.expression[categoryNo]); } ociCharMale.animeSpeed = ociCharMale.animeSpeed; ociCharMale.animeOptionParam1 = ociCharMale.animeOptionParam1; ociCharMale.animeOptionParam2 = ociCharMale.animeOptionParam2; _status.visibleSonAlways = _info.visibleSon; ociCharMale.SetSonLength(_info.sonLength); ociCharMale.SetVisibleSimple(_info.visibleSimple); ociCharMale.SetSimpleColor(_info.simpleColor); AddObjectAssist.UpdateState((OCIChar)ociCharMale, _status); return(ociCharMale); }
public static void CreateBodyTexturePrefix(ChaControl __instance) => UncensorSelector.CurrentBodyGUID = UncensorSelector.GetController(__instance)?.BodyData?.BodyGUID;
internal static void CreateBodyTextureHook(ChaControl __instance) { var controller = GetCharaController(__instance); if (controller != null) controller.CharacterLoading = true; }
public static void InitBaseCustomTextureBodyPrefix(ChaControl __instance) => UncensorSelector.CurrentBodyGUID = UncensorSelector.GetController(__instance)?.BodyData?.BodyGUID;
public static void InitShapeFace(ChaControl __instance) => GetController(__instance).UpdateVisible(true);
private static void SetShapeBodyValueInfoSliderFixPost(ChaControl __instance, bool __state) { AccessTools.Property(typeof(ChaControl), nameof(ChaControl.hiPoly)).SetValue(__instance, __state, null); }
/// <summary> /// Get the InvisibleBodyCharaController for the character /// </summary> public static InvisibleBodyCharaController GetController(ChaControl character) => character?.gameObject?.GetComponent <InvisibleBodyCharaController>();
private static EyeShakingController GetController(ChaControl character) => character?.gameObject?.GetComponent <EyeShakingController>();
public static void SetBodyBaseMaterial(ChaControl __instance) => UncensorSelector.GetController(__instance)?.UpdateSkinColor();
public OverlayStorage(CharaCustomFunctionController controller) { _chaControl = controller.ChaControl; _textureStorage = new TextureStorage(); _allOverlayTextures = new Dictionary <CoordinateType, Dictionary <TexType, int> >(); }
public static void VisibleAddBodyLine(ChaControl __instance) => UncensorSelector.GetController(__instance)?.UpdateSkinLine();
public void RefreshAllTextures(bool onlyMasks) { #if KK if (KKAPI.Studio.StudioAPI.InsideStudio) { // Studio needs a more aggresive refresh to update the textures // Refresh needs to happen through OCIChar or dynamic bones get messed up Studio.Studio.Instance.dicInfo.Values.OfType <Studio.OCIChar>() .FirstOrDefault(x => x.charInfo == ChaControl) ?.SetCoordinateInfo(CurrentCoordinate.Value, true); } else { // Needed for body masks var forceNeededParts = new[] { ChaFileDefine.ClothesKind.top, ChaFileDefine.ClothesKind.bra }; foreach (var clothesKind in forceNeededParts) { ForceClothesReload(clothesKind); } if (onlyMasks) { return; } var allParts = Enum.GetValues(typeof(ChaFileDefine.ClothesKind)).Cast <ChaFileDefine.ClothesKind>(); foreach (var clothesKind in allParts.Except(forceNeededParts)) { ChaControl.ChangeCustomClothes(true, (int)clothesKind, true, false, false, false, false); } // Triggered by ForceClothesReload on top so not necessary //for (var i = 0; i < ChaControl.cusClothesSubCmp.Length; i++) // ChaControl.ChangeCustomClothes(false, i, true, false, false, false, false); } #elif EC if (MakerAPI.InsideMaker && onlyMasks) { // Need to do the more aggresive version in maker to allow for clearing the mask without a character reload var forceNeededParts = new[] { ChaFileDefine.ClothesKind.top, ChaFileDefine.ClothesKind.bra }; foreach (var clothesKind in forceNeededParts) { ForceClothesReload(clothesKind); } } else { // Need to manually set the textures because calling ChangeClothesAsync (through ForceClothesReload) // to make the game do it results in a crash when editing nodes in a scene if (ChaControl.customMatBody) { Texture overlayTex = GetOverlayTex(MaskKind.BodyMask.ToString(), false)?.Texture; if (overlayTex != null) { ChaControl.customMatBody.SetTexture(ChaShader._AlphaMask, overlayTex); } } if (ChaControl.rendBra != null) { Texture overlayTex = GetOverlayTex(MaskKind.BraMask.ToString(), false)?.Texture; if (overlayTex != null) { if (ChaControl.rendBra[0]) { ChaControl.rendBra[0].material.SetTexture(ChaShader._AlphaMask, overlayTex); } if (ChaControl.rendBra[1]) { ChaControl.rendBra[1].material.SetTexture(ChaShader._AlphaMask, overlayTex); } } } if (ChaControl.rendInner != null) { Texture overlayTex = GetOverlayTex(MaskKind.InnerMask.ToString(), false)?.Texture; if (overlayTex != null) { if (ChaControl.rendInner[0]) { ChaControl.rendInner[0].material.SetTexture(ChaShader._AlphaMask, overlayTex); } if (ChaControl.rendInner[1]) { ChaControl.rendInner[1].material.SetTexture(ChaShader._AlphaMask, overlayTex); } } } } if (onlyMasks) { return; } var allParts = Enum.GetValues(typeof(ChaFileDefine.ClothesKind)).Cast <ChaFileDefine.ClothesKind>(); foreach (var clothesKind in allParts) { ChaControl.ChangeCustomClothes(true, (int)clothesKind, true, false, false, false, false); } for (var i = 0; i < ChaControl.cusClothesSubCmp.Length; i++) { ChaControl.ChangeCustomClothes(false, i, true, false, false, false, false); } #endif }
public static void ChangeSettingSkinGlossPower(ChaControl __instance) => UncensorSelector.GetController(__instance)?.UpdateSkinGloss();
/// <summary> /// 將給入的Material Data Backup儲存至ChaControl之Controller內 /// </summary> /// <param name="chaCtrl">目標ChaControl</param> /// <param name="objectType">類型</param> /// <param name="MaterialBackup">要存入的Material Data Backup</param> /// <param name="Slot">Coordinate ClothesKind 或 Accessory Slot</param> public static void SetToController(ChaControl chaCtrl, ObjectType objectType, Dictionary <string, object> MaterialBackup = null, int Slot = -1) { Predicate <object> predicate = new Predicate <object>(x => (int)x.GetField("ObjectType") == (int)objectType && (int)x.GetField("CoordinateIndex") == chaCtrl.fileStatus.coordinateType && //若Slot有給入,則加上檢查Slot的判斷 (Slot < 0) ? true : (int)x.GetField("Slot") == Slot ); //是否有執行到 bool doFlag = false; MonoBehaviour MaterialEditorController; if (null == MaterialBackup) { MaterialEditorController = GetExtDataFromController(chaCtrl, out Dictionary <string, object> m, out Dictionary <int, object> _); if (null == MaterialBackup) { MaterialBackup = m; } } else { MaterialEditorController = chaCtrl.GetComponents <MonoBehaviour>().FirstOrDefault(x => Equals(x.GetType().Name, "MaterialEditorCharaController")); } if (null != MaterialEditorController && null != MaterialBackup) { for (int i = 0; i < storedValueInfos.Length; i++) { StoredValueInfo storedValue = storedValueInfos[i]; bool doFlag2 = false; object target = MaterialEditorController.GetField(storedValue.listName).ToListWithoutType(); //移除 doFlag2 = target.RemoveAll(predicate) > 0; //加回 object obj2Add = MaterialBackup[storedValue.listName].Where(predicate); if (obj2Add.Count() > 0) { doFlag2 = true; target.AddRange(obj2Add); } if (doFlag2) { MaterialEditorController.SetField(storedValue.listName, target); GetExtDataFromController(chaCtrl, out Dictionary <string, object> m, out Dictionary <int, object> t); Logger.LogDebug($"--->{storedValue.className}: {m[storedValue.listName].Count()}"); } doFlag |= doFlag2; } if (doFlag) { if (objectType == ObjectType.Clothing) { Logger.LogDebug($"-->Material Set: Clothes " + ((Slot >= 0) ? $", {Patches.ClothesKindName[Slot]}" : ", All Clothes")); } else if (objectType == ObjectType.Accessory) { Logger.LogDebug($"-->Material Set: Accessory" + ((Slot >= 0) ? ", Slot " + Slot : ", All Slots")); } } } }
public static void LateUpdateForce(ChaControl __instance) => __instance.hideMoz = true;
/// <summary> /// 拷貝Material Editor資料 /// </summary> /// <param name="sourceChaCtrl"></param> /// <param name="sourceSlot"></param> /// <param name="targetChaCtrl"></param> /// <param name="targetSlot"></param> /// <param name="gameObject">對象GameObject</param> /// <param name="objectType">對象分類</param> public static void CopyMaterialEditorData(ChaControl sourceChaCtrl, int sourceSlot, ChaControl targetChaCtrl, int targetSlot, GameObject gameObject, ObjectType objectType) { if (GameObjectTypeCheck(gameObject, objectType)) { return; } RemoveMaterialEditorData(targetChaCtrl, targetSlot, gameObject, objectType); SetMaterialEditorData(sourceChaCtrl, sourceSlot, targetChaCtrl, targetSlot, gameObject, objectType); }
public static GameObject GetHead(this ChaControl chaControl) => chaControl.objHead;