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;
}
}