// Magica Cloth. // Copyright (c) MagicaSoft, 2020-2022. // https://magicasoft.jp using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; namespace MagicaCloth { /// /// 移動範囲制限距離拘束 /// nextposおよびposを原点からの距離制限する /// public class ClampPositionConstraint : PhysicsManagerConstraint { /// /// グループごとの拘束データ /// public struct GroupData { public int teamId; public int active; public CurveParam limitLength; /// /// 軸ごとの移動制限割合(0.0-1.0) /// public float3 axisRatio; /// /// 速度影響 /// public float velocityInfluence; /// /// データが軸ごとの制限を行うか判定する /// /// public bool IsAxisCheck() { return axisRatio.x < 0.999f || axisRatio.y < 0.999f || axisRatio.z < 0.999f; } } public FixedNativeList groupList; //========================================================================================= public override void Create() { groupList = new FixedNativeList(); } public override void Release() { groupList.Dispose(); } //========================================================================================= public int AddGroup(int teamId, bool active, BezierParam limitLength, float3 axisRatio, float velocityInfluence) { var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId]; var gdata = new GroupData(); gdata.teamId = teamId; gdata.active = active ? 1 : 0; gdata.limitLength.Setup(limitLength); gdata.axisRatio = axisRatio; gdata.velocityInfluence = velocityInfluence; int group = groupList.Add(gdata); return group; } public override void RemoveTeam(int teamId) { var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId]; int group = teamData.clampPositionGroupIndex; if (group < 0) return; // データ削除 groupList.Remove(group); } public void ChangeParam(int teamId, bool active, BezierParam limitLength, float3 axisRatio, float velocityInfluence) { var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId]; int group = teamData.clampPositionGroupIndex; if (group < 0) return; var gdata = groupList[group]; gdata.active = active ? 1 : 0; gdata.limitLength.Setup(limitLength); gdata.axisRatio = axisRatio; 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 ClampPositionJob() { runCount = runCount, maxMoveLength = dtime * Define.Compute.ClampPositionMaxVelocity, // 最大1.0m/s clampPositionGroupList = groupList.ToJobArray(), teamDataList = Manager.Team.teamDataList.ToJobArray(), teamIdList = Manager.Particle.teamIdList.ToJobArray(), flagList = Manager.Particle.flagList.ToJobArray(), depthList = Manager.Particle.depthList.ToJobArray(), basePosList = Manager.Particle.basePosList.ToJobArray(), baseRotList = Manager.Particle.baseRotList.ToJobArray(), frictionList = Manager.Particle.frictionList.ToJobArray(), nextPosList = Manager.Particle.InNextPosList.ToJobArray(), posList = Manager.Particle.posList.ToJobArray(), }; jobHandle = job1.Schedule(Manager.Particle.Length, 64, jobHandle); return jobHandle; } /// /// 移動範囲制限拘束ジョブ /// パーティクルごとに計算 /// [BurstCompile] struct ClampPositionJob : IJobParallelFor { public int runCount; // 最大移動距離 public float maxMoveLength; [Unity.Collections.ReadOnly] public NativeArray clampPositionGroupList; [Unity.Collections.ReadOnly] public NativeArray teamDataList; [Unity.Collections.ReadOnly] public NativeArray teamIdList; [Unity.Collections.ReadOnly] public NativeArray flagList; [Unity.Collections.ReadOnly] public NativeArray depthList; [Unity.Collections.ReadOnly] public NativeArray basePosList; [Unity.Collections.ReadOnly] public NativeArray baseRotList; [Unity.Collections.ReadOnly] public NativeArray frictionList; public NativeArray nextPosList; public NativeArray posList; // パーティクルごと 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) return; if (team.clampPositionGroupIndex < 0) return; // 更新確認 if (team.IsUpdate(runCount) == false) return; // グループデータ var gdata = clampPositionGroupList[team.clampPositionGroupIndex]; if (gdata.active == 0) return; var nextpos = nextPosList[index]; var depth = depthList[index]; var limitLength = gdata.limitLength.Evaluate(depth); // チームスケール倍率 limitLength *= team.scaleRatio; // baseposからの最大移動距離制限 var basepos = basePosList[index]; var v = nextpos - basepos; // nextpos // 摩擦係数から移動率を算出 var friction = frictionList[index]; float moveratio = math.saturate(1.0f - friction * Define.Compute.FrictionMoveRatio); if (gdata.IsAxisCheck()) { // 楕円体判定 float3 axisRatio = gdata.axisRatio; // 基準軸のワールド回転 quaternion rot = baseRotList[index]; // 基準軸のローカルベクトルへ変換 quaternion irot = math.inverse(rot); float3 lv = math.mul(irot, v); // Boxクランプ float3 axisRatio1 = axisRatio * limitLength; lv = math.clamp(lv, -axisRatio1, axisRatio1); // 基準軸のワールドベクトルへ変換 // 最終的に(v)が楕円体でクランプされた移動制限ベクトルとなる v = math.mul(rot, lv); } // nextposの制限 v = MathUtility.ClampVector(v, 0.0f, limitLength); // 最大速度クランプ v = (basepos + v) - nextpos; // 移動位置 var opos = nextpos; var fpos = opos + v; // 摩擦係数による移動制限(衝突しているパーティクルは動きづらい) nextpos = math.lerp(opos, fpos, moveratio); // 書き込み nextPosList[index] = nextpos; // 速度影響 var av = (nextpos - opos) * (1.0f - gdata.velocityInfluence); posList[index] = posList[index] + av; } } } }