RotateParticleSystem use Warp

This commit is contained in:
ousttrue
2024-11-15 20:12:31 +09:00
parent bcf1991259
commit 77188fb148
13 changed files with 339 additions and 329 deletions

View File

@@ -0,0 +1,198 @@
using System.Collections.Generic;
using System.Linq;
using RotateParticle;
using RotateParticle.Components;
using SphereTriangle;
using UnityEngine;
namespace UniVRM10.Cloth.Viewer
{
public static class ClothGuess
{
public enum StrandConnectionType
{
Cloth,
ClothLoop,
Strand,
}
public static void Guess(RotateParticleSystem _system, Animator animator)
{
// skirt
{
if (TryAddGroup(animator, HumanBodyBones.Hips,
new[] { "skirt", "スカート", "スカート" }, out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.ClothLoop
}
}
{
if (TryAddGroupChildChild(animator, HumanBodyBones.Hips,
new[] { "skirt", "スカート", "スカート" }, new string[] { }, out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.ClothLoop,
}
}
{
if (TryAddGroup(animator, HumanBodyBones.Head,
new[] { "髪", "hair" }, out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.Strand,
}
}
{
if (TryAddGroup(animator, HumanBodyBones.Hips,
new[] { "裾" }, out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.Cloth,
}
}
{
if (TryAddGroupChildChild(animator, HumanBodyBones.LeftUpperArm,
new[] { "袖" }, new[] { "ひじ袖" }, out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.ClothLoop,
}
}
{
if (TryAddGroupChildChild(animator, HumanBodyBones.LeftLowerArm,
new[] { "袖" }, new string[] { }, out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.ClothLoop,
}
}
{
if (TryAddGroupChildChild(animator, HumanBodyBones.RightUpperArm,
new[] { "袖" }, new[] { "ひじ袖" }, out var g))
{
_system._warps.AddRange(g);
// , StrandConnectionType.ClothLoop
}
}
{
if (TryAddGroupChildChild(animator, HumanBodyBones.RightLowerArm,
new[] { "袖" }, new string[] { }, out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.ClothLoop, false,
}
}
{
if (TryAddGroup(animator, HumanBodyBones.Chest, new[] { "マント" },
out var g))
{
_system._warps.AddRange(g);
// StrandConnectionType.Cloth,
}
}
}
/// <summary>
///
/// </summary>
/// <param name="mask"></param>
/// <param name="name"></param>
/// <param name="animator"></param>
/// <param name="humanBone"></param>
/// <param name="targets"></param>
/// <param name="excludes"></param>
/// <param name="type"></param>
/// <param name="sort"></param>
/// <param name="group"></param>
/// <returns></returns>
static bool TryAddGroupChildChild(
Animator animator, HumanBodyBones humanBone,
string[] targets, string[] excludes,
out List<Warp> group)
{
var bone = animator.GetBoneTransform(humanBone);
if (bone == null)
{
Debug.LogWarning($"{humanBone} not found");
group = default;
return false;
}
List<Warp> transforms = new();
foreach (Transform child in bone)
{
foreach (Transform childchild in child)
{
if (excludes.Any(x => childchild.name.ToLower().Contains(x.ToLower())))
{
continue;
}
foreach (var target in targets)
{
if (childchild.name.ToLower().Contains(target.ToLower()))
{
var warp = childchild.gameObject.AddComponent<Warp>();
// Name = name,
// CollisionMask = mask,
warp.BaseSettings.HitRadius = 0.02f;
// Connection = type
transforms.Add(warp);
break;
}
}
}
}
if (transforms.Count == 0)
{
// Debug.LogWarning($"{string.Join(',', targets)} not found");
group = default;
return false;
}
group = transforms;
return true;
}
static bool TryAddGroup(Animator animator, HumanBodyBones humanBone, string[] targets,
out List<Warp> group)
{
var bone = animator.GetBoneTransform(humanBone);
if (bone == null)
{
Debug.LogWarning($"{humanBone} not found");
group = default;
return false;
}
List<Warp> transforms = new();
foreach (Transform child in bone)
{
foreach (var target in targets)
{
if (child.name.ToLower().Contains(target.ToLower()))
{
var warp = child.gameObject.AddComponent<Warp>();
// CollisionMask = mask,
warp.BaseSettings.HitRadius = 0.02f;
// Connection = type
transforms.Add(warp);
break;
}
}
}
if (transforms.Count == 0)
{
// Debug.LogWarning($"{string.Join(',', targets)} not found");
group = default;
return false;
}
group = transforms;
return true;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: efab592ca762fea46adb3c38b41e65d0
guid: 8ad944ee2631d2b4eb6a33e6bbcd349f
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -6,7 +6,8 @@
"GUID:8d76e605759c3f64a957d63ef96ada7c",
"GUID:1cd941934d098654fa21a13f28346412",
"GUID:e47c917724578cc43b5506c17a27e9a0",
"GUID:308b348fb80d89d42a9620951b0f60db"
"GUID:308b348fb80d89d42a9620951b0f60db",
"GUID:0138a2c7639266f45b948ea162125de5"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -0,0 +1,34 @@
using System.Collections;
using System.Collections.Generic;
using RotateParticle;
using UnityEngine;
namespace UniVRM10.Cloth.Viewer
{
public static class HumanCollider
{
static (string group, HumanBodyBones head, HumanBodyBones tail, float radius)[] Capsules = new[]
{
("Leg", HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg, 0.06f),
("Leg", HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot, 0.05f),
("Leg", HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg, 0.06f),
("Leg", HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot, 0.05f),
("Arm", HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, 0.03f),
("Arm", HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand, 0.03f),
("Arm", HumanBodyBones.LeftHand, HumanBodyBones.LeftMiddleProximal, 0.02f),
("Arm", HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm, 0.03f),
("Arm", HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand, 0.03f),
("Arm", HumanBodyBones.RightHand, HumanBodyBones.RightMiddleProximal, 0.02f),
};
public static void AddColliders(RotateParticleSystem _system, Animator animator)
{
foreach (var (group, head, tail, radius) in Capsules)
{
_system.AddColliderIfNotExists(group,
animator.GetBoneTransform(head), animator.GetBoneTransform(tail), radius);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6fd7b5b1e402644088796a17d7c2df9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,16 +1,5 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace SphereTriangle
namespace RotateParticle
{
public enum StrandConnectionType
{
Cloth,
ClothLoop,
Strand,
}
[System.Flags]
public enum CollisionGroupMask : uint
{
@@ -49,25 +38,4 @@ namespace SphereTriangle
Group32 = 0x80000000,
All = uint.MaxValue,
}
[Serializable]
public class StrandGroup
{
[SerializeField]
public string Name;
[SerializeField]
public CollisionGroupMask CollisionMask;
[SerializeField]
public StrandConnectionType Connection;
[SerializeField]
public List<Transform> Roots = new List<Transform>();
[SerializeField]
[Range(0.001f, 0.5f)]
public float DefaultStrandRaius = 0.05f;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ac7f1d83935aa84b9e80739be4ae02b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -10,7 +10,7 @@ namespace RotateParticle.Components
public class RectCloth : MonoBehaviour
{
[SerializeField]
public bool Loop = false;
public bool LoopIsClosed = false;
[SerializeField]
public List<Warp> Warps = new();

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using SphereTriangle;
using RotateParticle.Components;
using UnityEngine;
@@ -12,35 +12,28 @@ namespace RotateParticle
public List<RotateParticle> _particles = new();
public List<Transform> _particleTransforms = new();
public Strand MakeParticleStrand(SimulationEnv env, Transform t, float radius, CollisionGroupMask mask)
public Strand MakeParticleStrand(SimulationEnv env, Warp warp, CollisionGroupMask mask)
{
var strand = new Strand(mask);
_MakeParticlesRecursive(strand, env, 0, t, radius, null);
// root kinematic
var particle_index = _MakeAParticle(null, env, warp.transform, 0, 0, strand.CollisionMask);
var joint = _particles[particle_index];
strand.Particles.Add(joint);
foreach (var particle in warp.Particles)
{
var child_index = _MakeAParticle(joint, env, particle.Transform,
particle.GetSettings(warp.BaseSettings).HitRadius, 1, strand.CollisionMask);
var child = _particles[child_index];
joint.Children.Add(child);
joint = child;
}
return strand;
}
RotateParticle _MakeParticlesRecursive(Strand strand, SimulationEnv env, int child_index, Transform t, float radius, RotateParticle parent)
{
var mass = parent == null ? 0 : 1;
if (child_index > 0)
{
// 枝分かれ動かない
mass = 0;
}
var particle_index = _MakeAParticle(env, t, radius, parent, mass, strand.CollisionMask);
var joint = _particles[particle_index];
strand.Particles.Add(joint);
for (int i = 0; i < t.childCount; ++i)
{
var child = t.GetChild(i);
var child_joint = _MakeParticlesRecursive(strand, env, i, child, radius, joint);
joint.Children.Add(child_joint);
}
return joint;
}
int _MakeAParticle(SimulationEnv env, Transform t, float radius, RotateParticle parent, float mass, CollisionGroupMask collisionMask)
int _MakeAParticle(RotateParticle parent, SimulationEnv env, Transform t, float radius, float mass, CollisionGroupMask collisionMask)
{
var index = _particles.Count;
_particleTransforms.Add(t);

View File

@@ -1,19 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SphereTriangle;
using RotateParticle.Components;
using UniGLTF;
using UniGLTF.SpringBoneJobs.Blittables;
using UnityEngine;
using UniVRM10;
namespace RotateParticle
{
/// <summary>
/// SpcriptedImporter 経由の import 向け。
/// NativeArray の確保や DontDestroyOnLoad を回避。
/// </summary>
public class RotateParticleSpringboneRuntime : IVrm10SpringBoneRuntime
{
RotateParticleSystem _system;
@@ -24,12 +18,6 @@ namespace RotateParticle
public async Task InitializeAsync(Vrm10Instance instance, IAwaitCaller awaitCaller)
{
await awaitCaller.NextFrame();
// if (_setup != null)
// {
// _setup(instance);
// }
var animator = instance.GetComponent<Animator>();
if (animator == null)
{
@@ -48,211 +36,16 @@ namespace RotateParticle
_system.Env.DragForce = 0.6f;
_system.Env.Stiffness = 0.07f;
foreach (var (group, head, tail, radius) in Capsules)
foreach (var warp in instance.GetComponentsInChildren<Warp>())
{
_system.AddColliderIfNotExists(group,
animator.GetBoneTransform(head), animator.GetBoneTransform(tail), radius);
}
_system._warps.Add(warp);
// skirt
{
if (TryAddGroup(CollisionGroupMask.Group01, "Skirt", animator, HumanBodyBones.Hips, new[] { "skirt", "スカート", "スカート" }, StrandConnectionType.ClothLoop,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroupChildChild(CollisionGroupMask.Group01, "Skirt", animator, HumanBodyBones.Hips, new[] { "skirt", "スカート", "スカート" }, new string[] { }, StrandConnectionType.ClothLoop, false,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroup(CollisionGroupMask.Group02, "髪", animator, HumanBodyBones.Head, new[] { "髪", "hair" }, StrandConnectionType.Strand,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroup(CollisionGroupMask.Group01, "裾", animator, HumanBodyBones.Hips, new[] { "裾" }, StrandConnectionType.Cloth,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroupChildChild(CollisionGroupMask.Group02, "左袖", animator, HumanBodyBones.LeftUpperArm, new[] { "袖" }, new[] { "ひじ袖" }, StrandConnectionType.ClothLoop,
false,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroupChildChild(CollisionGroupMask.Group02, "左ひじ袖", animator, HumanBodyBones.LeftLowerArm, new[] { "袖" }, new string[] { }, StrandConnectionType.ClothLoop,
false,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroupChildChild(CollisionGroupMask.Group02, "右袖", animator, HumanBodyBones.RightUpperArm, new[] { "袖" }, new[] { "ひじ袖" }, StrandConnectionType.ClothLoop,
false,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroupChildChild(CollisionGroupMask.Group02, "右ひじ袖", animator, HumanBodyBones.RightLowerArm, new[] { "袖" }, new string[] { }, StrandConnectionType.ClothLoop,
false,
out var g))
{
_system._strandGroups.Add(g);
}
}
{
if (TryAddGroup(CollisionGroupMask.Group01 | CollisionGroupMask.Group02, "マント", animator, HumanBodyBones.Chest, new[] { "マント" }, StrandConnectionType.Cloth,
out var g))
{
_system._strandGroups.Add(g);
}
await awaitCaller.NextFrame();
}
_system.Initialize();
}
static (string group, HumanBodyBones head, HumanBodyBones tail, float radius)[] Capsules = new[]
{
("Leg", HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg, 0.06f),
("Leg", HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot, 0.05f),
("Leg", HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg, 0.06f),
("Leg", HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot, 0.05f),
("Arm", HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, 0.03f),
("Arm", HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand, 0.03f),
("Arm", HumanBodyBones.LeftHand, HumanBodyBones.LeftMiddleProximal, 0.02f),
("Arm", HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm, 0.03f),
("Arm", HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand, 0.03f),
("Arm", HumanBodyBones.RightHand, HumanBodyBones.RightMiddleProximal, 0.02f),
};
// T GetOrAddComponent<T>() where T : Component
// {
// var t = GetComponent<T>();
// if (t != null)
// {
// return t;
// }
// return gameObject.AddComponent<T>();
// }
bool TryAddGroup(CollisionGroupMask mask, string name, Animator animator, HumanBodyBones humanBone, string[] targets, StrandConnectionType type,
out StrandGroup group)
{
var bone = animator.GetBoneTransform(humanBone);
if (bone == null)
{
Debug.LogWarning($"{humanBone} not found");
group = default;
return false;
}
List<Transform> transforms = new();
foreach (Transform child in bone)
{
foreach (var target in targets)
{
if (child.name.ToLower().Contains(target.ToLower()))
{
transforms.Add(child);
break;
}
}
}
if (transforms.Count == 0)
{
// Debug.LogWarning($"{string.Join(',', targets)} not found");
group = default;
return false;
}
group = new StrandGroup
{
Name = name,
CollisionMask = mask,
DefaultStrandRaius = 0.02f,
Connection = type
};
group.Roots.AddRange(transforms);
// sort
group.Roots.Sort(new TransformSort(bone.position));
return true;
}
bool TryAddGroupChildChild(CollisionGroupMask mask, string name, Animator animator, HumanBodyBones humanBone,
string[] targets, string[] excludes,
StrandConnectionType type,
bool sort,
out StrandGroup group)
{
var bone = animator.GetBoneTransform(humanBone);
if (bone == null)
{
Debug.LogWarning($"{humanBone} not found");
group = default;
return false;
}
List<Transform> transforms = new();
foreach (Transform child in bone)
{
foreach (Transform childchild in child)
{
if (excludes.Any(x => childchild.name.ToLower().Contains(x.ToLower())))
{
continue;
}
foreach (var target in targets)
{
if (childchild.name.ToLower().Contains(target.ToLower()))
{
transforms.Add(childchild);
break;
}
}
}
}
if (transforms.Count == 0)
{
// Debug.LogWarning($"{string.Join(',', targets)} not found");
group = default;
return false;
}
group = new StrandGroup
{
Name = name,
CollisionMask = mask,
DefaultStrandRaius = 0.02f,
Connection = type
};
group.Roots.AddRange(transforms);
// sort
if (sort)
{
group.Roots.Sort(new TransformSort(bone.position));
}
return true;
}
public void Process()
{
_system.Process(Time.deltaTime);

View File

@@ -15,7 +15,7 @@ namespace RotateParticle
public SimulationEnv Env = new();
[SerializeField]
public List<StrandGroup> _strandGroups = new List<StrandGroup>();
public List<Components.Warp> _warps = new();
[SerializeField]
public List<ColliderGroup> _colliderGroups = new();
@@ -96,22 +96,53 @@ namespace RotateParticle
HashSet<int> _clothUsedParticles = new();
public void InitializeCloth(
StrandGroup g,
Components.RectCloth g,
ParticleList list,
List<Strand> strands,
List<(SpringConstraint, ClothRectCollision)> clothRects)
{
if (g.Connection == StrandConnectionType.Cloth || g.Connection == StrandConnectionType.ClothLoop)
for (int i = 1; i < strands.Count; ++i)
{
for (int i = 1; i < strands.Count; ++i)
var s0 = strands[i - 1];
var s1 = strands[i];
for (int j = 1; j < s0.Particles.Count && j < s1.Particles.Count; ++j)
{
var s0 = strands[i - 1];
var s1 = strands[i];
// d x x c
// | |
// a x-x b
var a = s0.Particles[j];
var b = s1.Particles[j];
var c = s1.Particles[j - 1];
var d = s0.Particles[j - 1];
_clothUsedParticles.Add(a.Init.Index);
_clothUsedParticles.Add(b.Init.Index);
_clothUsedParticles.Add(c.Init.Index);
_clothUsedParticles.Add(d.Init.Index);
if (i % 2 == 1)
{
// 互い違いに
// abcd to badc
(a, b) = (b, a);
(c, d) = (d, c);
}
clothRects.Add((
new SpringConstraint(
list._particles[a.Init.Index],
list._particles[b.Init.Index]),
new ClothRectCollision(
a.Init.Index, b.Init.Index, c.Init.Index, d.Init.Index)));
}
}
if (strands.Count >= 3)
{
if (g.LoopIsClosed)
{
var i = strands.Count;
var s0 = strands.Last();
var s1 = strands.First();
for (int j = 1; j < s0.Particles.Count && j < s1.Particles.Count; ++j)
{
// d x x c
// | |
// a x-x b
var a = s0.Particles[j];
var b = s1.Particles[j];
var c = s1.Particles[j - 1];
@@ -132,42 +163,9 @@ namespace RotateParticle
list._particles[a.Init.Index],
list._particles[b.Init.Index]),
new ClothRectCollision(
a.Init.Index, b.Init.Index, c.Init.Index, d.Init.Index)));
}
}
if (strands.Count >= 3)
{
if (g.Connection == StrandConnectionType.ClothLoop)
{
var i = strands.Count;
var s0 = strands.Last();
var s1 = strands.First();
for (int j = 1; j < s0.Particles.Count && j < s1.Particles.Count; ++j)
{
var a = s0.Particles[j];
var b = s1.Particles[j];
var c = s1.Particles[j - 1];
var d = s0.Particles[j - 1];
_clothUsedParticles.Add(a.Init.Index);
_clothUsedParticles.Add(b.Init.Index);
_clothUsedParticles.Add(c.Init.Index);
_clothUsedParticles.Add(d.Init.Index);
if (i % 2 == 1)
{
// 互い違いに
// abcd to badc
(a, b) = (b, a);
(c, d) = (d, c);
}
clothRects.Add((
new SpringConstraint(
list._particles[a.Init.Index],
list._particles[b.Init.Index]),
new ClothRectCollision(
a.Init.Index, b.Init.Index, c.Init.Index, d.Init.Index
)
));
}
a.Init.Index, b.Init.Index, c.Init.Index, d.Init.Index
)
));
}
}
}
@@ -175,20 +173,13 @@ namespace RotateParticle
public void Initialize()
{
foreach (var g in _strandGroups)
foreach (var g in _warps)
{
if (g.Roots?.Count == 0)
{
continue;
}
var strands = new List<Strand>();
foreach (var root in g.Roots)
{
var strand = _list.MakeParticleStrand(Env, root, g.DefaultStrandRaius, g.CollisionMask);
strands.Add(strand);
}
var strand = _list.MakeParticleStrand(Env, g, default);
strands.Add(strand);
InitializeCloth(g, _list, strands, _clothRects);
// InitializeCloth(g, _list, strands, _clothRects);
_strands.AddRange(strands);
}

View File

@@ -0,0 +1,3 @@
# SphereTriangle
球と三角形の衝突判定に関する補助ライブラリー

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 08a7b8ca5ff1079449b681d78a8f9c34
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: