// 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 ClampDistanceConstraint : PhysicsManagerConstraint
{
///
/// 最大最小距離拘束データ
/// todo:共有化可能
///
[System.Serializable]
public struct ClampDistanceData
{
///
/// 計算頂点インデックス
///
public ushort vertexIndex;
///
/// ターゲット頂点インデックス
///
public ushort targetVertexIndex;
///
/// パーティクル距離(v1.7.0)
///
public float length;
///
/// データが有効か判定する
///
///
public bool IsValid()
{
return vertexIndex > 0 || targetVertexIndex > 0;
}
}
FixedChunkNativeArray dataList;
///
/// 頂点インデックスごとの書き込みバッファ参照
///
FixedChunkNativeArray refDataList;
///
/// グループごとの拘束データ
///
public struct GroupData
{
public int teamId;
public int active;
///
/// 最小距離割合
///
public float minRatio;
///
/// 最大距離割合
///
public float maxRatio;
///
/// 速度影響
///
public float velocityInfluence;
public ChunkData dataChunk;
public ChunkData refChunk;
}
public FixedNativeList groupList;
//=========================================================================================
public override void Create()
{
dataList = new FixedChunkNativeArray();
refDataList = new FixedChunkNativeArray();
groupList = new FixedNativeList();
}
public override void Release()
{
dataList.Dispose();
refDataList.Dispose();
groupList.Dispose();
}
//=========================================================================================
public int AddGroup(int teamId, bool active, float minRatio, float maxRatio, float velocityInfluence, ClampDistanceData[] dataArray, ReferenceDataIndex[] refDataArray)
{
if (dataArray == null || dataArray.Length == 0 || refDataArray == null || refDataArray.Length == 0)
return -1;
var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
var gdata = new GroupData();
gdata.teamId = teamId;
gdata.active = active ? 1 : 0;
gdata.minRatio = minRatio;
gdata.maxRatio = maxRatio;
gdata.velocityInfluence = velocityInfluence;
gdata.dataChunk = dataList.AddChunk(dataArray.Length);
gdata.refChunk = refDataList.AddChunk(refDataArray.Length);
// チャンクデータコピー
dataList.ToJobArray().CopyFromFast(gdata.dataChunk.startIndex, dataArray);
refDataList.ToJobArray().CopyFromFast(gdata.refChunk.startIndex, refDataArray);
int group = groupList.Add(gdata);
return group;
}
public override void RemoveTeam(int teamId)
{
var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
int group = teamData.clampDistanceGroupIndex;
if (group < 0)
return;
var cdata = groupList[group];
// チャンクデータ削除
dataList.RemoveChunk(cdata.dataChunk);
refDataList.RemoveChunk(cdata.refChunk);
// データ削除
groupList.Remove(group);
}
public void ChangeParam(int teamId, bool active, float minRatio, float maxRatio, float velocityInfluence)
{
var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
int group = teamData.clampDistanceGroupIndex;
if (group < 0)
return;
var gdata = groupList[group];
gdata.active = active ? 1 : 0;
gdata.minRatio = minRatio;
gdata.maxRatio = maxRatio;
gdata.velocityInfluence = velocityInfluence;
groupList[group] = gdata;
}
//=========================================================================================
///
/// 拘束の解決
///
///
///
///
public override JobHandle SolverConstraint(int runCount, float dtime, float updatePower, int iteration, JobHandle jobHandle)
{
if (groupList.Count == 0)
return jobHandle;
// 最大最小距離拘束(パーティクルごとに実行する)
var job1 = new ClampDistanceJob()
{
runCount = runCount,
clampDistanceList = dataList.ToJobArray(),
groupList = groupList.ToJobArray(),
refDataList = refDataList.ToJobArray(),
teamDataList = Manager.Team.teamDataList.ToJobArray(),
teamIdList = Manager.Particle.teamIdList.ToJobArray(),
flagList = Manager.Particle.flagList.ToJobArray(),
nextPosList = Manager.Particle.InNextPosList.ToJobArray(),
basePosList = Manager.Particle.basePosList.ToJobArray(),
posList = Manager.Particle.posList.ToJobArray(),
frictionList = Manager.Particle.frictionList.ToJobArray(),
};
jobHandle = job1.Schedule(Manager.Particle.Length, 64, jobHandle);
return jobHandle;
}
///
/// 最大最小距離拘束ジョブ
/// パーティクルごとに計算
///
[BurstCompile]
struct ClampDistanceJob : IJobParallelFor
{
public int runCount;
[Unity.Collections.ReadOnly]
public NativeArray clampDistanceList;
[Unity.Collections.ReadOnly]
public NativeArray groupList;
[Unity.Collections.ReadOnly]
public NativeArray refDataList;
// チーム
[Unity.Collections.ReadOnly]
public NativeArray teamDataList;
[Unity.Collections.ReadOnly]
public NativeArray teamIdList;
[Unity.Collections.ReadOnly]
public NativeArray flagList;
[NativeDisableParallelForRestriction]
public NativeArray nextPosList;
[Unity.Collections.ReadOnly]
public NativeArray basePosList;
public NativeArray posList;
[Unity.Collections.ReadOnly]
public NativeArray frictionList;
// パーティクルごと
public void Execute(int index)
{
// 頂点フラグ
var flag = flagList[index];
if (flag.IsValid() == false || flag.IsFixed())
return;
// チーム
var team = teamDataList[teamIdList[index]];
if (team.IsActive() == false || team.clampDistanceGroupIndex < 0)
return;
// 更新確認
if (team.IsUpdate(runCount) == false)
return;
int pstart = team.particleChunk.startIndex;
int vindex = index - pstart;
// クロスごとの拘束データ
var gdata = groupList[team.clampDistanceGroupIndex];
if (gdata.active == 0)
return;
// アニメーションされた姿勢の使用
bool useAnimatedPose = team.IsFlag(PhysicsManagerTeamData.Flag_AnimatedPose);
var nextpos = nextPosList[index];
var basepos = basePosList[index];
// 参照データ情報
var refdata = refDataList[gdata.refChunk.startIndex + vindex];
if (refdata.count > 0)
{
int dataIndex = gdata.dataChunk.startIndex + refdata.startIndex;
ClampDistanceData data = clampDistanceList[dataIndex];
if (data.IsValid() == false)
return;
// ターゲット
int pindex2 = pstart + data.targetVertexIndex;
float3 nextpos2 = nextPosList[pindex2];
// 現在のベクトル
float3 v = nextpos - nextpos2;
// 復元長さ
float length = data.length; // v1.7.0
length *= team.scaleRatio; // チームスケール倍率
if (useAnimatedPose)
{
// アニメーションされた距離を使用
//length = math.distance(basepos, basePosList[pindex2]); // 現在のオリジナル距離
//length = math.max(math.distance(basepos, basePosList[pindex2]), length); // 長い方を採用する
length = (math.distance(basepos, basePosList[pindex2]) + length) * 0.5f; // 平均
}
// ベクトル長クランプ
v = MathUtility.ClampVector(v, length * gdata.minRatio, length * gdata.maxRatio);
// 位置
var opos = nextpos;
nextpos = nextpos2 + v;
// 摩擦係数から移動率を算出
float friction = frictionList[index];
float moveratio = math.saturate(1.0f - friction * Define.Compute.FrictionMoveRatio);
nextpos = math.lerp(opos, nextpos, moveratio);
// 書き出し
nextPosList[index] = nextpos;
// 速度影響
var av = (nextpos - opos) * (1.0f - gdata.velocityInfluence);
posList[index] = posList[index] + av;
}
}
}
}
}