/// <summary> /// 角度制限 /// </summary> /// <param name="data"></param> /// <param name="cindex"></param> /// <param name="nextpos"></param> /// <param name="outpos"></param> /// <param name="ang"></param> /// <returns></returns> private bool AnglePenetration(ref PenetrationData data, int cindex, float3 nextpos, out float3 outpos, float ang) { var cpos = nextPosList[cindex]; var crot = nextRotList[cindex]; // スケール var tindex = transformIndexList[cindex]; var cscl = boneSclList[tindex]; //float scl = cscl.x; // X軸を採用(基本的には均等スケールのみを想定) // 押し出し平面を求める var c = math.mul(crot, data.localPos * cscl) + cpos; var n = math.mul(crot, data.localDir); var v = nextpos - c; float3 v2; if (MathUtility.ClampAngle(v, n, ang, out v2)) { outpos = c + v2; return(true); } outpos = nextpos; return(false); }
// ボーン //[Unity.Collections.ReadOnly] //public NativeArray<quaternion> boneRotList; // パーティクルごと public void Execute(int index) { var flag = flagList[index]; if (flag.IsValid() == false) { return; } // チームデータ int teamId = teamIdList[index]; var teamData = teamDataList[teamId]; // ここからは更新がある場合のみ実行(グローバルチームは除く) if (teamId != 0 && teamData.IsUpdate() == false) { return; } var oldpos = oldPosList[index]; var oldrot = oldRotList[index]; float3 nextPos = oldpos; quaternion nextRot = oldrot; //if (flag.IsCollider()) //{ // // コライダー // // todo:こっちのほうがよい? // nextPos = basePosList[index]; // nextRot = baseRotList[index]; //} if (flag.IsFixed()) { // キネマティックパーティクル // nextPos/nextRotが前回の姿勢 var oldNextPos = nextPosList[index]; var oldNextRot = nextRotList[index]; // OldPos/Rot から BasePos/Rot に step で補間して現在姿勢とする #if true // 高フレームレートで位置がスキップされてしまう問題の対処(1.8.3) float stime = teamData.startTime + updateDeltaTime * teamData.runCount; float oldtime = teamData.oldTime; float interval = teamData.time - oldtime; float step = math.saturate((stime - oldtime) / interval); #else float stime = teamData.startTime + updateDeltaTime * teamData.runCount; float oldtime = teamData.time - teamData.addTime; float step = math.saturate((stime - oldtime) / teamData.addTime); #endif nextPos = math.lerp(oldpos, basePosList[index], step); nextRot = math.slerp(oldrot, baseRotList[index], step); // 前回の姿勢をoldpos/rotとしてposList/rotListに格納する if (flag.IsCollider() && teamId == 0) { // グローバルコライダー // 移動量と回転量に制限をかける(1.7.5) // 制限をかけないと高速移動/回転時に遠く離れたパーティクルが押し出されてしまう問題が発生する。 oldpos = MathUtility.ClampDistance(nextPos, oldNextPos, Define.Compute.GlobalColliderMaxMoveDistance); oldrot = MathUtility.ClampAngle(nextRot, oldNextRot, math.radians(Define.Compute.GlobalColliderMaxRotationAngle)); } else { oldpos = oldNextPos; oldrot = oldNextRot; } // debug //nextPos = basePosList[index]; //nextRot = baseRotList[index]; } else { // 動的パーティクル var depth = depthList[index]; var maxVelocity = teamMaxVelocityList[teamId].Evaluate(depth); var drag = teamDragList[teamId].Evaluate(depth); var gravity = teamGravityList[teamId].Evaluate(depth); var mass = teamMassList[teamId].Evaluate(depth); var velocity = velocityList[index]; // チームスケール倍率 maxVelocity *= teamData.scaleRatio; // massは主に伸縮を中心に調整されるので、フォース適用時は少し調整する mass = (mass - 1.0f) * teamData.forceMassInfluence + 1.0f; // 安定化用の速度ウエイト velocity *= teamData.velocityWeight; // 最大速度 velocity = MathUtility.ClampVector(velocity, 0.0f, maxVelocity); // 空気抵抗(90ups基準) // 重力に影響させたくないので先に計算する(※通常はforce適用後に行うのが一般的) velocity *= math.pow(1.0f - drag, updatePower); // フォース // フォースは空気抵抗を無視して加算する float3 force = 0; // 重力 // 重力は質量に関係なく一定 #if false // 方向減衰 if (teamData.IsFlag(PhysicsManagerTeamData.Flag_DirectionalDamping) && teamData.directionalDampingBoneIndex >= 0) { float3 dampDir = math.mul(boneRotList[teamData.directionalDampingBoneIndex], teamData.directionalDampingLocalDir); var dot = math.dot(dampDir, new float3(0, -1, 0)) * 0.5f + 0.5f; // 1.0(0) - 0.5(90) - 0.0(180) var damp = teamDirectionalDampingList[teamId].Evaluate(dot); gravity *= damp; } #endif // (最後に質量で割るためここでは質量をかける) force.y += gravity * mass; // 外部フォース if (loopIndex == 0) { switch (teamData.forceMode) { case PhysicsManagerTeamData.ForceMode.VelocityAdd: force += teamData.impactForce; break; case PhysicsManagerTeamData.ForceMode.VelocityAddWithoutMass: force += teamData.impactForce * mass; break; case PhysicsManagerTeamData.ForceMode.VelocityChange: force += teamData.impactForce; velocity = 0; break; case PhysicsManagerTeamData.ForceMode.VelocityChangeWithoutMass: force += teamData.impactForce * mass; velocity = 0; break; } // 外力 force += teamData.externalForce; } // 外力チームスケール倍率 force *= teamData.scaleRatio; // 速度計算(質量で割る) velocity += (force / mass) * updateDeltaTime; // 速度を理想位置に反映させる nextPos = oldpos + velocity * updateDeltaTime; } // 予定座標更新 ============================================================== // 摩擦減衰 var friction = frictionList[index]; friction = friction * Define.Compute.FrictionDampingRate; frictionList[index] = friction; //frictionList[index] = 0; // 移動前の姿勢 posList[index] = oldpos; rotList[index] = oldrot; // 予測位置 nextPosList[index] = nextPos; nextRotList[index] = nextRot; // コリジョン用 //velocityList[index] = nextPos; }
// ルートラインごと 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() == 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) { //v = MathUtility.ClampAngle(v, tv, maxAngle); MathUtility.ClampAngle(v, tv, maxAngle, out v); } var mv = (ppos + v * vlen) - npos; // 最大速度クランプ mv = MathUtility.ClampVector(mv, 0.0f, maxMoveLength); // debug //var ln = math.length(mv); //if (ln >= maxMoveLength) // Debug.Log($"mv={ln}"); 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, localRot); //nrot = math.mul(prot, data.localRot); // v1.7.0 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; } }