2026-06-20 뉴페어리

This commit is contained in:
2026-06-20 16:46:29 +09:00
parent 833dd50fa9
commit 295e284dd0
849 changed files with 116860 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 95fa82dc80bceb64688ab606c9212a14
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,528 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
public class DataUtility
{
/// <summary>
/// つのintをint2にパックする
/// データは昇順にソートされる
/// </summary>
/// <param name="d0"></param>
/// <param name="d1"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int2 PackInt2(int d0, int d1)
{
return d0 < d1 ? new int2(d0, d1) : new int2(d1, d0);
}
/// <summary>
/// int2をデータの昇順にソートして格納し直す
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int2 PackInt2(in int2 d) => PackInt2(d.x, d.y);
/// <summary>
/// つのintをint3にパックする
/// データは昇順にソートされる
/// </summary>
/// <param name="d0"></param>
/// <param name="d1"></param>
/// <param name="d2"></param>
/// <returns></returns>
public static int3 PackInt3(int d0, int d1, int d2)
{
if (d0 < d1 && d0 < d2)
{
// d0,x,x
if (d1 < d2)
return new int3(d0, d1, d2);
else
return new int3(d0, d2, d1);
}
if (d1 < d2)
{
// d1,x,x
if (d0 < d2)
return new int3(d1, d0, d2);
else
return new int3(d1, d2, d0);
}
else
{
// d2,x,x
if (d0 < d1)
return new int3(d2, d0, d1);
else
return new int3(d2, d1, d0);
}
}
public static int3 PackInt3(in int3 d) => PackInt3(d.x, d.y, d.z);
/// <summary>
/// つのintをint4にパックする
/// データは昇順にソートされる
/// </summary>
/// <param name="d0"></param>
/// <param name="d1"></param>
/// <param name="d2"></param>
/// <param name="d3"></param>
/// <returns></returns>
public static int4 PackInt4(int d0, int d1, int d2, int d3)
{
int w;
// step1
if (d0 > d3)
{
w = d0;
d0 = d3;
d3 = w;
}
if (d1 > d2)
{
w = d1;
d1 = d2;
d2 = w;
}
// step2
if (d0 > d1)
{
w = d0;
d0 = d1;
d1 = w;
}
if (d2 > d3)
{
w = d2;
d2 = d3;
d3 = w;
}
// step3
if (d1 > d2)
{
w = d1;
d1 = d2;
d2 = w;
}
return new int4(d0, d1, d2, d3);
}
public static int4 PackInt4(int4 d) => PackInt4(d.x, d.y, d.z, d.w);
/// <summary>
/// つのintをushortに変換しつのuintにパッキングする
/// </summary>
/// <param name="hi"></param>
/// <param name="low"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Pack32(int hi, int low)
{
return (uint)hi << 16 | (uint)low & 0xffff;
}
/// <summary>
/// つのintをushortに変換しつのuintにパッキングする
/// データの小さいほうが上位に格納されるようにソートされる
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Pack32Sort(int a, int b)
{
if (a > b)
{
return (uint)b << 16 | (uint)a & 0xffff;
}
else
{
return (uint)a << 16 | (uint)b & 0xffff;
}
}
/// <summary>
/// uintパックデータから上位16bitをintにして返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack32Hi(uint pack)
{
return (int)((pack >> 16) & 0xffff);
}
/// <summary>
/// uintパックデータから下位16bitをintにして返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack32Low(uint pack)
{
return (int)(pack & 0xffff);
}
#if false
/// <summary>
/// つのintをhi(10bit)とlow(22bit)に切り詰めてつのuintにパッキングする
/// </summary>
/// <param name="hi"></param>
/// <param name="low"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Pack10_22(int hi, int low)
{
return (uint)hi << 22 | (uint)low & 0x3fffff;
}
/// <summary>
/// uint10-22パックデータから上位10bitデータをintにして返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack10_22Hi(uint pack)
{
return (int)((pack >> 22) & 0x3ff);
}
/// <summary>
/// uint10-22パックデータから下位22bitデータをintにして返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack10_22Low(uint pack)
{
return (int)(pack & 0x3fffff);
}
/// <summary>
/// uint10-22パックデータを分解してつのintとして返す
/// </summary>
/// <param name="pack"></param>
/// <param name="hi"></param>
/// <param name="low"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Unpack10_22(uint pack, out int hi, out int low)
{
hi = (int)((pack >> 22) & 0x3ff);
low = (int)(pack & 0x3fffff);
}
#endif
/// <summary>
/// つのintをhi(12bit)とlow(20bit)に切り詰めてつのuintにパッキングする
/// </summary>
/// <param name="hi"></param>
/// <param name="low"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Pack12_20(int hi, int low)
{
return (uint)hi << 20 | (uint)low & 0xfffff;
}
/// <summary>
/// uint12-20パックデータから上位12bitデータをintにして返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack12_20Hi(uint pack)
{
return (int)((pack >> 20) & 0xfff);
}
/// <summary>
/// uint12-20パックデータから下位20bitデータをintにして返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack12_20Low(uint pack)
{
return (int)(pack & 0xfffff);
}
/// <summary>
/// uint12-20パックデータを分解してつのintとして返す
/// </summary>
/// <param name="pack"></param>
/// <param name="hi"></param>
/// <param name="low"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Unpack12_20(uint pack, out int hi, out int low)
{
hi = (int)((pack >> 20) & 0xfff);
low = (int)(pack & 0xfffff);
}
/// <summary>
/// つのintをushortに変換しつのulongにパッキングする
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
/// <param name="w"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pack64(int x, int y, int z, int w)
{
return (((ulong)x) & 0xffff) << 48 | (((ulong)y) & 0xffff) << 32 | (((ulong)z) & 0xffff) << 16 | ((ulong)w) & 0xffff;
}
/// <summary>
/// int4をushortに変換しつのulongにパッキングする
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pack64(in int4 a)
{
return Pack64(a.x, a.y, a.z, a.w);
}
/// <summary>
/// ulongパックデータからint4に展開して返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int4 Unpack64(in ulong pack)
{
return new int4(
(int)((pack >> 48) & 0xffff),
(int)((pack >> 32) & 0xffff),
(int)((pack >> 16) & 0xffff),
(int)(pack & 0xffff)
);
}
/// <summary>
/// ulongパックデータからx値を取り出す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack64X(in ulong pack)
{
return (int)((pack >> 48) & 0xffff);
}
/// <summary>
/// ulongパックデータからy値を取り出す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack64Y(in ulong pack)
{
return (int)((pack >> 32) & 0xffff);
}
/// <summary>
/// ulongパックデータからz値を取り出す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack64Z(in ulong pack)
{
return (int)((pack >> 16) & 0xffff);
}
/// <summary>
/// ulongパックデータからw値を取り出す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Unpack64W(in ulong pack)
{
return (int)(pack & 0xffff);
}
/// <summary>
/// つのintをbyteに変換しつのuintにパッキングする
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
/// <param name="w"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Pack32(int x, int y, int z, int w)
{
return (((uint)x) & 0xff) << 24 | (((uint)y) & 0xff) << 16 | (((uint)z) & 0xff) << 8 | ((uint)w) & 0xff;
}
/// <summary>
/// int4をbyteに変換しつのuintにパッキングする
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pack32(in int4 a)
{
return Pack64(a.x, a.y, a.z, a.w);
}
/// <summary>
/// uintパックデータからint4に展開して返す
/// </summary>
/// <param name="pack"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int4 Unpack32(in uint pack)
{
return new int4(
(int)((pack >> 24) & 0xff),
(int)((pack >> 16) & 0xff),
(int)((pack >> 8) & 0xff),
(int)(pack & 0xff)
);
}
/// <summary>
/// int3のうちuse(int2)で使われていない残りの1つのデータを返す
/// </summary>
/// <param name="data"></param>
/// <param name="use"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int RemainingData(in int3 data, in int2 use)
{
if (data.x != use.x && data.x != use.y)
return data.x;
if (data.y != use.x && data.y != use.y)
return data.y;
return data.z;
}
//=========================================================================================
/// <summary>
/// AnimationCurveを16個のfloatのリスト(float4x4)に変換する
/// </summary>
/// <param name="curve"></param>
/// <returns></returns>
public static float4x4 ConvertAnimationCurve(AnimationCurve curve)
{
float4x4 m = 0;
for (int i = 0; i < 16; i++)
{
float time = i / 15.0f;
float val = curve.Evaluate(time);
m.MC2SetValue(i, val);
}
return m;
}
/// <summary>
/// AnimationCurveが格納されたfloat4x4からデータを取得する
/// </summary>
/// <param name="curve"></param>
/// <param name="time"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float EvaluateCurve(in float4x4 curve, float time)
{
const float interval = 1.0f / 15.0f;
int index = (int)(math.saturate(time) * 15);
time -= index * interval;
float t = time / interval;
return math.lerp(curve.MC2GetValue(index), curve.MC2GetValue(index + 1), t);
}
//=========================================================================================
/// <summary>
/// 16bitフラグにコライダータイプを設定する
/// </summary>
/// <param name="flag"></param>
/// <param name="ctype"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ExBitFlag16 SetColliderType(ExBitFlag16 flag, ColliderManager.ColliderType ctype)
{
flag.Value = (ushort)((flag.Value & 0xfff0) | (ushort)ctype);
return flag;
}
/// <summary>
/// 16bitフラグからコライダータイプを取得する
/// </summary>
/// <param name="flag"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ColliderManager.ColliderType GetColliderType(in ExBitFlag16 flag)
{
return (ColliderManager.ColliderType)(flag.Value & 0x000f);
}
/// <summary>
/// 16bitフラグにシンメトリータイプを設定する
/// </summary>
/// <param name="flag"></param>
/// <param name="stype"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ExBitFlag16 SetSymmetryType(ExBitFlag16 flag, ColliderManager.SymmetryType stype)
{
flag.Value = (ushort)((flag.Value & 0xff0f) | (((ushort)stype) << 4));
return flag;
}
/// <summary>
/// 16bitフラグからシンメトリータイプを取得する
/// </summary>
/// <param name="flag"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ColliderManager.SymmetryType GetSymmetryType(in ExBitFlag16 flag)
{
return (ColliderManager.SymmetryType)((flag.Value & 0x00f0) >> 4);
}
//=========================================================================================
/// <summary>
/// 配列をDeepコピーする
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="src"></param>
/// <param name="dst"></param>
public static void ArrayCopy<T>(T[] src, ref T[] dst)
{
if (src == null)
{
dst = null;
return;
}
if (src.Length == 0)
{
dst = new T[0];
}
else
{
dst = new T[src.Length];
Array.Copy(src, dst, src.Length);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5749112885888a44fa11de392614144a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Data/DataUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,98 @@
// Magica Cloth 2.
// Copyright (c) 2026 MagicaSoft.
// https://magicasoft.jp
using System;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// Unityのバージョン差異InstanceID vs EntityIdを吸収するID構造体。
/// 現在はUnity6.4以上でEntityIdに切り替え
/// </summary>
public readonly struct MagicaObjectId : IEquatable<MagicaObjectId>
{
// -------------------------------------------------------
// 内部データ保持
// -------------------------------------------------------
#if UNITY_6000_4_OR_NEWER
private readonly EntityId _value;
public MagicaObjectId(EntityId id)
{
_value = id;
}
// 無効なIDの定義EntityIdの仕様に合わせる
public static readonly MagicaObjectId Invalid = new(EntityId.None);
public bool IsValid() => _value.IsValid();
#else
private readonly int _value;
public MagicaObjectId(int id)
{
_value = id;
}
// 従来のInstanceIDでは0が事実上の無効値として扱われることが多い
public static readonly MagicaObjectId Invalid = new MagicaObjectId(0);
public bool IsValid() => _value != 0;
#endif
public bool Equals(MagicaObjectId other)
{
return _value.Equals(other._value);
}
public override bool Equals(object obj)
{
return obj is MagicaObjectId other && Equals(other);
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static bool operator ==(MagicaObjectId left, MagicaObjectId right)
{
return left.Equals(right);
}
public static bool operator !=(MagicaObjectId left, MagicaObjectId right)
{
return !left.Equals(right);
}
// これはデバッグ用途以外では使わない
public override string ToString()
{
return _value.ToString();
}
}
// -------------------------------------------------------
// 拡張メソッド
// -------------------------------------------------------
public static class MagicaObjectIdExtensions
{
/// <summary>
/// GameObjectやComponentからバージョンに合わせたIDを取得する
/// </summary>
public static MagicaObjectId GetMagicaId(this UnityEngine.Object obj)
{
if (obj == null) return MagicaObjectId.Invalid;
#if UNITY_6000_4_OR_NEWER
// Unity 6.4 API
return new MagicaObjectId(obj.GetEntityId());
#else
// 従来の API
return new MagicaObjectId(obj.GetInstanceID());
#endif
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 527929c5c74a0224b91228de398467f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Data/MagicaObjectId.cs
uploadId: 893596

View File

@@ -0,0 +1,133 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Collections.Generic;
using Unity.Collections;
namespace MagicaCloth2
{
/// <summary>
/// T型のデータリストを構築し要素ごとにそのスタートインデックスとデータカウンタを生成する
/// 出力はT型のデータ配列と、要素ごとのスタートインデックスとカウンタがつのuintにパックされた配列の
/// </summary>
/// <typeparam name="T"></typeparam>
public class MultiDataBuilder<T> : IDisposable where T : unmanaged
{
int indexCount;
public NativeParallelMultiHashMap<int, T> Map;
//=========================================================================================
public MultiDataBuilder(int indexCount, int dataCapacity)
{
this.indexCount = indexCount;
Map = new NativeParallelMultiHashMap<int, T>(dataCapacity, Allocator.Persistent);
}
public void Dispose()
{
if (Map.IsCreated)
Map.Dispose();
}
public int Count() => Map.Count();
public int GetDataCount(int index)
{
if (Map.ContainsKey(index) == false)
return 0;
return Map.CountValuesForKey(index);
}
public void Add(int key, T data)
{
Map.Add(key, data);
}
//public int AddAndReturnIndex(int key, T data)
//{
// int cnt = Map.CountValuesForKey(key);
// Map.Add(key, data);
// return cnt;
//}
public int CountValuesForKey(int key)
{
return Map.CountValuesForKey(key);
}
//=========================================================================================
/// <summary>
/// 内部HashMapのデータをT型配列と要素ごとのスタートインデックスとカウンタ配列のつに分離して返す
/// 出力はT型のデータ配列と、要素ごとのスタートインデックス(20bit)とカウンタ(12bit)をつのuintにパックした配列となる
/// </summary>
/// <returns></returns>
public (T[], uint[]) ToArray()
{
if (Map.IsCreated == false || indexCount == 0)
return (null, null);
var indexArray = new uint[indexCount];
var dataList = new List<T>(Map.Capacity);
for (int i = 0; i < indexCount; i++)
{
int start = dataList.Count;
int cnt = 0;
if (Map.ContainsKey(i))
{
foreach (var data in Map.GetValuesForKey(i))
{
dataList.Add(data);
cnt++;
}
}
indexArray[i] = DataUtility.Pack12_20(cnt, start);
}
return (dataList.ToArray(), indexArray);
}
public uint[] ToIndexArray()
{
(var _, uint[] indexArray) = ToArray();
return indexArray;
}
/// <summary>
/// 内部HashMapのデータをT型配列と要素ごとのスタートインデックス+カウンタのつのNativeArrayに分離して返す
/// 出力はT型のデータ配列と、要素ごとのスタートインデックス(20bit)とカウンタ(12bit)をつのuintにパックした配列となる
/// </summary>
/// <param name="indexArray"></param>
/// <param name="dataArray"></param>
public void ToNativeArray(out NativeArray<uint> indexArray, out NativeArray<T> dataArray)
{
indexArray = new NativeArray<uint>(indexCount, Allocator.Persistent);
var dataList = new List<T>(Map.Capacity);
for (int i = 0; i < indexCount; i++)
{
int start = dataList.Count;
int cnt = 0;
if (Map.ContainsKey(i))
{
foreach (var data in Map.GetValuesForKey(i))
{
dataList.Add(data);
cnt++;
}
}
indexArray[i] = DataUtility.Pack12_20(cnt, start);
}
dataArray = new NativeArray<T>(dataList.ToArray(), Allocator.Persistent);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 437001a616042194796b2fa354bebbb3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Data/MultiDataBuilder.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a4a34e42bc2ef104fb3237f69dbdd2f0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,233 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Mathematics;
namespace MagicaCloth2
{
/// <summary>
/// グリッドマップとユーティリティ関数群
/// Jobで利用するために最低限の管理データのみ
/// そのためGridSizeなどのデータはこのクラスでは保持しない
/// GridSize>0である必要あり!
/// </summary>
/// <typeparam name="T"></typeparam>
public class GridMap<T> : IDisposable where T : unmanaged // , IEquatable<T>
{
private NativeParallelMultiHashMap<int3, T> gridMap;
//=========================================================================================
public GridMap(int capacity = 0)
{
gridMap = new NativeParallelMultiHashMap<int3, T>(capacity, Allocator.Persistent);
}
public void Dispose()
{
if (gridMap.IsCreated)
gridMap.Dispose();
}
public NativeParallelMultiHashMap<int3, T> GetMultiHashMap() => gridMap;
public int DataCount => gridMap.Count();
//=========================================================================================
/// <summary>
/// グリッド範囲を走査するEnumeratorを返す
/// </summary>
/// <param name="startGrid"></param>
/// <param name="endGrid"></param>
/// <returns></returns>
public static GridEnumerator GetArea(int3 startGrid, int3 endGrid, NativeParallelMultiHashMap<int3, T> gridMap)
{
return new GridEnumerator
{
gridMap = gridMap,
startGrid = math.min(startGrid, endGrid),
endGrid = math.max(startGrid, endGrid),
currentGrid = math.min(startGrid, endGrid),
isFirst = true,
};
}
/// <summary>
/// 球範囲を走査するEnumeratorを返す
/// </summary>
/// <param name="pos"></param>
/// <param name="radius"></param>
/// <returns></returns>
public static GridEnumerator GetArea(float3 pos, float radius, NativeParallelMultiHashMap<int3, T> gridMap, float gridSize)
{
// 検索グリッド範囲
int3 minGrid = GetGrid(pos - radius, gridSize);
int3 maxGrid = GetGrid(pos + radius, gridSize);
return GetArea(minGrid, maxGrid, gridMap);
}
/// <summary>
/// グリッド走査用Enumerator
/// </summary>
public struct GridEnumerator : IEnumerator<int3>
{
internal NativeParallelMultiHashMap<int3, T> gridMap;
internal int3 startGrid;
internal int3 endGrid;
internal int3 currentGrid;
internal bool isFirst;
public int3 Current => currentGrid;
object IEnumerator.Current => Current;
public void Dispose()
{
}
public bool MoveNext()
{
// データが存在しなくとも走査する
if (isFirst)
{
isFirst = false;
return true;
}
currentGrid.x++;
if (currentGrid.x > endGrid.x)
{
currentGrid.x = startGrid.x;
currentGrid.y++;
if (currentGrid.y > endGrid.y)
{
currentGrid.y = startGrid.y;
currentGrid.z++;
if (currentGrid.z > endGrid.z)
{
return false;
}
}
}
return true;
}
public void Reset()
{
currentGrid = startGrid;
isFirst = true;
}
public GridEnumerator GetEnumerator() { return this; }
}
//=========================================================================================
/// <summary>
/// 座標から3次元グリッド座標を割り出す
/// </summary>
/// <param name="pos"></param>
/// <param name="gridSize"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int3 GetGrid(float3 pos, float gridSize)
{
Develop.Assert(gridSize > 0);
return new int3(math.floor(pos / gridSize));
}
/// <summary>
/// グリッドマップにデータを追加する
/// </summary>
/// <param name="grid"></param>
/// <param name="data"></param>
/// <param name="gridMap"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddGrid(int3 grid, T data, NativeParallelMultiHashMap<int3, T> gridMap)
{
gridMap.Add(grid, data);
}
/// <summary>
/// 座標からグリッドマップにデータを追加する
/// </summary>
/// <param name="pos"></param>
/// <param name="data"></param>
/// <param name="gridMap"></param>
/// <param name="gridSize"></param>
/// <param name="aabbRef"></param>
/// <returns>追加されたグリッドを返す</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int3 AddGrid(float3 pos, T data, NativeParallelMultiHashMap<int3, T> gridMap, float gridSize)
{
int3 grid = GetGrid(pos, gridSize);
gridMap.Add(grid, data);
return grid;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int3 AddGrid(float3 pos, T data, NativeParallelMultiHashMap<int3, T>.ParallelWriter gridMap, float gridSize)
{
int3 grid = GetGrid(pos, gridSize);
gridMap.Add(grid, data);
return grid;
}
/// <summary>
/// グリッドマップからデータを削除する
/// </summary>
/// <param name="grid"></param>
/// <param name="data"></param>
/// <param name="gridMap"></param>
/// <returns>削除に成功した場合はtrue</returns>
public static bool RemoveGrid(int3 grid, T data, NativeParallelMultiHashMap<int3, T> gridMap)
{
if (gridMap.ContainsKey(grid))
{
NativeParallelMultiHashMapIterator<int3> it;
T item;
if (gridMap.TryGetFirstValue(grid, out item, out it))
{
do
{
if (item.Equals(data))
{
gridMap.Remove(it);
return true;
}
}
while (gridMap.TryGetNextValue(out item, ref it));
}
}
return false;
}
/// <summary>
/// グリッドマップからデータを移動させる
/// </summary>
/// <param name="fromGrid"></param>
/// <param name="toGrid"></param>
/// <param name="data"></param>
/// <param name="gridMap"></param>
/// <returns>データが移動された場合true, 移動の必要がない場合はfalse</returns>
public static bool MoveGrid(int3 fromGrid, int3 toGrid, T data, NativeParallelMultiHashMap<int3, T> gridMap)
{
// 移動の必要がなければ終了
if (fromGrid.Equals(toGrid))
return false;
// remove
RemoveGrid(fromGrid, data, gridMap);
// add
AddGrid(toGrid, data, gridMap);
return true;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f11ea30bdf2932444ae39633749006cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Grid/GridMap.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a2f52f64422b8ad49ae673c135e28e9e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,114 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Runtime.CompilerServices;
using System.Threading;
using Unity.Mathematics;
namespace MagicaCloth2
{
/// <summary>
/// Nativeバッファへのインターロック書き込み制御関連
/// </summary>
public static class InterlockUtility
{
/// <summary>
/// 固定小数点への変換倍率
/// </summary>
internal const int ToFixed = 1000000;
/// <summary>
/// 少数への復元倍率
/// </summary>
internal const float ToFloat = 0.000001f;
//=========================================================================================
/// <summary>
/// 集計バッファの指定インデックスにfloat3を固定小数点として加算しカウンタをインクリメントする
/// </summary>
/// <param name="index"></param>
/// <param name="add"></param>
/// <param name="cntPt"></param>
/// <param name="sumPt"></param>
unsafe internal static void AddFloat3(int index, float3 add, int* cntPt, int* sumPt)
{
Interlocked.Increment(ref cntPt[index]);
int3 iadd = (int3)(add * ToFixed);
//Debug.Log($"InterlockAdd [{index}]:{iadd}");
index *= 3;
for (int i = 0; i < 3; i++, index++)
{
if (iadd[i] != 0)
Interlocked.Add(ref sumPt[index], iadd[i]);
}
}
/// <summary>
/// 集計バッファの指定インデックスにfloat3を固定小数点として加算するカウントは操作しない
/// </summary>
/// <param name="index"></param>
/// <param name="add"></param>
/// <param name="sumPt"></param>
unsafe internal static void AddFloat3(int index, float3 add, int* sumPt)
{
int3 iadd = (int3)(add * ToFixed);
index *= 3;
for (int i = 0; i < 3; i++, index++)
{
if (iadd[i] != 0)
Interlocked.Add(ref sumPt[index], iadd[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe internal static void Max(int index, float value, int* pt)
{
int ival = (int)value * ToFixed;
int now = pt[index];
int oldNow = now + 1;
while (ival > now && now != oldNow)
{
oldNow = now;
now = Interlocked.CompareExchange(ref pt[index], ival, now);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe internal static float3 ReadAverageFloat3(int index, int* cntPt, int* sumPt)
{
int count = cntPt[index];
if (count == 0)
return 0;
int dataIndex = index * 3;
// 集計
float3 add = new float3(sumPt[dataIndex], sumPt[dataIndex + 1], sumPt[dataIndex + 2]);
add /= count;
// データは固定小数点なので戻す
add *= ToFloat;
return add;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe internal static float3 ReadFloat3(int index, int* vecPt)
{
int dataIndex = index * 3;
float3 v = new float3(vecPt[dataIndex], vecPt[dataIndex + 1], vecPt[dataIndex + 2]);
// データは固定小数点なので戻す
v *= ToFloat;
return v;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe internal static float ReadFloat(int index, int* floatPt)
{
return floatPt[index] * ToFloat;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a2fbf5e2678edef418b3d95dd46af37b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Jobs/InterlockUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,717 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
public static class JobUtility
{
//=========================================================================================
/// <summary>
/// 配列をValueで埋めるジョブを発行します
/// ジェネリック型ジョブは明示的に型を<T>で指定する必要があるため型ごとに関数が発生します
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <param name="length"></param>
/// <param name="value"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle Fill(NativeArray<int> array, int length, int value, JobHandle dependsOn = new JobHandle())
{
var job = new FillJob<int>() { value = value, array = array, };
return job.Schedule(length, 32, dependsOn);
}
public static JobHandle Fill(NativeArray<Vector4> array, int length, Vector4 value, JobHandle dependsOn = new JobHandle())
{
var job = new FillJob<Vector4>() { value = value, array = array, };
return job.Schedule(length, 32, dependsOn);
}
public static JobHandle Fill(NativeArray<VirtualMeshBoneWeight> array, int length, VirtualMeshBoneWeight value, JobHandle dependsOn = new JobHandle())
{
var job = new FillJob<VirtualMeshBoneWeight>() { value = value, array = array, };
return job.Schedule(length, 32, dependsOn);
}
public static JobHandle Fill(NativeArray<byte> array, int length, byte value, JobHandle dependsOn = new JobHandle())
{
var job = new FillJob<byte>() { value = value, array = array, };
return job.Schedule(length, 32, dependsOn);
}
public static void FillRun(NativeArray<int> array, int length, int value)
{
var job = new FillJob<int>() { value = value, array = array, };
job.Run(length);
}
public static void FillRun(NativeArray<Vector4> array, int length, Vector4 value)
{
var job = new FillJob<Vector4>() { value = value, array = array, };
job.Run(length);
}
public static void FillRun(NativeArray<quaternion> array, int length, quaternion value)
{
var job = new FillJob<quaternion>() { value = value, array = array, };
job.Run(length);
}
public static void FillRun(NativeArray<VirtualMeshBoneWeight> array, int length, VirtualMeshBoneWeight value)
{
var job = new FillJob<VirtualMeshBoneWeight>() { value = value, array = array, };
job.Run(length);
}
[BurstCompile]
struct FillJob<T> : IJobParallelFor where T : unmanaged
{
public T value;
[Unity.Collections.WriteOnly]
public NativeArray<T> array;
public void Execute(int index)
{
array[index] = value;
}
}
/// <summary>
/// 配列をValueで埋めるジョブを発行します(startIndexあり)
/// ジェネリック型ジョブは明示的に型を<T>で指定する必要があるため型ごとに関数が発生します
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <param name="value"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle Fill(NativeArray<int> array, int startIndex, int length, int value, JobHandle dependsOn = new JobHandle())
{
var job = new FillJob2<int>() { value = value, startIndex = startIndex, array = array, };
return job.Schedule(length, 32, dependsOn);
}
[BurstCompile]
struct FillJob2<T> : IJobParallelFor where T : unmanaged
{
public T value;
public int startIndex;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<T> array;
public void Execute(int index)
{
array[startIndex + index] = value;
}
}
public static JobHandle Fill(NativeReference<int> reference, int value, JobHandle dependsOn = new JobHandle())
{
var job = new FillRefJob<int>() { value = value, reference = reference, };
return job.Schedule(dependsOn);
}
[BurstCompile]
struct FillRefJob<T> : IJob where T : unmanaged
{
public T value;
[Unity.Collections.WriteOnly]
public NativeReference<T> reference;
public void Execute()
{
reference.Value = value;
}
}
//=========================================================================================
/// <summary>
/// 配列に連番を格納するジョブを発行します
/// </summary>
/// <param name="array"></param>
/// <param name="length"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle SerialNumber(NativeArray<int> array, int length, JobHandle dependsOn = new JobHandle())
{
var job = new SerialNumberJob()
{
array = array,
};
return job.Schedule(length, 32, dependsOn);
}
public static void SerialNumberRun(NativeArray<int> array, int length)
{
var job = new SerialNumberJob()
{
array = array,
};
job.Run(length);
}
[BurstCompile]
struct SerialNumberJob : IJobParallelFor
{
[Unity.Collections.WriteOnly]
public NativeArray<int> array;
public void Execute(int index)
{
array[index] = index;
}
}
//=========================================================================================
/// <summary>
/// NativeHashSetのキーをNativeListに変換するジョブを発行します
/// ジェネリック型ジョブは明示的に型を<T>で指定する必要があるため型ごとに関数が発生します
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="hashSet"></param>
/// <param name="list"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle ConvertHashSetToNativeList(NativeParallelHashSet<int> hashSet, NativeList<int> list, JobHandle dependsOn = new JobHandle())
{
var job = new ConvertHashSetToListJob<int>() { hashSet = hashSet, list = list, };
return job.Schedule(dependsOn);
}
[BurstCompile]
struct ConvertHashSetToListJob<T> : IJob where T : unmanaged, IEquatable<T>
{
[Unity.Collections.ReadOnly]
public NativeParallelHashSet<T> hashSet;
[Unity.Collections.WriteOnly]
public NativeList<T> list;
public void Execute()
{
foreach (var key in hashSet)
{
list.AddNoResize(key);
}
}
}
//=========================================================================================
#if false
/// <summary>
/// NativeMultiHashMapのキーをNativeListに変換するジョブを発行する
/// ジェネリック型ジョブは明示的に型を<T>で指定する必要があるため型ごとに関数が発生します
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="U"></typeparam>
/// <param name="hashMap"></param>
/// <param name="keyList"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle ConvertMultiHashMapKeyToNativeList(
NativeParallelMultiHashMap<int2, int> hashMap,
NativeList<int2> keyList,
JobHandle dependsOn = new JobHandle())
{
// todo:この処理は重い
var job = new ConvertMultiHashMapKeyToListJob<int2, int>()
{
hashMap = hashMap,
list = keyList,
};
return job.Schedule(dependsOn);
}
[BurstCompile]
struct ConvertMultiHashMapKeyToListJob<T, U> : IJob where T : unmanaged, IEquatable<T> where U : unmanaged
{
[Unity.Collections.ReadOnly]
public NativeParallelMultiHashMap<T, U> hashMap;
[Unity.Collections.WriteOnly]
public NativeList<T> list;
public void Execute()
{
var keySet = new NativeParallelHashSet<T>(hashMap.Count(), Allocator.Temp); // ここが問題となる可能性がある(unity2023.1.5事件)
var keyArray = hashMap.GetKeyArray(Allocator.Temp); // ここが問題となる可能性がある(unity2023.1.5事件)
// GetKeyArray()の結果はキーが重複しまた順不同なので注意!
for (int i = 0; i < keyArray.Length; i++)
keySet.Add(keyArray[i]);
foreach (var key in keySet)
list.Add(key);
}
}
#endif
//=========================================================================================
/// <summary>
/// NativeHashSetの内容をNativeListに変換するジョブを発行する
/// ジェネリック型ジョブは明示的に型を<T>で指定する必要があるため型ごとに関数が発生します
/// </summary>
/// <param name="hashSet"></param>
/// <param name="keyList"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle ConvertHashSetKeyToNativeList(
NativeParallelHashSet<int2> hashSet,
NativeList<int2> keyList,
JobHandle dependsOn = new JobHandle())
{
var job = new ConvertHashSetKeyToListJob<int2>() { hashSet = hashSet, list = keyList, };
return job.Schedule(dependsOn);
}
public static JobHandle ConvertHashSetKeyToNativeList(
NativeParallelHashSet<int4> hashSet,
NativeList<int4> keyList,
JobHandle dependsOn = new JobHandle())
{
var job = new ConvertHashSetKeyToListJob<int4>() { hashSet = hashSet, list = keyList, };
return job.Schedule(dependsOn);
}
[BurstCompile]
struct ConvertHashSetKeyToListJob<T> : IJob where T : unmanaged, IEquatable<T>
{
[Unity.Collections.ReadOnly]
public NativeParallelHashSet<T> hashSet;
[Unity.Collections.WriteOnly]
public NativeList<T> list;
public void Execute()
{
// 順不同なので注意!
foreach (var key in hashSet)
list.Add(key);
}
}
//=========================================================================================
/// <summary>
/// AABBを計算して返すジョブを発行する(NativeArray)
/// </summary>
/// <param name="positions"></param>
/// <param name="length"></param>
/// <param name="outAABB"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle CalcAABB(NativeArray<float3> positions, int length, NativeReference<AABB> outAABB, JobHandle dependsOn = new JobHandle())
{
var job = new CalcAABBJob()
{
length = length,
positions = positions,
outAABB = outAABB,
};
return job.Schedule(dependsOn);
}
public static void CalcAABBRun(NativeArray<float3> positions, int length, NativeReference<AABB> outAABB)
{
var job = new CalcAABBJob()
{
length = length,
positions = positions,
outAABB = outAABB,
};
job.Run();
}
/// <summary>
/// AABBを計算して返すジョブを発行する(NativeList)
/// </summary>
/// <param name="positions"></param>
/// <param name="outAABB"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle CalcAABB(NativeList<float3> positions, NativeReference<AABB> outAABB, JobHandle dependsOn = new JobHandle())
{
var job = new CalcAABBDeferJob()
{
positions = positions,
outAABB = outAABB,
};
return job.Schedule(dependsOn);
}
public static void CalcAABBRun(NativeList<float3> positions, NativeReference<AABB> outAABB)
{
var job = new CalcAABBDeferJob()
{
positions = positions,
outAABB = outAABB,
};
job.Run();
}
[BurstCompile]
struct CalcAABBJob : IJob
{
public int length;
[Unity.Collections.ReadOnly]
public NativeArray<float3> positions;
public NativeReference<AABB> outAABB;
public void Execute()
{
outAABB.Value = CalcAABBInternal(positions, length);
}
}
[BurstCompile]
struct CalcAABBDeferJob : IJob
{
[Unity.Collections.ReadOnly]
public NativeList<float3> positions;
public NativeReference<AABB> outAABB;
public void Execute()
{
outAABB.Value = CalcAABBInternal(positions.AsArray(), positions.Length);
}
}
static AABB CalcAABBInternal(in NativeArray<float3> positions, int length)
{
if (positions.Length == 0)
{
return new AABB();
}
//float3 min = 0;
//float3 max = 0;
float3 min = float.MaxValue;
float3 max = float.MinValue;
for (int i = 0; i < length; i++)
{
float3 pos = positions[i];
min = math.min(min, pos);
max = math.max(max, pos);
}
var aabb = new AABB(min, max);
return aabb;
}
//=========================================================================================
/// <summary>
/// スフィアマッピングを行いUVを算出するジョブを発行する
/// このUVは接線計算用でありテクスチャ用ではないので注意
/// </summary>
/// <param name="positions"></param>
/// <param name="length"></param>
/// <param name="aabb"></param>
/// <param name="outUVs"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle CalcUVWithSphereMapping(NativeArray<float3> positions, int length, NativeReference<AABB> aabb, NativeArray<float2> outUVs, JobHandle dependsOn = new JobHandle())
{
var job = new CalcUVJob()
{
positions = positions,
aabb = aabb,
uvs = outUVs,
};
return job.Schedule(length, 32, dependsOn);
}
public static void CalcUVWithSphereMappingRun(NativeArray<float3> positions, int length, NativeReference<AABB> aabb, NativeArray<float2> outUVs)
{
var job = new CalcUVJob()
{
positions = positions,
aabb = aabb,
uvs = outUVs,
};
job.Run(length);
}
[BurstCompile]
struct CalcUVJob : IJobParallelFor
{
[Unity.Collections.ReadOnly]
public NativeArray<float3> positions;
[Unity.Collections.ReadOnly]
public NativeReference<AABB> aabb;
[Unity.Collections.WriteOnly]
public NativeArray<float2> uvs;
public void Execute(int index)
{
// 中心位置からのスフィアマッピング
float3 lv = positions[index] - aabb.Value.Center;
lv = math.normalize(lv);
float u = math.atan2(lv.x, lv.z);
u = math.clamp(math.unlerp(-math.PI, math.PI, u), 0.0f, 1.0f);
float v = math.dot(math.up(), lv);
v = math.clamp(math.unlerp(1.0f, -1.0f, v), 0.0f, 1.0f);
//float2 uv = new float2(u, v); // こちらは横方向ベースになる
float2 uv = new float2(v, u); // こちらは縦方向ベースになる
#if false
float len = math.length(lv);
float add = index * 0.001234f; // UVを微妙にずらすための加算値
uv += len * 0.01f + add;
#endif
#if false
//float len = math.length(lv);
float add = index * 0.0001234f; // UVを微妙にずらすための加算値
//uv += len * 0.1f + add;
uv += add;
#endif
#if true
// 方向ベクトル上に同じUVが生成されてしまうのを避けるためUVに距離を加算してずらす
float add = index * 0.0001234f; // UVを微妙にずらすための加算値
// 頂点間のUVの間隔を広めに取り、同じUVが発生しないようにインデックスを使い微妙に値をずらす
uv = uv * 10.0f + add;
#endif
//Debug.Log($"[{index}] {uv}");
uvs[index] = uv;
}
}
//=========================================================================================
/// <summary>
/// intデータを加算して新しい領域にコピーする
/// </summary>
[BurstCompile]
public struct AddIntDataCopyJob : IJobParallelFor
{
public int dstOffset;
public int addData;
// src
[Unity.Collections.ReadOnly]
public NativeArray<int> srcData;
// dst
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<int> dstData;
public void Execute(int index)
{
int dindex = dstOffset + index;
var data = srcData[index];
data += addData;
dstData[dindex] = data;
}
}
/// <summary>
/// int2データを加算して新しい領域にコピーする
/// </summary>
[BurstCompile]
public struct AddInt2DataCopyJob : IJobParallelFor
{
public int dstOffset;
public int2 addData;
// src
[Unity.Collections.ReadOnly]
public NativeArray<int2> srcData;
// dst
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<int2> dstData;
public void Execute(int index)
{
int dindex = dstOffset + index;
var data = srcData[index];
data += addData;
dstData[dindex] = data;
}
}
/// <summary>
/// int3データを加算して新しい領域にコピーする
/// </summary>
[BurstCompile]
public struct AddInt3DataCopyJob : IJobParallelFor
{
public int dstOffset;
public int3 addData;
// src
[Unity.Collections.ReadOnly]
public NativeArray<int3> srcData;
// dst
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<int3> dstData;
public void Execute(int index)
{
int dindex = dstOffset + index;
var data = srcData[index];
data += addData;
dstData[dindex] = data;
}
}
//=========================================================================================
/// <summary>
/// 座標を変換するジョブを発行します
/// </summary>
/// <param name="positions"></param>
/// <param name="length"></param>
/// <param name="toM"></param>
/// <param name="dependsOn"></param>
/// <returns></returns>
public static JobHandle TransformPosition(NativeArray<float3> positions, int length, in float4x4 toM, JobHandle dependsOn = new JobHandle())
{
var job = new TransformPositionJob() { toM = toM, positions = positions, };
return job.Schedule(length, 32, dependsOn);
}
public static void TransformPositionRun(NativeArray<float3> positions, int length, in float4x4 toM)
{
var job = new TransformPositionJob() { toM = toM, positions = positions, };
job.Run(length);
}
public static JobHandle TransformPosition(NativeArray<float3> srcPositions, NativeArray<float3> dstPositions, int length, in float4x4 toM, JobHandle dependsOn = new JobHandle())
{
var job = new TransformPositionJob2() { toM = toM, srcPositions = srcPositions, dstPositions = dstPositions };
return job.Schedule(length, 32, dependsOn);
}
public static void TransformPositionRun(NativeArray<float3> srcPositions, NativeArray<float3> dstPositions, int length, in float4x4 toM)
{
var job = new TransformPositionJob2() { toM = toM, srcPositions = srcPositions, dstPositions = dstPositions };
job.Run(length);
}
[BurstCompile]
public struct TransformPositionJob : IJobParallelFor
{
public float4x4 toM;
public NativeArray<float3> positions;
public void Execute(int vindex)
{
positions[vindex] = MathUtility.TransformPoint(positions[vindex], toM);
}
}
[BurstCompile]
public struct TransformPositionJob2 : IJobParallelFor
{
public float4x4 toM;
[Unity.Collections.ReadOnly]
public NativeArray<float3> srcPositions;
[Unity.Collections.WriteOnly]
public NativeArray<float3> dstPositions;
public void Execute(int vindex)
{
dstPositions[vindex] = MathUtility.TransformPoint(srcPositions[vindex], toM);
}
}
//=========================================================================================
/// <summary>
/// スタートインデックスデータ数とデータのつの配列からHashMapを構築して返す
/// </summary>
/// <param name="indexArray"></param>
/// <param name="dataArray"></param>
/// <returns></returns>
public static NativeParallelMultiHashMap<int, ushort> ToNativeMultiHashMap(in NativeArray<uint> indexArray, in NativeArray<ushort> dataArray)
{
var map = new NativeParallelMultiHashMap<int, ushort>(dataArray.Length, Allocator.Persistent);
var job = new ConvertArrayToMapJob<ushort>()
{
indexArray = indexArray,
dataArray = dataArray,
map = map,
};
job.Run();
return map;
}
[BurstCompile]
struct ConvertArrayToMapJob<TData> : IJob where TData : unmanaged
{
[Unity.Collections.ReadOnly]
public NativeArray<uint> indexArray;
[Unity.Collections.ReadOnly]
public NativeArray<TData> dataArray;
[Unity.Collections.WriteOnly]
public NativeParallelMultiHashMap<int, TData> map;
public void Execute()
{
int cnt = indexArray.Length;
for (int i = 0; i < cnt; i++)
{
DataUtility.Unpack12_20(indexArray[i], out var dcnt, out var dstart);
for (int j = 0; j < dcnt; j++)
{
var data = dataArray[dstart + j];
map.Add(i, data);
}
}
}
}
//=========================================================================================
/// <summary>
/// NativeReferenceをクリアするジョブを発行する
/// </summary>
/// <param name="reference"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
public static JobHandle ClearReference(NativeReference<int> reference, JobHandle jobHandle)
{
var job = new ClearReferenceJob()
{
reference = reference,
};
return job.Schedule(jobHandle);
}
[BurstCompile]
struct ClearReferenceJob : IJob
{
public NativeReference<int> reference;
public void Execute()
{
reference.Value = 0;
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d429f1aa0fb37ca4d9eedbec119474ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Jobs/JobUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5abf9b52a60ea9843b4f61eee44d06f7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,238 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Mathematics;
namespace MagicaCloth2
{
/// <summary>
/// このAABB構造体はUnity.Mathematics.Geometry.MinMaxAABBを移植したものです。
/// MinMaxAABBはinternalのため外部から利用することができません。
/// このコードはすべてMinMaxAABBのコピーとなります。
/// </summary>
[System.Serializable]
public struct AABB : IEquatable<AABB>
{
/// <summary>
/// The minimum point contained by the AABB.
/// </summary>
/// <remarks>
/// If any component of <see cref="Min"/> is greater than <see cref="Max"/> then this AABB is invalid.
/// </remarks>
/// <seealso cref="IsValid"/>
public float3 Min;
/// <summary>
/// The maximum point contained by the AABB.
/// </summary>
/// <remarks>
/// If any component of <see cref="Max"/> is less than <see cref="Min"/> then this AABB is invalid.
/// </remarks>
/// <seealso cref="IsValid"/>
public float3 Max;
/// <summary>
/// Constructs the AABB with the given minimum and maximum.
/// </summary>
/// <remarks>
/// If you have a center and extents, you can call <see cref="CreateFromCenterAndExtents"/> or <see cref="CreateFromCenterAndHalfExtents"/>
/// to create the AABB.
/// </remarks>
/// <param name="min">Minimum point inside AABB.</param>
/// <param name="max">Maximum point inside AABB.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AABB(in float3 min, in float3 max)
{
Min = min;
Max = max;
}
/// <summary>
/// Creates the AABB from a center and extents.
/// </summary>
/// <remarks>
/// This function takes full extents. It is the distance between <see cref="Min"/> and <see cref="Max"/>.
/// If you have half extents, you can call <see cref="CreateFromCenterAndHalfExtents"/>.
/// </remarks>
/// <param name="center">Center of AABB.</param>
/// <param name="extents">Full extents of AABB.</param>
/// <returns>AABB created from inputs.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AABB CreateFromCenterAndExtents(float3 center, float3 extents)
{
return CreateFromCenterAndHalfExtents(center, extents * 0.5f);
}
/// <summary>
/// Creates the AABB from a center and half extents.
/// </summary>
/// <remarks>
/// This function takes half extents. It is half the distance between <see cref="Min"/> and <see cref="Max"/>.
/// If you have full extents, you can call <see cref="CreateFromCenterAndExtents"/>.
/// </remarks>
/// <param name="center">Center of AABB.</param>
/// <param name="halfExtents">Half extents of AABB.</param>
/// <returns>AABB created from inputs.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AABB CreateFromCenterAndHalfExtents(float3 center, float3 halfExtents)
{
return new AABB(center - halfExtents, center + halfExtents);
}
/// <summary>
/// Computes the extents of the AABB.
/// </summary>
/// <remarks>
/// Extents is the componentwise distance between min and max.
/// </remarks>
public float3 Extents => Max - Min;
/// <summary>
/// Computes the half extents of the AABB.
/// </summary>
/// <remarks>
/// HalfExtents is half of the componentwise distance between min and max. Subtracting HalfExtents from Center
/// gives Min and adding HalfExtents to Center gives Max.
/// </remarks>
public float3 HalfExtents => (Max - Min) * 0.5f;
/// <summary>
/// Computes the center of the AABB.
/// </summary>
public float3 Center => (Max + Min) * 0.5f;
/// <summary>
/// 最大の辺の長さを返す
/// </summary>
public float MaxSideLength
{
get
{
var ext = Extents;
return math.max(math.max(ext.x, ext.y), ext.z);
}
}
/// <summary>
/// Check if the AABB is valid.
/// </summary>
/// <remarks>
/// An AABB is considered valid if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.
/// </remarks>
/// <returns>True if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.</returns>
public bool IsValid => math.all(Min <= Max);
/// <summary>
/// Computes the surface area for this axis aligned bounding box.
/// </summary>
public float SurfaceArea
{
get
{
float3 diff = Max - Min;
return 2 * math.dot(diff, diff.yzx);
}
}
/// <summary>
/// Tests if the input point is contained by the AABB.
/// </summary>
/// <param name="point">Point to test.</param>
/// <returns>True if AABB contains the input point.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(in float3 point) => math.all(point >= Min & point <= Max);
/// <summary>
/// Tests if the input AABB is contained entirely by this AABB.
/// </summary>
/// <param name="aabb">AABB to test.</param>
/// <returns>True if input AABB is contained entirely by this AABB.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(in AABB aabb) => math.all((Min <= aabb.Min) & (Max >= aabb.Max));
/// <summary>
/// Tests if the input AABB overlaps this AABB.
/// </summary>
/// <param name="aabb">AABB to test.</param>
/// <returns>True if input AABB overlaps with this AABB.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Overlaps(in AABB aabb)
{
return math.all(Max >= aabb.Min & Min <= aabb.Max);
}
/// <summary>
/// Expands the AABB by the given signed distance.
/// </summary>
/// <remarks>
/// Positive distance expands the AABB while negative distance shrinks the AABB.
/// </remarks>
/// <param name="signedDistance">Signed distance to expand the AABB with.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Expand(float signedDistance)
{
Min -= signedDistance;
Max += signedDistance;
}
/// <summary>
/// Encapsulates the given AABB.
/// </summary>
/// <remarks>
/// Modifies this AABB so that it contains the given AABB. If the given AABB is already contained by this AABB,
/// then this AABB doesn't change.
/// </remarks>
/// <seealso cref="Contains(Unity.Mathematics.Geometry.MinMaxAABB)"/>
/// <param name="aabb">AABB to encapsulate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Encapsulate(in AABB aabb)
{
Min = math.min(Min, aabb.Min);
Max = math.max(Max, aabb.Max);
}
/// <summary>
/// Encapsulate the given point.
/// </summary>
/// <remarks>
/// Modifies this AABB so that it contains the given point. If the given point is already contained by this AABB,
/// then this AABB doesn't change.
/// </remarks>
/// <seealso cref="Contains(Unity.Mathematics.float3)"/>
/// <param name="point">Point to encapsulate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Encapsulate(in float3 point)
{
Min = math.min(Min, point);
Max = math.max(Max, point);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(AABB other)
{
return Min.Equals(other.Min) && Max.Equals(other.Max);
}
/// <summary>
/// 変換マトリックスにより座標を変換させる
/// </summary>
/// <param name="toM"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Transform(in float4x4 toM)
{
float3 min = math.transform(toM, Min);
float3 max = math.transform(toM, Max);
Min = math.min(min, max);
Max = math.max(min, max);
}
public override string ToString()
{
//return string.Format("AABB({0}, {1})", Min, Max);
return $"AABB Center:{Center}, HalfExtents:{HalfExtents}, Min:{Min}, Max:{Max}";
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ae15307e07485a447aa84cda0ddb05e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Math/AABB.cs
uploadId: 893596

View File

@@ -0,0 +1,158 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Mathematics;
namespace MagicaCloth2
{
/// <summary>
/// int3型のAABB
/// </summary>
[System.Serializable]
public struct IntAABB : IEquatable<IntAABB>
{
/// <summary>
/// The minimum point contained by the AABB.
/// </summary>
/// <remarks>
/// If any component of <see cref="Min"/> is greater than <see cref="Max"/> then this AABB is invalid.
/// </remarks>
/// <seealso cref="IsValid"/>
public int3 Min;
/// <summary>
/// The maximum point contained by the AABB.
/// </summary>
/// <remarks>
/// If any component of <see cref="Max"/> is less than <see cref="Min"/> then this AABB is invalid.
/// </remarks>
/// <seealso cref="IsValid"/>
public int3 Max;
/// <summary>
/// Constructs the AABB with the given minimum and maximum.
/// </summary>
/// <remarks>
/// If you have a center and extents, you can call <see cref="CreateFromCenterAndExtents"/> or <see cref="CreateFromCenterAndHalfExtents"/>
/// to create the AABB.
/// </remarks>
/// <param name="min">Minimum point inside AABB.</param>
/// <param name="max">Maximum point inside AABB.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IntAABB(int3 min, int3 max)
{
Min = min;
Max = max;
}
/// <summary>
/// Computes the extents of the AABB.
/// </summary>
/// <remarks>
/// Extents is the componentwise distance between min and max.
/// </remarks>
public int3 Extents => Max - Min;
/// <summary>
/// Computes the center of the AABB.
/// </summary>
public int3 Center => (Max + Min) / 2;
/// <summary>
/// Check if the AABB is valid.
/// </summary>
/// <remarks>
/// An AABB is considered valid if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.
/// </remarks>
/// <returns>True if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.</returns>
public bool IsValid => math.all(Min <= Max);
/// <summary>
/// Tests if the input point is contained by the AABB.
/// </summary>
/// <param name="point">Point to test.</param>
/// <returns>True if AABB contains the input point.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int3 point) => math.all(point >= Min & point <= Max);
/// <summary>
/// Tests if the input AABB is contained entirely by this AABB.
/// </summary>
/// <param name="aabb">AABB to test.</param>
/// <returns>True if input AABB is contained entirely by this AABB.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(IntAABB aabb) => math.all((Min <= aabb.Min) & (Max >= aabb.Max));
/// <summary>
/// Tests if the input AABB overlaps this AABB.
/// </summary>
/// <param name="aabb">AABB to test.</param>
/// <returns>True if input AABB overlaps with this AABB.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Overlaps(IntAABB aabb)
{
return math.all(Max >= aabb.Min & Min <= aabb.Max);
}
/// <summary>
/// Expands the AABB by the given signed distance.
/// </summary>
/// <remarks>
/// Positive distance expands the AABB while negative distance shrinks the AABB.
/// </remarks>
/// <param name="signedDistance">Signed distance to expand the AABB with.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Expand(int signedDistance)
{
Min -= signedDistance;
Max += signedDistance;
}
/// <summary>
/// Encapsulates the given AABB.
/// </summary>
/// <remarks>
/// Modifies this AABB so that it contains the given AABB. If the given AABB is already contained by this AABB,
/// then this AABB doesn't change.
/// </remarks>
/// <seealso cref="Contains(Unity.Mathematics.Geometry.MinMaxAABB)"/>
/// <param name="aabb">AABB to encapsulate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Encapsulate(IntAABB aabb)
{
Min = math.min(Min, aabb.Min);
Max = math.max(Max, aabb.Max);
}
/// <summary>
/// Encapsulate the given point.
/// </summary>
/// <remarks>
/// Modifies this AABB so that it contains the given point. If the given point is already contained by this AABB,
/// then this AABB doesn't change.
/// </remarks>
/// <seealso cref="Contains(Unity.Mathematics.float3)"/>
/// <param name="point">Point to encapsulate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Encapsulate(int3 point)
{
Min = math.min(Min, point);
Max = math.max(Max, point);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(IntAABB other)
{
return Min.Equals(other.Min) && Max.Equals(other.Max);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString()
{
return string.Format("AABB({0}, {1})", Min, Max);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 93dbd06f10c49dd4b9ae88b2a9c4c099
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Math/IntAABB.cs
uploadId: 893596

View File

@@ -0,0 +1,61 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Runtime.CompilerServices;
using Unity.Mathematics;
namespace MagicaCloth2
{
public static class MathExtensions
{
/// <summary>
/// float4x4を16の配列として扱うための拡張
/// </summary>
/// <param name="m"></param>
/// <param name="index"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float MC2GetValue(in this float4x4 m, int index)
{
index = math.clamp(index, 0, 15);
return m[index / 4][index % 4];
}
/// <summary>
/// float4x4を16の配列として扱うための拡張
/// </summary>
/// <param name="m"></param>
/// <param name="index"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2SetValue(ref this float4x4 m, int index, float value)
{
index = math.clamp(index, 0, 15);
m[index / 4][index % 4] = value;
}
/// <summary>
/// AnimationCurveが格納されたfloat4x4からデータを取得する(0.0 ~ 1.0でクランプ)
/// </summary>
/// <param name="m"></param>
/// <param name="time">0.0 ~ 1.0</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float MC2EvaluateCurveClamp01(in this float4x4 m, float time)
{
return math.saturate(DataUtility.EvaluateCurve(m, time));
}
/// <summary>
/// AnimationCurveが格納されたfloat4x4からデータを取得する
/// </summary>
/// <param name="m"></param>
/// <param name="time">0.0 ~ 1.0</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float MC2EvaluateCurve(in this float4x4 m, float time)
{
return DataUtility.EvaluateCurve(m, time);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ae8fc4e69ea083a4081671c5eff29606
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Math/MathExtensions.cs
uploadId: 893596

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7ad45eb87d191fb4890da353d511a024
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Math/MathUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,57 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Collections.Generic;
namespace MagicaCloth2
{
/// <summary>
/// 最小距離のデータのみを保持するクラス
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
public class MinimumData<T1, T2> where T1 : unmanaged where T2 : unmanaged
{
T1 minDist;
T2 minData;
bool isValid = false;
/// <summary>
/// データの追加。現在のデータより距離が短い場合のみ上書きする
/// </summary>
/// <param name="distance"></param>
/// <param name="data"></param>
public void Add(T1 distance, T2 data)
{
if (isValid)
{
if (Comparer<T1>.Default.Compare(distance, minDist) < 0)
{
minDist = distance;
minData = data;
}
}
else
{
minDist = distance;
minData = data;
isValid = true;
}
}
public void Clear()
{
isValid = false;
}
public bool IsValid => isValid;
public T1 MinDistance => minDist;
public T2 MinData => minData;
public override string ToString()
{
return $"MinimumData. IsValid:{isValid}, minDist:{minDist}, minData:{minData}";
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 334bdeb060898f14f811bc5b01ac3f19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Math/MinimumData.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e1339b9964cd3cc4db9e724b185506c2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,99 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEngine;
namespace MagicaCloth2
{
public static class MeshUtility
{
/// <summary>
/// レンダラーからSharedMeshを取得する
/// </summary>
/// <param name="ren"></param>
/// <returns></returns>
public static Mesh GetSharedMesh(Renderer ren)
{
if (ren == null)
return null;
if (ren is SkinnedMeshRenderer)
{
var sren = ren as SkinnedMeshRenderer;
return sren.sharedMesh;
}
else
{
// mesh filter
if (!ren.TryGetComponent<MeshFilter>(out var filter))
{
Debug.LogError("Not found MeshFilter!");
return null;
}
return filter.sharedMesh;
}
}
/// <summary>
/// レンダラーにメッシュを設定する
/// </summary>
/// <param name="ren"></param>
/// <param name="mesh"></param>
/// <returns></returns>
public static bool SetMesh(Renderer ren, Mesh mesh, Transform[] skinBones = null)
{
if (ren is SkinnedMeshRenderer)
{
var sren = ren as SkinnedMeshRenderer;
sren.sharedMesh = mesh;
if (skinBones != null && skinBones.Length > 0)
sren.bones = skinBones;
}
else
{
// mesh filter
if (!ren.TryGetComponent<MeshFilter>(out var filter))
{
Debug.LogError("Not found MeshFilter!");
return false;
}
filter.mesh = mesh;
}
return true;
}
/// <summary>
/// このレンダラーが利用しているTransformの数を返す
/// この数は概算であり正確ではないので注意!
/// </summary>
/// <param name="ren"></param>
/// <returns></returns>
public static int GetTransformCount(Renderer ren)
{
int tcnt = 0;
if (ren)
{
// renderer
tcnt++;
if (ren is SkinnedMeshRenderer)
{
var sren = ren as SkinnedMeshRenderer;
// root bone
tcnt++;
// skin bones
tcnt += sren.bones?.Length ?? 0;
}
}
return tcnt;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b4de31f1b32056a4d868784716d5ff27
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Mesh/MeshUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 881419a3fff203d4b9c3bdaa61adf5c7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// 開発用ユーティリティ
/// </summary>
public static class Develop
{
public static void Log(in object mes)
{
Debug.Log($"[MC2] {mes}");
}
public static void LogWarning(in object mes)
{
Debug.LogWarning($"[MC2] {mes}");
}
public static void LogError(in object mes)
{
Debug.LogError($"[MC2] {mes}");
}
[System.Diagnostics.Conditional("MC2_LOG")]
public static void DebugLog(in object mes)
{
Debug.Log($"[MC2 DEBUG] {mes}");
}
[System.Diagnostics.Conditional("MC2_DEBUG")]
public static void DebugLogWarning(in object mes)
{
Debug.LogWarning($"[MC2 DEBUG] {mes}");
}
[System.Diagnostics.Conditional("MC2_DEBUG")]
public static void DebugLogError(in object mes)
{
Debug.LogError($"[MC2 DEBUG] {mes}");
}
[System.Diagnostics.Conditional("MC2_DEBUG")]
public static void Assert(bool condition)
{
// 何故かメッセージを追加すると機能しない..
Debug.Assert(condition);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e02e1cb346cdf1d4babc2bf1470dac90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Misc/Develop.cs
uploadId: 893596

View File

@@ -0,0 +1,97 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Text;
namespace MagicaCloth2
{
/// <summary>
/// 静的StringBuilderクラス
/// </summary>
public class StaticStringBuilder
{
private static StringBuilder stringBuilder = new StringBuilder(1024);
/// <summary>
/// StringBuilderのインスタンスを取得する
/// </summary>
public static StringBuilder Instance
{
get
{
return stringBuilder;
}
}
/// <summary>
/// 内部をクリアする
/// </summary>
public static void Clear()
{
stringBuilder.Length = 0;
}
/// <summary>
/// 与えられた文字を結合する
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static StringBuilder Append(params object[] args)
{
for (int i = 0; i < args.Length; i++)
{
stringBuilder.Append(args[i]);
}
return stringBuilder;
}
/// <summary>
/// 与えられた文字列を結合し、最後に改行コードを挿入する
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static StringBuilder AppendLine(params object[] args)
{
for (int i = 0; i < args.Length; i++)
{
stringBuilder.Append(args[i]);
}
stringBuilder.Append("\n");
return stringBuilder;
}
/// <summary>
/// 改行を追加する
/// </summary>
/// <returns></returns>
public static StringBuilder AppendLine()
{
stringBuilder.Append("\n");
return stringBuilder;
}
/// <summary>
/// 与えられた文字を結合して、結合文字列を返す
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static string AppendToString(params object[] args)
{
stringBuilder.Length = 0;
for (int i = 0; i < args.Length; i++)
{
stringBuilder.Append(args[i]);
}
return stringBuilder.ToString();
}
/// <summary>
/// 文字列を返す
/// </summary>
/// <returns></returns>
public static new string ToString()
{
return stringBuilder.ToString();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4d925c104701fe242b9f070700e73997
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Misc/StaticStringBuilder.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 05c459b696cc77144b66c9cfc2a404b6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
namespace MagicaCloth2
{
/// <summary>
/// 配列の断片を管理する
/// </summary>
public struct DataChunk
{
/// <summary>
/// 開始インデックス
/// </summary>
public int startIndex;
/// <summary>
/// データ数
/// </summary>
public int dataLength;
public bool IsValid => dataLength > 0;
public static DataChunk Empty
{
get
{
return new DataChunk();
}
}
public DataChunk(int sindex, int length)
{
startIndex = sindex;
dataLength = length;
}
public DataChunk(int sindex)
{
startIndex = sindex;
dataLength = 1;
}
public void Clear()
{
startIndex = 0;
dataLength = 0;
}
public override string ToString()
{
return $"[startIndex={startIndex}, dataLength={dataLength}]";
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1da73ff56787abd45804252538471bf6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/DataChunk.cs
uploadId: 893596

View File

@@ -0,0 +1,51 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Runtime.CompilerServices;
namespace MagicaCloth2
{
/// <summary>
/// 16ビットフラグ
/// </summary>
[System.Serializable]
public struct ExBitFlag16
{
public ushort Value;
public ExBitFlag16(ushort initialValue = 0)
{
Value = initialValue;
}
public void Clear()
{
Value = 0;
}
/// <summary>
/// フラグ設定(ビット直指定)
/// </summary>
/// <param name="flag"></param>
/// <param name="sw"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetFlag(ushort flag, bool sw)
{
if (sw)
Value |= flag;
else
Value = (ushort)(Value & ~flag);
}
/// <summary>
/// フラグ判定(ビット直指定)
/// </summary>
/// <param name="flag"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSet(ushort flag)
{
return (Value & flag) != 0;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 82468a45671e5e1498d4df5c19611f32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExBitFlag16.cs
uploadId: 893596

View File

@@ -0,0 +1,52 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Runtime.CompilerServices;
namespace MagicaCloth2
{
/// <summary>
/// 8ビットフラグ
/// </summary>
[System.Serializable]
public struct ExBitFlag8
{
public byte Value;
public ExBitFlag8(byte initialValue = 0)
{
Value = initialValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
Value = 0;
}
/// <summary>
/// フラグ設定
/// </summary>
/// <param name="flag"></param>
/// <param name="sw"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetFlag(byte flag, bool sw)
{
if (sw)
Value |= flag;
else
Value = (byte)(Value & ~flag);
}
/// <summary>
/// フラグ判定
/// </summary>
/// <param name="flag"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSet(byte flag)
{
return (Value & flag) != 0;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: eb16200a6206d394d9e12963263352b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExBitFlag8.cs
uploadId: 893596

View File

@@ -0,0 +1,73 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// 最も低いコストとデータをつ格納するSortedList
/// コストは0以上でなければならない
/// 必ずコンストラクタでマイナスコストを指定してから利用すること
/// var dlist = new ExCostSortedList1(-1);
/// </summary>
public struct ExCostSortedList1 : IComparable<ExCostSortedList1>
{
internal float cost;
internal int data;
/// <summary>
/// 必ずマイナス距離で初期化すること
/// </summary>
/// <param name="invalidCost"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ExCostSortedList1(float invalidCost)
{
cost = invalidCost;
data = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ExCostSortedList1(float invalidCost, int initData)
{
cost = invalidCost;
data = initData;
}
public bool IsValid => cost >= 0.0f;
public int Count => IsValid ? 1 : 0;
public float Cost => cost;
public int Data => data;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(float cost, int item)
{
Debug.Assert(cost >= 0.0f);
if (IsValid == false || cost < this.cost)
{
this.cost = cost;
data = item;
}
}
public int CompareTo(ExCostSortedList1 other)
{
// コストの昇順
if (cost != other.cost)
return cost < other.cost ? -1 : 1;
else
return 0;
}
public override string ToString()
{
return $"({cost} : {data})";
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b8b3cd069c36d174ea801cadc4d95919
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExCostSortedList1.cs
uploadId: 893596

View File

@@ -0,0 +1,186 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// コストの昇順につまでデータを格納できる固定SortedList
/// コストは0以上でなければならない
/// 必ずコンストラクタでマイナスコストを指定してから利用すること
/// var dlist = new ExCostSortedList4(-1);
/// </summary>
public struct ExCostSortedList4
{
internal float4 costs;
internal int4 data;
/// <summary>
/// 必ずマイナス距離で初期化すること
/// </summary>
/// <param name="invalidCost"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ExCostSortedList4(float invalidCost)
{
costs = invalidCost;
data = 0;
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
for (int i = 3; i >= 0; i--)
{
if (costs[i] >= 0.0f)
return i + 1;
}
return 0;
}
}
public bool IsValid => costs[0] >= 0.0f;
public bool Add(float cost, int item)
{
Debug.Assert(cost >= 0.0f);
// すでにデータが一杯で最大コストより上なら入らない
if (costs[3] >= 0.0f && cost > costs[3])
return false;
// 距離の昇順で挿入する、最大数は4
for (int i = 0; i < 4; i++)
{
float d = costs[i];
if (d < 0.0f)
{
// 追加
costs[i] = cost;
data[i] = item;
return true;
}
else if (cost < d)
{
// 挿入
for (int j = 2; j >= i; j--)
{
costs[j + 1] = costs[j];
data[j + 1] = data[j];
}
costs[i] = cost;
data[i] = item;
return true;
}
}
// 入らない
return false;
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public (float, int) Get(int index)
//{
// return (costs[index], data[index]);
//}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int item)
{
for (int i = 0; i < 4; i++)
{
if (costs[i] >= 0.0f && data[i] == item)
return true;
}
return false;
}
/// <summary>
/// データ内のアイテムを検索してその登録インデックスを返す。(-1=なし)
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public int indexOf(int item)
{
for (int i = 0; i < 4; i++)
{
if (costs[i] >= 0.0f && data[i] == item)
return i;
}
return -1;
}
/// <summary>
/// データ内のアイテムを削除しデータを1つ詰める
/// </summary>
/// <param name="item"></param>
public void RemoveItem(int item)
{
int itemIndex = -1;
for (int i = 0; i < 4; i++)
{
if (costs[i] >= 0.0f && data[i] == item)
{
itemIndex = i;
break;
}
}
if (itemIndex < 0)
return;
for (int j = itemIndex; j < 3; j++)
{
costs[j] = costs[j + 1];
data[j] = data[j + 1];
}
costs[3] = -1;
}
/// <summary>
/// データ内の最小のコストを返す
/// </summary>
public float MinCost
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return costs[0];
}
}
/// <summary>
/// データ内の最大のコストを返す
/// </summary>
public float MaxCost
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
for (int i = 3; i >= 0; i--)
{
if (costs[i] >= 0.0f)
return costs[i];
}
return 0.0f;
}
}
public override string ToString()
{
var s = new FixedString512Bytes();
for (int i = 0; i < Count; i++)
{
s.Append($"({costs[i]} : {data[i]}) ");
}
return s.ToString();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 117c9e1f7003d1b418193a8ee6f8a850
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExCostSortedList4.cs
uploadId: 893596

View File

@@ -0,0 +1,709 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Collections.Generic;
using System.Text;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// 拡張可能なNativeArrayクラス
/// 領域が不足すると自動で拡張する
/// データはChankDataにより開始インデックスと長さが管理される
/// データは削除可能で削除された領域は管理され再利用される
/// 領域の管理が必要なためExSimpleNativeArrayに比べてやや重いので注意
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExNativeArray<T> : IDisposable where T : unmanaged
{
NativeArray<T> nativeArray;
List<DataChunk> emptyChunks = new List<DataChunk>();
int useCount;
public void Dispose()
{
if (nativeArray.IsCreated)
{
nativeArray.Dispose();
}
emptyChunks.Clear();
useCount = 0;
}
public bool IsValid => nativeArray.IsCreated;
/// <summary>
/// NativeArrayの領域サイズ
/// 実際に利用されているサイズとは異なるので注意!
/// </summary>
public int Length => nativeArray.IsCreated ? nativeArray.Length : 0;
/// <summary>
/// 実際に利用されているデータ数(最後のチャンクの最後尾+1)
/// </summary>
public int Count => useCount;
//=========================================================================================
public ExNativeArray()
{
}
public ExNativeArray(int emptyLength, bool create = false) : this()
{
if (emptyLength > 0)
{
nativeArray = new NativeArray<T>(emptyLength, Allocator.Persistent);
var chunk = new DataChunk(0, emptyLength);
emptyChunks.Add(chunk);
if (create)
{
// 領域を確保する
AddRange(emptyLength);
}
}
else if (create)
{
// Native配列のみで確保主にジョブでのエラー対策
nativeArray = new NativeArray<T>(0, Allocator.Persistent);
}
}
public ExNativeArray(int emptyLength, T fillData) : this(emptyLength)
{
if (emptyLength > 0)
Fill(fillData);
}
public ExNativeArray(NativeArray<T> dataArray) : this()
{
AddRange(dataArray);
}
public ExNativeArray(T[] dataArray) : this()
{
AddRange(dataArray);
}
//=========================================================================================
#if false
/// <summary>
/// 使用配列カウントを設定する
/// 有効数を書き換えすべてのデータを1つのチャンクとして使用中とする
/// かなり強力な機能なので扱いには注意すること!
/// </summary>
/// <param name="count"></param>
public void SetUseCount(int count)
{
useCount = count;
emptyChunks.Clear();
if (useCount > Length)
{
// 未使用領域を1つの空チャンクとして登録する
var chunk = new DataChunk(useCount, Length - useCount);
emptyChunks.Add(chunk);
}
}
#endif
/// <summary>
/// 指定サイズの領域を追加しそのチャンクを返す
/// </summary>
/// <param name="dataLength"></param>
/// <returns></returns>
public DataChunk AddRange(int dataLength)
{
// サイズ0対応
if (dataLength == 0)
{
// 領域だけは0で確保する
if (nativeArray.IsCreated == false)
nativeArray = new NativeArray<T>(0, Allocator.Persistent);
return DataChunk.Empty;
}
var chunk = GetEmptyChunk(dataLength);
if (chunk.IsValid == false)
{
// 空きを増やす
int nowLength = Length;
int nextLength = Length + math.max(dataLength, nowLength);
if (nowLength == 0)
{
// 新規
if (nativeArray.IsCreated)
nativeArray.Dispose();
nativeArray = new NativeArray<T>(nextLength, Allocator.Persistent);
chunk.dataLength = dataLength;
}
else
{
// 拡張
var newNativeArray = new NativeArray<T>(nextLength, Allocator.Persistent);
// copy
NativeArray<T>.Copy(nativeArray, newNativeArray, nowLength);
nativeArray.Dispose();
nativeArray = newNativeArray;
// data chunk
chunk.startIndex = nowLength;
chunk.dataLength = dataLength;
int last = nowLength + dataLength;
if (last < nextLength)
{
var emptyChunk = new DataChunk(last, nextLength - last);
AddEmptyChunk(emptyChunk);
}
}
}
// 使用量
useCount = math.max(useCount, chunk.startIndex + chunk.dataLength);
return chunk;
}
public DataChunk AddRange(int dataLength, T fillData = default(T))
{
var chunk = AddRange(dataLength);
Fill(chunk, fillData);
return chunk;
}
public DataChunk AddRange(T[] array)
{
if (array == null || array.Length == 0)
return DataChunk.Empty;
int dataLength = array.Length;
var chunk = AddRange(dataLength);
// copy
NativeArray<T>.Copy(array, 0, nativeArray, chunk.startIndex, dataLength);
return chunk;
}
public DataChunk AddRange(NativeArray<T> narray, int length = 0)
{
if (narray.IsCreated == false || narray.Length == 0)
return DataChunk.Empty;
int dataLength = length > 0 ? length : narray.Length;
var chunk = AddRange(dataLength);
// copy
NativeArray<T>.Copy(narray, 0, nativeArray, chunk.startIndex, dataLength);
return chunk;
}
public DataChunk AddRange(ExNativeArray<T> exarray)
{
return AddRange(exarray.GetNativeArray(), exarray.Count);
}
public DataChunk AddRange(ExSimpleNativeArray<T> exarray)
{
return AddRange(exarray.GetNativeArray(), exarray.Count);
}
/// <summary>
/// 型は異なるが型のサイズは同じ配列を追加する。Vector3->float3など。
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
public unsafe DataChunk AddRange<U>(U[] array) where U : struct
{
if (array == null || array.Length == 0)
return DataChunk.Empty;
int dstSize = UnsafeUtility.SizeOf<T>();
int dataLength = array.Length;
var chunk = AddRange(dataLength);
ulong src_gcHandle;
void* src_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out src_gcHandle);
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
UnsafeUtility.MemCpy(dst_p + chunk.startIndex * dstSize, src_p, dataLength * dstSize);
UnsafeUtility.ReleaseGCObject(src_gcHandle);
return chunk;
}
/// <summary>
/// 型は異なるが型のサイズは同じNativeArrayを追加する。Vector3->float3など。
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="udata"></param>
/// <returns></returns>
public DataChunk AddRange<U>(NativeArray<U> udata) where U : struct
{
if (udata.IsCreated == false || udata.Length == 0)
return DataChunk.Empty;
int dataLength = udata.Length;
var chunk = AddRange(dataLength);
// copy
NativeArray<T>.Copy(udata.Reinterpret<T>(), 0, nativeArray, chunk.startIndex, dataLength);
return chunk;
}
/// <summary>
/// 型もサイズも異なる配列を追加する。int[] -> int3[]など。
/// データはそのままメモリコピーされる。例えばint[]からint3[]へ追加すると次のようになる。
/// int[]{1, 2, 3, 4, 5, 6} => int3[]{{1, 2, 3}, {4, 5, 6}}
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
public unsafe DataChunk AddRangeTypeChange<U>(U[] array) where U : struct
{
if (array == null || array.Length == 0)
return DataChunk.Empty;
int srcSize = UnsafeUtility.SizeOf<U>();
int dstSize = UnsafeUtility.SizeOf<T>();
int dataLength = (array.Length * srcSize) / dstSize;
var chunk = AddRange(dataLength);
ulong src_gcHandle;
void* src_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out src_gcHandle);
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
UnsafeUtility.MemCpy(dst_p + chunk.startIndex * dstSize, src_p, dataLength * dstSize);
UnsafeUtility.ReleaseGCObject(src_gcHandle);
return chunk;
}
/// <summary>
/// 型もサイズも異なる配列を部分的にコピーする。Vector4[] -> float3など。
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
public unsafe DataChunk AddRangeStride<U>(U[] array) where U : struct
{
if (array == null || array.Length == 0)
return DataChunk.Empty;
int srcSize = UnsafeUtility.SizeOf<U>();
int dstSize = UnsafeUtility.SizeOf<T>();
int dataLength = array.Length;
var chunk = AddRange(dataLength);
ulong src_gcHandle;
void* src_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out src_gcHandle);
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
int elementSize = math.min(srcSize, dstSize);
UnsafeUtility.MemCpyStride(dst_p + chunk.startIndex * dstSize, dstSize, src_p, srcSize, elementSize, dataLength);
UnsafeUtility.ReleaseGCObject(src_gcHandle);
return chunk;
}
public DataChunk Add(T data)
{
var chunk = AddRange(1);
nativeArray[chunk.startIndex] = data;
return chunk;
}
/// <summary>
/// 指定チャンクのデータ数を拡張し新しいチャンクを返す
/// 古いチャンクのデータは新しいチャンクにコピーされる
/// </summary>
/// <param name="c"></param>
/// <param name="newDataLength"></param>
/// <returns></returns>
public DataChunk Expand(DataChunk c, int newDataLength)
{
Develop.Assert(c.IsValid);
if (c.IsValid == false)
return c;
if (newDataLength <= c.dataLength)
return c;
// 新しい領域を確保する
var nc = AddRange(newDataLength);
// 古い領域をコピーする
NativeArray<T>.Copy(nativeArray, c.startIndex, nativeArray, nc.startIndex, c.dataLength);
// 古い領域を開放する
Remove(c);
return nc;
}
/// <summary>
/// 指定チャンクのデータ数を拡張し新しいチャンクを返す
/// 古いチャンクのデータは新しいチャンクにコピーされる
/// </summary>
/// <param name="c"></param>
/// <param name="newDataLength"></param>
/// <returns></returns>
public DataChunk ExpandAndFill(DataChunk c, int newDataLength, T fillData = default(T), T clearData = default(T))
{
Develop.Assert(c.IsValid);
if (c.IsValid == false)
return c;
if (newDataLength <= c.dataLength)
return c;
// 新しい領域を確保する
var nc = AddRange(newDataLength, fillData);
// 古い領域をコピーする
NativeArray<T>.Copy(nativeArray, c.startIndex, nativeArray, nc.startIndex, c.dataLength);
// 古い領域を開放する
RemoveAndFill(c, clearData);
return nc;
}
public T[] ToArray()
{
return nativeArray.ToArray();
}
public void CopyTo(T[] array)
{
NativeArray<T>.Copy(nativeArray, array);
}
public void CopyTo(T[] array, int startIndex)
{
Debug.Assert(array != null);
NativeArray<T>.Copy(nativeArray, startIndex, array, 0, array.Length);
}
public void CopyTo<U>(U[] array) where U : struct
{
NativeArray<U>.Copy(nativeArray.Reinterpret<U>(), array);
}
public void CopyFrom(NativeArray<T> array)
{
NativeArray<T>.Copy(array, nativeArray);
}
public void CopyFrom(T[] array, int startIndex)
{
Debug.Assert(array != null);
NativeArray<T>.Copy(array, 0, nativeArray, startIndex, array.Length);
}
public void CopyFrom<U>(NativeArray<U> array) where U : struct
{
NativeArray<T>.Copy(array.Reinterpret<T>(), nativeArray);
}
public void CopyFrom<U>(NativeArray<U> array, int dstIndex, int length) where U : struct
{
NativeArray<T>.Copy(array.Reinterpret<T>(), 0, nativeArray, dstIndex, length);
}
/// <summary>
/// 型もサイズも異なる配列にデータをコピーする。
/// int3 -> int[]など
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
public unsafe void CopyTypeChange<U>(U[] array) where U : struct
{
int srcSize = UnsafeUtility.SizeOf<T>();
int dstSize = UnsafeUtility.SizeOf<U>();
int dataLength = (Length * srcSize) / dstSize;
byte* src_p = (byte*)nativeArray.GetUnsafePtr();
ulong dst_gcHandle;
void* dst_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out dst_gcHandle);
UnsafeUtility.MemCpy(dst_p, src_p, dataLength * dstSize);
UnsafeUtility.ReleaseGCObject(dst_gcHandle);
}
/// <summary>
/// 型もサイズも異なる配列にデータを断片的にコピーする。
/// float3 -> Vector4[]など。この場合はVector4にはxyzのみ書き込まれる。
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
public unsafe void CopyTypeChangeStride<U>(U[] array) where U : struct
{
int srcSize = UnsafeUtility.SizeOf<T>();
int dstSize = UnsafeUtility.SizeOf<U>();
int dataLength = Length;
byte* src_p = (byte*)nativeArray.GetUnsafePtr();
ulong dst_gcHandle;
void* dst_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out dst_gcHandle);
int elementSize = srcSize;
UnsafeUtility.MemCpyStride(dst_p, dstSize, src_p, srcSize, elementSize, dataLength);
UnsafeUtility.ReleaseGCObject(dst_gcHandle);
}
/// <summary>
/// すぐに利用できる空領域のみ追加する
/// </summary>
/// <param name="dataLength"></param>
public void AddEmpty(int dataLength)
{
var chunk = AddRange(dataLength);
Remove(chunk);
}
public void Remove(DataChunk chunk)
{
if (chunk.IsValid == false)
return;
AddEmptyChunk(chunk);
// 使用量の再計算
if ((chunk.startIndex + chunk.dataLength) == useCount)
{
useCount = 0;
foreach (var echunk in emptyChunks)
{
useCount = math.max(useCount, echunk.startIndex);
}
}
}
public void Remove(int index)
{
Remove(new DataChunk(index));
}
public void RemoveAndFill(DataChunk chunk, T clearData = default(T))
{
Remove(chunk);
// データクリア
// C#
//Parallel.For(0, chunk.dataLength, i =>
//{
// nativeArray[chunk.startIndex + i] = clearData;
//});
//FillInternal(chunk.startIndex, chunk.dataLength, clearData);
Fill(chunk, clearData);
}
public void Fill(T fillData = default(T))
{
if (IsValid == false)
return;
// C#
//Parallel.For(0, nativeArray.Length, i =>
//{
// nativeArray[i] = fillData;
//});
FillInternal(0, nativeArray.Length, fillData);
}
public void Fill(DataChunk chunk, T fillData = default(T))
{
if (IsValid == false || chunk.IsValid == false)
return;
// C#
//Parallel.For(0, chunk.dataLength, i =>
//{
// nativeArray[chunk.startIndex + i] = fillData;
//});
FillInternal(chunk.startIndex, chunk.dataLength, fillData);
}
unsafe void FillInternal(int start, int size, T fillData = default(T))
{
//byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
void* dst_p = nativeArray.GetUnsafePtr();
int index = start;
for (int i = 0; i < size; i++, index++)
{
UnsafeUtility.WriteArrayElement<T>(dst_p, index, fillData);
}
}
public void Clear()
{
emptyChunks.Clear();
useCount = 0;
// empty chunk
if (IsValid && Length > 0)
{
var chunk = new DataChunk(0, Length);
emptyChunks.Add(chunk);
}
}
public T this[int index]
{
get
{
return nativeArray[index];
}
set
{
nativeArray[index] = value;
}
}
public unsafe ref T GetRef(int index)
{
T* p = (T*)nativeArray.GetUnsafePtr();
return ref *(p + index);
}
//public unsafe ref T GetRef(int index)
//{
// var span = new Span<T>(nativeArray.GetUnsafePtr(), nativeArray.Length);
// return ref span[index];
//}
/// <summary>
/// Jobで利用する場合はこの関数でNativeArrayに変換して受け渡す
/// </summary>
/// <returns></returns>
public NativeArray<T> GetNativeArray()
{
return nativeArray;
}
/// <summary>
/// Jobで利用する場合はこの関数でNativeArrayに変換して受け渡す(型変更あり)
/// </summary>
/// <typeparam name="U"></typeparam>
/// <returns></returns>
public NativeArray<U> GetNativeArray<U>() where U : struct
{
return nativeArray.Reinterpret<U>();
}
//=========================================================================================
DataChunk GetEmptyChunk(int dataLength)
{
if (dataLength <= 0)
return new DataChunk();
for (int i = 0; i < emptyChunks.Count; i++)
{
var c = emptyChunks[i];
if (dataLength == c.dataLength)
{
// このチャンクをすべて利用する
emptyChunks.RemoveAtSwapBack(i);
return c;
}
else if (dataLength < c.dataLength)
{
// このチャンクを一部利用する
var chunk = new DataChunk();
chunk.startIndex = c.startIndex;
chunk.dataLength = dataLength;
c.startIndex += dataLength;
c.dataLength -= dataLength;
emptyChunks[i] = c;
return chunk;
}
}
// 利用できるチャンクはなし
return new DataChunk();
}
void AddEmptyChunk(DataChunk chunk)
{
if (chunk.IsValid == false)
return;
// 後ろに連結できる場所を探す
for (int i = 0; i < emptyChunks.Count; i++)
{
var c = emptyChunks[i];
if ((c.startIndex + c.dataLength) == chunk.startIndex)
{
// ここに連結する
c.dataLength += chunk.dataLength;
chunk = c;
// cを削除する
emptyChunks.RemoveAtSwapBack(i);
break;
}
}
// 前に連結できる場所を探す
for (int i = 0; i < emptyChunks.Count; i++)
{
var c = emptyChunks[i];
if (c.startIndex == (chunk.startIndex + chunk.dataLength))
{
// ここに連結する
chunk.dataLength += c.dataLength;
// cを削除する
emptyChunks.RemoveAtSwapBack(i);
break;
}
}
// chunkを追加する
emptyChunks.Add(chunk);
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"ExNativeArray Length:{Length} Count:{Count} IsValid:{IsValid}");
sb.AppendLine("---- Datas[100] ----");
if (IsValid)
{
for (int i = 0; i < Length && i < 100; i++)
{
sb.AppendLine(nativeArray[i].ToString());
}
}
sb.AppendLine("---- Empty Chunks ----");
foreach (var c in emptyChunks)
{
sb.AppendLine(c.ToString());
}
sb.AppendLine();
return sb.ToString();
}
public string ToSummary()
{
return $"ExNativeArray Length:{Length} Count:{Count} IsValid:{IsValid}";
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 10d8d6da3ac2b13489c729db68ef39ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExNativeArray.cs
uploadId: 893596

View File

@@ -0,0 +1,80 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace MagicaCloth2
{
/// <summary>
/// 1つのバッファに並列にデータを書き込めるようにするための構造。
/// カウンターをアトミック操作することによりその開始インデックスを管理する。
/// </summary>
/// <typeparam name="T"></typeparam>
public unsafe class ExProcessingList<T> : IDisposable, IValid where T : struct
{
/// <summary>
/// バッファの現在のデータ数をカウントするためのカウンター
/// </summary>
public NativeReference<int> Counter;
/// <summary>
/// データバッファ
/// </summary>
public NativeArray<T> Buffer;
//=========================================================================================
public void Dispose()
{
if (Counter.IsCreated)
Counter.Dispose();
if (Buffer.IsCreated)
Buffer.Dispose();
}
public bool IsValid()
{
return Counter.IsCreated;
}
//=========================================================================================
public ExProcessingList()
{
Counter = new NativeReference<int>(Allocator.Persistent);
}
//=========================================================================================
/// <summary>
/// キャパシティが収まるようにバッファを拡張する。
/// すでに容量が確保できている場合は何もしない。
/// </summary>
/// <param name="capacity"></param>
public void UpdateBuffer(int capacity)
{
if (Buffer.IsCreated == false || Buffer.Length < capacity)
{
if (Buffer.IsCreated)
Buffer.Dispose();
Buffer = new NativeArray<T>(capacity, Allocator.Persistent);
}
}
/// <summary>
/// ジョブスケジュール用のカウントintポインターを取得する
/// </summary>
/// <param name="counter"></param>
/// <returns></returns>
public int* GetJobSchedulePtr()
{
return (int*)Counter.GetUnsafePtrWithoutChecks();
}
public override string ToString()
{
int counter = Counter.IsCreated ? Counter.Value : 0;
int bufferLength = Buffer.IsCreated ? Buffer.Length : 0;
return $"ExProcessingList BufferLength:{bufferLength} Counter:{counter}";
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5a3ca7c202548c1439db91f92cc30790
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExProcessingList.cs
uploadId: 893596

View File

@@ -0,0 +1,603 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Text;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// サイズ拡張可能なNativeArray管理クラス
/// 領域が不足すると自動でサイズを拡張する
/// ただし領域の拡張のみで削除や領域の再利用はできない
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExSimpleNativeArray<T> : IDisposable where T : unmanaged
{
NativeArray<T> nativeArray;
int count;
int length;
//=========================================================================================
public ExSimpleNativeArray()
{
count = 0;
length = 0;
}
/// <summary>
/// 領域を確保する
/// </summary>
/// <param name="dataLength"></param>
/// <param name="areaOnly">true=領域のみで利用カウントを進めない</param>
public ExSimpleNativeArray(int dataLength, bool areaOnly = false) : this()
{
nativeArray = new NativeArray<T>(dataLength, Allocator.Persistent);
length = dataLength;
if (areaOnly == false)
count = length;
}
public ExSimpleNativeArray(T[] dataArray) : this()
{
Debug.Assert(dataArray != null);
nativeArray = new NativeArray<T>(dataArray, Allocator.Persistent);
length = dataArray.Length;
count = length;
}
public ExSimpleNativeArray(NativeArray<T> array) : this()
{
Debug.Assert(array.IsCreated);
nativeArray = new NativeArray<T>(array, Allocator.Persistent);
length = array.Length;
count = length;
}
public ExSimpleNativeArray(NativeList<T> array) : this()
{
Debug.Assert(array.IsCreated);
nativeArray = new NativeArray<T>(array.AsArray(), Allocator.Persistent);
length = array.Length;
count = length;
}
public ExSimpleNativeArray(SerializationData sdata)
{
Deserialize(sdata);
}
public void Dispose()
{
if (nativeArray.IsCreated)
{
nativeArray.Dispose();
}
count = 0;
length = 0;
}
public bool IsValid
{
get
{
return nativeArray.IsCreated;
}
}
/// <summary>
/// 実際に使用されている要素数
/// </summary>
public int Count => count;
/// <summary>
/// 確保されている配列の要素数
/// </summary>
public int Length => length;
/// <summary>
/// 使用配列カウントを設定する
/// </summary>
/// <param name="newCount"></param>
public void SetCount(int newCount)
{
Debug.Assert(newCount <= length && newCount >= 0);
count = newCount;
}
//=========================================================================================
/// <summary>
/// 領域のみ拡張する
/// すでにその長さの領域が確保されている場合は何もしない
/// </summary>
/// <param name="newLength"></param>
public void SetLength(int newLength)
{
if (newLength > length)
Expand(newLength - length, force: true, copy: true);
}
/// <summary>
/// サイズ分の空データを追加する
/// </summary>
/// <param name="dataLength"></param>
public void AddRange(int dataLength)
{
Expand(dataLength);
count += dataLength;
}
/// <summary>
/// 配列データを追加する
/// </summary>
/// <param name="dataArray"></param>
public void AddRange(T[] dataArray)
{
Debug.Assert(dataArray != null);
if (length == 0)
{
if (nativeArray.IsCreated)
nativeArray.Dispose();
nativeArray = new NativeArray<T>(dataArray, Allocator.Persistent);
length = dataArray.Length;
count = length;
}
else
{
int dataLength = dataArray.Length;
Expand(dataLength);
// copy
NativeArray<T>.Copy(dataArray, 0, nativeArray, count, dataLength);
count += dataLength;
}
}
/// <summary>
/// 配列データを追加する
/// </summary>
/// <param name="dataArray"></param>
public void AddRange(T[] dataArray, int cnt)
{
Debug.Assert(dataArray != null);
if (length == 0)
{
if (nativeArray.IsCreated)
nativeArray.Dispose();
nativeArray = new NativeArray<T>(cnt, Allocator.Persistent);
// copy
NativeArray<T>.Copy(dataArray, 0, nativeArray, 0, cnt);
length = cnt;
count = length;
}
else
{
int dataLength = cnt;
Expand(dataLength);
// copy
NativeArray<T>.Copy(dataArray, 0, nativeArray, count, dataLength);
count += dataLength;
}
}
/// <summary>
/// 領域を確保し設定値で埋める(それなりのコストが発生するので注意!)
/// </summary>
/// <param name="dataLength"></param>
/// <param name="fillData"></param>
public void AddRange(int dataLength, T fillData = default(T))
{
Expand(dataLength);
Fill(count, dataLength, fillData);
count += dataLength;
}
public void AddRange(NativeArray<T> narray)
{
Debug.Assert(narray.IsCreated);
if (length == 0)
{
if (nativeArray.IsCreated)
nativeArray.Dispose();
nativeArray = new NativeArray<T>(narray, Allocator.Persistent);
length = narray.Length;
count = length;
}
else
{
int dataLength = narray.Length;
Expand(dataLength);
// copy
NativeArray<T>.Copy(narray, 0, nativeArray, count, dataLength);
count += dataLength;
}
}
public void AddRange(NativeArray<T> narray, int start, int length)
{
if (length > 0)
{
Expand(length);
NativeArray<T>.Copy(narray, start, nativeArray, count, length);
count += length;
}
}
public void AddRange(NativeList<T> nlist)
{
Debug.Assert(nlist.IsCreated);
AddRange(nlist.AsArray());
}
public void AddRange(ExSimpleNativeArray<T> exarray)
{
AddRange(exarray.GetNativeArray());
}
/// <summary>
/// 型は異なるが型のサイズは同じ配列を追加する。Vector3->float3など。
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
public unsafe void AddRange<U>(U[] array) where U : struct
{
Debug.Assert(array != null);
int dataLength = array.Length;
Expand(dataLength);
// copy
int dstSize = UnsafeUtility.SizeOf<T>();
ulong src_gcHandle;
void* src_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out src_gcHandle);
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
UnsafeUtility.MemCpy(dst_p + count * dstSize, src_p, dataLength * dstSize);
UnsafeUtility.ReleaseGCObject(src_gcHandle);
count += dataLength;
}
/// <summary>
/// 型もサイズも異なる配列を追加する。int[] -> int3[]など。
/// データはそのままメモリコピーされる。例えばint[]からint3[]へ追加すると次のようになる。
/// int[]{1, 2, 3, 4, 5, 6} => int3[]{{1, 2, 3}, {4, 5, 6}}
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
public unsafe void AddRangeTypeChange<U>(U[] array) where U : struct
{
Debug.Assert(array != null);
int srcSize = UnsafeUtility.SizeOf<U>();
int dstSize = UnsafeUtility.SizeOf<T>();
int dataLength = (array.Length * srcSize) / dstSize;
Expand(dataLength);
ulong src_gcHandle;
void* src_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out src_gcHandle);
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
UnsafeUtility.MemCpy(dst_p + count * dstSize, src_p, dataLength * dstSize);
UnsafeUtility.ReleaseGCObject(src_gcHandle);
count += dataLength;
}
public unsafe void AddRangeTypeChange<U>(NativeArray<U> array) where U : struct
{
Debug.Assert(array.IsCreated);
int srcSize = UnsafeUtility.SizeOf<U>();
int dstSize = UnsafeUtility.SizeOf<T>();
int dataLength = (array.Length * srcSize) / dstSize;
Expand(dataLength);
byte* src_p = (byte*)array.GetUnsafePtr();
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
UnsafeUtility.MemCpy(dst_p + count * dstSize, src_p, dataLength * dstSize);
count += dataLength;
}
/// <summary>
/// 型もサイズも異なる配列を部分的にコピーする。Vector4[] -> float3など。
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
public unsafe void AddRangeStride<U>(U[] array) where U : struct
{
Debug.Assert(array != null);
int dataLength = array.Length;
Expand(dataLength);
int srcSize = UnsafeUtility.SizeOf<U>();
int dstSize = UnsafeUtility.SizeOf<T>();
ulong src_gcHandle;
void* src_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out src_gcHandle);
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
int elementSize = math.min(srcSize, dstSize);
UnsafeUtility.MemCpyStride(dst_p + count * dstSize, dstSize, src_p, srcSize, elementSize, dataLength);
UnsafeUtility.ReleaseGCObject(src_gcHandle);
count += dataLength;
}
public void Add(T data)
{
if (Length == 0)
{
// ある程度のバッファを確保
Expand(16);
}
else if (count == Length)
{
// 倍に拡張する
Expand(Length);
}
nativeArray[count] = data;
count++;
}
public T[] ToArray()
{
return nativeArray.ToArray();
}
public void CopyTo(T[] array)
{
NativeArray<T>.Copy(nativeArray, array);
}
public void CopyTo<U>(U[] array) where U : struct
{
NativeArray<U>.Copy(nativeArray.Reinterpret<U>(), array);
}
/// <summary>
/// 型もサイズも異なる配列にデータをコピーする。
/// int3 -> int[]など
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
public unsafe void CopyToWithTypeChange<U>(U[] array) where U : struct
{
int srcSize = UnsafeUtility.SizeOf<T>();
int dstSize = UnsafeUtility.SizeOf<U>();
int dataLength = (Length * srcSize) / dstSize;
byte* src_p = (byte*)nativeArray.GetUnsafePtr();
ulong dst_gcHandle;
void* dst_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out dst_gcHandle);
UnsafeUtility.MemCpy(dst_p, src_p, dataLength * dstSize);
UnsafeUtility.ReleaseGCObject(dst_gcHandle);
}
/// <summary>
/// 型もサイズも異なる配列にデータを断片的にコピーする。
/// float3 -> Vector4[]など。この場合はVector4にはxyzのみ書き込まれる。
/// </summary>
/// <typeparam name="U"></typeparam>
/// <param name="array"></param>
public unsafe void CopyToWithTypeChangeStride<U>(U[] array) where U : struct
{
int srcSize = UnsafeUtility.SizeOf<T>();
int dstSize = UnsafeUtility.SizeOf<U>();
int dataLength = Length;
byte* src_p = (byte*)nativeArray.GetUnsafePtr();
ulong dst_gcHandle;
void* dst_p = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out dst_gcHandle);
int elementSize = srcSize;
UnsafeUtility.MemCpyStride(dst_p, dstSize, src_p, srcSize, elementSize, dataLength);
UnsafeUtility.ReleaseGCObject(dst_gcHandle);
}
public void CopyFrom(NativeArray<T> array)
{
NativeArray<T>.Copy(array, nativeArray);
}
public void CopyFrom<U>(NativeArray<U> array) where U : struct
{
NativeArray<T>.Copy(array.Reinterpret<T>(), nativeArray);
}
public unsafe void CopyFromWithTypeChangeStride<U>(NativeArray<U> array) where U : struct
{
int srcSize = UnsafeUtility.SizeOf<U>();
int dstSize = UnsafeUtility.SizeOf<T>();
int dataLength = array.Length;
byte* src_p = (byte*)array.GetUnsafePtr();
byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
int elementSize = dstSize;
UnsafeUtility.MemCpyStride(dst_p, dstSize, src_p, srcSize, elementSize, dataLength);
}
/// <summary>
/// 設定値で埋める(それなりのコストが発生するので注意!)
/// </summary>
/// <param name="startIndex"></param>
/// <param name="dataLength"></param>
/// <param name="fillData"></param>
public void Fill(int startIndex, int dataLength, T fillData = default(T))
{
// C#
//Parallel.For(0, dataLength, i =>
//{
// nativeArray[startIndex + i] = fillData;
//});
FillInternal(startIndex, dataLength, fillData);
}
unsafe void FillInternal(int start, int size, T fillData = default(T))
{
//byte* dst_p = (byte*)nativeArray.GetUnsafePtr();
void* dst_p = nativeArray.GetUnsafePtr();
int index = start;
for (int i = 0; i < size; i++, index++)
{
UnsafeUtility.WriteArrayElement<T>(dst_p, index, fillData);
}
}
public T this[int index]
{
get
{
return nativeArray[index];
}
set
{
nativeArray[index] = value;
}
}
/// <summary>
/// Jobで利用する場合はこの関数でNativeArrayに変換して受け渡す
/// </summary>
/// <returns></returns>
public NativeArray<T> GetNativeArray()
{
return nativeArray;
}
/// <summary>
/// Jobで利用する場合はこの関数でNativeArrayに変換して受け渡す(型変更あり)
/// </summary>
/// <typeparam name="U"></typeparam>
/// <returns></returns>
public NativeArray<U> GetNativeArray<U>() where U : struct
{
return nativeArray.Reinterpret<U>();
}
//=========================================================================================
/// <summary>
/// 領域を拡張する(必要がなければ何もしない)
/// </summary>
/// <param name="dataLength"></param>
/// <param name="force">強制的に領域を追加</param>
/// <param name="copy">古いデータをコピーするかどうか</param>
void Expand(int dataLength, bool force = false, bool copy = true)
{
int newlength = force ? length + dataLength : count + dataLength;
if (length == 0)
{
if (nativeArray.IsCreated)
nativeArray.Dispose();
nativeArray = new NativeArray<T>(dataLength, Allocator.Persistent);
length = dataLength;
}
else if (newlength > Length)
{
// 拡張
var newNativeArray = new NativeArray<T>(newlength, Allocator.Persistent);
// copy
if (copy)
{
// コピーは使用分だけ
NativeArray<T>.Copy(nativeArray, newNativeArray, count);
}
nativeArray.Dispose();
nativeArray = newNativeArray;
length = newlength;
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"ExSimpleNativeArray Length:{Length} Count:{Count} IsValid:{IsValid}");
sb.AppendLine("---- Datas[~100] ----");
if (IsValid)
{
for (int i = 0; i < Length && i < 100; i++)
{
sb.AppendLine(nativeArray[i].ToString());
}
}
return sb.ToString();
}
//=========================================================================================
/// <summary>
/// シリアライズデータ
/// </summary>
[System.Serializable]
public class SerializationData
{
public int count;
public int length;
public byte[] arrayBytes;
}
/// <summary>
/// シリアライズする
/// </summary>
/// <returns></returns>
public SerializationData Serialize()
{
var data = new SerializationData();
data.count = count;
data.length = length;
if (nativeArray.IsCreated && nativeArray.Length > 0)
{
data.arrayBytes = nativeArray.MC2ToRawBytes();
}
return data;
}
/// <summary>
/// デシリアライズする
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public bool Deserialize(SerializationData data)
{
try
{
Dispose();
count = data.count;
length = data.length;
if (data.length > 0 && data.arrayBytes != null)
{
nativeArray = NativeArrayExtensions.MC2FromRawBytes<T>(data.arrayBytes, Allocator.Persistent);
}
return true;
}
catch (Exception exception)
{
Debug.LogException(exception);
return false;
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5184cf224e308d044a655a8da0580401
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExSimpleNativeArray.cs
uploadId: 893596

View File

@@ -0,0 +1,210 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Jobs;
namespace MagicaCloth2
{
/// <summary>
/// 固定インデックス型の固定インデックスTransformAccessArray管理クラス
/// 一度確保したインデックスはズレない(ここ重要)
/// 同じトランスフォームに関しては参照カウンタでまとめるTransformAccessArrayは重複を許さないため
/// </summary>
public class ExTransformAccessArray : IDisposable
{
TransformAccessArray transformArray;
/// <summary>
/// ネイティブリストの配列数
/// ※ジョブでエラーが出ないように事前に確保しておく
/// </summary>
int nativeLength;
/// <summary>
/// 空インデックススタック
/// </summary>
Queue<int> emptyStack;
/// <summary>
/// 使用インデックス辞書
/// </summary>
Dictionary<int, MagicaObjectId> useIndexDict;
/// <summary>
/// トランスフォームインデックス辞書
/// </summary>
Dictionary<MagicaObjectId, int> indexDict;
/// <summary>
/// トランスフォーム参照カウンタ辞書
/// </summary>
Dictionary<MagicaObjectId, int> referenceDict;
//=========================================================================================
public ExTransformAccessArray(int capacity, int desiredJobCount = -1)
{
transformArray = new TransformAccessArray(capacity, desiredJobCount);
nativeLength = transformArray.length;
emptyStack = new Queue<int>(capacity);
useIndexDict = new Dictionary<int, MagicaObjectId>(capacity);
indexDict = new Dictionary<MagicaObjectId, int>(capacity);
referenceDict = new Dictionary<MagicaObjectId, int>(capacity);
}
public void Dispose()
{
if (transformArray.isCreated)
transformArray.Dispose();
emptyStack.Clear();
useIndexDict.Clear();
indexDict.Clear();
referenceDict.Clear();
nativeLength = 0;
}
/// <summary>
/// TransformAccessArrayを取得する
/// </summary>
/// <returns></returns>
public TransformAccessArray GetTransformAccessArray()
{
return transformArray;
}
/// データ追加
/// 追加したインデックスを返す
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public int Add(Transform element)
{
int index = 0;
MagicaObjectId id = element.GetMagicaId();
if (referenceDict.ContainsKey(id))
{
// 参照カウンタ+
referenceDict[id] = referenceDict[id] + 1;
return indexDict[id];
}
if (emptyStack.Count > 0)
{
// 再利用
index = emptyStack.Dequeue();
transformArray[index] = element;
}
else
{
// 新規
index = transformArray.length;
transformArray.Add(element);
}
useIndexDict.Add(index, id);
indexDict.Add(id, index);
referenceDict.Add(id, 1);
nativeLength = transformArray.length;
return index;
}
/// <summary>
/// データ削除
/// 削除されたインデックスは再利用される
/// </summary>
/// <param name="index"></param>
public void Remove(int index)
{
if (useIndexDict.ContainsKey(index))
{
MagicaObjectId id = useIndexDict[index];
int cnt = referenceDict[id] - 1;
if (cnt > 0)
{
// 参照カウンタ-
referenceDict[id] = cnt;
return;
}
// 削除
transformArray[index] = null;
emptyStack.Enqueue(index);
useIndexDict.Remove(index);
indexDict.Remove(id);
referenceDict.Remove(id);
nativeLength = transformArray.length;
}
}
public bool Exist(int index)
{
return useIndexDict.ContainsKey(index);
}
public bool Exist(Transform element)
{
if (element == null)
return false;
return indexDict.ContainsKey(element.GetMagicaId());
}
/// <summary>
/// データ使用量
/// </summary>
public int Count
{
get
{
return useIndexDict.Count;
}
}
/// <summary>
/// データ配列数
/// </summary>
public int Length
{
get
{
return nativeLength;
}
}
public Transform this[int index]
{
get
{
return transformArray[index];
}
}
public int GetIndex(Transform element)
{
if (element == null)
return -1;
MagicaObjectId id = element.GetMagicaId();
if (indexDict.ContainsKey(id))
return indexDict[id];
else
return -1;
}
public void Clear()
{
// 配列数はそのままにクリアする
foreach (var index in useIndexDict.Keys)
emptyStack.Enqueue(index);
useIndexDict.Clear();
for (int i = 0, cnt = Length; i < cnt; i++)
transformArray[i] = null;
indexDict.Clear();
referenceDict.Clear();
nativeLength = 0;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 01fb00afd02fc7f48bfe4f6519f7d358
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/ExTransformAccessArray.cs
uploadId: 893596

View File

@@ -0,0 +1,117 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Collections;
using UnityEngine;
namespace MagicaCloth2
{
//[BurstCompatible]
public static class FixedList128BytesExtensions
{
//=====================================================================
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool MC2IsCapacity<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
//=====================================================================
// Set
//=====================================================================
/// <summary>
/// データが存在しない場合のみ追加する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Set<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// データが存在しない場合のみ追加する
/// すでに容量が一杯の場合は警告を表示し追加しない。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2SetLimit<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
Debug.LogWarning($"FixedSet128.Limit!:{fixedList.Capacity}");
return;
}
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// リストからデータを検索して削除する
/// 削除領域にはリストの最後のデータが移動する(SwapBack)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2RemoveItemAtSwapBack<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
for (int i = 0; i < fixedList.Length; i++)
{
if (fixedList.ElementAt(i).Equals(item))
{
fixedList.RemoveAtSwapBack(i);
break;
}
}
}
//=====================================================================
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Push<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Pop<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
fixedList.RemoveAt(index);
return item;
}
//=====================================================================
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Enqueue<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Dequque<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);
return item;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a5001a2e03a42e64cade535d198c1e91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/FixedList128BytesExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,117 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Collections;
using UnityEngine;
namespace MagicaCloth2
{
public static class FixedList32BytesExtensions
{
//=====================================================================
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool MC2IsCapacity<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
//=====================================================================
// Set
//=====================================================================
/// <summary>
/// データが存在しない場合のみ追加する
/// 容量がオーバーすると例外が発生する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Set<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// データが存在しない場合のみ追加する
/// すでに容量が一杯の場合は警告を表示し追加しない。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2SetLimit<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
Debug.LogWarning($"FixedSet32.Limit!:{fixedList.Capacity}");
return;
}
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// リストからデータを検索して削除する
/// 削除領域にはリストの最後のデータが移動する(SwapBack)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2RemoveItemAtSwapBack<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
for (int i = 0; i < fixedList.Length; i++)
{
if (fixedList.ElementAt(i).Equals(item))
{
fixedList.RemoveAtSwapBack(i);
break;
}
}
}
//=====================================================================
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Push<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Pop<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
fixedList.RemoveAt(index);
return item;
}
//=====================================================================
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Enqueue<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Dequque<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);
return item;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5876be4e9da8a2943b26b98bbeb9cb22
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/FixedList32BytesExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,117 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Collections;
using UnityEngine;
namespace MagicaCloth2
{
//[BurstCompatible]
public static class FixedList4096BytesExtensions
{
//=====================================================================
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool MC2IsCapacity<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
//=====================================================================
// Set
//=====================================================================
/// <summary>
/// データが存在しない場合のみ追加する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Set<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// データが存在しない場合のみ追加する
/// すでに容量が一杯の場合は警告を表示し追加しない。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2SetLimit<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
Debug.LogWarning($"FixedSet4096.Limit!:{fixedList.Capacity}");
return;
}
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// リストからデータを検索して削除する
/// 削除領域にはリストの最後のデータが移動する(SwapBack)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2RemoveItemAtSwapBack<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
for (int i = 0; i < fixedList.Length; i++)
{
if (fixedList.ElementAt(i).Equals(item))
{
fixedList.RemoveAtSwapBack(i);
break;
}
}
}
//=====================================================================
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Push<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Pop<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
fixedList.RemoveAt(index);
return item;
}
//=====================================================================
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Enqueue<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Dequque<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);
return item;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a672a8e0e07ce1344817a15060cd375f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/FixedList4096BytesExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,117 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Collections;
using UnityEngine;
namespace MagicaCloth2
{
//[BurstCompatible]
public static class FixedList512BytesExtensions
{
//=====================================================================
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool MC2IsCapacity<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
//=====================================================================
// Set
//=====================================================================
/// <summary>
/// データが存在しない場合のみ追加する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Set<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// データが存在しない場合のみ追加する
/// すでに容量が一杯の場合は警告を表示し追加しない。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2SetLimit<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
Debug.LogWarning($"FixedSet512.Limit!:{fixedList.Capacity}");
return;
}
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// リストからデータを検索して削除する
/// 削除領域にはリストの最後のデータが移動する(SwapBack)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2RemoveItemAtSwapBack<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
for (int i = 0; i < fixedList.Length; i++)
{
if (fixedList.ElementAt(i).Equals(item))
{
fixedList.RemoveAtSwapBack(i);
break;
}
}
}
//=====================================================================
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Push<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Pop<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
fixedList.RemoveAt(index);
return item;
}
//=====================================================================
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Enqueue<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Dequque<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);
return item;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 844ad8e2dd5bec94c8684bd46b5ae53b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/FixedList512BytesExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,117 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Collections;
using UnityEngine;
namespace MagicaCloth2
{
//[BurstCompatible]
public static class FixedList64BytesExtensions
{
//=====================================================================
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool MC2IsCapacity<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
//=====================================================================
// Set
//=====================================================================
/// <summary>
/// データが存在しない場合のみ追加する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Set<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// データが存在しない場合のみ追加する
/// すでに容量が一杯の場合は警告を表示し追加しない。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2SetLimit<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
Debug.LogWarning($"FixedSet64.Limit!:{fixedList.Capacity}");
return;
}
if (fixedList.Contains(item) == false)
{
fixedList.Add(item);
}
}
/// <summary>
/// リストからデータを検索して削除する
/// 削除領域にはリストの最後のデータが移動する(SwapBack)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2RemoveItemAtSwapBack<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
for (int i = 0; i < fixedList.Length; i++)
{
if (fixedList.ElementAt(i).Equals(item))
{
fixedList.RemoveAtSwapBack(i);
break;
}
}
}
//=====================================================================
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Push<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Pop<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
fixedList.RemoveAt(index);
return item;
}
//=====================================================================
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MC2Enqueue<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
fixedList.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MC2Dequque<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);
return item;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 29fde9f6ce7d36a449d5ed4e3d952f35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/FixedList64BytesExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,85 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using Unity.Collections;
namespace MagicaCloth2
{
/// <summary>
/// NativeArrayの拡張メソッド
/// </summary>
public static class NativeArrayExtensions
{
/// <summary>
/// NativeArrayが確保されている場合のみDispose()する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
public static void MC2DisposeSafe<T>(ref this NativeArray<T> array) where T : unmanaged
{
if (array.IsCreated)
array.Dispose();
}
/// <summary>
/// NativeArrayをリサイズする
/// 指定サイズ未満の場合にメモリを解放して新しいサイズで確保し直す
/// リサイズ時に内容はコピーしない
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <param name="size"></param>
/// <param name="allocator"></param>
/// <param name="options"></param>
public static void MC2Resize<T>(ref this NativeArray<T> array, int size, Allocator allocator = Allocator.Persistent, NativeArrayOptions options = NativeArrayOptions.ClearMemory) where T : unmanaged
{
if (array.IsCreated == false || array.Length < size)
{
array.MC2DisposeSafe();
array = new NativeArray<T>(size, allocator, options);
}
}
/// <summary>
/// NativeArrayをbyte[]に変換する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <returns></returns>
public static byte[] MC2ToRawBytes<T>(ref this NativeArray<T> array) where T : unmanaged
{
if (array.IsCreated == false || array.Length == 0)
return null;
var slice = new NativeSlice<T>(array).SliceConvert<byte>();
var bytes = new byte[slice.Length];
slice.CopyTo(bytes);
return bytes;
}
/// <summary>
/// byte[]からNativeArrayを作成する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="bytes"></param>
/// <param name="allocator"></param>
/// <returns></returns>
public static NativeArray<T> MC2FromRawBytes<T>(byte[] bytes, Allocator allocator = Allocator.Persistent) where T : unmanaged
{
if (bytes == null)
return new NativeArray<T>();
int structSize = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf<T>();
int length = bytes.Length / structSize;
var array = new NativeArray<T>(length, allocator);
if (length > 0)
{
using var byteArray = new NativeArray<byte>(bytes, Allocator.Temp);
//using var byteArray = new NativeArray<byte>(bytes, Allocator.Persistent);
var slice = new NativeSlice<byte>(byteArray).SliceConvert<T>();
slice.CopyTo(array);
}
return array;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 93d2b52144a75c64b91e75d4f974d74a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/NativeArrayExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,227 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// NativeMultiHashMapの拡張メソッド
/// </summary>
public static class NativeMultiHashMapExtensions
{
/// <summary>
/// NativeParallelMultiHashMapのキーに指定データが存在するか判定する
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="map"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
#if MC2_COLLECTIONS_200
public static bool MC2Contains<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#else
public static bool MC2Contains<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue>
#endif
{
foreach (TValue val in map.GetValuesForKey(key))
{
if (val.Equals(value))
return true;
}
return false;
}
/// <summary>
/// NativeParallelMultiHashMapキーに対して重複なしのデータを追加する
/// すでにキーに同じデータが存在する場合は追加しない。
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="map"></param>
/// <param name="key"></param>
/// <param name="value"></param>
#if MC2_COLLECTIONS_200
public static void MC2UniqueAdd<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#else
public static void MC2UniqueAdd<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue>
#endif
{
if (map.MC2Contains(key, value) == false)
{
map.Add(key, value);
}
}
/// <summary>
/// NativeMultiHashMapのキーに存在するデータを削除する
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="map"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
#if MC2_COLLECTIONS_200
public static bool MC2RemoveValue<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#else
public static bool MC2RemoveValue<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue>
#endif
{
NativeParallelMultiHashMapIterator<TKey> it;
TValue item;
if (map.TryGetFirstValue(key, out item, out it))
{
do
{
if (item.Equals(value))
{
map.Remove(it);
return true;
}
}
while (map.TryGetNextValue(out item, ref it));
}
return false;
}
/// <summary>
/// 現在のキーのデータをFixedList512Bytesに変換して返す
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
#if MC2_COLLECTIONS_200
public static FixedList512Bytes<TValue> MC2ToFixedList512Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#else
public static FixedList512Bytes<TValue> MC2ToFixedList512Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : struct, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#endif
{
var fixlist = new FixedList512Bytes<TValue>();
if (map.ContainsKey(key))
{
foreach (var data in map.GetValuesForKey(key))
{
fixlist.Add(data);
}
}
return fixlist;
}
/// <summary>
/// 現在のキーのデータをFixedList128Bytesに変換して返す
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
#if MC2_COLLECTIONS_200
public static FixedList128Bytes<TValue> MC2ToFixedList128Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#else
public static FixedList128Bytes<TValue> MC2ToFixedList128Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : struct, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#endif
{
var fixlist = new FixedList128Bytes<TValue>();
if (map.ContainsKey(key))
{
foreach (var data in map.GetValuesForKey(key))
{
fixlist.Add(data);
}
}
return fixlist;
}
/// <summary>
/// NativeParallelMultiHashMapをKeyとValueの配列に変換します
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="map"></param>
/// <returns></returns>
#if MC2_COLLECTIONS_200
public static (TKey[], TValue[]) MC2Serialize<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged
#else
public static (TKey[], TValue[]) MC2Serialize<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map) where TKey : struct, IEquatable<TKey> where TValue : struct
#endif
{
if (map.IsCreated == false || map.Count() == 0 || map.IsEmpty)
return (null, null);
using var keyNativeArray = map.GetKeyArray(Allocator.Persistent);
using var valueNativeArray = map.GetValueArray(Allocator.Persistent);
return (keyNativeArray.ToArray(), valueNativeArray.ToArray());
}
/// <summary>
/// KeyとValueの配列からNativeParallelMultiHashMapを復元します
/// 高速化のためBurstを利用
/// ジェネリック型ジョブは明示的に型を指定する必要があるため型ごとに関数が発生します
/// </summary>
/// <param name="keyArray"></param>
/// <param name="valueArray"></param>
/// <returns></returns>
public static NativeParallelMultiHashMap<int2, ushort> MC2Deserialize(int2[] keyArray, ushort[] valueArray)
{
int keyCount = keyArray?.Length ?? 0;
int valueCount = valueArray?.Length ?? 0;
Debug.Assert(keyCount == valueCount);
var map = new NativeParallelMultiHashMap<int2, ushort>(keyCount, Allocator.Persistent);
if (keyCount > 0 && valueCount > 0)
{
using var keyNativeArray = new NativeArray<int2>(keyArray, Allocator.Persistent);
using var valueNativeArray = new NativeArray<ushort>(valueArray, Allocator.Persistent);
var job = new SetParallelMultiHashMapJob<int2, ushort>() { map = map, keyArray = keyNativeArray, valueArray = valueNativeArray };
job.Run();
}
return map;
}
[BurstCompile]
#if MC2_COLLECTIONS_200
struct SetParallelMultiHashMapJob<TKey, TValue> : IJob where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged
#else
struct SetParallelMultiHashMapJob<TKey, TValue> : IJob where TKey : struct, IEquatable<TKey> where TValue : struct
#endif
{
public NativeParallelMultiHashMap<TKey, TValue> map;
[Unity.Collections.ReadOnly]
public NativeArray<TKey> keyArray;
[Unity.Collections.ReadOnly]
public NativeArray<TValue> valueArray;
public void Execute()
{
int cnt = keyArray.Length;
for (int i = 0; i < cnt; i++)
{
map.Add(keyArray[i], valueArray[i]);
}
}
}
/// <summary>
/// NativeParallelMultiHashMapが確保されている場合のみDispose()する
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="map"></param>
#if MC2_COLLECTIONS_200
public static void MC2DisposeSafe<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#else
public static void MC2DisposeSafe<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue>
#endif
{
if (map.IsCreated)
map.Dispose();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b133d9becc3f7ce46b4504e63586056b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/NativeMultiHashMapExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,30 @@
// Magica Cloth 2.
// Copyright (c) 2024 MagicaSoft.
// https://magicasoft.jp
using System;
using Unity.Collections;
namespace MagicaCloth2
{
/// <summary>
/// NativeParallelHashMapの拡張メソッド
/// </summary>
public static class NativeParallelHashMap
{
/// <summary>
/// NativeParallelMultiHashMapが確保されている場合のみDispose()する
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="map"></param>
#if MC2_COLLECTIONS_200
public static void MC2DisposeSafe<TKey, TValue>(ref this NativeParallelHashMap<TKey, TValue> map) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
#else
public static void MC2DisposeSafe<TKey, TValue>(ref this NativeParallelHashMap<TKey, TValue> map) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue>
#endif
{
if (map.IsCreated)
map.Dispose();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f6dcd839c56f63b42af8231ec3cb1841
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/NativeParallelHashMapExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,28 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Runtime.CompilerServices;
using System.Threading;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace MagicaCloth2
{
static class NativeReferenceExtensions
{
/// <summary>
/// カウンターにデータ数を追加してその追加前の開始インデックスを返す
/// この関数はスレッドセーフである
/// </summary>
/// <param name="counter"></param>
/// <param name="dataCount"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe public static int MC2InterlockedStartIndex(ref this NativeReference<int> counter, int dataCount)
{
int* cntPt = (int*)counter.GetUnsafePtr();
int start = Interlocked.Add(ref *cntPt, dataCount) - dataCount;
return start;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a86bf182de69e9b4b8b212407378a96a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/NativeCollection/NativeReferenceExtensions.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d7a14441047ec414c86b093b4a7940ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
namespace MagicaCloth2
{
/// <summary>
/// 処理例外
/// これは予期されたエラーにより処理を中断する場合に用いる
/// </summary>
[Serializable]
public class MagicaClothProcessingException : Exception
{
public MagicaClothProcessingException() : base() { }
public MagicaClothProcessingException(string message) : base(message) { }
public MagicaClothProcessingException(string message, Exception inner) : base(message, inner) { }
}
/// <summary>
/// キャンセル例外
/// これはエラー無しに処理を打ち切る場合に用いる
/// </summary>
[Serializable]
public class MagicaClothCanceledException : Exception
{
public MagicaClothCanceledException() : base() { }
public MagicaClothCanceledException(string message) : base(message) { }
public MagicaClothCanceledException(string message, Exception inner) : base(message, inner) { }
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ab191ee61f2d8e14598d1adc5359890d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/ResultCode/Exception.cs
uploadId: 893596

View File

@@ -0,0 +1,165 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Diagnostics;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// 様々な処理の結果
/// </summary>
[System.Serializable]
public struct ResultCode
{
[SerializeField]
volatile Define.Result result;
/// <summary>
/// 警告:警告は1つのみ保持
/// </summary>
[SerializeField]
volatile Define.Result warning;
public Define.Result Result => result;
public static ResultCode None => new ResultCode(Define.Result.None);
public static ResultCode Empty => new ResultCode(Define.Result.Empty);
public static ResultCode Success => new ResultCode(Define.Result.Success);
public static ResultCode Error => new ResultCode(Define.Result.Error);
public ResultCode(Define.Result initResult)
{
result = initResult;
warning = Define.Result.None;
}
public void Clear()
{
result = Define.Result.None;
warning = Define.Result.None;
}
public void SetResult(Define.Result code)
{
result = code;
}
public void SetSuccess()
{
SetResult(Define.Result.Success);
}
public void SetCancel()
{
SetResult(Define.Result.Cancel);
}
public void SetError(Define.Result code = Define.Result.Error)
{
result = code;
#if MC2_DEBUG
Develop.DebugLogError(GetResultString());
#endif
}
public void SetWarning(Define.Result code = Define.Result.Warning)
{
if (code == Define.Result.None)
return;
warning = code;
#if MC2_DEBUG
Develop.DebugLogWarning(GetWarningString());
#endif
}
public void Merge(ResultCode src)
{
if (src.IsError())
result = src.result;
if (src.IsWarning())
warning = src.warning;
}
public void SetProcess()
{
SetResult(Define.Result.Process);
}
public bool IsResult(Define.Result code)
{
return result == code;
}
public bool IsNone() => result == Define.Result.None;
public bool IsSuccess() => result == Define.Result.Success;
public bool IsFaild() => !IsSuccess();
public bool IsCancel() => result == Define.Result.Cancel;
public bool IsNormal() => result < Define.Result.Warning;
public bool IsError() => result >= Define.Result.Error;
public bool IsProcess() => result == Define.Result.Process;
public bool IsWarning() => warning != Define.Result.None;
public string GetResultString()
{
if (IsNormal())
return result.ToString();
else
return $"({(int)result}) {result}";
}
public string GetWarningString()
{
return $"({(int)warning}) {warning}";
}
/// <summary>
/// 結果コードに対する追加情報を取得する。ない場合はnullが返る。
/// </summary>
/// <returns></returns>
public string GetResultInformation()
{
switch (result)
{
case Define.Result.RenderSetup_Unreadable:
return "It is necessary to turn on [Read/Write] in the model import settings.";
case Define.Result.RenderSetup_Over65535vertices:
return "Original mesh must have no more than 65,535 vertices.";
case Define.Result.SerializeData_Over31Renderers:
return $"There are {Define.System.MaxRendererCount} renderers that can be set.";
case Define.Result.Init_ScaleIsZero:
return "Component scale values is 0.";
case Define.Result.Init_NegativeScale:
return "Component has negative scale.";
default:
return null;
}
}
public string GetWarningInformation()
{
switch (warning)
{
case Define.Result.RenderMesh_VertexWeightIs5BonesOrMore:
return "The source renderer mesh contains vertex weights that utilize more than 5 bones.\nA weight of 5 or more is invalid.";
case Define.Result.Init_NonUniformScale:
return "Component scale values should be uniform.\nIf the scale is not uniform, there is a risk that it will not work properly.";
default:
return null;
}
}
[Conditional("MC2_DEBUG")]
public void DebugLog(bool error = true, bool warning = true, bool normal = true)
{
if (IsError() && error)
Develop.DebugLogError(GetResultString());
else if (normal)
Develop.DebugLog(GetResultString());
if (IsWarning() && warning)
Develop.DebugLogWarning(GetWarningString());
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f7830dff664b1ff409e8ae3d8456371a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/ResultCode/ResultCode.cs
uploadId: 893596

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 87fbdda2219d11649892645df5504b0d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
namespace MagicaCloth2
{
/// <summary>
/// 時間計測クラス
/// </summary>
public class TimeSpan
{
string name = string.Empty;
DateTime stime;
DateTime etime;
bool isFinish;
public TimeSpan() { }
public TimeSpan(string name)
{
this.name = name;
stime = DateTime.Now;
isFinish = false;
}
public void Start()
{
stime = DateTime.Now;
isFinish = false;
}
public void Finish()
{
//etime = DateTime.Now;
if (isFinish == false)
{
etime = DateTime.Now;
isFinish = true;
}
}
public double TotalSeconds()
{
Finish();
return (etime - stime).TotalSeconds;
}
public double TotalMilliSeconds()
{
Finish();
return (etime - stime).TotalMilliseconds;
}
public override string ToString()
{
//return $"TimeSpan [{name}] : {TotalSeconds()}(s)";
return $"TimeSpan [{name}] : {TotalMilliSeconds()}(ms)";
}
public void DebugLog()
{
Develop.DebugLog(this);
}
public void Log()
{
Develop.Log(this);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 37c049d9587e21243b12c35054b5582b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Time/TimeSpan.cs
uploadId: 893596

View File

@@ -0,0 +1,56 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// 時間計測クラス(UnityEngine.Timeを使用する
/// </summary>
public class UnityTimeSpan
{
string name = string.Empty;
float stime;
float etime;
bool isFinish;
public UnityTimeSpan(string name)
{
this.name = name;
stime = Time.realtimeSinceStartup;
}
public void Finish()
{
if (isFinish == false)
{
etime = Time.realtimeSinceStartup;
isFinish = true;
}
}
public float TotalSeconds()
{
Finish();
return (etime - stime);
}
public float TotalMilliSeconds()
{
Finish();
return (etime - stime) * 1000.0f;
}
public override string ToString()
{
//return $"TimeSpan [{name}] : {TotalSeconds()}(s)";
return $"UnityTimeSpan [{name}] : {TotalMilliSeconds()}(ms)";
}
public void DebugLog()
{
Debug.Log(this);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 60bc0f15f2991074f9db6ecacf0eb4bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Core/Utility/Time/UnityTimeSpan.cs
uploadId: 893596