diff --git a/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.Main.cs b/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.Main.cs index b5abc9e..c51dfe9 100644 --- a/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.Main.cs +++ b/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.Main.cs @@ -226,6 +226,29 @@ internal bool GetInflatedVerticies(SkinnedMeshRenderer smr, float sphereRadius, var bellyVertIndex = md[rendererName].bellyVerticieIndexes; var alteredVerts = md[rendererName].alteredVerticieIndexes; + //Pre compute some values needed by SculptInflatedVerticie, doin it here saves on compute in the big loop + var vertsLength = origVerts.Length; + var sphereCenterLs = meshRootTf.InverseTransformPoint(sphereCenter); + var preMorphSphereCenter = sphereCenter - GetUserMoveTransform(meshRootTf); + var pmSphereCenterLs = meshRootTf.InverseTransformPoint(preMorphSphereCenter); + //calculate the furthest back morph point based on the back bone position, include character rotation + var backExtentPos = new Vector3(preMorphSphereCenter.x, sphereCenter.y, preMorphSphereCenter.z) + meshRootTf.forward * -bellyInfo.ZLimit; + var backExtentPosLs = meshRootTf.InverseTransformPoint(backExtentPos); + //calculate the furthest top morph point based under the breast position, include character animated height differences + var topExtentPos = new Vector3(preMorphSphereCenter.x, preMorphSphereCenter.y, preMorphSphereCenter.z) + meshRootTf.up * bellyInfo.YLimit; + var topExtentPosLs = meshRootTf.InverseTransformPoint(topExtentPos); + var vertNormalCaluRadius = sphereRadius + waistWidth/10;//Only recalculate normals for verts within this radius to prevent shadows under breast at small belly sizes + var yOffsetDir = Vector3.up * md[rendererName].yOffset;//Any offset direction needed to align all meshes to the same local y height + var reduceClothFlattenOffset = 0f; + + //I dont think transforms are thread safe so get the values we need now + var meshRootTfPos = meshRootTf.position; + var meshRootTfUp = meshRootTf.up; + var mrTfTransPt = meshRootTf.localToWorldMatrix; + var mrTfInvTransPt = mrTfTransPt.inverse; + var smrTfTransPt = smr.transform.localToWorldMatrix; + var smrTfInvTransPt = smrTfTransPt.inverse; + //Heavy compute task below, run in separate thread WaitCallback threadAction = (System.Object stateInfo) => { @@ -239,30 +262,14 @@ internal bool GetInflatedVerticies(SkinnedMeshRenderer smr, float sphereRadius, if (PregnancyPlusPlugin.DebugCalcs.Value) PregnancyPlusPlugin.Logger.LogInfo($" Mesh affected vert count {bellyVertsCount}"); #endif - //Pre compute some values needed by SculptInflatedVerticie, doin it here saves on compute in the big loop - var vertsLength = origVerts.Length; - var sphereCenterLs = meshRootTf.InverseTransformPoint(sphereCenter); - var preMorphSphereCenter = sphereCenter - GetUserMoveTransform(meshRootTf); - var pmSphereCenterLs = meshRootTf.InverseTransformPoint(preMorphSphereCenter); - //calculate the furthest back morph point based on the back bone position, include character rotation - var backExtentPos = new Vector3(preMorphSphereCenter.x, sphereCenter.y, preMorphSphereCenter.z) + meshRootTf.forward * -bellyInfo.ZLimit; - var backExtentPosLs = meshRootTf.InverseTransformPoint(backExtentPos); - //calculate the furthest top morph point based under the breast position, include character animated height differences - var topExtentPos = new Vector3(preMorphSphereCenter.x, preMorphSphereCenter.y, preMorphSphereCenter.z) + meshRootTf.up * bellyInfo.YLimit; - var topExtentPosLs = meshRootTf.InverseTransformPoint(topExtentPos); - var vertNormalCaluRadius = sphereRadius + waistWidth/10;//Only recalculate normals for verts within this radius to prevent shadows under breast at small belly sizes - var yOffsetDir = Vector3.up * md[rendererName].yOffset;//Any offset direction needed to align all meshes to the same local y height - var reduceClothFlattenOffset = 0f; - - //Set each verticies inflated postion, with some constraints (SculptInflatedVerticie) to make it look more natural for (int i = 0; i < vertsLength; i++) { //Only care about inflating belly verticies if (!bellyVertIndex[i] && !PregnancyPlusPlugin.DebugVerts.Value) continue; - //Convert to worldspace, and apply and mesh offset needed - var origVertWs = smr.transform.TransformPoint(origVerts[i] - yOffsetDir); + //Convert to worldspace (in a threadsafe way), and apply and mesh offset needed + var origVertWs = smrTfTransPt.MultiplyPoint3x4(origVerts[i] - yOffsetDir); var vertDistance = Vector3.Distance(origVertWs, sphereCenter); //Ignore verts outside the sphere radius @@ -287,13 +294,13 @@ internal bool GetInflatedVerticies(SkinnedMeshRenderer smr, float sphereRadius, //Make adjustments to the shape to make it smooth, and feed in user slider input inflatedVertWs = SculptInflatedVerticie(origVertWs, verticieToSpherePos, sphereCenter, waistWidth, - meshRootTf, preMorphSphereCenter, sphereRadius, backExtentPos, - topExtentPos, sphereCenterLs, pmSphereCenterLs, backExtentPosLs, + meshRootTf, mrTfTransPt, mrTfInvTransPt, meshRootTfPos, meshRootTfUp, + preMorphSphereCenter, sphereRadius, + backExtentPos, topExtentPos, sphereCenterLs, pmSphereCenterLs, backExtentPosLs, topExtentPosLs); //Convert back to local space, and undo any temporary offset - inflatedVerts[i] = smr.transform.InverseTransformPoint(inflatedVertWs) + yOffsetDir; - + inflatedVerts[i] = smrTfInvTransPt.MultiplyPoint3x4(inflatedVertWs) + yOffsetDir; } //When this thread task is complete, execute the below in main thread @@ -428,9 +435,10 @@ public float ApplyConditionalSphereCenterOffset(bool isClothingMesh, Vector3 _sp /// The center of the imaginary sphere /// The characters waist width that limits the width of the belly (future implementation) /// The transform used to convert a mesh vector from local space to worldspace and back, also servers as the point where we want to stop making mesh changes when Z < 0 - internal Vector3 SculptInflatedVerticie(Vector3 originalVerticeWs, Vector3 inflatedVerticieWs, Vector3 sphereCenterWs, - float waistWidth, Transform meshRootTf, Vector3 preMorphSphereCenterWs, float sphereRadius, - Vector3 backExtentPos, Vector3 topExtentPos, Vector3 sphereCenterLs, Vector3 pmSphereCenterLs, + internal Vector3 SculptInflatedVerticie(Vector3 originalVerticeWs, Vector3 inflatedVerticieWs, Vector3 sphereCenterWs, float waistWidth, + Transform meshRootTf, Matrix4x4 mrTfTransPt, Matrix4x4 mrTfInvTransPt, Vector3 meshRootTfPos, Vector3 meshRootTfUp, + Vector3 preMorphSphereCenterWs, float sphereRadius, Vector3 backExtentPos, Vector3 topExtentPos, + Vector3 sphereCenterLs, Vector3 pmSphereCenterLs, Vector3 backExtentPosLs, Vector3 topExtentPosLs) { //No smoothing modification in debug mode @@ -449,68 +457,68 @@ internal Vector3 SculptInflatedVerticie(Vector3 originalVerticeWs, Vector3 infla //Pre compute some constant Vert values so we dont have to do it for each transform //Most all of the measurements below are done in local space to ignore character rotation and position - var originalVerticeLs = meshRootTf.InverseTransformPoint(originalVerticeWs); - var inflatedVerticieLs = meshRootTf.InverseTransformPoint(inflatedVerticieWs); + var originalVerticeLs = mrTfInvTransPt.MultiplyPoint3x4(originalVerticeWs); + var inflatedVerticieLs = mrTfInvTransPt.MultiplyPoint3x4(inflatedVerticieWs); //Get the base shape with XY plane size limits - var smoothedVectorLs = SculptBaseShape(meshRootTf, originalVerticeLs, inflatedVerticieLs, sphereCenterLs); + var smoothedVectorLs = SculptBaseShape(originalVerticeLs, inflatedVerticieLs, sphereCenterLs); //Allow user adjustment of the height and width placement of the belly if (GetInflationShiftY() != 0 || GetInflationShiftZ() != 0) { - smoothedVectorLs = GetUserShiftTransform(meshRootTf, smoothedVectorLs, sphereCenterLs, skinToCenterDist); + smoothedVectorLs = GetUserShiftTransform(smoothedVectorLs, sphereCenterLs, skinToCenterDist); } //Allow user adjustment of the width of the belly if (GetInflationStretchX() != 0) { - smoothedVectorLs = GetUserStretchXTransform(meshRootTf, smoothedVectorLs, sphereCenterLs); + smoothedVectorLs = GetUserStretchXTransform(smoothedVectorLs, sphereCenterLs); } //Allow user adjustment of the height of the belly if (GetInflationStretchY() != 0) { - smoothedVectorLs = GetUserStretchYTransform(meshRootTf, smoothedVectorLs, sphereCenterLs); + smoothedVectorLs = GetUserStretchYTransform(smoothedVectorLs, sphereCenterLs); } if (GetInflationRoundness() != 0) { - smoothedVectorLs = GetUserRoundnessTransform(meshRootTf, originalVerticeLs, smoothedVectorLs, sphereCenterLs, skinToCenterDist); + smoothedVectorLs = GetUserRoundnessTransform(originalVerticeLs, smoothedVectorLs, sphereCenterLs, skinToCenterDist); } //Allow user adjustment of the egg like shape of the belly if (GetInflationTaperY() != 0) { - smoothedVectorLs = GetUserTaperYTransform(meshRootTf, smoothedVectorLs, sphereCenterLs, skinToCenterDist); + smoothedVectorLs = GetUserTaperYTransform(smoothedVectorLs, sphereCenterLs, skinToCenterDist); } //Allow user adjustment of the front angle of the belly if (GetInflationTaperZ() != 0) { - smoothedVectorLs = GetUserTaperZTransform(meshRootTf, originalVerticeLs, smoothedVectorLs, sphereCenterLs, skinToCenterDist, backExtentPosLs); + smoothedVectorLs = GetUserTaperZTransform(originalVerticeLs, smoothedVectorLs, sphereCenterLs, skinToCenterDist, backExtentPosLs); } //Allow user adjustment of the fat fold line through the middle of the belly if (GetInflationFatFold() > 0) { - smoothedVectorLs = GetUserFatFoldTransform(meshRootTf, originalVerticeLs, smoothedVectorLs, sphereCenterLs, sphereRadius); + smoothedVectorLs = GetUserFatFoldTransform(originalVerticeLs, smoothedVectorLs, sphereCenterLs, sphereRadius); } //If the user has selected a drop slider value if (GetInflationDrop() > 0) { - smoothedVectorLs = GetUserDropTransform(meshRootTf, smoothedVectorLs, sphereCenterLs, skinToCenterDist, sphereRadius); + smoothedVectorLs = GetUserDropTransform(meshRootTfUp, smoothedVectorLs, sphereCenterLs, skinToCenterDist, sphereRadius); } //After all user transforms are applied, remove the edges from the sides/top of the belly - smoothedVectorLs = RoundToSides(meshRootTf, originalVerticeLs, smoothedVectorLs, backExtentPosLs); + smoothedVectorLs = RoundToSides(originalVerticeLs, smoothedVectorLs, backExtentPosLs); //Less skin stretching under breast area with large slider values if (originalVerticeLs.y > pmSphereCenterLs.y) { - smoothedVectorLs = ReduceRibStretchingZ(meshRootTf, originalVerticeLs, smoothedVectorLs, topExtentPosLs); + smoothedVectorLs = ReduceRibStretchingZ(originalVerticeLs, smoothedVectorLs, topExtentPosLs); } // //Experimental, move more polygons to the front of the belly at max, Measured by trying to keep belly button size the same at 0 and max inflation size @@ -525,20 +533,20 @@ internal Vector3 SculptInflatedVerticie(Vector3 originalVerticeWs, Vector3 infla //At this point if the smoothed vector is the originalVector just return it - if (smoothedVectorLs.Equals(originalVerticeLs)) return meshRootTf.TransformPoint(smoothedVectorLs); + if (smoothedVectorLs.Equals(originalVerticeLs)) return mrTfTransPt.MultiplyPoint3x4(smoothedVectorLs); //**** All of the below are post mesh change checks to make sure the vertex position don't go outside of bounds //Smoothed vert back to worldspace - var smoothedVectorWs = meshRootTf.TransformPoint(smoothedVectorLs); + var smoothedVectorWs = mrTfTransPt.MultiplyPoint3x4(smoothedVectorLs); var currentVectorDistance = Math.Abs(Vector3.Distance(sphereCenterWs, smoothedVectorWs)); var pmCurrentVectorDistance = Math.Abs(Vector3.Distance(preMorphSphereCenterWs, smoothedVectorWs)); //Get core point on the same y plane as the original vert - var coreLineVertWs = meshRootTf.position + meshRootTf.up * (meshRootTf.InverseTransformPoint(originalVerticeWs).y * bellyInfo.TotalCharScale.y); + var coreLineVertWs = meshRootTfPos + meshRootTfUp * (mrTfInvTransPt.MultiplyPoint3x4(originalVerticeWs).y * bellyInfo.TotalCharScale.y); var origCoreDist = Math.Abs(Vector3.Distance(originalVerticeWs, coreLineVertWs));//Get line from feet to head that verts must respect distance from //Get core point on the same y plane as the smoothed vert - var coreLineSmoothedVertWs = meshRootTf.position + meshRootTf.up * (meshRootTf.InverseTransformPoint(smoothedVectorWs).y * bellyInfo.TotalCharScale.y); + var coreLineSmoothedVertWs = meshRootTfPos + meshRootTfUp * (mrTfInvTransPt.MultiplyPoint3x4(smoothedVectorWs).y * bellyInfo.TotalCharScale.y); var currentCoreDist = Math.Abs(Vector3.Distance(smoothedVectorWs, coreLineSmoothedVertWs)); //Don't allow any morphs to shrink towards the sphere center more than its original distance, only outward morphs allowed diff --git a/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.transforms.cs b/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.transforms.cs index 032d505..f776175 100644 --- a/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.transforms.cs +++ b/PregnancyPlus/PregnancyPlus.Core/PPCharaController.MeshInflation.transforms.cs @@ -25,7 +25,7 @@ internal Vector3 GetUserMoveTransform(Transform fromPosition) /// /// This will help pvent too much XY direction change, keeping the belly more round than disk like at large sizes /// - internal Vector3 SculptBaseShape(Transform meshRootTf, Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs) + internal Vector3 SculptBaseShape(Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs) { //We only want to limit expansion n XY plane for this lerp var sphereCenterXY = new Vector2(sphereCenterLs.x, sphereCenterLs.y); @@ -46,7 +46,7 @@ internal Vector3 SculptBaseShape(Transform meshRootTf, Vector3 originalVerticeLs /// /// This will shift the sphereCenter position *After sphereifying* on Y or Z axis (This stretches the mesh, where pre sphereifying, it would move the sphere within the mesh like Move Y) /// - internal Vector3 GetUserShiftTransform(Transform meshRootTf, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist) + internal Vector3 GetUserShiftTransform(Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist) { //IF the user has selected a y value, lerp the top and bottom slower and lerp any verts closer to z = 0 slower if (GetInflationShiftY() != 0) @@ -81,7 +81,7 @@ internal Vector3 GetUserShiftTransform(Transform meshRootTf, Vector3 smoothedVec /// /// This will stretch the belly mesh wider /// - internal Vector3 GetUserStretchXTransform(Transform meshRootTf, Vector3 smoothedVectorLs, Vector3 sphereCenterLs) + internal Vector3 GetUserStretchXTransform(Vector3 smoothedVectorLs, Vector3 sphereCenterLs) { //local Distance left or right from sphere center var distFromXCenterLs = smoothedVectorLs.x - sphereCenterLs.x; @@ -94,7 +94,7 @@ internal Vector3 GetUserStretchXTransform(Transform meshRootTf, Vector3 smoothed } - internal Vector3 GetUserStretchYTransform(Transform meshRootTf, Vector3 smoothedVectorLs, Vector3 sphereCenterLs) + internal Vector3 GetUserStretchYTransform(Vector3 smoothedVectorLs, Vector3 sphereCenterLs) { //Allow user adjustment of the height of the belly //local Distance up or down from sphere center @@ -112,7 +112,7 @@ internal Vector3 GetUserStretchYTransform(Transform meshRootTf, Vector3 smoothed /// /// This sill taper the belly shape based on user input slider. shrinking the top width, and expanding the bottom width along the YX adis /// - internal Vector3 GetUserTaperYTransform(Transform meshRootTf, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist) + internal Vector3 GetUserTaperYTransform(Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist) { //local Distance up or down from sphere center var distFromYCenterLs = smoothedVectorLs.y - sphereCenterLs.y; @@ -138,7 +138,7 @@ internal Vector3 GetUserTaperYTransform(Transform meshRootTf, Vector3 smoothedVe /// /// This sill taper the belly shape based on user input slider. pulling out the bottom and pushing in the top along the XZ axis /// - internal Vector3 GetUserTaperZTransform(Transform meshRootTf, Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, + internal Vector3 GetUserTaperZTransform(Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist, Vector3 backExtentPosLs) { //local Distance up or down from sphere center @@ -168,7 +168,7 @@ internal Vector3 GetUserTaperZTransform(Transform meshRootTf, Vector3 originalVe /// /// This will add a fat fold across the middle of the belly /// - internal Vector3 GetUserFatFoldTransform(Transform meshRootTf, Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float sphereRadius) + internal Vector3 GetUserFatFoldTransform(Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float sphereRadius) { var origSmoothVectorLs = smoothedVectorLs; var inflationFatFold = GetInflationFatFold(); @@ -201,7 +201,7 @@ internal Vector3 GetUserFatFoldTransform(Transform meshRootTf, Vector3 originalV /// /// This will make the front of the belly more, or less round /// - internal Vector3 GetUserRoundnessTransform(Transform meshRootTf, Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist) + internal Vector3 GetUserRoundnessTransform(Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist) { var zDistFromCenter = smoothedVectorLs.z - sphereCenterLs.z; @@ -222,18 +222,18 @@ internal Vector3 GetUserRoundnessTransform(Transform meshRootTf, Vector3 origina /// /// This Drop the belly down /// - internal Vector3 GetUserDropTransform(Transform meshRootTf, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist, float sphereRadius) + internal Vector3 GetUserDropTransform(Vector3 meshRootTfUp, Vector3 smoothedVectorLs, Vector3 sphereCenterLs, float skinToCenterDist, float sphereRadius) { //Move the verts closest to sphere center Z more slowly than verts at the belly button. Otherwise you stretch the ones near the body too much var lerpZ = Mathf.Lerp(0, GetInflationDrop(), (smoothedVectorLs.z - sphereCenterLs.z)/(bellyInfo.ScaledRadius(BellyDir.z) * 1.5f)); - return smoothedVectorLs + meshRootTf.up * -(sphereRadius * lerpZ); + return smoothedVectorLs + meshRootTfUp * -(sphereRadius * lerpZ); } /// /// Dampen any mesh changed near edged of the belly (sides, top, and bottom) to prevent too much vertex stretching. The more forward the vertex is from Z the more it's allowd to be altered by sliders /// - internal Vector3 RoundToSides(Transform meshRootTf, Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 backExtentPosLs) + internal Vector3 RoundToSides(Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 backExtentPosLs) { var origRad = bellyInfo.ScaledOrigRadius(BellyDir.z)/1.8f; var multipliedRad = bellyInfo.ScaledRadius(BellyDir.z)/2.5f; @@ -252,7 +252,7 @@ internal Vector3 RoundToSides(Transform meshRootTf, Vector3 originalVerticeLs, V /// /// Reduce the stretching of the skin at the top of the belly where it connects to the ribs at large Multiplier values /// - internal Vector3 ReduceRibStretchingZ(Transform meshRootTf, Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 topExtentPosLs) + internal Vector3 ReduceRibStretchingZ(Vector3 originalVerticeLs, Vector3 smoothedVectorLs, Vector3 topExtentPosLs) { //The distance from topExtent that we want to start lerping movement more slowly var topExtentOffset = topExtentPosLs.y/10;