// パーティクルごと public void Execute(int index) { var flag = flagList[index]; if (flag.IsValid() == false) { return; } // チームデータ int teamId = teamIdList[index]; var teamData = teamDataList[teamId]; // ここからは更新がある場合のみ実行 if (teamData.IsUpdate() == false) { return; } // 速度更新(m/s) if (flag.IsFixed() == false) { // 移動パーティクルのみ var nextPos = nextPosList[index]; var nextRot = nextRotList[index]; nextRot = math.normalize(nextRot); // 回転蓄積で精度が落ちていくので正規化しておく float3 velocity = 0; // posListには移動影響を考慮した最終座標が入っている var pos = posList[index]; // 速度更新(m/s) velocity = (nextPos - pos) / updateDeltaTime; velocity *= teamData.velocityWeight; // 安定化用の速度ウエイト // 摩擦による速度減衰 float friction = frictionList[index]; //friction *= teamData.friction; // チームごとの摩擦係数 velocity *= math.pow(1.0f - math.saturate(friction), updatePower); // 実際の移動速度(localPosに格納) var realVelocity = (nextPos - oldPosList[index]) / updateDeltaTime; var depth = depthList[index]; var maxVelocity = teamMaxVelocityList[teamId].Evaluate(depth); realVelocity = MathUtility.ClampVector(realVelocity, 0.0f, maxVelocity); // 最大速度は考慮する localPosList[index] = realVelocity; // 書き戻し velocityList[index] = velocity; oldPosList[index] = nextPos; oldRotList[index] = nextRot; } }
// ボーン //[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 index) { // 初期化コピー var nextpos = nextPosList[index]; outNextPosList[index] = nextpos; // 頂点フラグ 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() == false) { return; } int pstart = team.particleChunk.startIndex; int vindex = index - pstart; // クロスごとの拘束データ var gdata = groupList[team.clampDistanceGroupIndex]; if (gdata.active == 0) { return; } // 参照データ情報 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 = math.distance(basePosList[index], basePosList[pindex2]); // ベクトル長クランプ //v = MathUtility.ClampVector(v, data.length * gdata.minRatio, data.length * gdata.maxRatio); v = MathUtility.ClampVector(v, length * gdata.minRatio, length * gdata.maxRatio); // 位置 var opos = nextpos; nextpos = nextpos2 + v; // 書き出し outNextPosList[index] = nextpos; // 速度影響 var av = (nextpos - opos) * (1.0f - gdata.velocityInfluence); posList[index] = posList[index] + av; } }
// ルートラインごと 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; } }
// パーティクルごと 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() == 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; // todo: ClampPosition最大距離クランプ、これはあまり良くないかも //if (team.IsFlag(PhysicsManagerTeamData.Flag_IgnoreClampPositionVelocity) == false) // v = MathUtility.ClampVector(v, 0.0f, maxMoveLength); // 移動位置 var opos = nextpos; var fpos = opos + v; // 摩擦係数による移動制限(衝突しているパーティクルは動きづらい) nextpos = math.lerp(opos, fpos, moveratio); // test //nextpos = fpos; // 書き込み nextPosList[index] = nextpos; // 速度影響 var av = (nextpos - opos) * (1.0f - gdata.velocityInfluence); posList[index] = posList[index] + av; }
// ボーン //[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.IsFixed()) { // キネマティックパーティクル // OldPos/Rot から BasePos/Rot に step で補間して現在姿勢とする float stime = teamData.startTime + updateDeltaTime * teamData.runCount; float oldtime = teamData.time - teamData.addTime; float step = math.saturate((stime - oldtime) / teamData.addTime); nextPos = math.lerp(oldPosList[index], basePosList[index], step); nextRot = math.slerp(oldRotList[index], baseRotList[index], step); } 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]; // massは主に伸縮を中心に調整されるので、フォース適用時は少し調整する mass = (mass - 1.0f) * teamData.forceMassInfluence + 1.0f; // 最大速度 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; } // 速度計算(質量で割る) velocity += (force / mass) * updateDeltaTime; // 速度を理想位置に反映させる nextPos = oldpos + velocity * updateDeltaTime; } // 予定座標更新 ============================================================== // 摩擦減衰 var friction = frictionList[index]; friction = friction * Define.Compute.FrictionDampingRate; frictionList[index] = friction; // 移動前の姿勢 posList[index] = oldpos; rotList[index] = oldrot; // 予測位置 nextPosList[index] = nextPos; nextRotList[index] = nextRot; }
// ルートラインごと public void Execute(int rootIndex) { // チーム int teamIndex = rootTeamList[rootIndex]; if (teamIndex == 0) { return; } var team = teamDataList[teamIndex]; if (team.IsActive() == false || team.clampDistance2GroupIndex < 0) { return; } // 更新確認 if (team.IsUpdate() == false) { return; } // グループデータ var gdata = groupList[team.clampDistance2GroupIndex]; 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; 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 || flag.IsFixed()) { continue; } var npos = nextPosList[index]; var opos = npos; var ppos = nextPosList[pindex]; // 現在のベクトル var v = npos - ppos; // 長さクランプ var len = data.length * team.scaleRatio; v = MathUtility.ClampVector(v, len * gdata.minRatio, len * gdata.maxRatio); npos = ppos + v; // 格納 nextPosList[index] = npos; // 速度影響 var av = (npos - opos) * (1.0f - gdata.velocityInfluence); posList[index] = posList[index] + av; } }
// パーティクルごと public void Execute(int index) { // 初期化コピー var nextpos = nextPosList[index]; outNextPosList[index] = nextpos; // 頂点フラグ 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() == false) { return; } int pstart = team.particleChunk.startIndex; int vindex = index - pstart; // クロスごとの拘束データ var gdata = groupList[team.clampDistanceGroupIndex]; if (gdata.active == 0) { return; } // 参照データ情報 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 (length == 0.0f) //{ // // 従来データ(basePosの長さから) // length = math.distance(basePosList[index], basePosList[pindex2]); //} // ベクトル長クランプ 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); // 書き出し outNextPosList[index] = nextpos; // 速度影響 var av = (nextpos - opos) * (1.0f - gdata.velocityInfluence); posList[index] = posList[index] + av; } }