// Magica Cloth. // Copyright (c) MagicaSoft, 2020-2022. // https://magicasoft.jp using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; namespace MagicaCloth { /// /// 回転クランプ拘束[Algorithm 1] /// public class ClampRotationConstraint : PhysicsManagerConstraint { /// /// 拘束データ /// [System.Serializable] public struct ClampRotationData { /// /// 計算頂点インデックス /// public int vertexIndex; /// /// 親頂点インデックス /// public int parentVertexIndex; /// /// 親から自身への本来のローカル方向(単位ベクトル)(v1.7.0) /// public float3 localPos; /// /// 親から自身への本来のローカル回転(v1.7.0) /// public quaternion localRot; /// /// データが有効か判定する /// /// public bool IsValid() { return vertexIndex > 0 || parentVertexIndex > 0; } } //========================================================================================= // Algorithm 1 //========================================================================================= FixedChunkNativeArray dataList; [System.Serializable] public struct ClampRotationRootInfo { public ushort startIndex; public ushort dataLength; } FixedChunkNativeArray rootInfoList; /// /// グループごとの拘束データ /// public struct GroupData { public int teamId; public int active; /// /// 最大角度 /// public CurveParam maxAngle; /// /// 速度影響 /// public float velocityInfluence; public ChunkData dataChunk; public ChunkData rootInfoChunk; } public FixedNativeList groupList; /// /// ルートごとのチームインデックス /// FixedChunkNativeArray rootTeamList; /// /// 拘束データごとの作業バッファ /// FixedChunkNativeArray lengthBuffer; //========================================================================================= public override void Create() { dataList = new FixedChunkNativeArray(); rootInfoList = new FixedChunkNativeArray(); groupList = new FixedNativeList(); rootTeamList = new FixedChunkNativeArray(); lengthBuffer = new FixedChunkNativeArray(); } public override void Release() { dataList.Dispose(); rootInfoList.Dispose(); groupList.Dispose(); rootTeamList.Dispose(); lengthBuffer.Dispose(); } //========================================================================================= public int AddGroup( int teamId, bool active, BezierParam maxAngle, float velocityInfluence, ClampRotationData[] dataArray, ClampRotationRootInfo[] rootInfoArray ) { if (dataArray == null || dataArray.Length == 0 || rootInfoArray == null || rootInfoArray.Length == 0) return -1; //var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId]; var gdata = new GroupData(); gdata.teamId = teamId; gdata.active = active ? 1 : 0; gdata.maxAngle.Setup(maxAngle); gdata.velocityInfluence = velocityInfluence; gdata.dataChunk = dataList.AddChunk(dataArray.Length); gdata.rootInfoChunk = rootInfoList.AddChunk(rootInfoArray.Length); // チャンクデータコピー dataList.ToJobArray().CopyFromFast(gdata.dataChunk.startIndex, dataArray); rootInfoList.ToJobArray().CopyFromFast(gdata.rootInfoChunk.startIndex, rootInfoArray); int group = groupList.Add(gdata); // ルートごとのチームインデックス var c = rootTeamList.AddChunk(rootInfoArray.Length); rootTeamList.Fill(c, teamId); // 作業バッファ lengthBuffer.AddChunk(dataArray.Length); return group; } public override void RemoveTeam(int teamId) { var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId]; // Algorithm 1 int group1 = teamData.clampRotationGroupIndex; if (group1 >= 0) { var cdata = groupList[group1]; // チャンクデータ削除 dataList.RemoveChunk(cdata.dataChunk); rootInfoList.RemoveChunk(cdata.rootInfoChunk); rootTeamList.RemoveChunk(cdata.rootInfoChunk); lengthBuffer.RemoveChunk(cdata.dataChunk); // データ削除 groupList.Remove(group1); } } public void ChangeParam(int teamId, bool active, BezierParam maxAngle, float velocityInfluence) { var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId]; int group = teamData.clampRotationGroupIndex; if (group < 0) return; var gdata = groupList[group]; gdata.active = active ? 1 : 0; gdata.maxAngle.Setup(maxAngle); gdata.velocityInfluence = velocityInfluence; groupList[group] = gdata; } //========================================================================================= /// /// 拘束の解決 /// /// /// /// public override JobHandle SolverConstraint(int runCount, float dtime, float updatePower, int iteration, JobHandle jobHandle) { //======================================================= // Algorithm 1 //======================================================= if (groupList.Count > 0) { // 回転拘束(ルートラインごと) var job1 = new ClampRotationJob() { runCount = runCount, maxMoveLength = dtime * Define.Compute.ClampRotationMaxVelocity, // 最大1.0m/s dataList = dataList.ToJobArray(), rootInfoList = rootInfoList.ToJobArray(), rootTeamList = rootTeamList.ToJobArray(), groupList = groupList.ToJobArray(), teamDataList = Manager.Team.teamDataList.ToJobArray(), flagList = Manager.Particle.flagList.ToJobArray(), //basePosList = Manager.Particle.basePosList.ToJobArray(), //baseRotList = Manager.Particle.baseRotList.ToJobArray(), depthList = Manager.Particle.depthList.ToJobArray(), frictionList = Manager.Particle.frictionList.ToJobArray(), nextPosList = Manager.Particle.InNextPosList.ToJobArray(), nextRotList = Manager.Particle.InNextRotList.ToJobArray(), posList = Manager.Particle.posList.ToJobArray(), lengthBuffer = lengthBuffer.ToJobArray(), }; jobHandle = job1.Schedule(rootTeamList.Length, 8, jobHandle); } return jobHandle; } //========================================================================================= // Algorithm 1 //========================================================================================= /// /// 回転クランプ拘束ジョブ /// [BurstCompile] struct ClampRotationJob : IJobParallelFor { public int runCount; public float maxMoveLength; [Unity.Collections.ReadOnly] public NativeArray dataList; [Unity.Collections.ReadOnly] public NativeArray rootInfoList; [Unity.Collections.ReadOnly] public NativeArray rootTeamList; [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 baseRotList; [Unity.Collections.ReadOnly] public NativeArray frictionList; [NativeDisableParallelForRestriction] public NativeArray nextPosList; [NativeDisableParallelForRestriction] public NativeArray nextRotList; [NativeDisableParallelForRestriction] public NativeArray posList; [NativeDisableParallelForRestriction] public NativeArray lengthBuffer; // ルートラインごと public void Execute(int rootIndex) { // チーム int teamIndex = rootTeamList[rootIndex]; if (teamIndex == 0) return; var team = teamDataList[teamIndex]; if (team.IsActive() == false || team.clampRotationGroupIndex < 0) return; // 更新確認 if (team.IsUpdate(runCount) == false) return; // グループデータ var gdata = groupList[team.clampRotationGroupIndex]; if (gdata.active == 0) return; // データ var rootInfo = rootInfoList[rootIndex]; int dataIndex = rootInfo.startIndex + gdata.dataChunk.startIndex; int dataCount = rootInfo.dataLength; int pstart = team.particleChunk.startIndex; // (1)現在の親からのベクトル長を保持する for (int i = 0; i < dataCount; i++) { var data = dataList[dataIndex + i]; int pindex = data.parentVertexIndex; if (pindex < 0) continue; var index = data.vertexIndex; index += pstart; pindex += pstart; var npos = nextPosList[index]; var ppos = nextPosList[pindex]; // 現在ベクトル長 float vlen = math.distance(npos, ppos); lengthBuffer[dataIndex + i] = vlen; } // (2)回転角度制限 for (int i = 0; i < dataCount; i++) { var data = dataList[dataIndex + i]; int pindex = data.parentVertexIndex; if (pindex < 0) continue; var index = data.vertexIndex; index += pstart; pindex += pstart; var flag = flagList[index]; if (flag.IsValid() == false) continue; var npos = nextPosList[index]; var nrot = nextRotList[index]; var opos = npos; var ppos = nextPosList[pindex]; var prot = nextRotList[pindex]; float depth = depthList[index]; //float stiffness = gdata.stiffness.Evaluate(depth); // 本来のローカルpos/rotを算出する //var bpos = basePosList[index]; //var brot = baseRotList[index]; //var pbpos = basePosList[pindex]; //var pbrot = baseRotList[pindex]; //float3 bv = math.normalize(bpos - pbpos); //var ipbrot = math.inverse(pbrot); //float3 localPos = math.mul(ipbrot, bv); //quaternion localRot = math.mul(ipbrot, brot); // 本来の方向ベクトル //float3 tv = math.mul(prot, localPos); //float3 tv = math.mul(prot, data.localPos); // v1.7.0 float3 tv = math.mul(prot, data.localPos * team.scaleDirection); // マイナススケール対応(v1.7.6) // ベクトル長 float vlen = math.distance(npos, ppos); // 最新の距離(※これは伸びる場合があるが、一番安定している) float blen = lengthBuffer[dataIndex + i]; // 計算前の距離 vlen = math.clamp(vlen, 0.0f, blen * 1.2f); // 現在ベクトル float3 v = math.normalize(npos - ppos); // ベクトル角度クランプ float maxAngle = gdata.maxAngle.Evaluate(depth); maxAngle = math.radians(maxAngle); float angle = math.acos(math.dot(v, tv)); if (flag.IsFixed() == false) { if (angle > maxAngle) { MathUtility.ClampAngle(v, tv, maxAngle, out v); } var mv = (ppos + v * vlen) - npos; // 最大速度クランプ mv = MathUtility.ClampVector(mv, 0.0f, maxMoveLength); var fpos = npos + mv; // 摩擦係数から移動率を算出 float friction = frictionList[index]; float moveratio = math.saturate(1.0f - friction * Define.Compute.FrictionMoveRatio); // 摩擦係数による移動制限(衝突しているパーティクルは動きづらい) npos = math.lerp(npos, fpos, moveratio); nextPosList[index] = npos; // 現在ベクトル更新(v1.8.0) v = math.normalize(npos - ppos); // 速度影響 var av = (npos - opos) * (1.0f - gdata.velocityInfluence); posList[index] = posList[index] + av; } // 回転補正 nrot = math.mul(prot, new quaternion(data.localRot.value * team.quaternionScale)); // マイナススケール対応(v1.7.6) var q = MathUtility.FromToRotation(tv, v); nrot = math.mul(q, nrot); nextRotList[index] = nrot; } } } } }