// Magica Cloth.
// Copyright (c) MagicaSoft, 2020-2022.
// https://magicasoft.jp
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace MagicaCloth
{
///
/// ねじれ補正拘束
///
public class TwistConstraint : PhysicsManagerConstraint
{
///
/// 拘束データ
///
[System.Serializable]
public struct TwistData
{
///
/// 計算頂点インデックス
///
public ushort vertexIndex0;
public ushort vertexIndex1;
///
/// データが有効か判定する
///
///
public bool IsValid()
{
return vertexIndex0 > 0 || vertexIndex1 > 0;
}
}
FixedChunkNativeArray dataList;
///
/// 頂点インデックスごとのデータ参照
///
FixedChunkNativeArray refDataList;
///
/// データ参照ごとのグループインデックス
///
//FixedChunkNativeArray groupIndexList;
///
/// グループごとの拘束データ
///
public struct GroupData
{
public int teamId;
public int active;
public float recoveryPower;
public ChunkData dataChunk;
public ChunkData refChunk;
}
public FixedNativeList groupList;
//=========================================================================================
public override void Create()
{
dataList = new FixedChunkNativeArray();
//groupIndexList = new FixedChunkNativeArray();
refDataList = new FixedChunkNativeArray();
groupList = new FixedNativeList();
}
public override void Release()
{
dataList.Dispose();
//groupIndexList.Dispose();
refDataList.Dispose();
groupList.Dispose();
}
//=========================================================================================
public int AddGroup(int teamId, bool active, float recoveryPower, TwistData[] dataArray, ReferenceDataIndex[] refArray)
{
if (dataArray == null || dataArray.Length == 0)
return -1;
var gdata = new GroupData();
gdata.teamId = teamId;
gdata.active = active ? 1 : 0;
gdata.recoveryPower = recoveryPower;
gdata.dataChunk = dataList.AddChunk(dataArray.Length);
gdata.refChunk = refDataList.AddChunk(refArray.Length);
//groupIndexList.AddChunk(refArray.Length);
// チャンクデータコピー
dataList.ToJobArray().CopyFromFast(gdata.dataChunk.startIndex, dataArray);
refDataList.ToJobArray().CopyFromFast(gdata.refChunk.startIndex, refArray);
int group = groupList.Add(gdata);
// データ参照ごとのグループインデックス
//groupIndexList.Fill(gdata.refChunk, (short)group);
return group;
}
public override void RemoveTeam(int teamId)
{
var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
int group = teamData.twistGroupIndex;
if (group < 0)
return;
var cdata = groupList[group];
// チャンクデータ削除
dataList.RemoveChunk(cdata.dataChunk);
//groupIndexList.RemoveChunk(cdata.refChunk);
refDataList.RemoveChunk(cdata.refChunk);
// データ削除
groupList.Remove(group);
}
public void ChangeParam(int teamId, bool active, float recoveryPower)
{
var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
int group = teamData.twistGroupIndex;
if (group < 0)
return;
var gdata = groupList[group];
gdata.active = active ? 1 : 0;
gdata.recoveryPower = recoveryPower;
groupList[group] = gdata;
}
//=========================================================================================
///
/// 拘束の解決
///
///
///
///
public override JobHandle SolverConstraint(int runCount, float dtime, float updatePower, int iteration, JobHandle jobHandle)
{
if (groupList.Count == 0)
return jobHandle;
#if false
// ねじれ拘束
var job1 = new TwistJob()
{
runCount = runCount,
dataList = dataList.ToJobArray(),
groupIndexList = groupIndexList.ToJobArray(),
groupList = groupList.ToJobArray(),
teamDataList = Manager.Team.teamDataList.ToJobArray(),
//depthList = Manager.Particle.depthList.ToJobArray(),
//flagList = Manager.Particle.flagList.ToJobArray(),
basePosList = Manager.Particle.basePosList.ToJobArray(),
frictionList = Manager.Particle.frictionList.ToJobArray(),
nextPosList = Manager.Particle.InNextPosList.ToJobArray(),
posList = Manager.Particle.posList.ToJobArray(),
};
jobHandle = job1.Schedule(dataList.Length, 2, jobHandle); // 数は多くないので2
#endif
// ねじれ拘束
var job = new TwistJob2()
{
runCount = runCount,
updatePower = updatePower,
dataList = dataList.ToJobArray(),
refDataList = refDataList.ToJobArray(),
groupList = groupList.ToJobArray(),
teamDataList = Manager.Team.teamDataList.ToJobArray(),
teamIdList = Manager.Particle.teamIdList.ToJobArray(),
flagList = Manager.Particle.flagList.ToJobArray(),
basePosList = Manager.Particle.basePosList.ToJobArray(),
nextPosList = Manager.Particle.InNextPosList.ToJobArray(),
frictionList = Manager.Particle.frictionList.ToJobArray(),
outNextPosList = Manager.Particle.OutNextPosList.ToJobArray(),
posList = Manager.Particle.posList.ToJobArray(),
};
jobHandle = job.Schedule(Manager.Particle.Length, 64, jobHandle);
Manager.Particle.SwitchingNextPosList();
return jobHandle;
}
[BurstCompile]
struct TwistJob2 : IJobParallelFor
{
public int runCount;
public float updatePower;
[Unity.Collections.ReadOnly]
public NativeArray dataList;
[Unity.Collections.ReadOnly]
public NativeArray refDataList;
[Unity.Collections.ReadOnly]
public NativeArray groupList;
// team
[Unity.Collections.ReadOnly]
public NativeArray teamDataList;
[Unity.Collections.ReadOnly]
public NativeArray teamIdList;
// particle
[Unity.Collections.ReadOnly]
public NativeArray flagList;
[Unity.Collections.ReadOnly]
public NativeArray basePosList;
[Unity.Collections.ReadOnly]
public NativeArray nextPosList;
[Unity.Collections.ReadOnly]
public NativeArray frictionList;
[Unity.Collections.WriteOnly]
public NativeArray outNextPosList;
public NativeArray posList;
// パーティクルごと
public void Execute(int index)
{
// 初期化コピー
var nextpos0 = nextPosList[index];
outNextPosList[index] = nextpos0;
var flag = flagList[index];
if (flag.IsValid() == false || flag.IsFixed())
return;
var team = teamDataList[teamIdList[index]];
if (team.twistGroupIndex < 0)
return;
// 更新確認
if (team.IsUpdate(runCount) == false)
return;
// グループ
var gdata = groupList[team.twistGroupIndex];
if (gdata.active == 0)
return;
int pstart = team.particleChunk.startIndex;
int vindex = index - pstart;
// 参照情報
var refdata = refDataList[gdata.refChunk.startIndex + vindex];
if (refdata.count == 0)
return;
// 自身の情報
var basepos0 = basePosList[index];
var friction0 = frictionList[index];
float moveratio = math.saturate(1.0f - friction0 * Define.Compute.FrictionMoveRatio);
// 剛性
float stiffness = math.saturate(gdata.recoveryPower * updatePower);
// データ処理
float3 addpos = 0;
int addcnt = 0;
int dataIndex = gdata.dataChunk.startIndex + refdata.startIndex;
for (int i = 0; i < refdata.count; i++, dataIndex++)
{
var data = dataList[dataIndex];
if (data.IsValid() == false)
continue;
// ターゲットの情報
int tindex = pstart + data.vertexIndex1;
var nextpos1 = nextPosList[tindex];
var basepos1 = basePosList[tindex];
#if false
// 平面押出方式
var v = basepos1 - basepos0;
float len = math.length(v);
float3 n = math.normalize(v);
// 中央から各頂点への中間点
float3 cen = (nextpos0 + nextpos1) * 0.5f;
float3 cen0 = cen - n * (len * 0.25f);
float3 cen1 = cen + n * (len * 0.25f);
// 面に対してそれぞれの頂点を初期方向に押し出す
float3 outPos0;
MathUtility.IntersectPointPlane(cen0, -n, nextpos0, out outPos0);
//float3 outPos1;
//MathUtility.IntersectPointPlane(cen1, n, nextpos1, out outPos1);
// 剛性
// 基本最大にしておく
float stiffness0 = 1.0f;
//float stiffness1 = 1.0f;
// 最終移動量
float3 addPos0 = (outPos0 - nextpos0) * stiffness0;
//float3 addPos1 = (outPos1 - nextpos1) * stiffness1;
// 摩擦影響
addPos0 *= moveratio;
//addPos1 *= moveratio1;
float3 add = addPos0;
#else
// 方向ベクトル補間方式
// こちらのほうが安定していると思われる
// 本来のベクトル
float3 tv = basepos1 - basepos0;
// 現在のベクトル
float3 v = nextpos1 - nextpos0;
float3 cen = nextpos0 + v * 0.5f;
// 角度復元
var q = MathUtility.FromToRotation(v, tv, stiffness);
v = math.mul(q, v);
float3 fpos = cen - v * 0.5f;
float3 add = fpos - nextpos0;
add *= moveratio; // 摩擦影響
#endif
// 移動加算
addpos += add;
addcnt++;
}
// 書き出し
if (addcnt > 0)
{
addpos /= addcnt;
outNextPosList[index] = nextpos0 + addpos;
// 速度影響
const float influence = 0.1f; // とりあえず固定
posList[index] = posList[index] + (addpos * (1.0f - influence));
}
}
}
#if false
///
/// ねじれ拘束ジョブ
///
[BurstCompile]
struct TwistJob : IJobParallelFor
{
public int runCount;
[Unity.Collections.ReadOnly]
public NativeArray dataList;
[Unity.Collections.ReadOnly]
public NativeArray groupIndexList;
[Unity.Collections.ReadOnly]
public NativeArray groupList;
// チーム
[Unity.Collections.ReadOnly]
public NativeArray teamDataList;
//[Unity.Collections.ReadOnly]
//public NativeArray depthList;
//[Unity.Collections.ReadOnly]
//public NativeArray flagList;
[Unity.Collections.ReadOnly]
public NativeArray basePosList;
[Unity.Collections.ReadOnly]
public NativeArray frictionList;
[NativeDisableParallelForRestriction]
public NativeArray nextPosList;
[NativeDisableParallelForRestriction]
public NativeArray posList;
// 拘束データごと
public void Execute(int dataIndex)
{
var data = dataList[dataIndex];
if (data.IsValid() == false)
return;
// group
int groupIndex = groupIndexList[dataIndex];
var gdata = groupList[groupIndex];
if (gdata.active == 0)
return;
// team
int teamIndex = gdata.teamId;
var team = teamDataList[teamIndex];
if (team.IsActive() == false || team.twistGroupIndex < 0)
return;
// 更新確認
if (team.IsUpdate(runCount) == false)
return;
// particle
int pstart = team.particleChunk.startIndex;
int pindex0 = data.vertexIndex0 + pstart;
int pindex1 = data.vertexIndex1 + pstart;
float3 basePos0 = basePosList[pindex0];
float3 basePos1 = basePosList[pindex1];
float3 nextPos0 = nextPosList[pindex0];
float3 nextPos1 = nextPosList[pindex1];
// 面法線
//var v = basePos1 - basePos0;
//float len = math.length(v);
//float3 n = math.normalize(v);
var bv = basePos1 - basePos0;
bv = math.normalize(bv);
// 押出平面位置
// 中央から各頂点への中間点
float3 cen = (nextPos0 + nextPos1) * 0.5f;
//float3 cen0 = cen - n * (len * 0.25f);
//float3 cen1 = cen + n * (len * 0.25f);
var v = nextPos1 - nextPos0;
//v = math.normalize(v);
// 剛性
// 基本最大にしておく
//float stiffness0 = 1.0f;
//float stiffness1 = 1.0f;
float stiffness = 0.5f;
// 面に対してそれぞれの頂点を初期方向に押し出す
//float3 outPos0;
//MathUtility.IntersectPointPlane(cen0, -n, nextPos0, out outPos0);
//float3 outPos1;
//MathUtility.IntersectPointPlane(cen1, n, nextPos1, out outPos1);
var q = MathUtility.FromToRotation(v, bv, stiffness);
var rv = math.mul(q, v);
// 最終移動量
//float3 addPos0 = (outPos0 - nextPos0) * stiffness0;
//float3 addPos1 = (outPos1 - nextPos1) * stiffness1;
float3 fpos0 = cen - rv * 0.5f;
float3 fpos1 = cen + rv * 0.5f;
float3 addPos0 = fpos0 - nextPos0;
float3 addPos1 = fpos1 - nextPos1;
// 摩擦
float friction0 = frictionList[pindex0];
float friction1 = frictionList[pindex1];
float moveratio0 = math.saturate(1.0f - friction0 * Define.Compute.FrictionMoveRatio);
float moveratio1 = math.saturate(1.0f - friction1 * Define.Compute.FrictionMoveRatio);
//stiffness0 *= moveratio0;
//stiffness1 *= moveratio1;
addPos0 *= moveratio0;
addPos1 *= moveratio1;
// 書き込み
nextPos0 += addPos0;
nextPos1 += addPos1;
nextPosList[pindex0] = nextPos0;
nextPosList[pindex1] = nextPos1;
// 速度影響
const float influence = 0.5f; // 低く抑える(0.1)
posList[pindex0] = posList[pindex0] + addPos0 * (1.0f - influence);
posList[pindex1] = posList[pindex1] + addPos1 * (1.0f - influence);
}
}
#endif
}
}