/// <summary> /// Will fetch number of weeks from KK_Pregnancy data for this character /// </summary> internal static int GetWeeksFromPregnancyPluginData(ChaControl chaControl, string targetBehaviorId) { var kkPregCtrlInst = PregnancyPlusHelper.GetCharacterBehaviorController <CharaCustomFunctionController>(chaControl, targetBehaviorId); if (kkPregCtrlInst == null) { return(-1); } //Get the pregnancy data object var data = kkPregCtrlInst.GetType().GetProperty("Data")?.GetValue(kkPregCtrlInst, null); if (data == null) { return(-1); } var week = Traverse.Create(data).Field("Week").GetValue <int>(); if (week.Equals(null) || week < -1) { return(-1); } return(week); }
/// <summary> /// fetch KK_Pregnancy Data.Week value for KK story mode integration (It works if you don't mind the clipping) /// </summary> internal void GetWeeksAndSetInflation(bool checkNewMesh = false, bool slidersChanged = false) { //If a card value is set for inflation size, use that first, otherwise check KK_Pregnancy for Weeks value var cardData = GetCardData(); if (cardData.inflationSize > 0 && cardData.GameplayEnabled) { MeshInflate(cardData, checkNewMesh, slidersChanged); return; } var week = PregnancyPlusHelper.GetWeeksFromPregnancyPluginData(ChaControl, KK_PregnancyPluginName); if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" GetWeeksAndSetInflation {ChaControl.name} > Week:{week} checkNewMesh:{checkNewMesh} slidersChanged:{slidersChanged}"); } if (week < 0) { return; } //Compute the additonal belly size added based on user configured vallue from 0-40 var additionalPregPlusSize = Mathf.Lerp(0, week, PregnancyPlusPlugin.MaxStoryModeBelly.Value / 40); MeshInflate(additionalPregPlusSize, checkNewMesh, slidersChanged); }
/// <summary> /// In special cases we need to apply a small offset to the sphereCenter to align the mesh correctly with the other meshes. Otherwise you get tons of clipping /// Mostly used to fix the default KK body which seems to be mis aligned from uncensor, and AI/HS2 meshes /// </summary> public void ApplyConditionalSphereCenterOffset(Transform meshRootTf, bool isClothingMesh, Vector3 _sphereCenter, out Vector3 sphereCenter, out Vector3 bodySphereCenterOffset) { //Fixes for different mesh localspace positions/rotations between KK and HS2/AI #if KK //When mesh is the default kk body, we have to adjust the mesh to match some strange offset that comes up var isDefaultBody = !PregnancyPlusHelper.IsUncensorBody(ChaControl, UncensorCOMName); var defaultBodyOffsetFix = 0.0277f * bellyInfo.TotalCharScale.y * bellyInfo.NHeightScale.y; //Where does this offset even come from? if (!isClothingMesh && isDefaultBody) { bodySphereCenterOffset = meshRootTf.position + GetUserMoveTransform(meshRootTf) - meshRootTf.up * defaultBodyOffsetFix; ////at 0,0,0, once again what is this crazy small offset? sphereCenter = meshRootTf.position + GetUserMoveTransform(meshRootTf); //at belly button - offset from meshroot } else { //For uncensor body mesh, and any clothing bodySphereCenterOffset = sphereCenter = _sphereCenter; //at belly button } if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" [KK only] corrected sphereCenter {_sphereCenter} isDefaultBody {isDefaultBody}"); } #elif HS2 || AI //Its so simple when its not KK default mesh :/ bodySphereCenterOffset = sphereCenter = _sphereCenter; #endif }
/// <summary> /// If the current characters mesh is set by the Uncensor plugin we need to know this to correctly shift the mesh's localspace vertex positions /// The LS positions for uncensor match that of HS2 and AI, but not the defulat KK body mesh (This took forever to track down!) /// </summary> internal static bool IsUncensorBody(ChaControl chaControl, string UncensorCOMName) { //grab the active uncensor controller of it exists var uncensorController = PregnancyPlusHelper.GetCharacterBehaviorController <CharaCustomFunctionController>(chaControl, UncensorCOMName); if (uncensorController == null) { return(false); } //Get the body type name, and see if it is the default mesh name var bodyData = uncensorController.GetType().GetProperty("BodyData")?.GetValue(uncensorController, null); if (bodyData == null) { return(false); } var bodyGUID = Traverse.Create(bodyData).Field("BodyGUID")?.GetValue <string>(); if (bodyGUID == null) { return(false); } return(bodyGUID != PregnancyPlusCharaController.DefaultBodyFemaleGUID && bodyGUID != PregnancyPlusCharaController.DefaultBodyMaleGUID); }
/// <summary> /// Loads a blendshape from character card and sets it to the correct mesh /// </summary> /// <param name="data">The characters card data for this plugin</param> internal void LoadBlendShapes(PregnancyPlusData data) { if (data.meshBlendShape == null) { return; } if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" MeshBlendShape size > {data.meshBlendShape.Length/1024}KB "); } meshWithBlendShapes = new List <SkinnedMeshRenderer>(); //Unserialize the blendshape from characters card var meshBlendShapes = MessagePack.LZ4MessagePackSerializer.Deserialize <List <MeshBlendShape> >(data.meshBlendShape); if (meshBlendShapes == null || meshBlendShapes.Count <= 0) { return; } //For each stores meshBlendShape foreach (var meshBlendShape in meshBlendShapes) { //Loop through all meshes and find matching name var clothRenderers = PregnancyPlusHelper.GetMeshRenderers(ChaControl.objClothes, true); LoopMeshAndAddExistingBlendShape(clothRenderers, meshBlendShape, true); //do the same for body meshs var bodyRenderers = PregnancyPlusHelper.GetMeshRenderers(ChaControl.objBody, true); LoopMeshAndAddExistingBlendShape(bodyRenderers, meshBlendShape); } }
/// <summary> /// Will reset all meshes stored in the mesh dictionaries to default positons /// </summary> internal void ResetInflation() { //Resets all mesh inflations var keyList = new List <string>(originalVertices.Keys); //For every active meshRenderer key we have created foreach (var renderKey in keyList) { var smr = PregnancyPlusHelper.GetMeshRenderer(ChaControl, renderKey); //Normally triggered when user changes clothes, the old clothes render wont be found if (smr == null) { if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogWarning($" ResetInflation > smr was not found {renderKey}"); } continue; } //Create an instance of sharedMesh so we don't modify the mesh shared between characters, that was a fun issue Mesh meshCopy = (Mesh)UnityEngine.Object.Instantiate(smr.sharedMesh); smr.sharedMesh = meshCopy; var sharedMesh = smr.sharedMesh; var hasValue = originalVertices.TryGetValue(renderKey, out Vector3[] origVerts); //On change clothes original verts become useless, so skip this if (!hasValue) { return; } //Some meshes are not readable and cant be touched... if (!sharedMesh.isReadable) { PregnancyPlusPlugin.errorCodeCtrl.LogErrorCode(ChaControl.chaID, ErrorCode.PregPlus_MeshNotReadable, $"ResetInflation > smr '{renderKey}' is not readable, skipping"); continue; } if (!sharedMesh || origVerts.Equals(null) || origVerts.Length == 0) { continue; } if (origVerts.Length != sharedMesh.vertexCount) { PregnancyPlusPlugin.errorCodeCtrl.LogErrorCode(ChaControl.chaID, ErrorCode.PregPlus_IncorrectVertCount, $"ResetInflation > smr '{renderKey}' has incorrect vert count {origVerts.Length}|{sharedMesh.vertexCount}"); continue; } sharedMesh.vertices = origVerts; sharedMesh.RecalculateBounds(); NormalSolver.RecalculateNormals(sharedMesh, 40f, alteredVerticieIndexes[renderKey]); //sharedMesh.RecalculateNormals(); //old way that leaves skin seams sharedMesh.RecalculateTangents(); } }
/// <summary> /// Get the distance from the characters feet to the belly button collapsed into a straight Y line.null (Ignores animation and scale, gives true measurement) /// </summary> internal float GetBellyButtonLocalHeight() { //Calculate the belly button height by getting each bone distance from foot to belly button (even during animation the height is correct!) #if KK var bbHeight = PregnancyPlusHelper.BoneChainStraigntenedDistance(ChaControl, bellyInfo.TotalCharScale, "cf_j_foot_L", "cf_j_waist01"); #elif HS2 || AI var bbHeight = PregnancyPlusHelper.BoneChainStraigntenedDistance(ChaControl, bellyInfo.TotalCharScale, "cf_J_Toes01_L", "cf_J_Kosi01"); #endif return(bbHeight); }
/// <summary> /// This will create a blendshape frame for a mesh, that can be used in timeline, required there be a renderKey for inflatedVertices for this smr /// </summary> /// <param name="smr">Target mesh renderer to update (original shape)</param> /// <param name="renderKey">The Shared Mesh render name, used in dictionary keys to get the current verticie values</param> /// <returns>Returns the MeshBlendShape that is created. Can be null</returns> internal MeshBlendShape CreateBlendShape(SkinnedMeshRenderer smr, string renderKey) { //Make a copy of the mesh. We dont want to affect the existing for this var meshCopyOrig = PregnancyPlusHelper.CopyMesh(smr.sharedMesh); if (!meshCopyOrig.isReadable) { PregnancyPlusPlugin.errorCodeCtrl.LogErrorCode(ChaControl.chaID, ErrorCode.PregPlus_MeshNotReadable, $"CreateBlendShape > smr '{renderKey}' is not readable, skipping"); return(null); } //Make sure we have an existing belly shape to work with (can be null if user hasnt used sliders yet) var exists = originalVertices.TryGetValue(renderKey, out var val); if (!exists) { if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo( $"CreateBlendShape > smr '{renderKey}' does not exists, skipping"); } return(null); } if (originalVertices[renderKey].Length != meshCopyOrig.vertexCount) { PregnancyPlusPlugin.errorCodeCtrl.LogErrorCode(ChaControl.chaID, ErrorCode.PregPlus_IncorrectVertCount, $"CreateBlendShape > smr.sharedMesh '{renderKey}' has incorrect vert count {originalVertices[renderKey].Length}|{meshCopyOrig.vertexCount}"); return(null); } //Calculate the original normals, but don't show them. We just want it for the blendshape shape destination meshCopyOrig.vertices = originalVertices[renderKey]; meshCopyOrig.RecalculateBounds(); NormalSolver.RecalculateNormals(meshCopyOrig, 40f, bellyVerticieIndexes[renderKey]); meshCopyOrig.RecalculateTangents(); // LogMeshBlendShapes(smr); //Create a blend shape object on the mesh var bsc = new BlendShapeController(meshCopyOrig, smr, $"{renderKey}_{PregnancyPlusPlugin.GUID}"); //Return the blendshape format that can be saved to character card return(ConvertToMeshBlendShape(smr.name, bsc.blendShape)); }
/// <summary> /// Get the root position of the mesh, so we can calculate the true position of its mesh verticies later /// </summary> internal void GetMeshRoot(out Transform meshRootTf, out float distanceMoved) { distanceMoved = 0f; meshRootTf = null; #if KK //Get normal mesh root attachment position, and if its not near 0,0,0 fix it so that it is (Match it to the chacontrol y pos) var kkMeshRoot = PregnancyPlusHelper.GetBoneGO(ChaControl, "p_cf_body_00.cf_o_root"); if (kkMeshRoot == null) { return; } //If the mesh root y is too far from the ChaControl origin if (ChaControl.transform.InverseTransformPoint(kkMeshRoot.transform.position).y > 0.01f) { // if (PregnancyPlusPlugin.DebugLog.Value) PregnancyPlusPlugin.Logger.LogInfo($"$ GetMeshRoot pos {kkMeshRoot.transform.position}"); // if (PregnancyPlusPlugin.DebugLog.Value) PregnancyPlusPlugin.Logger.LogInfo($"$ char pos {ChaControl.transform.position}"); distanceMoved = FastDistance(ChaControl.transform.position, kkMeshRoot.transform.position); if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" MeshRoot moved to charRoot by {distanceMoved}f"); } //Set the meshroot.pos to the chaControl.pos to make it more in line with HS2/AI, and KK Uncensor mesh kkMeshRoot.transform.position = ChaControl.transform.position; bellyInfo.MeshRootDidMove = true; // if (PregnancyPlusPlugin.DebugLog.Value) PregnancyPlusPlugin.Logger.LogInfo($"$ GetMeshRoot pos after {meshRoot.transform.position}"); } meshRootTf = kkMeshRoot.transform; #elif HS2 || AI //For HS2, get the equivalent position game object (near bellybutton) var meshRootGo = PregnancyPlusHelper.GetBoneGO(ChaControl, "p_cf_body_00.n_o_root"); if (meshRootGo == null) { return; } meshRootTf = meshRootGo.transform; #endif }
/// <summary> /// On user button click. Create blendshape from current belly state. Add it to infConfig so it will be saved to char card if the user chooses save scene /// </summary> /// <param name="temporary">If Temporary, the blendshape will not be saved to char card</param> /// <returns>boolean true if any blendshapes were created</returns> internal bool OnCreateBlendShapeSelected(bool temporary = false) { if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" "); } if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" OnCreateBlendShapeSelected "); } var meshBlendShapes = new List <MeshBlendShape>(); meshWithBlendShapes = new List <SkinnedMeshRenderer>(); //Get all cloth renderes and attempt to create blendshapes from preset inflatedVerticies var clothRenderers = PregnancyPlusHelper.GetMeshRenderers(ChaControl.objClothes); meshBlendShapes = LoopAndCreateBlendShape(clothRenderers, meshBlendShapes, true); //do the same for body meshs var bodyRenderers = PregnancyPlusHelper.GetMeshRenderers(ChaControl.objBody); meshBlendShapes = LoopAndCreateBlendShape(bodyRenderers, meshBlendShapes); //Save any meshBlendShapes to card if (!temporary) { AddBlendShapesToData(meshBlendShapes); } //Reset belly size to 0 so the blendshape can be used with out interference PregnancyPlusGui.ResetSlider(PregnancyPlusGui.inflationSize, 0); //Append the smrs that have new blendspahes to the GUI to be seen blendShapeGui.OnSkinnedMeshRendererBlendShapesCreated(meshWithBlendShapes); return(meshBlendShapes.Count > 0); }
/// <summary> /// Creates a mesh dictionary key based on mesh name and vert count. (because mesh names can be the same, vertex count makes it almost always unique) /// </summary> internal string GetMeshKey(SkinnedMeshRenderer smr) { return(PregnancyPlusHelper.KeyFromNameAndVerts(smr)); }
/// <summary> /// On Restore, set sliders to last non zero shape, and set characters belly state /// </summary> public static void OnRestore(List <MakerSlider> _sliders, PregnancyPlusData restoreToState = null) { if (!MakerAPI.InsideAndLoaded) { return; } if (_sliders == null || _sliders.Count <= 0 || !_sliders[0].Exists) { return; } var chaControl = MakerAPI.GetCharacterControl(); var charCustFunCtrl = PregnancyPlusHelper.GetCharacterBehaviorController <PregnancyPlusCharaController>(chaControl, PregnancyPlusPlugin.GUID); if (charCustFunCtrl == null) { return; } var _infConfig = restoreToState != null ? restoreToState : PregnancyPlusPlugin.lastBellyState; //For each slider, set to default which will reset the belly shape foreach (var slider in _sliders) { //Get the private slider object name from the game GUI var settingName = Traverse.Create(slider).Field("_settingName").GetValue <string>(); if (settingName == null) { continue; } if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" Restoring slider > {settingName}"); } //Set the correct slider with it's old config value switch (settingName) { #region Look away! im being lazy again case var _ when settingName == inflationSizeMaker: //Ohh boy, cant have const and static strings in switch case, thus this was created! slider.SetValue(_infConfig.inflationSize); continue; case var _ when settingName == inflationMultiplierMaker: slider.SetValue(_infConfig.inflationMultiplier); continue; case var _ when settingName == inflationMoveYMaker: slider.SetValue(_infConfig.inflationMoveY); continue; case var _ when settingName == inflationMoveZMaker: slider.SetValue(_infConfig.inflationMoveZ); continue; case var _ when settingName == inflationStretchXMaker: slider.SetValue(_infConfig.inflationStretchX); continue; case var _ when settingName == inflationStretchYMaker: slider.SetValue(_infConfig.inflationStretchY); continue; case var _ when settingName == inflationShiftYMaker: slider.SetValue(_infConfig.inflationShiftY); continue; case var _ when settingName == inflationShiftZMaker: slider.SetValue(_infConfig.inflationShiftZ); continue; case var _ when settingName == inflationTaperYMaker: slider.SetValue(_infConfig.inflationTaperY); continue; case var _ when settingName == inflationTaperZMaker: slider.SetValue(_infConfig.inflationTaperZ); continue; case var _ when settingName == inflationClothOffsetMaker: slider.SetValue(_infConfig.inflationClothOffset); continue; case var _ when settingName == inflationFatFoldMaker: slider.SetValue(_infConfig.inflationFatFold); continue; case var _ when settingName == inflationRoundnessMaker: slider.SetValue(_infConfig.inflationRoundness); continue; default: continue; #endregion } } }
/// <summary> /// Calculate the waist mesaurements that are used to set the default belly size /// </summary> /// <returns>Boolean if all measurements are valid</returns> internal bool MeasureWaist(ChaControl chaControl, Vector3 charScale, Vector3 nHeightScale, out float waistToRibDist, out float waistToBackThickness, out float waistWidth, out float bellyToBreastDist) { //Bone names #if KK var breastRoot = "cf_d_bust00"; var ribName = "cf_s_spine02"; var waistName = "cf_s_waist02"; var thighLName = "cf_j_thigh00_L"; var thighRName = "cf_j_thigh00_R"; var backName = "a_n_back"; var bellyButton = "cf_j_waist01"; #elif HS2 || AI var breastRoot = "cf_J_Mune00"; var ribName = "cf_J_Spine02_s"; var waistName = "cf_J_Kosi02_s"; var thighLName = "cf_J_LegUp00_L"; var thighRName = "cf_J_LegUp00_R"; var backName = "N_Back"; var bellyButton = "cf_J_Kosi01"; #endif //Init out params waistToRibDist = 0; waistToBackThickness = 0; waistWidth = 0; bellyToBreastDist = 0; //Get the characters Y bones to measure from var ribBone = PregnancyPlusHelper.GetBone(ChaControl, ribName); var waistBone = PregnancyPlusHelper.GetBone(ChaControl, waistName); if (ribBone == null || waistBone == null) { return(waistWidth > 0 && waistToBackThickness > 0 && waistToRibDist > 0); } //Measures from the wasist to the bottom of the ribs waistToRibDist = Vector3.Distance(waistBone.transform.InverseTransformPoint(waistBone.position), waistBone.transform.InverseTransformPoint(ribBone.position)); //Get the characters z waist thickness var backBone = PregnancyPlusHelper.GetBone(ChaControl, backName); var breastBone = PregnancyPlusHelper.GetBone(ChaControl, breastRoot); if (ribBone == null || breastBone == null) { return(waistWidth > 0 && waistToBackThickness > 0 && waistToRibDist > 0); } //Measures from breast root to the back spine distance waistToBackThickness = Math.Abs(breastBone.transform.InverseTransformPoint(backBone.position).z); //Get the characters X bones to measure from, in localspace to ignore n_height scale var thighLBone = PregnancyPlusHelper.GetBone(ChaControl, thighLName); var thighRBone = PregnancyPlusHelper.GetBone(ChaControl, thighRName); if (thighLBone == null || thighRBone == null) { return(waistWidth > 0 && waistToBackThickness > 0 && waistToRibDist > 0); } //Measures Left to right hip bone distance waistWidth = Vector3.Distance(thighLBone.transform.InverseTransformPoint(thighLBone.position), thighLBone.transform.InverseTransformPoint(thighRBone.position)); //Verts above this position are not allowed to move var bellyButtonBone = PregnancyPlusHelper.GetBone(ChaControl, bellyButton); //Distance from waist to breast root bellyToBreastDist = Math.Abs(bellyButtonBone.transform.InverseTransformPoint(ribBone.position).y) + Math.Abs(ribBone.transform.InverseTransformPoint(breastBone.position).y); if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" MeasureWaist Recalc "); } return(waistWidth > 0 && waistToBackThickness > 0 && waistToRibDist > 0); }
/// <summary> /// Get the characters waist width and calculate the appropriate belly sphere radius from it /// Smaller characters have smaller bellies, wider characters have wider bellies etc... /// </summary> /// <param name="chaControl">The character to measure</param> /// <param name="forceRecalc">For debuggin, will recalculate from scratch each time when true</param> /// <returns>Boolean if all measurements are valid</returns> internal bool MeasureWaistAndSphere(ChaControl chaControl, bool forceRecalc = false) { var bodyTopScale = PregnancyPlusHelper.GetBodyTopScale(ChaControl); var nHeightScale = PregnancyPlusHelper.GetN_HeightScale(ChaControl); var charScale = ChaControl.transform.localScale; var totalScale = new Vector3(bodyTopScale.x * charScale.x, bodyTopScale.y * charScale.y, bodyTopScale.z * charScale.z); var needsWaistRecalc = bellyInfo != null?bellyInfo.NeedsBoneDistanceRecalc(bodyTopScale, nHeightScale, charScale) : true; var needsSphereRecalc = bellyInfo != null?bellyInfo.NeedsSphereRecalc(infConfig, GetInflationMultiplier()) : true; //We should reuse existing measurements when we can, because characters waise bone distance chan change with animation, which affects belly size. if (bellyInfo != null) { if (!forceRecalc && needsSphereRecalc && !needsWaistRecalc)//Sphere radius calc needed { var _valid = MeasureSphere(chaControl, bodyTopScale, nHeightScale, totalScale); if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo(bellyInfo.Log()); } return(_valid); } else if (!forceRecalc && needsWaistRecalc && !needsSphereRecalc)//Measurements needed which also requires sphere recalc { var _valid = MeasureWaist(chaControl, charScale, nHeightScale, out float _waistToRibDist, out float _waistToBackThickness, out float _waistWidth, out float _bellyToBreastDist); MeasureSphere(chaControl, bodyTopScale, nHeightScale, totalScale); //Store all these values for reuse later bellyInfo = new BellyInfo(_waistWidth, _waistToRibDist, bellyInfo.SphereRadius, bellyInfo.OriginalSphereRadius, bodyTopScale, GetInflationMultiplier(), _waistToBackThickness, nHeightScale, _bellyToBreastDist, charScale, bellyInfo.MeshRootDidMove); if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo(bellyInfo.Log()); } return(_valid); } else if (!forceRecalc && !needsSphereRecalc && !needsWaistRecalc)//No changed needed { //Just return the original measurements and sphere radius when no updates needed if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo(bellyInfo.Log()); } //Measeurements are fine and can be reused if above 0 return(bellyInfo.WaistWidth > 0 && bellyInfo.SphereRadius > 0 && bellyInfo.WaistThick > 0); } } //Measeurements need to be recalculated from scratch if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" MeasureWaistAndSphere init "); } //Get waist measurements from bone distances var valid = MeasureWaist(chaControl, charScale, nHeightScale, out float waistToRibDist, out float waistToBackThickness, out float waistWidth, out float bellyToBreastDist); //Check for bad values if (!valid) { return(false); } //Calculate sphere radius based on distance from waist to ribs (seems big, but lerping later will trim much of it), added Math.Min for skinny waists var sphereRadius = GetSphereRadius(waistToRibDist, waistWidth, totalScale); var sphereRadiusMultiplied = sphereRadius * (GetInflationMultiplier() + 1); //Store all these values for reuse later bellyInfo = new BellyInfo(waistWidth, waistToRibDist, sphereRadiusMultiplied, sphereRadius, bodyTopScale, GetInflationMultiplier(), waistToBackThickness, nHeightScale, bellyToBreastDist, charScale); if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo(bellyInfo.Log()); } return(waistWidth > 0 && sphereRadiusMultiplied > 0 && waistToBackThickness > 0 && bellyToBreastDist > 0); }
/// <summary> /// Triggers belly mesh inflation for the current ChaControl for any active meshs (not hidden clothes) /// It will check the inflationSize dictionary for a valid value (last set via config slider or MeshInflate(value)) /// If size 0 is used it will clear all active mesh inflations /// This will not run twice for the same parameters, a change of config value is required /// </summary> /// <param name="checkForNewMesh">Lets you force bypass the check for values changed to check for new meshes</param> /// <param name="freshStart">Will recalculate verts like a first time run</param> /// <param name="pluginConfigSliderChanged">Will treat as if some slider values changed, which they did in global plugin config</param> /// <returns>Will return True if the mesh was altered and False if not</returns> public bool MeshInflate(bool checkForNewMesh = false, bool freshStart = false, bool pluginConfigSliderChanged = false) { if (ChaControl.objBodyBone == null) { return(false); //Make sure chatacter objs exists first } if (!PregnancyPlusPlugin.AllowMale.Value && ChaControl.sex == 0) { return(false); // Only female characters, unless plugin config says otherwise } var sliderHaveChanged = NeedsMeshUpdate(pluginConfigSliderChanged); //Only continue if one of the config values changed if (!sliderHaveChanged) { //Only stop here, if no recalculation needed if (!freshStart && !checkForNewMesh) { return(false); } } ResetInflation(); if (!AllowedToInflate()) { return(false); //if outside studio/maker, make sure StoryMode is enabled first } if (!infConfig.GameplayEnabled) { return(false); //Only if gameplay enabled } //Resets all stored vert values, so the script will have to recalculate all from base body if (freshStart) { CleanSlate(); } //Only continue when size above 0 if (infConfig.inflationSize <= 0) { infConfigHistory.inflationSize = 0; return(false); } if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" ---------- "); } if (PregnancyPlusPlugin.DebugLog.Value) { PregnancyPlusPlugin.Logger.LogInfo($" inflationSize > {infConfig.inflationSize} for {charaFileName} "); } //Get the measurements that determine the base belly size var hasMeasuerments = MeasureWaistAndSphere(ChaControl); if (!hasMeasuerments) { PregnancyPlusPlugin.errorCodeCtrl.LogErrorCode(ChaControl.chaID, ErrorCode.PregPlus_BadMeasurement, $"Could not get belly measurements from character"); return(false); } var anyMeshChanges = false; //Get and apply all clothes render mesh changes var clothRenderers = PregnancyPlusHelper.GetMeshRenderers(ChaControl.objClothes); anyMeshChanges = LoopAndApplyMeshChanges(clothRenderers, sliderHaveChanged, anyMeshChanges, true); //do the same for body meshs var bodyRenderers = PregnancyPlusHelper.GetMeshRenderers(ChaControl.objBody, true); anyMeshChanges = LoopAndApplyMeshChanges(bodyRenderers, sliderHaveChanged, anyMeshChanges); //If any changes were applied, updated the last used shape for the Restore GUI button if (infConfig.HasAnyValue()) { PregnancyPlusPlugin.lastBellyState = (PregnancyPlusData)infConfig.Clone();//CLone so we don't accidently overwright the lastState later } //Update config history when mesh changes were made if (anyMeshChanges) { infConfigHistory = (PregnancyPlusData)infConfig.Clone(); } return(anyMeshChanges); }