Obi Official Forum
Softbody for a certain region - Printable Version

+- Obi Official Forum (https://obi.virtualmethodstudio.com/forum)
+-- Forum: Obi Users Category (https://obi.virtualmethodstudio.com/forum/forum-1.html)
+--- Forum: Obi Softbody (https://obi.virtualmethodstudio.com/forum/forum-12.html)
+--- Thread: Softbody for a certain region (/thread-3860.html)



Softbody for a certain region - Spartan190 - 08-05-2023

Hi,

I'm currently working on a game where we want to have wiggly boobs for the character. The problem seems to be that the connection between the vertices I select by script and the particles I get at the and aren't working well.
My approach is the following: like if I would create the blueprint by hand I selected certain vertices that are connected to the bones of the boobs. Then I invert the selected particles and use the RemoveSelectedParticles method with the optimize to true.
The remaining selected particles I then use for the "chest" group which will be attaches to the body so they don't wiggle/move. Sadly the results aren't working well. There is particles that still wiggle even they they should be part of the body and others which are part of the boobs don't move at all.

Maybe you can also help me that I might be able to create the particles on my own and place particles based on the vertices I have:

This screenshot shows the particles that are created:
[attachment=1726]

This screenshot shows the vertices that I have from the bones:
[attachment=1727]

This is the vertices that have been found in the vertexToParticles list after all the optimization etc.
[attachment=1728]

Here is the code I'm using:

Code:
using System.Collections;
using UnityEngine;
using Obi;
using ExternalPropertyAttributes;
using System;
using CurvedUI;
using System.Collections.Generic;
using System.Linq;

public class BoobsInteraction : MonoBehaviour
{
    [System.Serializable]
    public class BonesWithWeights
    {
        public string BoneName;
        public float WeightThreshold;
    }

    public Transform UpperChest;
    public SkinnedMeshRenderer BodyRenderer;
    public List<SkinnedMeshRenderer> AdditionalRenderers = new List<SkinnedMeshRenderer>();
    public ObiSoftbody SofbodyPrefab;
    public float DefaultBoobsHeight = 1.13f;
    public List<BonesWithWeights> bonesThresholds = new List<BonesWithWeights>()
    {
        new BonesWithWeights() { BoneName = "Bust1", WeightThreshold = 0.75f},
        new BonesWithWeights() { BoneName = "Bust2", WeightThreshold = 0.0f }
    };

    private ObiParticleAttachment _attachment;

    private struct BoneWithThreshold {
        public int BoneIndex;
        public float Threshold;
    }


        [Button]
    public void Initialize()
    {
        if (UpperChest == null) throw new ArgumentNullException($"Property {nameof(UpperChest)} is not set");
        if (BodyRenderer == null) throw new ArgumentNullException($"Property {nameof(BodyRenderer)} is not set");
        if (SofbodyPrefab == null) throw new ArgumentNullException($"Property {nameof(SofbodyPrefab)} is not set");


        ObiSoftbody softbodyInstance = GameObject.Instantiate(SofbodyPrefab);
        softbodyInstance.transform.position = BodyRenderer.transform.position;
        try
        {
            softbodyInstance.softbodyBlueprint = SetupBlueprint();
        } catch (Exception ex)
        {
            Debug.LogException(ex, this);
            float delta = BodyRenderer.transform.position.y + DefaultBoobsHeight - UpperChest.position.y;
            softbodyInstance.transform.position = BodyRenderer.transform.position - new Vector3(0.0f, delta, 0.0f);
        }

        _attachment = softbodyInstance.AddComponentIfMissing<ObiParticleAttachment>();
        _attachment.target = UpperChest;

        var bodySkinner = BodyRenderer.AddComponentIfMissing<ObiSoftbodySkinner>();
        bodySkinner.Source = softbodyInstance;
        bodySkinner.m_SkinningMaxDistance = 0.05f;

        IEnumerator bind = bodySkinner.BindSkin();
        while (bind.MoveNext()) { }

        foreach (var renderer in AdditionalRenderers)
        {
            var additionalSkinner = renderer.AddComponentIfMissing<ObiSoftbodySkinner>();
            additionalSkinner.Source = softbodyInstance;
            additionalSkinner.m_SkinningMaxDistance = 0.05f;

            IEnumerator additionalBind = additionalSkinner.BindSkin();
            while (additionalBind.MoveNext()) { }
        }

        softbodyInstance.transform.SetParent(BodyRenderer.transform);
        softbodyInstance.transform.localPosition = Vector3.zero;
    }

    private ObiSoftbodySurfaceBlueprint SetupBlueprint()
    {
        var blueprint = ScriptableObject.CreateInstance<ObiSoftbodySurfaceBlueprint>();

        // set the blueprint's particle radius and cluster radius:
        blueprint.inputMesh = BodyRenderer.sharedMesh;
        blueprint.surfaceSamplingMode = ObiSoftbodySurfaceBlueprint.SurfaceSamplingMode.Vertices;
        blueprint.surfaceResolution = 48;
        blueprint.GenerateImmediate();

        List<BoneWithThreshold> boneIndeces = new List<BoneWithThreshold>();
        for (int i = 0; i < BodyRenderer.bones.Length; i++)
        {
            for(int j=0; j < bonesThresholds.Count; j++)
            {
                if (BodyRenderer.bones[i].name.Contains(bonesThresholds[j].BoneName))
                {
                    boneIndeces.Add(new BoneWithThreshold()
                    {
                        BoneIndex = i,
                        Threshold = bonesThresholds[j].WeightThreshold
                    });
                }

            }
        }

        Mesh mesh = BodyRenderer.sharedMesh;

        HashSet<int> bonesParticles = new();

        for (int i = 0; i < mesh.vertexCount; i++)
        {
            BoneWeight boneWeight = mesh.boneWeights[i];
            for(int j=0; j < boneIndeces.Count; j++)
            {
                if(boneIndeces[j].BoneIndex == boneWeight.boneIndex0 && boneWeight.weight0 >= boneIndeces[j].Threshold ||
                    boneIndeces[j].BoneIndex == boneWeight.boneIndex1 && boneWeight.weight1 >= boneIndeces[j].Threshold ||
                    boneIndeces[j].BoneIndex == boneWeight.boneIndex2 && boneWeight.weight2 >= boneIndeces[j].Threshold ||
                    boneIndeces[j].BoneIndex == boneWeight.boneIndex3 && boneWeight.weight3 >= boneIndeces[j].Threshold)
                {
                    bonesParticles.Add(blueprint.vertexToParticle[i]);
                    var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube.transform.localScale = Vector3.one * 0.01f;
                    cube.transform.position = BodyRenderer.transform.TransformPoint(mesh.vertices[i]);
                    cube.transform.SetParent(BodyRenderer.transform, true);
                }
            }
        }

        bool[] selectedParticles = new bool[blueprint.particleCount];

        for (int i = 0; i < blueprint.particleCount; i++)
        {
            if (bonesParticles.Contains(i))
            {
                selectedParticles[i] = false;
            }
            else
            {
                selectedParticles[i] = true;
            }
        }
        blueprint.distanceConstraintsData = new();
        blueprint.bendConstraintsData = new();
        blueprint.RemoveSelectedParticles(ref selectedParticles, true);
        var group = blueprint.AppendNewParticleGroup("chest");
        for (int i = 0; i < selectedParticles.Length; i++)
        {
            if (selectedParticles[i])
            {
                group.particleIndices.Add(i);
            }
        }

        return blueprint;
    }

}


Best regards,

Spartan190