// Magica Cloth. // Copyright (c) MagicaSoft, 2020-2022. // https://magicasoft.jp using Unity.Mathematics; using UnityEngine; namespace MagicaCloth { /// /// 風データ /// public class PhysicsManagerWindData : PhysicsManagerAccess { /// /// 風タイプ /// public enum WindType { None = 0, Direction, Area, } /// /// 形状タイプ /// public enum ShapeType { Box = 0, Sphere = 1, } /// /// 風向き /// public enum DirectionType { OneDirection = 0, // 一定方向 Radial = 1, // 放射状 } /// /// 風フラグビット /// public const uint Flag_Enable = 0x00000001; // 有効フラグ public const uint Flag_Addition = 0x00000002; // 加算モード /// /// 風データ /// public struct WindData { /// /// フラグビットデータ /// public uint flag; /// /// 風タイプ /// public WindType windType; /// /// 形状 /// public ShapeType shapeType; /// /// 連動トランスフォームインデックス /// public int transformIndex; /// /// 風エリアのサイズ(トランスフォームのローカル軸サイズ) /// 球形の場合はxに半径 /// public float3 areaSize; /// /// 風量 /// public float main; /// /// 乱流率(0.0-1.0) /// public float turbulence; /// /// 振動の周期(1.0が基準) /// public float frequency; /// /// 風の中心位置(-1.0 - +1.0) /// //public float3 anchor; /// /// 現在の風の方向(ローカル) /// public float3 direction; /// /// 風向きのタイプ /// public DirectionType directionType; /// /// 風エリアの体積 /// public float areaVolume; /// /// 風エリアの最大距離 /// public float areaLength; /// /// 減衰カーブ /// public CurveParam attenuation; /// /// フラグ判定 /// /// /// public bool IsFlag(uint flag) { return (this.flag & flag) != 0; } /// /// フラグ設定 /// /// /// public void SetFlag(uint flag, bool sw) { if (sw) this.flag |= flag; else this.flag &= ~flag; } /// /// 有効フラグの設定 /// /// public void SetEnable(bool sw) { if (sw) flag |= Flag_Enable; else flag &= ~Flag_Enable; } /// /// データが有効か判定する /// /// public bool IsActive() { return (flag & Flag_Enable) != 0; } } //========================================================================================= /// /// 風データリスト /// public FixedNativeList windDataList; //========================================================================================= /// /// 初期設定 /// public override void Create() { windDataList = new FixedNativeList(); } /// /// 破棄 /// public override void Dispose() { if (windDataList == null) return; windDataList.Dispose(); } //========================================================================================= public int CreateWind( WindType windType, ShapeType shapeType, float3 areaSize, bool addition, float main, float turbulence, float frequency, float3 direction, DirectionType directinType, float areaVolume, float areaLength, BezierParam attenuation ) { var data = new WindData(); uint flag = Flag_Enable; flag |= addition ? Flag_Addition : 0; data.flag = flag; data.windType = windType; data.shapeType = shapeType; data.transformIndex = -1; data.areaSize = areaSize; data.main = main; data.turbulence = turbulence; data.frequency = frequency; //data.anchor = math.clamp(anchor, -1, 1); data.direction = direction; // local data.directionType = directinType; data.areaVolume = areaVolume; data.areaLength = areaLength; data.attenuation.Setup(attenuation); int windId = windDataList.Add(data); return windId; } public void RemoveWind(int windId) { if (windId >= 0) { windDataList.Remove(windId); } } /// /// 風の有効フラグ切り替え /// /// /// public void SetEnable(int windId, bool sw, Transform target) { if (windId >= 0) { WindData data = windDataList[windId]; data.SetEnable(sw); // 連動トランスフォームを登録/解除 if (sw) { if (data.transformIndex == -1) { data.transformIndex = Bone.AddBone(target); } } else { if (data.transformIndex >= 0) { Bone.RemoveBone(data.transformIndex); data.transformIndex = -1; } } windDataList[windId] = data; } } /// /// 風が有効状態か判定する /// /// /// public bool IsActive(int windId) { if (windId >= 0) return windDataList[windId].IsActive(); else return false; } /// /// 風の状態フラグ設定 /// /// /// /// public void SetFlag(int windId, uint flag, bool sw) { if (windId < 0) return; WindData data = windDataList[windId]; data.SetFlag(flag, sw); windDataList[windId] = data; } public void SetParameter( int windId, float3 areaSize, bool addition, float main, float turbulence, float frequency, float3 direction, float areaVolume, float areaLength, BezierParam attenuation ) { if (windId < 0) return; WindData data = windDataList[windId]; data.SetFlag(Flag_Addition, addition); data.areaSize = areaSize; data.main = main; data.turbulence = turbulence; data.frequency = frequency; //data.anchor = math.clamp(anchor, -1, 1); data.direction = direction; // local data.areaVolume = areaVolume; data.areaLength = areaLength; data.attenuation.Setup(attenuation); windDataList[windId] = data; } public int Count { get { if (windDataList == null) return 0; return windDataList.Count; } } //========================================================================================= /// /// 座標をもとに風の力を計算して返す /// /// /// /// /// /// /// /// /// internal static float3 CalcWindForce(float time, float2 noiseBasePos, float3 mainDir, float main, float turbulence, float frequency, float randomScale) { // 風量による計算比率 float ratio = main / 30.0f; // 風速30を基準 // 風向きのランダム角度(風量に比例する) float rang = 15.0f + 15.0f * ratio; // 風向きの周期 float dirFreq = 1.0f + 2.0f * ratio; // 1.0 - 3.0 dirFreq *= frequency; // 方向ノイズ var noisePos1 = noiseBasePos.xy; var noisePos2 = noiseBasePos.yx; noisePos1.x += time * dirFreq; // 周期(数値を高くするとランダム性が増す)2.0f? noisePos2.y += time * dirFreq; // 周期(数値を高くするとランダム性が増す)2.0f? var nv1 = noise.snoise(noisePos1); // -1.0f~1.0f var nv2 = noise.snoise(noisePos2); // -1.0f~1.0f // 方向のランダム性 var ang1 = math.radians(nv1 * rang); var ang2 = math.radians(nv2 * rang); ang1 *= turbulence; // 乱流率 ang2 *= turbulence; // 乱流率 var rq = quaternion.Euler(ang1, ang2, 0.0f); // XY var dirq = MathUtility.AxisQuaternion(mainDir); float3 wdir = math.forward(math.mul(dirq, rq)); // 風力ノイズ var noisePos3 = noiseBasePos * 6.36913f; //noisePos3.x += time * frequency; noisePos3.x += time * (1.0f + 1.0f * ratio) * frequency; //float nv = noise.snoise(noisePos3); // -1.0f~1.0f float nv = noise.cnoise(noisePos3); // -1.0f~1.0f // 風力のランダム性 float scl = math.max(nv * randomScale, -1.0f); // scale main += main * scl; // 最終合成 float3 force = wdir * main; return force; } //========================================================================================= #if false // 風の計算はすべてチーム処理へ移動 /// /// 風の更新 /// public void UpdateWind() { var job = new UpdateWindJob() { dtime = manager.UpdateTime.DeltaTime, elapsedTime = Time.time, bonePosList = Bone.bonePosList.ToJobArray(), boneRotList = Bone.boneRotList.ToJobArray(), windData = windDataList.ToJobArray(), }; Compute.MasterJob = job.Schedule(windDataList.Length, 1, Compute.MasterJob); } [BurstCompile] struct UpdateWindJob : IJobParallelFor { public float dtime; public float elapsedTime; [Unity.Collections.ReadOnly] public NativeArray bonePosList; [Unity.Collections.ReadOnly] public NativeArray boneRotList; public NativeArray windData; // 風データごと public void Execute(int index) { var wdata = windData[index]; if (wdata.IsActive() == false || wdata.transformIndex < 0) return; // コンポーネント姿勢 var bpos = bonePosList[wdata.transformIndex]; var brot = boneRotList[wdata.transformIndex]; // 風量による計算比率 float ratio = wdata.main / 30.0f; // 風速30を基準 // 周期(風向きが変わる速度) float freq = 1.0f + 2.0f * ratio; // 1.0 - 3.0 // 風向きのランダム角度 float rang = 15.0f + 15.0f * ratio; // 15 - 30 // ノイズ参照 var noisePos1 = new float2(bpos.x, bpos.z) * 0.1f; var noisePos2 = new float2(bpos.x, bpos.z) * 0.1f; noisePos1.x += elapsedTime * freq; // 周期(数値を高くするとランダム性が増す)2.0f? noisePos2.y += elapsedTime * freq; // 周期(数値を高くするとランダム性が増す)2.0f? var nv1 = noise.snoise(noisePos1); // -1.0f~1.0f var nv2 = noise.snoise(noisePos2); // -1.0f~1.0f // 方向のランダム性 var ang1 = math.radians(nv1 * rang); var ang2 = math.radians(nv2 * rang); ang1 *= wdata.turbulence; // 乱流率 ang2 *= wdata.turbulence; // 乱流率 var rq = quaternion.Euler(ang1, ang2, 0.0f); // XY var dir = math.forward(math.mul(brot, rq)); // ランダムはローカル回転 wdata.direction = dir; // 書き戻し windData[index] = wdata; } } #endif } }