Modify: 매지카클로스2 스크립트 최신화

This commit is contained in:
Yamo4490 2025-05-15 00:16:11 +09:00
parent 60f8e6dc25
commit 3f3d799879
111 changed files with 11459 additions and 9724 deletions

View File

@ -1,6 +1,6 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-4012764583101539669
--- !u!114 &-3443301767334639713
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}

View File

@ -9,6 +9,13 @@ namespace MagicaCloth2
{
public Vector3 eulers = new Vector3(0, 90, 0);
public Space space = Space.World;
public enum UpdateMode
{
Update,
FixedUpdate,
}
[SerializeField]
private UpdateMode updateMode = UpdateMode.Update;
[SerializeField]
[Range(0.1f, 5.0f)]
@ -19,11 +26,23 @@ namespace MagicaCloth2
private float time = 0;
private void FixedUpdate()
{
if (updateMode == UpdateMode.FixedUpdate)
UpdatePosition(Time.fixedDeltaTime);
}
void Update()
{
if (updateMode == UpdateMode.Update)
UpdatePosition(Time.deltaTime);
}
void UpdatePosition(float dtime)
{
if (useSin)
{
time += Time.deltaTime;
time += dtime;
float ang = (time % interval) / interval * Mathf.PI * 2.0f;
var t = Mathf.Sin(ang);
if (space == Space.World)
@ -33,7 +52,7 @@ namespace MagicaCloth2
}
else
{
transform.Rotate(eulers * Time.deltaTime, space);
transform.Rotate(eulers * dtime, space);
}
}
}

View File

@ -124,10 +124,7 @@ namespace MagicaCloth2
return;
// カメラターゲットポジション
if (cameraTarget)
{
cameraTargetPos = cameraTarget.position;
}
cameraTargetPos = cameraTarget ? cameraTarget.position : transform.position;
// 補間
cameraDist = Mathf.SmoothDamp(cameraDist, setCameraDist, ref cameraDistVelocity, cameraDistHokanTime);
@ -147,13 +144,13 @@ namespace MagicaCloth2
Vector3 pos = q * v;
// ターゲットポジション
Vector3 tarpos = cameraTargetPos + cameraTargetOffset;
Vector3 tarpos = cameraTargetPos + transform.TransformVector(cameraTargetOffset);
Vector3 fixpos = tarpos + pos;
cameraTransform.localPosition = fixpos;
cameraTransform.position = fixpos;
// 回転確定
Vector3 relativePos = tarpos - cameraTransform.position;
Quaternion rot = Quaternion.LookRotation(relativePos);
Quaternion rot = Quaternion.LookRotation(relativePos, transform.up);
cameraTransform.rotation = rot;
}
@ -182,8 +179,8 @@ namespace MagicaCloth2
}
else if (moveMode == MoveMode.Free)
{
Vector3 offset = cameraTransform.up * -speed.y * moveSpeed;
offset += cameraTransform.right * -speed.x * moveSpeed;
Vector3 offset = transform.InverseTransformDirection(cameraTransform.up) * -speed.y * moveSpeed;
offset += transform.InverseTransformDirection(cameraTransform.right) * -speed.x * moveSpeed;
cameraTargetOffset += offset;
}
@ -207,7 +204,7 @@ namespace MagicaCloth2
/// <param name="screenVelocity"></param>
private void OnTouchMove(int fid, Vector2 screenPos, Vector2 screenVelocity, Vector2 cmVelocity)
{
screenVelocity *= Time.deltaTime * 60.0f;
screenVelocity *= SpeedAdjustment();
if (fid == 2)
{
@ -225,6 +222,8 @@ namespace MagicaCloth2
private void OnDoubleTouchMove(int fid, Vector2 screenPos, Vector2 screenVelocity, Vector2 cmVelocity)
{
screenVelocity *= SpeedAdjustment();
if (SimpleInputManager.Instance.GetTouchCount() >= 3)
updateOffset(screenVelocity);
}
@ -236,9 +235,15 @@ namespace MagicaCloth2
/// <param name="speedcm"></param>
private void OnTouchPinch(float speedscr, float speedcm)
{
//if (Mathf.Abs(speedcm) > 1.0f)
speedcm *= SpeedAdjustment();
if (SimpleInputManager.Instance.GetTouchCount() < 3)
updateZoom(speedcm);
}
private float SpeedAdjustment()
{
return Time.deltaTime * 60.0f;
}
}
}

View File

@ -233,6 +233,7 @@ namespace MagicaCloth2
sdata.colliderCollisionConstraint.mode = ColliderCollisionConstraint.Mode.Point;
// setup collider
// UpperLeg L
var lobj = new GameObject("CapsuleCollider_L");
lobj.transform.SetParent(gameObjectContainer.GetGameObject("Character1_LeftUpLeg").transform);
lobj.transform.localPosition = new Vector3(0.0049f, 0.0f, -0.0832f);
@ -240,17 +241,10 @@ namespace MagicaCloth2
var colliderL = lobj.AddComponent<MagicaCapsuleCollider>();
colliderL.direction = MagicaCapsuleCollider.Direction.Z;
colliderL.SetSize(0.082f, 0.094f, 0.3f);
var robj = new GameObject("CapsuleCollider_R");
robj.transform.SetParent(gameObjectContainer.GetGameObject("Character1_RightUpLeg").transform);
robj.transform.localPosition = new Vector3(-0.0049f, 0.0f, -0.0832f);
robj.transform.localEulerAngles = new Vector3(0.23f, -16.376f, -0.028f);
var colliderR = robj.AddComponent<MagicaCapsuleCollider>();
colliderR.direction = MagicaCapsuleCollider.Direction.Z;
colliderR.SetSize(0.082f, 0.094f, 0.3f);
// UpperLeg R (Symmetry)
colliderL.symmetryMode = ColliderSymmetryMode.AutomaticHumanBody;
colliderL.UpdateParameters(); // Required when changing parameters.
sdata.colliderCollisionConstraint.colliderList.Add(colliderL);
sdata.colliderCollisionConstraint.colliderList.Add(colliderR);
// start build
cloth.BuildAndRun();

View File

@ -105,6 +105,8 @@ namespace MagicaCloth2
//=========================================================================================
protected override void InitSingleton()
{
SimpleInput.Init();
// スクリーン情報
CalcScreenDpi();
@ -188,7 +190,7 @@ namespace MagicaCloth2
public int GetTouchCount()
{
return Input.touchCount;
return SimpleInput.touchCount;
}
public bool IsUI()
@ -199,7 +201,7 @@ namespace MagicaCloth2
if (mobilePlatform)
{
// モバイル用タッチ入力
return EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId);
return EventSystem.current.IsPointerOverGameObject(SimpleInput.GetTouch(0).fingerId);
}
else
{
@ -214,7 +216,7 @@ namespace MagicaCloth2
/// </summary>
private void UpdateMobile()
{
int count = Input.touchCount;
int count = SimpleInput.touchCount;
if (count == 0)
{
@ -223,7 +225,7 @@ namespace MagicaCloth2
// バックボタン
if (Application.platform == RuntimePlatform.Android)
{
if (Input.GetKey(KeyCode.Escape) && lastTime + 0.2f < Time.time)
if (SimpleInput.GetKey(KeyCode.Escape) && lastTime + 0.2f < Time.time)
{
lastTime = Time.time;
if (OnBackButton != null)
@ -239,7 +241,7 @@ namespace MagicaCloth2
// メイン
for (int i = 0; i < count; i++)
{
Touch touch = Input.GetTouch(i);
Touch touch = SimpleInput.GetTouch(i);
int fid = touch.fingerId;
// フィンガーIDが以外は無視する
@ -289,7 +291,7 @@ namespace MagicaCloth2
int setcnt = 0;
for (int j = 0; j < count; j++)
{
Touch t = Input.GetTouch(j);
Touch t = SimpleInput.GetTouch(j);
if (mainFingerId == t.fingerId)
{
t1pos = t.position;
@ -497,7 +499,7 @@ namespace MagicaCloth2
private void UpdateMouse()
{
// BackSpace を Android 端末のバックボタンに割り当てる
if (Input.GetKeyDown(KeyCode.Backspace))
if (SimpleInput.GetKeyDown(KeyCode.Backspace))
{
if (OnBackButton != null)
OnBackButton();
@ -507,7 +509,7 @@ namespace MagicaCloth2
for (int i = 0; i < 3; i++)
{
// マウスボタンダウン
if (Input.GetMouseButtonDown(i))
if (SimpleInput.GetMouseButtonDown(i))
{
if (IsUI())
continue;
@ -519,46 +521,46 @@ namespace MagicaCloth2
mouseDown[i] = true;
// 入力位置を記録
downPos[i] = Input.mousePosition;
mouseOldMovePos[i] = Input.mousePosition;
downPos[i] = SimpleInput.mousePosition;
mouseOldMovePos[i] = SimpleInput.mousePosition;
if (i == 0)
flickDownPos[i] = Input.mousePosition;
flickDownPos[i] = SimpleInput.mousePosition;
// タッチダウンイベント発行
if (OnTouchDown != null)
OnTouchDown(i, Input.mousePosition);
OnTouchDown(i, SimpleInput.mousePosition);
}
// マウスボタンアップ
if (Input.GetMouseButtonUp(i) && mouseDown[i])
if (SimpleInput.GetMouseButtonUp(i) && mouseDown[i])
{
mouseDown[i] = false;
// フリック判定
if (i == 0)
{
CheckFlic(i, mouseOldMovePos[i], Input.mousePosition, flickDownPos[i], flickDownTime[i]);
CheckFlic(i, mouseOldMovePos[i], SimpleInput.mousePosition, flickDownPos[i], flickDownTime[i]);
}
mouseOldMovePos[i] = Vector2.zero;
// タッチアップイベント
if (OnTouchUp != null)
OnTouchUp(i, Input.mousePosition);
OnTouchUp(i, SimpleInput.mousePosition);
// タップ判定
float distcm = Vector2.Distance(downPos[0], Input.mousePosition) / screenDpc;
float distcm = Vector2.Distance(downPos[0], SimpleInput.mousePosition) / screenDpc;
if (distcm <= tapRadiusCm)
{
if (OnTouchTap != null)
OnTouchTap(i, Input.mousePosition);
OnTouchTap(i, SimpleInput.mousePosition);
}
}
// 移動
if (mouseDown[i])
{
Vector2 spos = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
Vector2 spos = new Vector2(SimpleInput.mousePosition.x, SimpleInput.mousePosition.y);
Vector2 delta = spos - mouseOldMovePos[i];
if (spos != mouseOldMovePos[i])
@ -569,10 +571,10 @@ namespace MagicaCloth2
// 移動通知(現在スクリーン座標、速度(スクリーン比率/s)、速度(cm/s))
if (OnTouchMove != null)
OnTouchMove(i, Input.mousePosition, CalcScreenRatioVector(delta) / Time.deltaTime, speedcm);
OnTouchMove(i, SimpleInput.mousePosition, CalcScreenRatioVector(delta) / Time.deltaTime, speedcm);
}
mouseOldMovePos[i] = Input.mousePosition;
mouseOldMovePos[i] = SimpleInput.mousePosition;
// フリックダウン位置更新
flickDownPos[i] = (flickDownPos[i] + spos) * 0.5f;
@ -582,7 +584,7 @@ namespace MagicaCloth2
}
// ピンチイン/アウト
float w = Input.GetAxis("Mouse ScrollWheel");
float w = SimpleInput.GetMouseScrollWheel();
if (Mathf.Abs(w) > 0.01f)
{
// モバイル入力とスケール感を合わせるために係数を掛ける

View File

@ -1,5 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8535022332011870411
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7
--- !u!114 &-5755898939291106094
MonoBehaviour:
m_ObjectHideFlags: 11
@ -116,16 +129,3 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _Tiling: {r: 1, g: 1, b: 0, a: 0}
m_BuildTextureStacks: []
--- !u!114 &3484866536248413851
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7

View File

@ -1,5 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-2691252781391532706
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7
--- !u!21 &2100000
Material:
serializedVersion: 8
@ -116,16 +129,3 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
version: 0
--- !u!114 &8408605057506184450
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7

View File

@ -1,5 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-7295792319158048354
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7
--- !u!114 &-4086932747498456647
MonoBehaviour:
m_ObjectHideFlags: 11
@ -13,19 +26,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
version: 0
--- !u!114 &-164983938586400980
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7
--- !u!21 &2100000
Material:
serializedVersion: 8

View File

@ -1,6 +1,6 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-4330475528230009683
--- !u!114 &-3407187510348448959
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}

View File

@ -13,19 +13,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
version: 0
--- !u!114 &-1058601315364651215
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7
--- !u!21 &2100000
Material:
serializedVersion: 8
@ -129,3 +116,16 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _Tiling: {r: 1, g: 1, b: 0, a: 0}
m_BuildTextureStacks: []
--- !u!114 &5988508712152931550
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7

View File

@ -1,6 +1,6 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8786398318607239453
--- !u!114 &-178249263625412926
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}

View File

@ -1,18 +1,5 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-6925851060112298428
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7
--- !u!21 &2100000
Material:
serializedVersion: 8
@ -116,6 +103,19 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _Tiling: {r: 1, g: 1, b: 0, a: 0}
m_BuildTextureStacks: []
--- !u!114 &1491462379427701739
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7
--- !u!114 &6265247899772061283
MonoBehaviour:
m_ObjectHideFlags: 11

View File

@ -116,7 +116,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
version: 0
--- !u!114 &9070933888469703597
--- !u!114 &7169070195589252005
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -19,5 +19,11 @@ namespace MagicaCloth2
{
return 0;
}
/// <summary>
/// Gizmo display state.
/// </summary>
public bool IsGizmoVisible { get; set; }
protected virtual void OnDrawGizmos() => IsGizmoVisible = true;
}
}

View File

@ -24,7 +24,7 @@ namespace MagicaCloth2
/// <summary>
/// 重力方向(ワールド空間)
/// </summary>
public float3 gravityDirection;
public float3 worldGravityDirection;
/// <summary>
/// 初期姿勢での重力の減衰率(0.0 ~ 1.0)
@ -63,6 +63,9 @@ namespace MagicaCloth2
public float rotationalInterpolation;
public float rootRotation;
// カリング(Culling)
public CullingSettings.CullingParams culling;
// 慣性制約(Inertia)
public InertiaConstraint.InertiaConstraintParams inertiaConstraint;

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
@ -16,20 +17,31 @@ namespace MagicaCloth2
{
public MagicaCloth cloth { get; internal set; }
/// <summary>
/// 同期中の参照クロス。これは同期階層の最上位のクロスを指す
/// </summary>
public MagicaCloth SyncTopCloth { get; internal set; }
/// <summary>
/// 状態フラグ(0 ~ 31)
/// </summary>
public const int State_Valid = 0;
public const int State_Enable = 1;
public const int State_ParameterDirty = 2;
public const int State_InitComplete = 3;
public const int State_Build = 4;
public const int State_Running = 5;
public const int State_DisableAutoBuild = 6;
public const int State_CullingInvisible = 7; // チームデータの同フラグのコピー
public const int State_CullingKeep = 8; // チームデータの同フラグのコピー
public const int State_SkipWriting = 9; // 書き込み停止(ストップモーション用)
public const int State_SkipWritingDirty = 10; // 書き込み停止フラグ更新サイン
//public const int State_ParameterDirty = 2;
public const int State_InitSuccess = 3;
public const int State_InitComplete = 4;
public const int State_Build = 5;
public const int State_Running = 6;
public const int State_DisableAutoBuild = 7;
public const int State_CameraCullingInvisible = 8; // チームデータの同フラグのコピー
public const int State_CameraCullingKeep = 9; // チームデータの同フラグのコピー
public const int State_SkipWriting = 10; // 書き込み停止(ストップモーション用)
//public const int State_SkipWritingDirty = 11; // 書き込み停止フラグ更新サイン
public const int State_UsePreBuild = 12; // PreBuildを利用
public const int State_DistanceCullingInvisible = 13; // チームデータの同フラグのコピー
public const int State_UpdateTangent = 14; // 接線の更新
public const int State_Component = 15; // コンポーネントの有効状態
public const int State_Verification = 16; // 検証結果による有効状態
/// <summary>
/// 現在の状態
@ -45,7 +57,7 @@ namespace MagicaCloth2
/// レンダー情報へのハンドル
/// (レンダラーのセットアップデータ)
/// </summary>
List<int> renderHandleList = new List<int>();
internal List<int> renderHandleList = new List<int>();
/// <summary>
/// BoneClothのセットアップデータ
@ -58,8 +70,11 @@ namespace MagicaCloth2
public class RenderMeshInfo
{
public int renderHandle;
public VirtualMesh renderMesh;
public VirtualMeshContainer renderMeshContainer;
public DataChunk mappingChunk;
//public DataChunk renderMeshPositionAndNormalChunk;
//public DataChunk renderMeshTangentChunk;
public int renderDataWorkIndex;
}
internal List<RenderMeshInfo> renderMeshInfoList = new List<RenderMeshInfo>();
@ -96,6 +111,11 @@ namespace MagicaCloth2
internal ResultCode result;
public ResultCode Result => result;
/// <summary>
/// 初期化データ参照結果
/// </summary>
public ResultCode InitDataResult { get; internal set; }
/// <summary>
/// Cloth Type
/// </summary>
@ -120,18 +140,15 @@ namespace MagicaCloth2
/// <summary>
/// プロキシメッシュ
/// </summary>
public VirtualMesh ProxyMesh { get; private set; } = null;
public VirtualMeshContainer ProxyMeshContainer { get; private set; } = null;
/// <summary>
/// コライダーリスト
/// コライダーが格納されるインデックスは他のデータのインデックスと一致している
/// 登録中のコライダー
/// int2 (メインコライダー・ローカルインデックス, シンメトリーコライダー・ローカルインデックス)
/// メインコライダーのインデックス0はあり得る
/// シンメトリーコライダーのインデックス0はシンメトリーが存在しないことを示す
/// </summary>
internal List<ColliderComponent> colliderList = new List<ColliderComponent>();
/// <summary>
/// コライダー配列数
/// </summary>
internal int ColliderCapacity => colliderList.Count;
internal Dictionary<ColliderComponent, int2> colliderDict = new Dictionary<ColliderComponent, int2>();
//=========================================================================================
/// <summary>
@ -156,14 +173,37 @@ namespace MagicaCloth2
//=========================================================================================
/// <summary>
/// カリング用対象アニメーター
/// 連動アニメーター
/// ・カリング
/// ・更新モード
/// </summary>
internal Animator cullingAnimator = null;
internal Animator interlockingAnimator = null;
/// <summary>
/// カリング用アニメーター配下のレンダラーリスト
/// </summary>
internal List<Renderer> cullingAnimatorRenderers = new List<Renderer>();
internal List<Renderer> interlockingAnimatorRenderers = new List<Renderer>();
/// <summary>
/// 現在アンカーとして設定されているTransformのインスタンスID
/// </summary>
internal int anchorTransformId = 0;
/// <summary>
/// 現在距離カリングの参照として設定されているオブジェクトのインスタンスID
/// </summary>
internal int distanceReferenceObjectId = 0;
/// <summary>
/// コンポーネントの登録TransformIndex
/// tdata.componentTransformIndexのコピー
/// </summary>
//internal int componentTransformIndex = 0;
internal Animator cameraCullingAnimator = null;
internal List<Renderer> cameraCullingRenderers = null;
internal CullingSettings.CameraCullingMode cameraCullingMode;
internal bool cameraCullingOldInvisible = false;
//=========================================================================================
/// <summary>
@ -171,12 +211,12 @@ namespace MagicaCloth2
/// </summary>
CancellationTokenSource cts = new CancellationTokenSource();
volatile object lockObject = new object();
volatile object lockState = new object();
//volatile object lockState = new object();
/// <summary>
/// 初期化待機カウンター
/// </summary>
volatile int suspendCounter = 0;
//volatile int suspendCounter = 0;
/// <summary>
/// 破棄フラグ
@ -195,7 +235,7 @@ namespace MagicaCloth2
public BitField32 GetStateFlag()
{
lock (lockState)
//lock (lockState)
{
// copy
var state = stateFlag;
@ -205,7 +245,7 @@ namespace MagicaCloth2
public bool IsState(int state)
{
lock (lockState)
//lock (lockState)
{
return stateFlag.IsSet(state);
}
@ -213,16 +253,19 @@ namespace MagicaCloth2
public void SetState(int state, bool sw)
{
lock (lockState)
//lock (lockState)
{
stateFlag.SetBits(state, sw);
}
}
public bool IsValid() => IsState(State_Valid);
public bool IsCullingInvisible() => IsState(State_CullingInvisible);
public bool IsCullingKeep() => IsState(State_CullingKeep);
public bool IsRunning() => IsState(State_Running);
public bool IsCameraCullingInvisible() => IsState(State_CameraCullingInvisible);
public bool IsCameraCullingKeep() => IsState(State_CameraCullingKeep);
public bool IsDistanceCullingInvisible() => IsState(State_DistanceCullingInvisible);
public bool IsSkipWriting() => IsState(State_SkipWriting);
public bool IsUpdateTangent() => IsState(State_UpdateTangent);
public bool IsEnable
{
@ -240,7 +283,7 @@ namespace MagicaCloth2
{
if (IsValid() == false || TeamId == 0)
return false;
return ProxyMesh?.IsSuccess ?? false;
return ProxyMeshContainer?.shareVirtualMesh?.IsSuccess ?? false;
}
}
@ -292,7 +335,7 @@ namespace MagicaCloth2
continue;
// 仮想メッシュ破棄
info.renderMesh?.Dispose();
info.renderMeshContainer?.Dispose();
}
renderMeshInfoList.Clear();
renderMeshInfoList = null;
@ -310,13 +353,24 @@ namespace MagicaCloth2
boneClothSetupData = null;
// プロキシメッシュ破棄
ProxyMesh?.Dispose();
ProxyMesh = null;
ProxyMeshContainer?.Dispose();
ProxyMeshContainer = null;
colliderList.Clear();
colliderDict.Clear();
cullingAnimator = null;
cullingAnimatorRenderers.Clear();
interlockingAnimator = null;
interlockingAnimatorRenderers.Clear();
// PreBuildデータ解除
MagicaManager.PreBuild?.UnregisterPreBuildData(cloth?.GetSerializeData2()?.preBuildData.GetSharePreBuildData());
// 作業バッファ破棄
SyncTopCloth = null;
int compId = cloth.GetInstanceID();
MagicaManager.Team.comp2SuspendCounterMap.Remove(compId);
MagicaManager.Team.comp2TeamIdMap.Remove(compId);
MagicaManager.Team.comp2SyncPartnerCompMap.Remove(compId);
MagicaManager.Team.comp2SyncTopCompMap.Remove(compId);
// 完全破棄フラグ
isDestoryInternal = true;
@ -329,23 +383,44 @@ namespace MagicaCloth2
internal void IncrementSuspendCounter()
{
lock (lockObject)
//suspendCounter++;
var tm = MagicaManager.Team;
int compId = cloth.GetInstanceID();
if (tm.comp2SuspendCounterMap.TryGetValue(compId, out int cnt))
{
suspendCounter++;
cnt++;
//tm.comp2SuspendCounterMap.Add(compId, cnt);
tm.comp2SuspendCounterMap[compId] = cnt;
}
else
tm.comp2SuspendCounterMap.Add(compId, 1);
}
internal void DecrementSuspendCounter()
{
lock (lockObject)
//suspendCounter--;
var tm = MagicaManager.Team;
int compId = cloth.GetInstanceID();
if (tm.comp2SuspendCounterMap.TryGetValue(compId, out int cnt))
{
suspendCounter--;
cnt--;
if (cnt > 0)
//tm.comp2SuspendCounterMap.Add(compId, cnt);
tm.comp2SuspendCounterMap[compId] = cnt;
else
tm.comp2SuspendCounterMap.Remove(compId);
}
}
internal int GetSuspendCounter()
{
return suspendCounter;
//return suspendCounter;
var tm = MagicaManager.Team;
int compId = cloth.GetInstanceID();
if (tm.comp2SuspendCounterMap.TryGetValue(compId, out int cnt))
return cnt;
else
return 0;
}
public RenderMeshInfo GetRenderMeshInfo(int index)
@ -364,16 +439,22 @@ namespace MagicaCloth2
public void GetUsedTransform(HashSet<Transform> transformSet)
{
cloth.SerializeData.GetUsedTransform(transformSet);
cloth.serializeData2.GetUsedTransform(transformSet);
clothTransformRecord?.GetUsedTransform(transformSet);
boneClothSetupData?.GetUsedTransform(transformSet);
renderHandleList.ForEach(handle => MagicaManager.Render.GetRendererData(handle).GetUsedTransform(transformSet));
customSkinningBoneRecords.ForEach(rd => rd.GetUsedTransform(transformSet));
normalAdjustmentTransformRecord?.GetUsedTransform(transformSet);
// nullを除外する
if (transformSet.Contains(null))
transformSet.Remove(null);
}
public void ReplaceTransform(Dictionary<int, Transform> replaceDict)
{
cloth.SerializeData.ReplaceTransform(replaceDict);
cloth.serializeData2.ReplaceTransform(replaceDict);
clothTransformRecord?.ReplaceTransform(replaceDict);
boneClothSetupData?.ReplaceTransform(replaceDict);
renderHandleList.ForEach(handle => MagicaManager.Render.GetRendererData(handle).ReplaceTransform(replaceDict));
@ -386,7 +467,44 @@ namespace MagicaCloth2
// ここではフラグのみ更新する
// 実際の更新はチームのAlwaysTeamUpdate()で行われる
SetState(State_SkipWriting, sw);
SetState(State_SkipWritingDirty, true);
//SetState(State_SkipWritingDirty, true);
MagicaManager.Team.skipWritingDirtyList.Add(this);
}
internal ClothUpdateMode GetClothUpdateMode()
{
switch (cloth.SerializeData.updateMode)
{
case ClothUpdateMode.Normal:
case ClothUpdateMode.UnityPhysics:
case ClothUpdateMode.Unscaled:
return cloth.SerializeData.updateMode;
case ClothUpdateMode.AnimatorLinkage:
if (interlockingAnimator)
{
switch (interlockingAnimator.updateMode)
{
case AnimatorUpdateMode.Normal:
return ClothUpdateMode.Normal;
#if UNITY_2023_1_OR_NEWER
case AnimatorUpdateMode.Fixed:
return ClothUpdateMode.UnityPhysics;
#else
case AnimatorUpdateMode.AnimatePhysics:
return ClothUpdateMode.UnityPhysics;
#endif
case AnimatorUpdateMode.UnscaledTime:
return ClothUpdateMode.Unscaled;
default:
Develop.DebugLogWarning($"[{cloth.name}] Unknown Animator UpdateMode:{interlockingAnimator.updateMode}");
break;
}
}
return ClothUpdateMode.Normal;
default:
Develop.LogError($"[{cloth.name}] Unknown Cloth Update Mode:{cloth.SerializeData.updateMode}");
return ClothUpdateMode.Normal;
}
}
}
}

View File

@ -1,13 +1,56 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
public partial class ClothProcess
{
public ResultCode GenerateStatusCheck()
{
ResultCode result = new ResultCode();
// スケール値チェック
var scl = cloth.transform.lossyScale;
if (Mathf.Approximately(scl.x, 0.0f) || Mathf.Approximately(scl.y, 0.0f) || Mathf.Approximately(scl.z, 0.0f))
{
// スケール値がゼロ
result.SetError(Define.Result.Init_ScaleIsZero);
}
else if (scl.x < 0.0f || scl.y < 0.0f || scl.z < 0.0f)
{
// 負のスケール
// 負のスケールでの初期化は、事前構築もしくは初期化データありの場合許可する
var sdata2 = cloth.GetSerializeData2();
if (sdata2.preBuildData.UsePreBuild() || (sdata2.initData?.HasData() ?? false))
{
// ただし許可されるのは一軸フリップのみ
int flipCount = (scl.x < 0.0f ? 1 : 0) + (scl.y < 0.0f ? 1 : 0) + (scl.z < 0.0f ? 1 : 0);
if (flipCount != 1)
{
result.SetError(Define.Result.Init_NegativeScale);
}
}
else
result.SetError(Define.Result.Init_NegativeScale);
}
else
{
float diff1 = Mathf.Abs(1.0f - scl.x / scl.y);
float diff2 = Mathf.Abs(1.0f - scl.x / scl.z);
const float diffTolerance = 0.01f; // 誤差(1%)
if (diff1 > diffTolerance || diff2 > diffTolerance)
{
// 一様スケールではない
result.SetWarning(Define.Result.Init_NonUniformScale);
}
}
return result;
}
internal bool GenerateInitialization()
{
result.SetProcess();

View File

@ -29,6 +29,13 @@ namespace MagicaCloth2
/// </summary>
public List<Renderer> sourceRenderers = new List<Renderer>();
/// <summary>
/// Write target to mesh in MeshCloth.
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
public ClothMeshWriteMode meshWriteMode = ClothMeshWriteMode.PositionAndNormal;
public enum PaintMode
{
Manual = 0,
@ -55,6 +62,14 @@ namespace MagicaCloth2
/// </summary>
public List<Texture2D> paintMaps = new List<Texture2D>();
/// <summary>
/// The UV channel that references the paint map.
/// [NG] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
[Range(0, 7)]
public int paintMapUvChannel = 0;
/// <summary>
/// Root bone list used in BoneCloth.
/// [NG] Runtime changes.
@ -92,7 +107,7 @@ namespace MagicaCloth2
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
public ClothUpdateMode updateMode = ClothUpdateMode.Normal;
public ClothUpdateMode updateMode = ClothUpdateMode.AnimatorLinkage;
/// <summary>
/// Blend ratio between initial pose and animation pose.
@ -180,7 +195,7 @@ namespace MagicaCloth2
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
[System.NonSerialized]
[Range(0.0f, 1.0f)]
public float blendWeight = 1.0f;
/// <summary>

View File

@ -11,8 +11,14 @@ namespace MagicaCloth2
/// Parts that cannot be exported externally.
/// </summary>
[System.Serializable]
public class ClothSerializeData2 : IDataValidate, IValid
public class ClothSerializeData2 : IDataValidate, IValid, ITransform
{
/// <summary>
/// Initialization Data.
/// </summary>
[SerializeField]
public ClothInitSerializeData initData = new ClothInitSerializeData();
/// <summary>
/// 頂点ペイントデータ
/// vertex paint data.
@ -26,8 +32,23 @@ namespace MagicaCloth2
/// Transform and vertex attribute dictionary data.
/// When creating BoneCloth/BoneSpring at runtime, you can store Transform and vertex attribute pairs in this dictionary and use it instead of vertex paint data.
/// </summary>
[System.NonSerialized]
public Dictionary<Transform, VertexAttribute> boneAttributeDict = new Dictionary<Transform, VertexAttribute>();
/// <summary>
/// Rendererに対応する頂点属性データ
/// 実行時にMeshClothを構築する場合に、このリストにレンダラーごとのメッシュ頂点数分の頂点属性を格納することでセレクションデータの代わりにすることができます
/// Vertex attribute data corresponding to the Renderer.
/// When constructing MeshCloth at runtime, you can substitute selection data by storing vertex attributes in this list for the number of mesh vertices per renderer.
/// </summary>
[System.NonSerialized]
public List<VertexAttribute[]> vertexAttributeList = new List<VertexAttribute[]>();
/// <summary>
/// PreBuild Data.
/// </summary>
public PreBuildSerializeData preBuildData = new PreBuildSerializeData();
//=========================================================================================
public ClothSerializeData2()
{
@ -57,5 +78,17 @@ namespace MagicaCloth2
int hash = 0;
return hash;
}
public void GetUsedTransform(HashSet<Transform> transformSet)
{
initData.GetUsedTransform(transformSet);
preBuildData.GetUsedTransform(transformSet);
}
public void ReplaceTransform(Dictionary<int, Transform> replaceDict)
{
initData.ReplaceTransform(replaceDict);
preBuildData.ReplaceTransform(replaceDict);
}
}
}

View File

@ -44,6 +44,11 @@ namespace MagicaCloth2
return false;
if (rootBones.Count(x => x != null) == 0)
return false;
if (rootBones.Distinct().Count() != rootBones.Count)
{
verificationResult.SetError(Define.Result.SerializeData_DuplicateRootBone);
return false;
}
break;
case ClothProcess.ClothType.MeshCloth:
if (sourceRenderers == null || sourceRenderers.Count == 0)
@ -57,6 +62,11 @@ namespace MagicaCloth2
verificationResult.SetError(Define.Result.SerializeData_Over31Renderers);
return false;
}
if (sourceRenderers.Distinct().Count() != sourceRenderers.Count)
{
verificationResult.SetError(Define.Result.SerializeData_DuplicateRenderer);
return false;
}
break;
default:
return false;
@ -107,10 +117,12 @@ namespace MagicaCloth2
/// <returns></returns>
public override int GetHashCode()
{
const int NullHash = -3910836;
int hash = 0;
hash += (int)clothType;
foreach (var ren in sourceRenderers)
hash += ren?.GetInstanceID() ?? 0;
hash += ren?.GetInstanceID() ?? NullHash;
foreach (var t in rootBones)
{
var stack = new Stack<Transform>(30);
@ -119,7 +131,10 @@ namespace MagicaCloth2
{
var t2 = stack.Pop();
if (t2 == null)
{
hash += NullHash;
continue;
}
hash += t2.GetInstanceID();
hash += t2.localPosition.GetHashCode();
hash += t2.localRotation.GetHashCode();
@ -142,6 +157,7 @@ namespace MagicaCloth2
hash += map.isReadable ? 1 : 0;
}
}
hash += paintMapUvChannel * 123;
hash += colliderCollisionConstraint.GetHashCode();
return hash;
@ -158,7 +174,7 @@ namespace MagicaCloth2
//cparams.solverFrequency = Define.System.SolverFrequency;
cparams.gravity = clothType == ClothProcess.ClothType.BoneSpring ? 0.0f : gravity; // BoneSpring has no gravity.
cparams.gravityDirection = gravityDirection;
cparams.worldGravityDirection = gravityDirection;
cparams.gravityFalloff = gravityFalloff;
cparams.stablizationTimeAfterReset = stablizationTimeAfterReset;
cparams.blendWeight = blendWeight;
@ -169,6 +185,7 @@ namespace MagicaCloth2
cparams.rotationalInterpolation = rotationalInterpolation;
cparams.rootRotation = rootRotation;
cparams.culling.Convert(cullingSettings);
cparams.inertiaConstraint.Convert(inertiaConstraint);
cparams.tetherConstraint.Convert(tetherConstraint, clothType);
cparams.distanceConstraint.Convert(distanceConstraint, clothType);
@ -187,8 +204,10 @@ namespace MagicaCloth2
{
ClothProcess.ClothType clothType;
List<Renderer> sourceRenderers;
ClothMeshWriteMode meshWriteMode;
PaintMode paintMode;
List<Texture2D> paintMaps;
int paintMapUvChannel;
List<Transform> rootBones;
RenderSetupData.BoneConnectionMode connectionMode;
float rotationalInterpolation;
@ -204,9 +223,9 @@ namespace MagicaCloth2
MagicaCloth synchronization;
float stablizationTimeAfterReset;
float blendWeight;
CullingSettings.CameraCullingMode cullingMode;
CullingSettings.CameraCullingMethod cullingMethod;
List<Renderer> cullingRenderers;
CullingSettings cullingSetting;
Transform anchor;
float anchorInertia;
internal TempBuffer(ClothSerializeData sdata)
{
@ -217,8 +236,10 @@ namespace MagicaCloth2
{
clothType = sdata.clothType;
sourceRenderers = new List<Renderer>(sdata.sourceRenderers);
meshWriteMode = sdata.meshWriteMode;
paintMode = sdata.paintMode;
paintMaps = new List<Texture2D>(sdata.paintMaps);
paintMapUvChannel = sdata.paintMapUvChannel;
rootBones = new List<Transform>(sdata.rootBones);
connectionMode = sdata.connectionMode;
rotationalInterpolation = sdata.rotationalInterpolation;
@ -234,17 +255,19 @@ namespace MagicaCloth2
synchronization = sdata.selfCollisionConstraint.syncPartner;
stablizationTimeAfterReset = sdata.stablizationTimeAfterReset;
blendWeight = sdata.blendWeight;
cullingMode = sdata.cullingSettings.cameraCullingMode;
cullingMethod = sdata.cullingSettings.cameraCullingMethod;
cullingRenderers = new List<Renderer>(sdata.cullingSettings.cameraCullingRenderers);
cullingSetting = sdata.cullingSettings.Clone();
anchor = sdata.inertiaConstraint.anchor;
anchorInertia = sdata.inertiaConstraint.anchorInertia;
}
internal void Pop(ClothSerializeData sdata)
{
sdata.clothType = clothType;
sdata.sourceRenderers = sourceRenderers;
sdata.meshWriteMode = meshWriteMode;
sdata.paintMode = paintMode;
sdata.paintMaps = paintMaps;
sdata.paintMapUvChannel = paintMapUvChannel;
sdata.rootBones = rootBones;
sdata.connectionMode = connectionMode;
sdata.rotationalInterpolation = rotationalInterpolation;
@ -260,9 +283,9 @@ namespace MagicaCloth2
sdata.selfCollisionConstraint.syncPartner = synchronization;
sdata.stablizationTimeAfterReset = stablizationTimeAfterReset;
sdata.blendWeight = blendWeight;
sdata.cullingSettings.cameraCullingMode = cullingMode;
sdata.cullingSettings.cameraCullingMethod = cullingMethod;
sdata.cullingSettings.cameraCullingRenderers = cullingRenderers;
sdata.cullingSettings = cullingSetting;
sdata.inertiaConstraint.anchor = anchor;
sdata.inertiaConstraint.anchorInertia = anchorInertia;
}
}
@ -325,6 +348,7 @@ namespace MagicaCloth2
sourceRenderers = new List<Renderer>(sdata.sourceRenderers);
paintMode = sdata.paintMode;
paintMaps = new List<Texture2D>(sdata.paintMaps);
paintMapUvChannel = sdata.paintMapUvChannel;
rootBones = new List<Transform>(sdata.rootBones);
connectionMode = sdata.connectionMode;
rotationalInterpolation = sdata.rotationalInterpolation;
@ -404,5 +428,17 @@ namespace MagicaCloth2
/// </summary>
/// <returns></returns>
public bool IsBoneSpring() => clothType == ClothProcess.ClothType.BoneSpring;
public int GetUvChannel()
{
switch (paintMode)
{
case PaintMode.Texture_Fixed_Move:
case PaintMode.Texture_Fixed_Move_Limit:
return paintMapUvChannel;
default:
return 0;
}
}
}
}

View File

@ -23,5 +23,14 @@ namespace MagicaCloth2
/// Updates are independent of Unity's Time.timeScale.
/// </summary>
Unscaled = 2,
/// <summary>
/// Automatically set from linked animator.
/// 連動アニメーターから自動設定する
/// - Animator.UpdateMode.Normal -> Normal
/// - Animator.UpdateMode.AnimatePhysics -> UnityPhysics
/// - Animator.UpdateMode.UnscaledTime -> Unscaled
/// </summary>
AnimatorLinkage = 10,
}
}

View File

@ -2,11 +2,12 @@
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace MagicaCloth2
{
public abstract class ColliderComponent : ClothBehaviour, IDataValidate
public abstract class ColliderComponent : ClothBehaviour, IDataValidate, ITransform
{
/// <summary>
/// トランスフォームからの中心ローカルオフセット
@ -23,6 +24,17 @@ namespace MagicaCloth2
[SerializeField]
protected Vector3 size;
/// <summary>
/// シンメトリーモード
/// Symmetry mode.
/// </summary>
public ColliderSymmetryMode symmetryMode = ColliderSymmetryMode.None;
/// <summary>
/// シンメトリーの接続対象
/// Symmetry connection target.
/// </summary>
public Transform symmetryTarget = null;
//=========================================================================================
/// <summary>
@ -42,6 +54,16 @@ namespace MagicaCloth2
/// </summary>
private HashSet<int> teamIdSet = new HashSet<int>();
/// <summary>
/// 現在登録中のシンメトリーモード
/// </summary>
public ColliderSymmetryMode? ActiveSymmetryMode { get; private set; } = null;
/// <summary>
/// 現在登録中のシンメトリーターゲット
/// </summary>
public Transform ActiveSymmetryTarget { get; private set; }
//=========================================================================================
/// <summary>
/// Get collider size.
@ -52,26 +74,30 @@ namespace MagicaCloth2
///
/// </summary>
/// <returns></returns>
public virtual Vector3 GetSize()
{
return size;
}
public virtual Vector3 GetSize() => size;
public void SetSize(Vector3 size)
{
this.size = size;
}
public void SetSize(Vector3 size) => this.size = size;
public void SetSizeX(float size) => this.size.x = size;
public void SetSizeY(float size) => this.size.y = size;
public void SetSizeZ(float size) => this.size.z = size;
/// <summary>
/// スケール値を取得
/// </summary>
/// <returns></returns>
public float GetScale()
public virtual float GetScale()
{
// X軸のみを見る
return transform.lossyScale.x;
}
/// <summary>
/// 方向の逆転(基本的にカプセルコライダー用)
/// </summary>
/// <returns></returns>
public virtual bool IsReverseDirection() => false;
/// <summary>
/// チームへのコライダー登録通知
/// </summary>
@ -85,9 +111,11 @@ namespace MagicaCloth2
/// チームからのコライダー解除通知
/// </summary>
/// <param name="teamId"></param>
internal void Exit(int teamId)
/// <returns>利用者ならtrue</returns>
internal bool Exit(int teamId)
{
teamIdSet.Remove(teamId);
return teamIdSet.Count == 0;
}
/// <summary>
@ -101,15 +129,269 @@ namespace MagicaCloth2
// パラメータの検証
DataValidate();
// Symmetry更新
// シンメトリーは削除もしくは追加がある。またTransformが変更される場合もある
var oldActiveSymmetryMode = ActiveSymmetryMode;
var oldActiveSymmetryTarget = ActiveSymmetryTarget;
SetActiveSymmetryMode(firstOnly: false); // 最新の状態に更新
bool changeSymmetry = oldActiveSymmetryMode != ActiveSymmetryMode || oldActiveSymmetryTarget != ActiveSymmetryTarget;
// 反映
foreach (int teamId in teamIdSet)
{
MagicaManager.Collider.UpdateParameters(this, teamId);
MagicaManager.Collider.UpdateParameters(this, teamId, changeSymmetry);
}
}
/// <summary>
/// 現在の状態から適切なシンメトリーモードとそのターゲットTransformを計算して返す
/// </summary>
/// <returns></returns>
public ColliderSymmetryMode CalcSymmetryMode(out Transform symmetryParent)
{
symmetryParent = symmetryTarget;
// 親
var parent = transform.parent;
if (parent == null)
return ColliderSymmetryMode.None;
switch (symmetryMode)
{
case ColliderSymmetryMode.None:
return ColliderSymmetryMode.None;
case ColliderSymmetryMode.AutomaticHumanBody:
case ColliderSymmetryMode.AutomaticTarget:
break;
case ColliderSymmetryMode.X_Symmetry:
case ColliderSymmetryMode.Y_Symmetry:
case ColliderSymmetryMode.Z_Symmetry:
case ColliderSymmetryMode.XYZ_Symmetry:
// ターゲットnullの場合は親
if (symmetryParent == null)
symmetryParent = parent;
return symmetryMode;
default:
Develop.LogError("Unknown symmetry mode.");
return ColliderSymmetryMode.None;
}
// Automatic
Animator ani = symmetryMode == ColliderSymmetryMode.AutomaticHumanBody ? gameObject.GetComponentInParent<Animator>(true) : null;
// 対象Transform
// AutomaticではSymmetryTargetは無視される
var target = symmetryMode == ColliderSymmetryMode.AutomaticTarget ? symmetryTarget : null;
if (target == null && ani)
{
// 自動判定
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.Hips, HumanBodyBones.Hips);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftUpperLeg, HumanBodyBones.RightUpperLeg);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightUpperLeg, HumanBodyBones.LeftUpperLeg);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftLowerLeg, HumanBodyBones.RightLowerLeg);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightLowerLeg, HumanBodyBones.LeftLowerLeg);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftFoot, HumanBodyBones.RightFoot);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightFoot, HumanBodyBones.LeftFoot);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.Spine, HumanBodyBones.Spine);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.Chest, HumanBodyBones.Chest);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.Neck, HumanBodyBones.Neck);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.Head, HumanBodyBones.Head);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftShoulder, HumanBodyBones.RightShoulder);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightShoulder, HumanBodyBones.LeftShoulder);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftUpperArm, HumanBodyBones.RightUpperArm);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightUpperArm, HumanBodyBones.LeftUpperArm);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftLowerArm, HumanBodyBones.RightLowerArm);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightLowerArm, HumanBodyBones.LeftLowerArm);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftHand, HumanBodyBones.RightHand);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightHand, HumanBodyBones.LeftHand);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.LeftToes, HumanBodyBones.RightToes);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.RightToes, HumanBodyBones.LeftToes);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.Jaw, HumanBodyBones.Jaw);
GetHumanoidSymmetryBone(ref target, parent, ani, HumanBodyBones.UpperChest, HumanBodyBones.UpperChest);
}
if (target == null)
target = parent;
symmetryParent = target;
// 親が同一かどうか
bool sameParent = target == parent;
// 各軸
var x = parent.right;
var y = parent.up;
var z = parent.forward;
var sx = target.right;
var sy = target.up;
var sz = target.forward;
// ベクトルの方向性情報
// Animatorがある場合はAnimatorから、ない場合は共通の親Transformから、それでも無い場合はワールドX軸
Vector3 H = Vector3.right;
if (ani)
H = ani.transform.right;
else
{
var commonParent = FindCommonParent(transform, symmetryParent);
if (commonParent)
{
//Debug.Log($"Find common parent:{commonParent.name}");
H = commonParent.right;
}
}
float xdot = Mathf.Abs(Vector3.Dot(H, x));
float ydot = Mathf.Abs(Vector3.Dot(H, y));
float zdot = Mathf.Abs(Vector3.Dot(H, z));
bool xsign = Vector3.Dot(x, sx) >= 0.0f;
bool ysign = Vector3.Dot(y, sy) >= 0.0f;
bool zsign = Vector3.Dot(z, sz) >= 0.0f;
if (xdot > ydot && xdot > zdot)
{
// (X)
if (sameParent)
return ColliderSymmetryMode.X_Symmetry;
if (Vector3.Dot(H, x) * Vector3.Dot(H, sx) > 0.0f)
{
if (ysign == false && zsign == false)
return ColliderSymmetryMode.XYZ_Symmetry;
else
return ColliderSymmetryMode.X_Symmetry;
}
else if (zsign)
return ColliderSymmetryMode.Y_Symmetry;
else
return ColliderSymmetryMode.Z_Symmetry;
}
else if (ydot > xdot && ydot > zdot)
{
// (Y)
if (sameParent)
return ColliderSymmetryMode.Y_Symmetry;
if (Vector3.Dot(H, y) * Vector3.Dot(H, sy) > 0.0f)
{
if (xsign == false && zsign == false)
return ColliderSymmetryMode.XYZ_Symmetry;
else
return ColliderSymmetryMode.Y_Symmetry;
}
else if (zsign)
return ColliderSymmetryMode.X_Symmetry;
else
return ColliderSymmetryMode.Z_Symmetry;
}
else
{
// (Z)
if (sameParent)
return ColliderSymmetryMode.Z_Symmetry;
if (Vector3.Dot(H, z) * Vector3.Dot(H, sz) > 0.0f)
{
if (xsign == false && ysign == false)
return ColliderSymmetryMode.XYZ_Symmetry;
else
return ColliderSymmetryMode.Z_Symmetry;
}
else if (xsign)
return ColliderSymmetryMode.Y_Symmetry;
else
return ColliderSymmetryMode.X_Symmetry;
}
}
bool GetHumanoidSymmetryBone(ref Transform target, Transform parent, Animator ani, HumanBodyBones src, HumanBodyBones dst)
{
var bone = ani.GetBoneTransform(src);
if (bone && parent == bone)
{
var bone2 = ani.GetBoneTransform(dst);
if (bone2)
{
target = bone2;
return true;
}
}
return false;
}
/// <summary>
/// at/btの共通の親を返す。無い場合はnull。
/// </summary>
/// <param name="at"></param>
/// <param name="bt"></param>
/// <returns></returns>
Transform FindCommonParent(Transform at, Transform bt)
{
if (at == null || bt == null)
return null;
// ハッシュセットでatの親を格納
var atParents = new HashSet<Transform>(16);
Transform current = at;
while (current != null)
{
atParents.Add(current);
current = current.parent;
}
// btの親をチェック
current = bt;
while (current != null)
{
if (atParents.Contains(current))
return current;
current = current.parent;
}
return null;
}
/// <summary>
/// 現在のシンメトリー設定に基づいて、シンメトリーのモードと対象を決定する
/// </summary>
internal void SetActiveSymmetryMode(bool firstOnly)
{
if (ActiveSymmetryMode.HasValue == false || firstOnly == false)
{
ActiveSymmetryMode = CalcSymmetryMode(out var target);
ActiveSymmetryTarget = target;
}
}
public int UseTeamCount => teamIdSet.Count;
//=========================================================================================
public void GetUsedTransform(HashSet<Transform> transformSet)
{
if (symmetryTarget)
transformSet.Add(symmetryTarget);
}
public void ReplaceTransform(Dictionary<int, Transform> replaceDict)
{
if (symmetryTarget)
{
int id = symmetryTarget.GetInstanceID();
if (id != 0 && replaceDict.ContainsKey(id))
symmetryTarget = replaceDict[id];
}
}
//=========================================================================================
protected virtual void Start()
{
SetActiveSymmetryMode(firstOnly: true);
}
protected virtual void OnValidate()
@ -138,11 +420,15 @@ namespace MagicaCloth2
protected virtual void OnDestroy()
{
// コライダーを削除する
foreach (int teamId in teamIdSet)
if (teamIdSet.Count > 0)
{
MagicaManager.Collider.RemoveCollider(this, teamId);
var teamList = teamIdSet.ToList();
foreach (int teamId in teamList)
{
MagicaManager.Collider.RemoveCollider(this, teamId);
}
teamIdSet.Clear();
}
teamIdSet.Clear();
}
}
}

View File

@ -29,6 +29,12 @@ namespace MagicaCloth2
/// </summary>
public Direction direction = Direction.X;
/// <summary>
/// Reverse direction.
/// 方向を逆転させる
/// </summary>
public bool reverseDirection = false;
/// <summary>
/// 半径をStart/End別々に設定
/// Set radius separately for Start/End.
@ -86,12 +92,14 @@ namespace MagicaCloth2
/// <returns></returns>
public Vector3 GetLocalDir()
{
float rev = reverseDirection ? -1 : 1;
if (direction == Direction.X)
return Vector3.right;
return Vector3.right * rev;
else if (direction == Direction.Y)
return Vector3.up;
return Vector3.up * rev;
else
return Vector3.forward;
return Vector3.forward * rev;
}
/// <summary>
@ -108,6 +116,12 @@ namespace MagicaCloth2
return Vector3.up;
}
/// <summary>
/// 方向の逆転(基本的にカプセルコライダー用)
/// </summary>
/// <returns></returns>
public override bool IsReverseDirection() => reverseDirection;
public override void DataValidate()
{
size.x = Mathf.Max(size.x, 0.001f);

View File

@ -3,9 +3,7 @@
// https://magicasoft.jp
using System;
using System.Text;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
@ -188,14 +186,6 @@ namespace MagicaCloth2
}
}
//=========================================================================================
NativeArray<float> lengthBuffer;
NativeArray<float3> localPosBuffer;
NativeArray<quaternion> localRotBuffer;
NativeArray<quaternion> rotationBuffer;
NativeArray<float3> restorationVectorBuffer;
//=========================================================================================
public AngleConstraint()
{
@ -203,174 +193,77 @@ namespace MagicaCloth2
public void Dispose()
{
lengthBuffer.DisposeSafe();
localPosBuffer.DisposeSafe();
localRotBuffer.DisposeSafe();
rotationBuffer.DisposeSafe();
restorationVectorBuffer.DisposeSafe();
}
internal void WorkBufferUpdate()
{
int pcnt = MagicaManager.Simulation.ParticleCount;
lengthBuffer.Resize(pcnt, options: NativeArrayOptions.UninitializedMemory);
localPosBuffer.Resize(pcnt, options: NativeArrayOptions.UninitializedMemory);
localRotBuffer.Resize(pcnt, options: NativeArrayOptions.UninitializedMemory);
rotationBuffer.Resize(pcnt, options: NativeArrayOptions.UninitializedMemory);
restorationVectorBuffer.Resize(pcnt, options: NativeArrayOptions.UninitializedMemory);
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"[AngleConstraint]");
sb.AppendLine($" -lengthBuffer:{(lengthBuffer.IsCreated ? lengthBuffer.Length : 0)}");
sb.AppendLine($" -localPosBuffer:{(localPosBuffer.IsCreated ? localPosBuffer.Length : 0)}");
sb.AppendLine($" -localRotBuffer:{(localRotBuffer.IsCreated ? localRotBuffer.Length : 0)}");
sb.AppendLine($" -rotationBuffer:{(rotationBuffer.IsCreated ? rotationBuffer.Length : 0)}");
sb.AppendLine($" -restorationVectorBuffer:{(restorationVectorBuffer.IsCreated ? restorationVectorBuffer.Length : 0)}");
return sb.ToString();
}
//=========================================================================================
/// <summary>
/// 制約の解決
/// </summary>
/// <param name="clothBase"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
internal unsafe JobHandle SolverConstraint(JobHandle jobHandle)
{
var tm = MagicaManager.Team;
var sm = MagicaManager.Simulation;
var vm = MagicaManager.VMesh;
// 角度復元と角度制限を1つに統合したもの
// 復元/制限ともにほぼMC1の移植。
// 他のアルゴリズムを散々テストした結果、MC1の動きが一番映えるという結論に至る。
// 微調整および堅牢性を上げるために反復回数を増やしている。
var job = new AngleConstraintJob()
{
simulationPower = MagicaManager.Time.SimulationPower,
stepBaseLineIndexArray = sm.processingStepBaseLine.Buffer,
teamDataArray = tm.teamDataArray.GetNativeArray(),
parameterArray = tm.parameterArray.GetNativeArray(),
attributes = vm.attributes.GetNativeArray(),
vertexDepths = vm.vertexDepths.GetNativeArray(),
vertexParentIndices = vm.vertexParentIndices.GetNativeArray(),
baseLineStartDataIndices = vm.baseLineStartDataIndices.GetNativeArray(),
baseLineDataCounts = vm.baseLineDataCounts.GetNativeArray(),
baseLineData = vm.baseLineData.GetNativeArray(),
nextPosArray = sm.nextPosArray.GetNativeArray(),
velocityPosArray = sm.velocityPosArray.GetNativeArray(),
frictionArray = sm.frictionArray.GetNativeArray(),
stepBasicPositionBuffer = sm.stepBasicPositionBuffer,
stepBasicRotationBuffer = sm.stepBasicRotationBuffer,
lengthBufferArray = lengthBuffer,
localPosBufferArray = localPosBuffer,
localRotBufferArray = localRotBuffer,
rotationBufferArray = rotationBuffer,
restorationVectorBufferArray = restorationVectorBuffer,
};
jobHandle = job.Schedule(sm.processingStepBaseLine.GetJobSchedulePtr(), 2, jobHandle);
return jobHandle;
}
[BurstCompile]
struct AngleConstraintJob : IJobParallelForDefer
{
public float4 simulationPower;
[Unity.Collections.ReadOnly]
public NativeArray<int> stepBaseLineIndexArray;
// Solver
//=========================================================================================
internal static void SolverConstraint(
DataChunk chunk,
in float4 simulationPower,
// team
[Unity.Collections.ReadOnly]
public NativeArray<TeamManager.TeamData> teamDataArray;
[Unity.Collections.ReadOnly]
public NativeArray<ClothParameters> parameterArray;
ref TeamManager.TeamData tdata,
ref ClothParameters param,
// vmesh
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> attributes;
[Unity.Collections.ReadOnly]
public NativeArray<float> vertexDepths;
[Unity.Collections.ReadOnly]
public NativeArray<int> vertexParentIndices;
[Unity.Collections.ReadOnly]
public NativeArray<ushort> baseLineStartDataIndices;
[Unity.Collections.ReadOnly]
public NativeArray<ushort> baseLineDataCounts;
[Unity.Collections.ReadOnly]
public NativeArray<ushort> baseLineData;
ref NativeArray<VertexAttribute> attributes,
ref NativeArray<float> vertexDepths,
ref NativeArray<int> vertexParentIndices,
ref NativeArray<ushort> baseLineStartDataIndices,
ref NativeArray<ushort> baseLineDataCounts,
ref NativeArray<ushort> baseLineData,
// particle
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> velocityPosArray;
[Unity.Collections.ReadOnly]
public NativeArray<float> frictionArray;
ref NativeArray<float3> nextPosArray,
ref NativeArray<float3> velocityPosArray,
ref NativeArray<float> frictionArray,
// buffer
ref NativeArray<float3> stepBasicPositionBuffer,
ref NativeArray<quaternion> stepBasicRotationBuffer,
// buffer2
ref NativeArray<float> lengthBufferArray,
ref NativeArray<float3> localPosBufferArray,
ref NativeArray<quaternion> localRotBufferArray,
ref NativeArray<quaternion> rotationBufferArray,
ref NativeArray<float3> restorationVectorBufferArray
)
{
var angleParam = param.angleConstraint;
if (angleParam.useAngleLimit == false && angleParam.useAngleRestoration == false)
return;
// temp
[Unity.Collections.ReadOnly]
public NativeArray<float3> stepBasicPositionBuffer;
[Unity.Collections.ReadOnly]
public NativeArray<quaternion> stepBasicRotationBuffer;
[NativeDisableParallelForRestriction]
public NativeArray<float> lengthBufferArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> localPosBufferArray;
[NativeDisableParallelForRestriction]
public NativeArray<quaternion> localRotBufferArray;
[NativeDisableParallelForRestriction]
public NativeArray<quaternion> rotationBufferArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> restorationVectorBufferArray;
int d_start = tdata.baseLineDataChunk.startIndex;
int p_start = tdata.particleChunk.startIndex;
int v_start = tdata.proxyCommonChunk.startIndex;
bool useAngleLimit = angleParam.useAngleLimit;
bool useAngleRestoration = angleParam.useAngleRestoration;
// 剛性
float limitStiffness = angleParam.limitstiffness;
float restorationAttn = angleParam.restorationVelocityAttenuation;
// 復元の重力減衰
// !この減衰は重力0でも発生するので注意!
float gravityFalloff = math.lerp(1.0f - angleParam.restorationGravityFalloff, 1.0f, tdata.gravityDot);
//Debug.Log($"gravityFalloff:{gravityFalloff}");
//float gravity = param.gravity;
//float3 gravityVector = gravity > Define.System.Epsilon ? param.gravityDirection : 0;
// ベースラインごと
public void Execute(int index)
//int bindex = tdata.baseLineChunk.startIndex;
int bindex = tdata.baseLineChunk.startIndex + chunk.startIndex;
//for (int a = 0; a < tdata.baseLineChunk.dataLength; a++, bindex++)
for (int a = 0; a < chunk.dataLength; a++, bindex++)
{
uint pack = (uint)stepBaseLineIndexArray[index];
int teamId = DataUtility.Unpack32Hi(pack);
int bindex = DataUtility.Unpack32Low(pack);
// チームは有効であることが保証されている
var tdata = teamDataArray[teamId];
var param = parameterArray[teamId];
var angleParam = param.angleConstraint;
if (angleParam.useAngleLimit == false && angleParam.useAngleRestoration == false)
return;
int d_start = tdata.baseLineDataChunk.startIndex;
int p_start = tdata.particleChunk.startIndex;
int v_start = tdata.proxyCommonChunk.startIndex;
int start = baseLineStartDataIndices[bindex];
int dcnt = baseLineDataCounts[bindex];
bool useAngleLimit = angleParam.useAngleLimit;
bool useAngleRestoration = angleParam.useAngleRestoration;
// 剛性
float limitStiffness = angleParam.limitstiffness;
float restorationAttn = angleParam.restorationVelocityAttenuation;
// 復元の重力減衰
// !この減衰は重力0でも発生するので注意!
float gravityFalloff = math.lerp(1.0f - angleParam.restorationGravityFalloff, 1.0f, tdata.gravityDot);
//Debug.Log($"gravityFalloff:{gravityFalloff}");
//float gravity = param.gravity;
//float3 gravityVector = gravity > Define.System.Epsilon ? param.gravityDirection : 0;
// バッファリング
int dataIndex = start + d_start;
for (int i = 0; i < dcnt; i++, dataIndex++)
@ -402,15 +295,29 @@ namespace MagicaCloth2
// 親からの基本姿勢
var bv = bpos - pbpos;
Develop.Assert(math.length(bv) > 0.0f);
var v = math.normalize(bv);
var ipq = math.inverse(pbrot);
float3 localPos = math.mul(ipq, v);
quaternion localRot = math.mul(ipq, brot);
float bvlen = math.length(bv);
if (vlen < Define.System.Epsilon || bvlen < Define.System.Epsilon)
{
// length=0
//Debug.Log($"NG1");
//エッジ長0対処
lengthBufferArray[pindex] = 0;
localPosBufferArray[pindex] = 0;
localRotBufferArray[pindex] = quaternion.identity;
}
else
{
//Develop.Assert(math.length(bv) > 0.0f);
//var v = math.normalize(bv);
var v = bv / bvlen;
var ipq = math.inverse(pbrot);
float3 localPos = math.mul(ipq, v);
quaternion localRot = math.mul(ipq, brot);
lengthBufferArray[pindex] = vlen;
localPosBufferArray[pindex] = localPos;
localRotBufferArray[pindex] = localRot;
lengthBufferArray[pindex] = vlen;
localPosBufferArray[pindex] = localPos;
localRotBufferArray[pindex] = localRot;
}
}
if (useAngleRestoration)
@ -418,6 +325,7 @@ namespace MagicaCloth2
// 復元ベクトル
float3 rv = bpos - pbpos;
restorationVectorBufferArray[pindex] = rv;
//Debug.Log($"[{pindex}] rv:{rv}");
}
}
}
@ -478,19 +386,46 @@ namespace MagicaCloth2
// 現在のベクトル
float3 v = cpos - ppos;
float vlen = math.length(v);
if (vlen < Define.System.Epsilon)
{
//エッジ長0対処
//Debug.Log($"NG2");
goto EndAngleLimit;
}
// 復元すべきベクトル
float3 tv = math.mul(prot, localPos);
float tvlen = math.length(tv);
if (tvlen < Define.System.Epsilon)
{
//エッジ長0対処
//Debug.Log($"NG3");
float3 add = ppos - cpos;
nextPosArray[pindex] = ppos;
velocityPosArray[pindex] = velocityPosArray[pindex] + add;
rotationBufferArray[pindex] = math.mul(prot, localRot);
goto EndAngleLimit;
}
v /= vlen;
tv /= tvlen;
// ベクトル長修正
float vlen = math.length(v);
float blen = lengthBufferArray[pindex];
vlen = math.lerp(vlen, blen, 0.5f); // 計算前の距離に徐々に近づける
Develop.Assert(vlen > 0.0f);
v = math.normalize(v) * vlen;
if (blen < Define.System.Epsilon || vlen < Define.System.Epsilon)
{
//エッジ長0対処
//Debug.Log($"NG4");
goto EndAngleLimit;
}
//Develop.Assert(vlen > 0.0f);
//v = math.normalize(v) * vlen;
v = v * vlen;
// ベクトル角度クランプ
float maxAngleDeg = angleParam.limitCurveData.EvaluateCurve(cdepth);
float maxAngleDeg = angleParam.limitCurveData.MC2EvaluateCurve(cdepth);
float maxAngleRad = math.radians(maxAngleDeg);
float angle = MathUtility.Angle(v, tv);
float3 rv = v;
@ -537,12 +472,23 @@ namespace MagicaCloth2
// 回転補正
v = cpos - ppos;
vlen = math.length(v);
if (vlen < Define.System.Epsilon)
{
//エッジ長0対処
//Debug.Log($"NG5");
goto EndAngleLimit;
}
v /= vlen;
var nrot = math.mul(prot, localRot);
var q = MathUtility.FromToRotation(tv, v);
//var q = MathUtility.FromToRotation(tv, v);
var q = MathUtility.FromToRotationWithoutNormalize(tv, v);
nrot = math.mul(q, nrot);
rotationBufferArray[pindex] = nrot;
}
EndAngleLimit:
//=====================================================
// Angle Restoration
//=====================================================
@ -550,14 +496,31 @@ namespace MagicaCloth2
{
//Debug.Log($"pindex:{pindex}, p_pindex:{p_pindex}");
// 現在のベクトル
float3 v = cpos - ppos;
// 復元すべきベクトル
float3 tv = restorationVectorBufferArray[pindex];
float tvlen = math.length(tv);
if (tvlen < Define.System.Epsilon)
{
//エッジ長0対処
//Debug.Log($"NG6");
float3 add = ppos - cpos;
nextPosArray[pindex] = ppos;
velocityPosArray[pindex] = velocityPosArray[pindex] + add;
continue;
}
// 現在のベクトル
float3 v = cpos - ppos;
float vlen = math.length(v);
if (vlen < Define.System.Epsilon)
{
//エッジ長0対処
//Debug.Log($"NG7");
continue;
}
// 復元力
float restorationStiffness = angleParam.restorationStiffness.EvaluateCurveClamp01(cdepth);
float restorationStiffness = angleParam.restorationStiffness.MC2EvaluateCurveClamp01(cdepth);
restorationStiffness = math.saturate(restorationStiffness * simulationPower.w);
//int _pindex = indexBuffer[i] + p_start;
@ -567,7 +530,7 @@ namespace MagicaCloth2
restorationStiffness *= gravityFalloff;
// 球面線形補間
var q = MathUtility.FromToRotation(v, tv, restorationStiffness);
var q = MathUtility.FromToRotationWithoutNormalize(v / vlen, tv / tvlen, restorationStiffness);
float3 rv = math.mul(q, v);
// 回転中心割合
@ -607,6 +570,25 @@ namespace MagicaCloth2
}
}
}
// バッファクリア
bindex = tdata.baseLineChunk.startIndex + chunk.startIndex;
for (int a = 0; a < chunk.dataLength; a++, bindex++)
{
int start = baseLineStartDataIndices[bindex];
int dcnt = baseLineDataCounts[bindex];
int dataIndex = start + d_start;
for (int i = 0; i < dcnt; i++, dataIndex++)
{
int l_index = baseLineData[dataIndex];
int pindex = p_start + l_index;
lengthBufferArray[pindex] = 0;
localPosBufferArray[pindex] = 0;
restorationVectorBufferArray[pindex] = 0;
}
}
}
}
}

View File

@ -4,9 +4,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
@ -85,9 +83,10 @@ namespace MagicaCloth2
/// <summary>
/// 制約データ
/// </summary>
internal class ConstraintData : IValid
[System.Serializable]
public class ConstraintData : IValid
{
internal ResultCode result;
public ResultCode result;
public uint[] indexArray;
public ushort[] dataArray;
@ -151,7 +150,7 @@ namespace MagicaCloth2
/// 制約データの作成
/// </summary>
/// <param name="cbase"></param>
internal static ConstraintData CreateData(VirtualMesh proxyMesh, in ClothParameters parameters)
public static ConstraintData CreateData(VirtualMesh proxyMesh, in ClothParameters parameters)
{
var constraintData = new ConstraintData();
@ -216,7 +215,7 @@ namespace MagicaCloth2
for (int l = 0; l < ecnt; l++)
{
int2 edge = proxyMesh.edges[l];
var tset = proxyMesh.edgeToTriangles.ToFixedList128Bytes(edge);
var tset = proxyMesh.edgeToTriangles.MC2ToFixedList128Bytes(edge);
int tcnt = tset.Length;
if (tcnt < 2)
continue;
@ -377,124 +376,55 @@ namespace MagicaCloth2
}
//=========================================================================================
/// <summary>
/// 制約の解決
/// </summary>
/// <param name="clothBase"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
unsafe internal JobHandle SolverConstraint(JobHandle jobHandle)
{
var tm = MagicaManager.Team;
var sm = MagicaManager.Simulation;
var vm = MagicaManager.VMesh;
var job = new DistanceConstraintJob()
{
simulationPower = MagicaManager.Time.SimulationPower,
stepParticleIndexArray = sm.processingStepParticle.Buffer,
teamDataArray = tm.teamDataArray.GetNativeArray(),
parameterArray = tm.parameterArray.GetNativeArray(),
attributes = vm.attributes.GetNativeArray(),
depthArray = vm.vertexDepths.GetNativeArray(),
teamIdArray = sm.teamIdArray.GetNativeArray(),
nextPosArray = sm.nextPosArray.GetNativeArray(),
basePosArray = sm.basePosArray.GetNativeArray(),
velocityPosArray = sm.velocityPosArray.GetNativeArray(),
frictionArray = sm.frictionArray.GetNativeArray(),
//stepBasicPositionBuffer = sm.stepBasicPositionBuffer,
indexArray = indexArray.GetNativeArray(),
dataArray = dataArray.GetNativeArray(),
distanceArray = distanceArray.GetNativeArray(),
};
jobHandle = job.Schedule(sm.processingStepParticle.GetJobSchedulePtr(), 32, jobHandle);
return jobHandle;
}
/// <summary>
/// 距離制約の解決
/// </summary>
[BurstCompile]
struct DistanceConstraintJob : IJobParallelForDefer
{
public float4 simulationPower;
[Unity.Collections.ReadOnly]
public NativeArray<int> stepParticleIndexArray;
// Solver
//=========================================================================================
internal static void SolverConstraint(
DataChunk chunk,
float4 simulationPower,
// team
[Unity.Collections.ReadOnly]
public NativeArray<TeamManager.TeamData> teamDataArray;
[Unity.Collections.ReadOnly]
public NativeArray<ClothParameters> parameterArray;
ref TeamManager.TeamData tdata,
ref ClothParameters param,
// vmesh
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> attributes;
[Unity.Collections.ReadOnly]
public NativeArray<float> depthArray;
ref NativeArray<VertexAttribute> attributes,
ref NativeArray<float> depthArray,
// particle
[Unity.Collections.ReadOnly]
public NativeArray<short> teamIdArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
[Unity.Collections.ReadOnly]
public NativeArray<float3> basePosArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> velocityPosArray;
[Unity.Collections.ReadOnly]
public NativeArray<float> frictionArray;
// buffer
//[Unity.Collections.ReadOnly]
//public NativeArray<float3> stepBasicPositionBuffer;
ref NativeArray<float3> nextPosArray,
ref NativeArray<float3> basePosArray,
ref NativeArray<float3> velocityPosArray,
ref NativeArray<float> frictionArray,
// constrants
[Unity.Collections.ReadOnly]
public NativeArray<uint> indexArray;
[Unity.Collections.ReadOnly]
public NativeArray<ushort> dataArray;
[Unity.Collections.ReadOnly]
public NativeArray<float> distanceArray;
ref NativeArray<uint> indexArray,
ref NativeArray<ushort> dataArray,
ref NativeArray<float> distanceArray
)
{
var sc = tdata.distanceStartChunk;
var dc = tdata.distanceDataChunk;
if (sc.dataLength == 0)
return;
int c_start = sc.startIndex;
int d_start = dc.startIndex;
// ステップ有効パーティクルごと
public void Execute(int index)
// 復元を基本姿勢で行うかアニメーション後の姿勢で行うかの判定
float blendRatio = tdata.animationPoseRatio;
// スケール倍率
float scl = tdata.InitScale * tdata.scaleRatio;
bool isSpring = tdata.IsSpring;
// パーティクルごと
int p_start = tdata.particleChunk.startIndex;
int pindex = p_start + chunk.startIndex;
//int pindex = p_start;
int v_start = tdata.proxyCommonChunk.startIndex;
int vindex = v_start + chunk.startIndex;
//int vindex = v_start;
int dataIndex = chunk.startIndex;
//int dataIndex = 0;
//for (int k = 0; k < tdata.particleChunk.dataLength; k++, pindex++, vindex++, dataIndex++)
for (int k = 0; k < chunk.dataLength; k++, pindex++, vindex++, dataIndex++)
{
// pindexのチームは有効であることが保証されている
int pindex = stepParticleIndexArray[index];
int teamId = teamIdArray[pindex];
var tdata = teamDataArray[teamId];
var parameter = parameterArray[teamId];
// 復元を基本姿勢で行うかアニメーション後の姿勢で行うかの判定
float blendRatio = tdata.animationPoseRatio;
// スケール倍率
float scl = tdata.InitScale * tdata.scaleRatio;
int p_start = tdata.particleChunk.startIndex;
int l_index = pindex - p_start;
var sc = tdata.distanceStartChunk;
var dc = tdata.distanceDataChunk;
if (sc.dataLength == 0)
return;
int c_start = sc.startIndex;
int d_start = dc.startIndex;
int v_start = tdata.proxyCommonChunk.startIndex;
int vindex = v_start + l_index;
// パーティクル情報
var nextPos = nextPosArray[pindex];
var attr = attributes[vindex];
@ -502,36 +432,31 @@ namespace MagicaCloth2
float friction = frictionArray[pindex];
if (attr.IsInvalid())
return;
continue;
// Spring利用中は固定も通す
bool isSpring = tdata.IsSpring;
if (attr.IsDontMove() && isSpring == false)
return;
continue;
// 固定点の重量
float fixMass = isSpring ? 10.0f : 50.0f;
// 重量
// BoneSpringでは固定点の重量加算を行わない
//float invMass = MathUtility.CalcInverseMass(friction, depth, attr.IsDontMove() && isSpring == false);
float invMass = MathUtility.CalcInverseMass(friction, depth, attr.IsDontMove(), fixMass);
//float invMass = isSpring && attr.IsDontMove() ? 1.0f / 2.0f : MathUtility.CalcInverseMass(friction, depth, attr.IsDontMove());
// 基本剛性
float stiffness = parameter.distanceConstraint.restorationStiffness.EvaluateCurveClamp01(depth);
//stiffness *= simulationPower;
//stiffness *= (simulationPower * simulationPower);
float stiffness = param.distanceConstraint.restorationStiffness.MC2EvaluateCurveClamp01(depth);
stiffness *= simulationPower.y;
var pack = indexArray[c_start + l_index];
//var pack = indexArray[c_start + k];
var pack = indexArray[c_start + dataIndex];
DataUtility.Unpack12_20(pack, out int dcnt, out int dstart);
if (dcnt > 0)
{
// 基準座標を切り替え
float3 basePos = basePosArray[pindex];
//float3 basicPos = stepBasicPositionBuffer[pindex];
float3 addPos = 0;
int addCnt = 0;
@ -550,22 +475,18 @@ namespace MagicaCloth2
int tvindex = v_start + t_l_index;
var t_nextPos = nextPosArray[tpindex];
float3 t_basePos = basePosArray[tpindex];
//float3 t_basicPos = stepBasicPositionBuffer[tpindex];
float t_depth = depthArray[tvindex];
float t_friction = frictionArray[tpindex];
var t_attr = attributes[tvindex];
// 重量
// BoneSpringでは固定点の重量加算を行わない
//float t_invMass = MathUtility.CalcInverseMass(t_friction, t_depth, t_attr.IsDontMove() && isSpring == false);
float t_invMass = MathUtility.CalcInverseMass(t_friction, t_depth, t_attr.IsDontMove(), fixMass);
//float t_invMass = isSpring && t_attr.IsDontMove() ? 1.0f / 2.0f : MathUtility.CalcInverseMass(t_friction, t_depth, t_attr.IsDontMove());
// 復元する長さ
// !Distance制約は初期化時に保存した距離を見るようにしないと駄目
// フラグにより初期値かアニメーション後の姿勢かを切り替える
float restLength = math.lerp(math.abs(restDist) * scl, math.distance(basePos, t_basePos), blendRatio);
//float restLength = math.distance(basicPos, t_basicPos);
var v = t_nextPos - nextPos;
@ -595,7 +516,7 @@ namespace MagicaCloth2
nextPosArray[pindex] = nextPos;
// 速度影響
float attn = parameter.distanceConstraint.velocityAttenuation;
float attn = param.distanceConstraint.velocityAttenuation;
velocityPosArray[pindex] = velocityPosArray[pindex] + addPos * attn;
}
}

View File

@ -39,6 +39,28 @@ namespace MagicaCloth2
[System.Serializable]
public class SerializeData : IDataValidate
{
/// <summary>
/// Anchor that cancels inertia.
/// Anchor translation and rotation are excluded from simulation.
/// This is useful if your character rides a vehicle.
/// 慣性を打ち消すアンカー
/// アンカーの移動と回転はシミュレーションから除外されます
/// これはキャラクターが乗り物に乗る場合に便利です
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
public Transform anchor;
/// <summary>
/// Anchor Influence (0.0 ~ 1.0)
/// アンカーの影響(0.0 ~ 1.0)
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
[Range(0.0f, 1.0f)]
public float anchorInertia;
/// <summary>
/// World Influence (0.0 ~ 1.0).
/// ワールド移動影響(0.0 ~ 1.0)
@ -49,6 +71,15 @@ namespace MagicaCloth2
[Range(0.0f, 1.0f)]
public float worldInertia;
/// <summary>
/// World Influence Smoothing (0.0 ~ 1.0).
/// ワールド移動影響平滑化(0.0 ~ 1.0)
/// [OK] Runtime changes.
/// [OK] Export/Import with Presets
/// </summary>
[Range(0.0f, 1.0f)]
public float movementInertiaSmoothing;
/// <summary>
/// World movement speed limit (m/s).
/// ワールド移動速度制限(m/s)
@ -144,7 +175,12 @@ namespace MagicaCloth2
public SerializeData()
{
anchor = null;
anchorInertia = 0.0f;
worldInertia = 1.0f;
//movementInertiaSmoothing = 0.65f; // ->0.0524
//movementInertiaSmoothing = 0.5f; // ->0.13375
movementInertiaSmoothing = 0.4f;
movementSpeedLimit = new CheckSliderSerializeData(true, 5.0f);
rotationSpeedLimit = new CheckSliderSerializeData(true, 720.0f);
localInertia = 1.0f;
@ -162,7 +198,10 @@ namespace MagicaCloth2
{
return new SerializeData()
{
anchor = anchor,
anchorInertia = anchorInertia,
worldInertia = worldInertia,
movementInertiaSmoothing = movementInertiaSmoothing,
movementSpeedLimit = movementSpeedLimit.Clone(),
rotationSpeedLimit = rotationSpeedLimit.Clone(),
localInertia = localInertia,
@ -179,7 +218,9 @@ namespace MagicaCloth2
public void DataValidate()
{
anchorInertia = Mathf.Clamp01(anchorInertia);
worldInertia = Mathf.Clamp01(worldInertia);
movementInertiaSmoothing = Mathf.Clamp01(movementInertiaSmoothing);
movementSpeedLimit.DataValidate(0.0f, Define.System.MaxMovementSpeedLimit);
rotationSpeedLimit.DataValidate(0.0f, Define.System.MaxRotationSpeedLimit);
localInertia = Mathf.Clamp01(localInertia);
@ -195,18 +236,30 @@ namespace MagicaCloth2
public struct InertiaConstraintParams
{
/// <summary>
/// アンカー影響率(0.0 ~ 1.0)
/// </summary>
public float anchorInertia;
/// <summary>
/// ワールド慣性影響(0.0 ~ 1.0)
/// </summary>
public float worldInertia;
/// <summary>
/// ワールド慣性スムージング率(0.0 ~ 1.0)
/// </summary>
public float movementInertiaSmoothing;
/// <summary>
/// ワールド移動速度制限(m/s)
/// 無制限時は(-1)
/// </summary>
public float movementSpeedLimit;
/// <summary>
/// ワールド回転速度制限(deg/s)
/// 無制限時は(-1)
/// </summary>
public float rotationSpeedLimit;
@ -258,7 +311,9 @@ namespace MagicaCloth2
public void Convert(SerializeData sdata)
{
anchorInertia = sdata.anchorInertia;
worldInertia = sdata.worldInertia;
movementInertiaSmoothing = sdata.movementInertiaSmoothing;
movementSpeedLimit = sdata.movementSpeedLimit.GetValue(-1);
rotationSpeedLimit = sdata.rotationSpeedLimit.GetValue(-1);
localInertia = sdata.localInertia;
@ -277,8 +332,26 @@ namespace MagicaCloth2
/// <summary>
/// センタートランスフォームのデータ
/// </summary>
[System.Serializable]
public struct CenterData
{
/// <summary>
/// 現在のアンカー姿勢
/// </summary>
public float3 anchorPosition;
public quaternion anchorRotation;
/// <summary>
/// 前フレームのアンカー姿勢
/// </summary>
public float3 oldAnchorPosition;
public quaternion oldAnchorRotation;
/// <summary>
/// アンカー空間でのコンポーネントのローカル座標
/// </summary>
public float3 anchorComponentLocalPosition;
/// <summary>
/// 参照すべきセンタートランスフォームインデックス
/// 同期時は同期先チームのもにになる
@ -290,12 +363,14 @@ namespace MagicaCloth2
/// </summary>
public float3 componentWorldPosition;
public quaternion componentWorldRotation;
public float3 componentWorldScale;
/// <summary>
/// 前フレームのコンポーネント姿勢
/// </summary>
public float3 oldComponentWorldPosition;
public quaternion oldComponentWorldRotation;
public float3 oldComponentWorldScale;
/// <summary>
/// 現フレームのコンポーネント移動量
@ -329,7 +404,7 @@ namespace MagicaCloth2
/// </summary>
public float3 nowWorldPosition;
public quaternion nowWorldRotation;
public float3 nowWorldScale; // ※現在未使用
//public float3 nowWorldScale; // ※現在未使用
/// <summary>
/// 前回ステップでの姿勢
@ -395,10 +470,26 @@ namespace MagicaCloth2
/// </summary>
public float3 initLocalGravityDirection;
/// <summary>
/// スムージングされた現在のワールド慣性速度ベクトル
/// </summary>
public float3 smoothingVelocity; // (m/s)
/// <summary>
/// マイナススケールによる反転を打ち消すための変換マトリックス
/// センター空間
/// </summary>
public float4x4 negativeScaleMatrix;
internal void Initialize()
{
anchorRotation = quaternion.identity;
oldAnchorRotation = quaternion.identity;
componentWorldRotation = quaternion.identity;
componentWorldScale = 1;
oldComponentWorldRotation = quaternion.identity;
oldComponentWorldScale = 1;
frameComponentShiftRotation = quaternion.identity;
frameWorldRotation = quaternion.identity;
@ -412,6 +503,7 @@ namespace MagicaCloth2
/// <summary>
/// 制約データ
/// </summary>
[System.Serializable]
public class ConstraintData
{
public ResultCode result;
@ -450,7 +542,7 @@ namespace MagicaCloth2
/// 制約データの作成
/// </summary>
/// <param name="cbase"></param>
internal static ConstraintData CreateData(VirtualMesh proxyMesh, in ClothParameters parameters)
public static ConstraintData CreateData(VirtualMesh proxyMesh, in ClothParameters parameters)
{
var constraintData = new ConstraintData();
@ -489,7 +581,7 @@ namespace MagicaCloth2
// 初期センター姿勢からローカル重力方向を算出する
var rot = MathUtility.ToRotation(math.normalize(nor), math.normalize(tan));
var irot = math.inverse(rot);
localGravityDirection = math.mul(irot, parameters.gravityDirection);
localGravityDirection = math.mul(irot, parameters.worldGravityDirection);
}
constraintData.initLocalGravityDirection = localGravityDirection;
@ -519,12 +611,13 @@ namespace MagicaCloth2
// 初期化時のローカル重力方向
cdata.initLocalGravityDirection = cprocess.inertiaConstraintData.initLocalGravityDirection;
//Debug.Log($"[{cprocess.TeamId}] initLocalGravityDirection:{cdata.initLocalGravityDirection}");
// 固定点リスト
var c = new DataChunk();
if (cprocess.ProxyMesh.CenterFixedPointCount > 0)
if (cprocess.ProxyMeshContainer.shareVirtualMesh.CenterFixedPointCount > 0)
{
c = fixedArray.AddRange(cprocess.ProxyMesh.centerFixedList);
c = fixedArray.AddRange(cprocess.ProxyMeshContainer.shareVirtualMesh.centerFixedList);
}
tdata.fixedDataChunk = c;
}

View File

@ -2,9 +2,7 @@
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
@ -134,97 +132,45 @@ namespace MagicaCloth2
}
//=========================================================================================
/// <summary>
/// 制約の解決
/// </summary>
/// <param name="jobHandle"></param>
/// <returns></returns>
unsafe internal JobHandle SolverConstraint(JobHandle jobHandle)
{
var tm = MagicaManager.Team;
var sm = MagicaManager.Simulation;
var vm = MagicaManager.VMesh;
var job = new MotionConstraintJob()
{
stepParticleIndexArray = sm.processingStepMotionParticle.Buffer,
teamDataArray = tm.teamDataArray.GetNativeArray(),
parameterArray = tm.parameterArray.GetNativeArray(),
attributes = vm.attributes.GetNativeArray(),
vertexDepths = vm.vertexDepths.GetNativeArray(),
teamIdArray = sm.teamIdArray.GetNativeArray(),
basePosArray = sm.basePosArray.GetNativeArray(),
baseRotArray = sm.baseRotArray.GetNativeArray(),
nextPosArray = sm.nextPosArray.GetNativeArray(),
velocityPosArray = sm.velocityPosArray.GetNativeArray(),
frictionArray = sm.frictionArray.GetNativeArray(),
collisionNormalArray = sm.collisionNormalArray.GetNativeArray(),
};
jobHandle = job.Schedule(sm.processingStepMotionParticle.GetJobSchedulePtr(), 32, jobHandle);
return jobHandle;
}
[BurstCompile]
struct MotionConstraintJob : IJobParallelForDefer
{
[Unity.Collections.ReadOnly]
public NativeArray<int> stepParticleIndexArray;
// Solver
//=========================================================================================
internal static void SolverConstraint(
DataChunk chunk,
// team
[Unity.Collections.ReadOnly]
public NativeArray<TeamManager.TeamData> teamDataArray;
[Unity.Collections.ReadOnly]
public NativeArray<ClothParameters> parameterArray;
ref TeamManager.TeamData tdata,
ref ClothParameters param,
// vmesh
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> attributes;
[Unity.Collections.ReadOnly]
public NativeArray<float> vertexDepths;
ref NativeArray<VertexAttribute> attributes,
ref NativeArray<float> vertexDepths,
// particle
[Unity.Collections.ReadOnly]
public NativeArray<short> teamIdArray;
[Unity.Collections.ReadOnly]
public NativeArray<float3> basePosArray;
[Unity.Collections.ReadOnly]
public NativeArray<quaternion> baseRotArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> velocityPosArray;
[NativeDisableParallelForRestriction]
public NativeArray<float> frictionArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> collisionNormalArray;
ref NativeArray<float3> basePosArray,
ref NativeArray<quaternion> baseRotArray,
ref NativeArray<float3> nextPosArray,
ref NativeArray<float3> velocityPosArray,
ref NativeArray<float> frictionArray,
ref NativeArray<float3> collisionNormalArray
)
{
if (param.motionConstraint.useMaxDistance == false && param.motionConstraint.useBackstop == false)
return;
// stiffness
float stiffness = param.motionConstraint.stiffness;
public void Execute(int index)
float backstopRadius = param.motionConstraint.backstopRadius;
// パーティクルごと
//int pindex = tdata.particleChunk.startIndex;
//int vindex = tdata.proxyCommonChunk.startIndex;
int pindex = tdata.particleChunk.startIndex + chunk.startIndex;
int vindex = tdata.proxyCommonChunk.startIndex + chunk.startIndex;
//for (int k = 0; k < tdata.particleChunk.dataLength; k++, pindex++, vindex++)
for (int k = 0; k < chunk.dataLength; k++, pindex++, vindex++)
{
// pindexのチームは有効であることが保証されている
int pindex = stepParticleIndexArray[index];
int teamId = teamIdArray[pindex];
var tdata = teamDataArray[teamId];
var param = parameterArray[teamId];
var motionParam = param.motionConstraint;
var normalAxis = param.normalAxis;
if (motionParam.useMaxDistance == false && motionParam.useBackstop == false)
return;
int p_start = tdata.particleChunk.startIndex;
int l_index = pindex - p_start;
int v_start = tdata.proxyCommonChunk.startIndex;
int vindex = v_start + l_index;
// 移動パーティクルのみ
var attr = attributes[vindex];
if (attr.IsMove() == false)
return;
continue;
var nextPos = nextPosArray[pindex];
var basePos = basePosArray[pindex];
@ -233,17 +179,13 @@ namespace MagicaCloth2
// !MaxDistanceとBackstop制約は常にアニメーション姿勢(basePose)から計算されるので注意!
// !そのためAnimationBlendRatioは影響しない。
// stiffness
float stiffness = motionParam.stiffness;
// 適用頂点属性チェック
if (attr.IsMotion())
{
var opos = nextPos;
// パーティクル半径
float radius = math.max(param.radiusCurveData.EvaluateCurve(depth), 0.0001f); // safe
//radius *= tdata.scaleRatio;
float radius = math.max(param.radiusCurveData.MC2EvaluateCurve(depth), 0.0001f); // safe
// 摩擦影響距離
float cfr = radius * 1.0f;
@ -256,7 +198,7 @@ namespace MagicaCloth2
//=========================================================
var baseRot = baseRotArray[pindex];
float3 dir = math.up();
switch (normalAxis)
switch (param.normalAxis)
{
case ClothNormalAxis.Right:
dir = math.right();
@ -282,9 +224,9 @@ namespace MagicaCloth2
//=========================================================
// Max Distance
//=========================================================
if (motionParam.useMaxDistance)
if (param.motionConstraint.useMaxDistance)
{
float maxDistance = motionParam.maxDistanceCurveData.EvaluateCurve(depth);
float maxDistance = param.motionConstraint.maxDistanceCurveData.MC2EvaluateCurve(depth);
//var cen = basePos + dir * (motionParam.maxDistanceOffset * maxDistance);
var cen = basePos;
var v = MathUtility.ClampVector(nextPos - cen, maxDistance);
@ -294,10 +236,9 @@ namespace MagicaCloth2
//=========================================================
// Backstop
//=========================================================
if (motionParam.useBackstop)
if (param.motionConstraint.useBackstop)
{
float backstopRadius = motionParam.backstopRadius;
float backstopDistance = motionParam.backstopDistanceCurveData.EvaluateCurve(depth);
float backstopDistance = param.motionConstraint.backstopDistanceCurveData.MC2EvaluateCurve(depth);
if (backstopRadius > 0.0f)
{
// バックストップは法線逆方向

View File

@ -3,7 +3,6 @@
// https://magicasoft.jp
using System;
using Unity.Jobs;
using UnityEngine;
namespace MagicaCloth2
@ -127,11 +126,5 @@ namespace MagicaCloth2
public void Dispose()
{
}
//=========================================================================================
unsafe internal JobHandle SolverConstraint(JobHandle jobHandle)
{
return jobHandle;
}
}
}

View File

@ -2,9 +2,7 @@
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
@ -32,7 +30,7 @@ namespace MagicaCloth2
public SerializeData()
{
distanceCompression = 0.9f;
distanceCompression = 0.4f;
}
public void DataValidate()
@ -85,102 +83,41 @@ namespace MagicaCloth2
}
//=========================================================================================
/// <summary>
/// 制約の解決
/// </summary>
/// <param name="clothBase"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
unsafe internal JobHandle SolverConstraint(JobHandle jobHandle)
{
var tm = MagicaManager.Team;
var sm = MagicaManager.Simulation;
var vm = MagicaManager.VMesh;
var job = new TethreConstraintJob()
{
stepParticleIndexArray = sm.processingStepParticle.Buffer,
teamDataArray = tm.teamDataArray.GetNativeArray(),
parameterArray = tm.parameterArray.GetNativeArray(),
centerDataArray = tm.centerDataArray.GetNativeArray(),
attributes = vm.attributes.GetNativeArray(),
vertexDepths = vm.vertexDepths.GetNativeArray(),
vertexRootIndices = vm.vertexRootIndices.GetNativeArray(),
teamIdArray = sm.teamIdArray.GetNativeArray(),
nextPosArray = sm.nextPosArray.GetNativeArray(),
velocityPosArray = sm.velocityPosArray.GetNativeArray(),
frictionArray = sm.frictionArray.GetNativeArray(),
stepBasicPositionBuffer = sm.stepBasicPositionBuffer,
};
jobHandle = job.Schedule(sm.processingStepParticle.GetJobSchedulePtr(), 32, jobHandle);
return jobHandle;
}
[BurstCompile]
struct TethreConstraintJob : IJobParallelForDefer
{
[Unity.Collections.ReadOnly]
public NativeArray<int> stepParticleIndexArray;
// Solver
//=========================================================================================
internal static void SolverConstraint(
DataChunk chunk,
// team
[Unity.Collections.ReadOnly]
public NativeArray<TeamManager.TeamData> teamDataArray;
[Unity.Collections.ReadOnly]
public NativeArray<ClothParameters> parameterArray;
[Unity.Collections.ReadOnly]
public NativeArray<InertiaConstraint.CenterData> centerDataArray;
ref TeamManager.TeamData tdata,
ref ClothParameters param,
ref InertiaConstraint.CenterData cdata,
// vmesh
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> attributes;
[Unity.Collections.ReadOnly]
public NativeArray<float> vertexDepths;
[Unity.Collections.ReadOnly]
public NativeArray<int> vertexRootIndices;
ref NativeArray<VertexAttribute> attributes,
ref NativeArray<float> vertexDepths,
ref NativeArray<int> vertexRootIndices,
// particle
[Unity.Collections.ReadOnly]
public NativeArray<short> teamIdArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> velocityPosArray;
[Unity.Collections.ReadOnly]
public NativeArray<float> frictionArray;
ref NativeArray<float3> nextPosArray,
ref NativeArray<float3> velocityPosArray,
ref NativeArray<float> frictionArray,
// buffer
[Unity.Collections.ReadOnly]
public NativeArray<float3> stepBasicPositionBuffer;
// パーティクルごと
public void Execute(int index)
ref NativeArray<float3> stepBasicPositionBuffer
)
{
int p_start = tdata.particleChunk.startIndex;
//int pindex = p_start;
int pindex = p_start + chunk.startIndex;
//int vindex = tdata.proxyCommonChunk.startIndex;
int vindex = tdata.proxyCommonChunk.startIndex + chunk.startIndex;
//for (int k = 0; k < tdata.particleChunk.dataLength; k++, pindex++, vindex++)
for (int k = 0; k < chunk.dataLength; k++, pindex++, vindex++)
{
// pindexのチームは有効であることが保証されている
int pindex = stepParticleIndexArray[index];
int teamId = teamIdArray[pindex];
var tdata = teamDataArray[teamId];
var param = parameterArray[teamId].tetherConstraint;
//if (param.stiffness < 1e-06f)
// return;
int p_start = tdata.particleChunk.startIndex;
int l_index = pindex - p_start;
int v_start = tdata.proxyCommonChunk.startIndex;
int vindex = v_start + l_index;
var attr = attributes[vindex];
if (attr.IsMove() == false)
return;
continue;
int rootIndex = vertexRootIndices[vindex];
if (rootIndex < 0)
return;
continue;
//Debug.Log($"Tether [{pindex}] root:{rootIndex + p_start}");
@ -198,7 +135,7 @@ namespace MagicaCloth2
// 距離がほぼ0ならば処理をスキップする(エラーの回避)
if (distance < Define.System.Epsilon)
return;
continue;
// 復元距離
// フラグにより初期姿勢かアニメーション後姿勢かを切り替える
@ -210,7 +147,7 @@ namespace MagicaCloth2
// 初期位置がまったく同じ状況を考慮
if (calcDistance == 0.0f)
return;
continue;
// 現在の伸縮割合
//Develop.Assert(calcDistance > 0.0f);
@ -220,8 +157,8 @@ namespace MagicaCloth2
float dist = 0;
float stiffness;
float attn;
float compressionLimit = 1.0f - param.compressionLimit;
float stretchLimit = 1.0f + param.stretchLimit;
float compressionLimit = 1.0f - param.tetherConstraint.compressionLimit;
float stretchLimit = 1.0f + param.tetherConstraint.stretchLimit;
//float widthRatio = math.max(param.stiffnessWidth, 0.001f); // 0.2?
//float widthRatio = 0.1f; // 0.2?
if (ratio < compressionLimit)
@ -243,7 +180,7 @@ namespace MagicaCloth2
attn = Define.System.TetherStretchVelocityAttenuation;
}
else
return;
continue;
// 移動量
float3 add = (v / distance) * (dist * stiffness);

View File

@ -4,9 +4,8 @@
using System;
using System.Collections.Generic;
using System.Text;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
@ -17,6 +16,11 @@ namespace MagicaCloth2
/// </summary>
public class TriangleBendingConstraint : IDisposable
{
/// <summary>
/// ボリュームとして処理する判定フラグ
/// </summary>
const sbyte VOLUME_SIGN = 100;
public enum Method
{
None = 0,
@ -29,7 +33,7 @@ namespace MagicaCloth2
/// <summary>
/// 方向性ありの2面角曲げ制約
/// 初期姿勢を保た持つように復元する
/// 初期姿勢を保つように復元する
/// </summary>
DirectionDihedralAngle = 2,
}
@ -73,16 +77,17 @@ namespace MagicaCloth2
public void Convert(SerializeData sdata)
{
//method = sdata.method;
// モードはDirectionDihedralAngleに固定する
method = sdata.stiffness > Define.System.Epsilon ? Method.DirectionDihedralAngle : Method.None;
//method = sdata.stiffness > Define.System.Epsilon ? Method.DihedralAngle : Method.None;
stiffness = sdata.stiffness;
}
}
//=========================================================================================
internal class ConstraintData : IValid
[System.Serializable]
public class ConstraintData : IValid
{
public ResultCode result;
public ulong[] trianglePairArray;
@ -116,29 +121,16 @@ namespace MagicaCloth2
public ExNativeArray<float> restAngleOrVolumeArray;
/// <summary>
/// トライアングルペアごとの復元方向もしくはボリューム判定(100=このペアはボリュームである)
/// トライアングルペアごとの復元方向もしくはボリューム判定(VOLUME_SIGN(100)=このペアはボリュームである)
/// </summary>
public ExNativeArray<sbyte> signOrVolumeArray;
/// <summary>
/// トライアングルペアごとの結果書き込みローカルインデックス
/// つのbyteをつのuintに結合したもの
/// </summary>
public ExNativeArray<uint> writeDataArray;
//public int DataCount => trianglePairArray?.Count ?? 0;
/// <summary>
/// 頂点ごとの書き込みバッファの数と開始インデックス
/// (上位10bit = カウンタ, 下位22bit = 開始インデックス)
/// ボリューム計算の浮動小数点誤差を回避するための倍数
/// </summary>
public ExNativeArray<uint> writeIndexArray;
/// <summary>
/// 頂点ごとの書き込みバッファ(集計用)
/// writeIndexArrayに従う
/// </summary>
public ExNativeArray<float3> writeBuffer;
public int DataCount => trianglePairArray?.Count ?? 0;
const float VolumeScale = 1000.0f;
//=========================================================================================
public TriangleBendingConstraint()
@ -146,9 +138,6 @@ namespace MagicaCloth2
trianglePairArray = new ExNativeArray<ulong>(0, true);
restAngleOrVolumeArray = new ExNativeArray<float>(0, true);
signOrVolumeArray = new ExNativeArray<sbyte>(0, true);
writeDataArray = new ExNativeArray<uint>(0, true);
writeIndexArray = new ExNativeArray<uint>(0, true);
writeBuffer = new ExNativeArray<float3>(0, true);
}
public void Dispose()
@ -156,9 +145,6 @@ namespace MagicaCloth2
trianglePairArray?.Dispose();
restAngleOrVolumeArray?.Dispose();
signOrVolumeArray?.Dispose();
writeDataArray?.Dispose();
writeIndexArray?.Dispose();
writeBuffer?.Dispose();
trianglePairArray = null;
restAngleOrVolumeArray = null;
@ -172,9 +158,6 @@ namespace MagicaCloth2
sb.AppendLine($" -trianglePairArray:{trianglePairArray.ToSummary()}");
sb.AppendLine($" -restAngleOrVolumeArray:{restAngleOrVolumeArray.ToSummary()}");
sb.AppendLine($" -signOrVolumeArray:{signOrVolumeArray.ToSummary()}");
sb.AppendLine($" -writeDataArray:{writeDataArray.ToSummary()}");
sb.AppendLine($" -writeIndexArray:{writeIndexArray.ToSummary()}");
sb.AppendLine($" -writeBuffer:{writeBuffer.ToSummary()}");
return sb.ToString();
}
@ -186,7 +169,7 @@ namespace MagicaCloth2
/// <param name="proxyMesh"></param>
/// <param name="parameters"></param>
/// <returns></returns>
internal static ConstraintData CreateData(VirtualMesh proxyMesh, in ClothParameters parameters)
public static ConstraintData CreateData(VirtualMesh proxyMesh, in ClothParameters parameters)
{
var constraintData = new ConstraintData();
@ -223,7 +206,7 @@ namespace MagicaCloth2
if (proxyMesh.edgeToTriangles.ContainsKey(edge) == false)
continue;
var triangles = proxyMesh.edgeToTriangles.ToFixedList128Bytes(edge);
var triangles = proxyMesh.edgeToTriangles.MC2ToFixedList128Bytes(edge);
int tcnt = triangles.Length;
// トライアングルの組み合わせ
@ -276,6 +259,7 @@ namespace MagicaCloth2
trianglePairList.Add(pair);
restAngleOrVolumeList.Add(restData);
signOrVolumeList.Add(signFlag);
//Debug.Log($"rest angle:{math.degrees(restData)}, signFlag:{signFlag}");
uint writeData = DataUtility.Pack32(
multiBuilder.CountValuesForKey(vtx.x),
@ -320,7 +304,7 @@ namespace MagicaCloth2
volumeCount++;
//Develop.DebugLog($"Volume Pair. edge:{edge}, tri:({tri0},{tri1}) restAngle:{degAngle}");
//Develop.DebugLog($"Volume Pair. edge:{edge}, tri:({tri0},{tri1}) restAngle:{degAngle}, restData:{restData}, signFlag:{signFlag}");
}
}
//if (math.all(tri0 - 243) == false || math.all(tri1 - 243) == false)
@ -357,13 +341,15 @@ namespace MagicaCloth2
static void InitVolume(VirtualMesh proxyMesh, int v0, int v1, int v2, int v3, out float volumeRest, out sbyte signFlag)
{
// 0/1が対角点,2/3が共通辺
float3 pos0 = proxyMesh.localPositions[v0];
float3 pos1 = proxyMesh.localPositions[v1];
float3 pos2 = proxyMesh.localPositions[v2];
float3 pos3 = proxyMesh.localPositions[v3];
// ここは実行時とボリューム値を合わせるためワールド座標で計算する必要がある。
float3 pos0 = MathUtility.TransformPoint(proxyMesh.localPositions[v0], proxyMesh.initLocalToWorld);
float3 pos1 = MathUtility.TransformPoint(proxyMesh.localPositions[v1], proxyMesh.initLocalToWorld);
float3 pos2 = MathUtility.TransformPoint(proxyMesh.localPositions[v2], proxyMesh.initLocalToWorld);
float3 pos3 = MathUtility.TransformPoint(proxyMesh.localPositions[v3], proxyMesh.initLocalToWorld);
volumeRest = (1.0f / 6.0f) * math.dot(math.cross(pos1 - pos0, pos2 - pos0), pos3 - pos0);
signFlag = 100; // Volume
volumeRest *= VolumeScale; // 浮動小数点演算誤差回避
signFlag = VOLUME_SIGN; // Volume
}
static void InitDihedralAngle(VirtualMesh proxyMesh, int v0, int v1, int v2, int v3, out float restAngle, out sbyte signFlag)
@ -409,11 +395,6 @@ namespace MagicaCloth2
tdata.bendingPairChunk = trianglePairArray.AddRange(cdata.trianglePairArray);
restAngleOrVolumeArray.AddRange(cdata.restAngleOrVolumeArray);
signOrVolumeArray.AddRange(cdata.signOrVolumeArray);
writeDataArray.AddRange(cdata.writeDataArray);
// write buffer
tdata.bendingWriteIndexChunk = writeIndexArray.AddRange(cdata.writeIndexArray);
tdata.bendingBufferChunk = writeBuffer.AddRange(cdata.writeBufferCount);
}
}
@ -430,159 +411,78 @@ namespace MagicaCloth2
trianglePairArray.Remove(tdata.bendingPairChunk);
restAngleOrVolumeArray.Remove(tdata.bendingPairChunk);
signOrVolumeArray.Remove(tdata.bendingPairChunk);
writeDataArray.Remove(tdata.bendingPairChunk);
// write buffer
writeIndexArray.Remove(tdata.bendingWriteIndexChunk);
writeBuffer.Remove(tdata.bendingBufferChunk);
tdata.bendingPairChunk.Clear();
tdata.bendingWriteIndexChunk.Clear();
tdata.bendingBufferChunk.Clear();
}
}
//=========================================================================================
/// <summary>
/// 制約の解決
/// </summary>
/// <param name="jobHandle"></param>
/// <returns></returns>
unsafe internal JobHandle SolverConstraint(JobHandle jobHandle)
{
var tm = MagicaManager.Team;
var sm = MagicaManager.Simulation;
var vm = MagicaManager.VMesh;
if (DataCount > 0)
{
var triangleBendingJob = new TriangleBendingJob()
{
simulationPower = MagicaManager.Time.SimulationPower,
stepTriangleBendIndexArray = sm.processingStepTriangleBending.Buffer,
teamDataArray = tm.teamDataArray.GetNativeArray(),
parameterArray = tm.parameterArray.GetNativeArray(),
attributes = vm.attributes.GetNativeArray(),
depthArray = vm.vertexDepths.GetNativeArray(),
nextPosArray = sm.nextPosArray.GetNativeArray(),
frictionArray = sm.frictionArray.GetNativeArray(),
trianglePairArray = trianglePairArray.GetNativeArray(),
restAngleOrVolumeArray = restAngleOrVolumeArray.GetNativeArray(),
signOrVolumeArray = signOrVolumeArray.GetNativeArray(),
writeDataArray = writeDataArray.GetNativeArray(),
writeIndexArray = writeIndexArray.GetNativeArray(),
writeBuffer = writeBuffer.GetNativeArray(),
};
jobHandle = triangleBendingJob.Schedule(sm.processingStepTriangleBending.GetJobSchedulePtr(), 16, jobHandle);
// 集計(速度影響はなし)
var aggregateJob = new SolveAggregateBufferJob()
{
stepParticleIndexArray = sm.processingStepParticle.Buffer,
teamDataArray = tm.teamDataArray.GetNativeArray(),
attributes = vm.attributes.GetNativeArray(),
teamIdArray = sm.teamIdArray.GetNativeArray(),
nextPosArray = sm.nextPosArray.GetNativeArray(),
writeIndexArray = writeIndexArray.GetNativeArray(),
writeBuffer = writeBuffer.GetNativeArray(),
};
jobHandle = aggregateJob.Schedule(sm.processingStepParticle.GetJobSchedulePtr(), 16, jobHandle);
}
return jobHandle;
}
[BurstCompile]
struct TriangleBendingJob : IJobParallelForDefer
{
public float4 simulationPower;
[Unity.Collections.ReadOnly]
public NativeArray<int> stepTriangleBendIndexArray;
// Solver
//=========================================================================================
internal unsafe static void SolverConstraint(
DataChunk chunk,
in float4 simulationPower,
// team
[Unity.Collections.ReadOnly]
public NativeArray<TeamManager.TeamData> teamDataArray;
[Unity.Collections.ReadOnly]
public NativeArray<ClothParameters> parameterArray;
ref TeamManager.TeamData tdata,
ref ClothParameters param,
// vmesh
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> attributes;
[Unity.Collections.ReadOnly]
public NativeArray<float> depthArray;
ref NativeArray<VertexAttribute> attributes,
ref NativeArray<float> depthArray,
// particle
[Unity.Collections.ReadOnly]
public NativeArray<float3> nextPosArray;
[Unity.Collections.ReadOnly]
public NativeArray<float> frictionArray;
ref NativeArray<float3> nextPosArray,
ref NativeArray<float> frictionArray,
// constraints
[Unity.Collections.ReadOnly]
public NativeArray<ulong> trianglePairArray;
[Unity.Collections.ReadOnly]
public NativeArray<float> restAngleOrVolumeArray;
[Unity.Collections.ReadOnly]
public NativeArray<sbyte> signOrVolumeArray;
[Unity.Collections.ReadOnly]
public NativeArray<uint> writeDataArray;
[Unity.Collections.ReadOnly]
public NativeArray<uint> writeIndexArray;
ref NativeArray<ulong> trianglePairArray,
ref NativeArray<float> restAngleOrVolumeArray,
ref NativeArray<sbyte> signOrVolumeArray,
// buffer2
ref NativeArray<float3> tempVectorBufferA,
ref NativeArray<int> tempCountBuffer
)
{
if (param.triangleBendingConstraint.method == Method.None)
return;
// output
[NativeDisableParallelForRestriction]
public NativeArray<float3> writeBuffer;
if (tdata.bendingPairChunk.IsValid == false)
return;
// ベンドトライアングルペアごと
public void Execute(int index)
// 剛性
float stiffness = param.triangleBendingConstraint.stiffness;
if (stiffness < 1e-06f)
return;
stiffness = math.saturate(stiffness * simulationPower.y);
int p_start = tdata.particleChunk.startIndex;
int v_start = tdata.proxyCommonChunk.startIndex;
int pindex;
int vindex;
int* sumPt = (int*)tempVectorBufferA.GetUnsafePtr();
int* cntPt = (int*)tempCountBuffer.GetUnsafePtr();
// ■計算
// ベンドペアごと
//int pairIndex = tdata.bendingPairChunk.startIndex;
int pairIndex = tdata.bendingPairChunk.startIndex + chunk.startIndex;
//for (int k = 0; k < tdata.bendingPairChunk.dataLength; k++, pairIndex++)
for (int k = 0; k < chunk.dataLength; k++, pairIndex++)
{
// インデックスのチームは有効であることが保証されている
//int pairIndex = stepTriangleBendIndexArray[index];
//int teamId = trianglePairTeamIdArray[pairIndex];
uint pack = (uint)stepTriangleBendIndexArray[index];
int pairIndex = DataUtility.Unpack12_20Low(pack);
int teamId = DataUtility.Unpack12_20Hi(pack);
var tdata = teamDataArray[teamId];
var parameter = parameterArray[teamId].triangleBendingConstraint;
if (parameter.method == Method.None)
return;
// 剛性
float stiffness = parameter.stiffness;
if (stiffness < 1e-06f)
return;
stiffness = math.saturate(stiffness * simulationPower.y);
int p_start = tdata.particleChunk.startIndex;
int v_start = tdata.proxyCommonChunk.startIndex;
// トライアングルペア
var pairData = trianglePairArray[pairIndex];
int4 vertices = DataUtility.Unpack64(pairData);
//Debug.Log(vertices);
int4 pindex4 = vertices + p_start;
int4 vindex4 = vertices + v_start;
// 状態
float3x4 nextPosBuffer = 0;
float3x4 addPosBuffer = 0;
float4 invMassBuffer = 1;
//int4 fixedBuffer = 0;
for (int i = 0; i < 4; i++)
{
int pindex = p_start + vertices[i];
int vindex = v_start + vertices[i];
pindex = pindex4[i];
vindex = vindex4[i];
nextPosBuffer[i] = nextPosArray[pindex];
float friction = frictionArray[pindex];
float depth = depthArray[vindex];
@ -598,220 +498,241 @@ namespace MagicaCloth2
// メソッドごとの解決
bool result = false;
if (signOrVolume == 100)
if (signOrVolume == VOLUME_SIGN)
{
// Volume
result = Volume(nextPosBuffer, invMassBuffer, restAngle, stiffness, ref addPosBuffer);
float volumeRest = restAngle * tdata.scaleRatio; // スケール倍率
// マイナススケール
volumeRest *= tdata.negativeScaleSign;
result = CalcVolume(nextPosBuffer, invMassBuffer, volumeRest, stiffness, ref addPosBuffer);
}
else
{
// Triangle Bending
float sign = signOrVolume < 0 ? -1 : 1;
if (parameter.method == Method.DihedralAngle)
if (param.triangleBendingConstraint.method == Method.DihedralAngle)
{
// 二面角
result = DihedralAngle(0, nextPosBuffer, invMassBuffer, restAngle, stiffness, ref addPosBuffer);
// 方向性なし二面角
result = CalcDihedralAngle(0, nextPosBuffer, invMassBuffer, restAngle, stiffness, ref addPosBuffer);
}
else if (parameter.method == Method.DirectionDihedralAngle)
else if (param.triangleBendingConstraint.method == Method.DirectionDihedralAngle)
{
// 方向性二面角
// 方向性あり二面角
float sign = signOrVolume < 0 ? -1 : 1;
restAngle *= sign;
result = DihedralAngle(sign, nextPosBuffer, invMassBuffer, restAngle, stiffness, ref addPosBuffer);
// マイナススケール
restAngle *= tdata.negativeScaleSign;
result = CalcDihedralAngle(sign, nextPosBuffer, invMassBuffer, restAngle, stiffness, ref addPosBuffer);
}
}
// 集計バッファへ格納
if (result)
{
int4 writeData = DataUtility.Unpack32(writeDataArray[dataIndex]);
int indexStart = tdata.bendingWriteIndexChunk.startIndex;
int bufferStart = tdata.bendingBufferChunk.startIndex;
for (int i = 0; i < 4; i++)
{
int l_vindex = vertices[i];
int start = DataUtility.Unpack12_20Low(writeIndexArray[indexStart + l_vindex]);
int bufferIndex = bufferStart + start + writeData[i];
writeBuffer[bufferIndex] = addPosBuffer[i];
pindex = pindex4[i];
InterlockUtility.AddFloat3(pindex, addPosBuffer[i], cntPt, sumPt);
}
}
}
}
bool Volume(in float3x4 nextPosBuffer, in float4 invMassBuffer, float volumeRest, float stiffness, ref float3x4 addPosBuffer)
internal unsafe static void SumConstraint(
DataChunk chunk,
// team
ref TeamManager.TeamData tdata,
ref ClothParameters param,
// vmesh
ref NativeArray<VertexAttribute> attributes,
// particle
ref NativeArray<float3> nextPosArray,
// buffer2
ref NativeArray<float3> tempVectorBufferA,
ref NativeArray<int> tempCountBuffer
)
{
if (param.triangleBendingConstraint.method == Method.None)
return;
if (tdata.bendingPairChunk.IsValid == false)
return;
// 剛性
float stiffness = param.triangleBendingConstraint.stiffness;
if (stiffness < 1e-06f)
return;
int p_start = tdata.particleChunk.startIndex;
int v_start = tdata.proxyCommonChunk.startIndex;
int* sumPt = (int*)tempVectorBufferA.GetUnsafePtr();
int* cntPt = (int*)tempCountBuffer.GetUnsafePtr();
// ■集計
// パーティクルごと
int pindex = p_start + chunk.startIndex;
int vindex = v_start + chunk.startIndex;
//for (int k = 0; k < tdata.particleChunk.dataLength; k++, pindex++, vindex++)
for (int k = 0; k < chunk.dataLength; k++, pindex++, vindex++)
{
float3 nextPos0 = nextPosBuffer[0];
float3 nextPos1 = nextPosBuffer[1];
float3 nextPos2 = nextPosBuffer[2];
float3 nextPos3 = nextPosBuffer[3];
float invMass0 = invMassBuffer[0];
float invMass1 = invMassBuffer[1];
float invMass2 = invMassBuffer[2];
float invMass3 = invMassBuffer[3];
float volume = (1.0f / 6.0f) * math.dot(math.cross(nextPos1 - nextPos0, nextPos2 - nextPos0), nextPos3 - nextPos0);
float3 grad0 = math.cross(nextPos1 - nextPos2, nextPos3 - nextPos2);
float3 grad1 = math.cross(nextPos2 - nextPos0, nextPos3 - nextPos0);
float3 grad2 = math.cross(nextPos0 - nextPos1, nextPos3 - nextPos1);
float3 grad3 = math.cross(nextPos1 - nextPos0, nextPos2 - nextPos0);
float lambda =
invMass0 * math.lengthsq(grad0) +
invMass1 * math.lengthsq(grad1) +
invMass2 * math.lengthsq(grad2) +
invMass3 * math.lengthsq(grad3);
if (math.abs(lambda) < 1e-06f)
return false;
lambda = stiffness * (volume - volumeRest) / lambda;
addPosBuffer[0] = -lambda * invMass0 * grad0;
addPosBuffer[1] = -lambda * invMass1 * grad1;
addPosBuffer[2] = -lambda * invMass2 * grad2;
addPosBuffer[3] = -lambda * invMass3 * grad3;
return true;
}
bool DihedralAngle(float sign, in float3x4 nextPosBuffer, in float4 invMassBuffer, float restAngle, float stiffness, ref float3x4 addPosBuffer)
{
float3 nextPos0 = nextPosBuffer[0];
float3 nextPos1 = nextPosBuffer[1];
float3 nextPos2 = nextPosBuffer[2];
float3 nextPos3 = nextPosBuffer[3];
float invMass0 = invMassBuffer[0];
float invMass1 = invMassBuffer[1];
float invMass2 = invMassBuffer[2];
float invMass3 = invMassBuffer[3];
float3 e = nextPos3 - nextPos2;
float elen = math.length(e);
if (elen < 1e-08f)
return false;
float invElen = 1.0f / elen;
float3 n1 = math.cross(nextPos2 - nextPos0, nextPos3 - nextPos0);
float3 n2 = math.cross(nextPos3 - nextPos1, nextPos2 - nextPos1);
float n1_lengsq = math.lengthsq(n1);
float n2_lengsq = math.lengthsq(n2);
// 稀に発生する長さ0に対処
if (n1_lengsq == 0.0f || n2_lengsq == 0.0f)
return false;
//Develop.Assert(n1_lengsq > 0.0f);
//Develop.Assert(n2_lengsq > 0.0f);
n1 /= n1_lengsq;
n2 /= n2_lengsq;
float3 d0 = elen * n1;
float3 d1 = elen * n2;
float3 d2 = math.dot(nextPos0 - nextPos3, e) * invElen * n1 + math.dot(nextPos1 - nextPos3, e) * invElen * n2;
float3 d3 = math.dot(nextPos2 - nextPos0, e) * invElen * n1 + math.dot(nextPos2 - nextPos1, e) * invElen * n2;
n1 = math.normalize(n1);
n2 = math.normalize(n2);
float dot = math.dot(n1, n2);
dot = MathUtility.Clamp1(dot);
float phi = math.acos(dot);
// 方向性
float dir = math.dot(math.cross(n1, n2), e);
if (sign != 0)
// 移動のみ
if (attributes[vindex].IsDontMove() == false)
{
phi *= math.sign(dir);
dir = 1; // lambdaを反転させるため
int cnt = cntPt[pindex];
if (cnt > 0)
{
int pindex2 = pindex * 3;
float3 add = new float3(sumPt[pindex2], sumPt[pindex2 + 1], sumPt[pindex2 + 2]);
add /= cnt;
// データは固定小数点なので戻す
add *= InterlockUtility.ToFloat;
nextPosArray[pindex] = nextPosArray[pindex] + add;
}
}
float lambda =
invMass0 * math.lengthsq(d0) +
invMass1 * math.lengthsq(d1) +
invMass2 * math.lengthsq(d2) +
invMass3 * math.lengthsq(d3);
if (lambda == 0.0f)
return false;
lambda = (phi - restAngle) / lambda * stiffness;
if (dir > 0.0f)
lambda = -lambda;
float3 corr0 = -invMass0 * lambda * d0;
float3 corr1 = -invMass1 * lambda * d1;
float3 corr2 = -invMass2 * lambda * d2;
float3 corr3 = -invMass3 * lambda * d3;
addPosBuffer[0] = corr0;
addPosBuffer[1] = corr1;
addPosBuffer[2] = corr2;
addPosBuffer[3] = corr3;
return true;
// バッファクリア
tempCountBuffer[pindex] = 0;
tempVectorBufferA[pindex] = 0;
}
}
[BurstCompile]
struct SolveAggregateBufferJob : IJobParallelForDefer
static bool CalcVolume(
in float3x4 nextPosBuffer,
in float4 invMassBuffer,
float volumeRest,
float stiffness,
ref float3x4 addPosBuffer
)
{
[Unity.Collections.ReadOnly]
public NativeArray<int> stepParticleIndexArray;
float3 nextPos0 = nextPosBuffer[0];
float3 nextPos1 = nextPosBuffer[1];
float3 nextPos2 = nextPosBuffer[2];
float3 nextPos3 = nextPosBuffer[3];
// team
[Unity.Collections.ReadOnly]
public NativeArray<TeamManager.TeamData> teamDataArray;
float invMass0 = invMassBuffer[0];
float invMass1 = invMassBuffer[1];
float invMass2 = invMassBuffer[2];
float invMass3 = invMassBuffer[3];
// vmesh
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> attributes;
float volume = (1.0f / 6.0f) * math.dot(math.cross(nextPos1 - nextPos0, nextPos2 - nextPos0), nextPos3 - nextPos0);
volume *= VolumeScale; // 浮動小数点演算誤差回避
// particle
[Unity.Collections.ReadOnly]
public NativeArray<short> teamIdArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
float3 grad0 = math.cross(nextPos1 - nextPos2, nextPos3 - nextPos2);
float3 grad1 = math.cross(nextPos2 - nextPos0, nextPos3 - nextPos0);
float3 grad2 = math.cross(nextPos0 - nextPos1, nextPos3 - nextPos1);
float3 grad3 = math.cross(nextPos1 - nextPos0, nextPos2 - nextPos0);
// constraint
[Unity.Collections.ReadOnly]
public NativeArray<uint> writeIndexArray;
[Unity.Collections.ReadOnly]
public NativeArray<float3> writeBuffer;
float lambda =
invMass0 * math.lengthsq(grad0) +
invMass1 * math.lengthsq(grad1) +
invMass2 * math.lengthsq(grad2) +
invMass3 * math.lengthsq(grad3);
lambda *= VolumeScale; // 浮動小数点演算誤差回避
// ステップパーティクルごと
public void Execute(int index)
if (math.abs(lambda) < 1e-06f)
return false;
lambda = stiffness * (volumeRest - volume) / lambda;
addPosBuffer[0] = lambda * invMass0 * grad0;
addPosBuffer[1] = lambda * invMass1 * grad1;
addPosBuffer[2] = lambda * invMass2 * grad2;
addPosBuffer[3] = lambda * invMass3 * grad3;
return true;
}
static bool CalcDihedralAngle(
float sign,
in float3x4 nextPosBuffer,
in float4 invMassBuffer,
float restAngle,
float stiffness,
ref float3x4 addPosBuffer
)
{
float3 nextPos0 = nextPosBuffer[0];
float3 nextPos1 = nextPosBuffer[1];
float3 nextPos2 = nextPosBuffer[2];
float3 nextPos3 = nextPosBuffer[3];
float invMass0 = invMassBuffer[0];
float invMass1 = invMassBuffer[1];
float invMass2 = invMassBuffer[2];
float invMass3 = invMassBuffer[3];
float3 e = nextPos3 - nextPos2;
float elen = math.length(e);
if (elen < 1e-08f)
return false;
float invElen = 1.0f / elen;
float3 n1 = math.cross(nextPos2 - nextPos0, nextPos3 - nextPos0);
float3 n2 = math.cross(nextPos3 - nextPos1, nextPos2 - nextPos1);
float n1_lengsq = math.lengthsq(n1);
float n2_lengsq = math.lengthsq(n2);
// 稀に発生する長さ0に対処
if (n1_lengsq == 0.0f || n2_lengsq == 0.0f)
return false;
//Develop.Assert(n1_lengsq > 0.0f);
//Develop.Assert(n2_lengsq > 0.0f);
n1 /= n1_lengsq;
n2 /= n2_lengsq;
float3 d0 = elen * n1;
float3 d1 = elen * n2;
float3 d2 = math.dot(nextPos0 - nextPos3, e) * invElen * n1 + math.dot(nextPos1 - nextPos3, e) * invElen * n2;
float3 d3 = math.dot(nextPos2 - nextPos0, e) * invElen * n1 + math.dot(nextPos2 - nextPos1, e) * invElen * n2;
n1 = math.normalize(n1);
n2 = math.normalize(n2);
float dot = math.dot(n1, n2);
dot = MathUtility.Clamp1(dot);
float phi = math.acos(dot);
float lambda =
invMass0 * math.lengthsq(d0) +
invMass1 * math.lengthsq(d1) +
invMass2 * math.lengthsq(d2) +
invMass3 * math.lengthsq(d3);
if (lambda == 0.0f)
return false;
// 方向性
float dirSign = math.sign(math.dot(math.cross(n1, n2), e));
if (sign != 0)
{
// pindexのチームは有効であることが保証されている
int pindex = stepParticleIndexArray[index];
int teamId = teamIdArray[pindex];
var tdata = teamDataArray[teamId];
if (tdata.bendingPairChunk.IsValid == false)
return;
int l_index = pindex - tdata.particleChunk.startIndex;
// 固定なら無効
int vindex = tdata.proxyCommonChunk.startIndex + l_index;
if (attributes[vindex].IsDontMove())
return;
// 書き込みバッファの値を平均化してnextPosに加算する
uint pack = writeIndexArray[tdata.bendingWriteIndexChunk.startIndex + l_index];
int cnt = DataUtility.Unpack12_20Hi(pack);
int start = DataUtility.Unpack12_20Low(pack);
int bufferIndex = tdata.bendingBufferChunk.startIndex + start;
float3 add = 0;
for (int i = 0; i < cnt; i++)
{
add += writeBuffer[bufferIndex + i];
}
if (cnt > 0)
{
add /= cnt;
nextPosArray[pindex] = nextPosArray[pindex] + add;
}
// 方向性あり(DirectionDihedralAngle)
phi *= dirSign;
}
else
{
// 方向性なし(DihedralAngle)
lambda *= dirSign;
}
lambda = (restAngle - phi) / lambda * stiffness;
float3 corr0 = -invMass0 * lambda * d0;
float3 corr1 = -invMass1 * lambda * d1;
float3 corr2 = -invMass2 * lambda * d2;
float3 corr3 = -invMass3 * lambda * d3;
addPosBuffer[0] = corr0;
addPosBuffer[1] = corr1;
addPosBuffer[2] = corr2;
addPosBuffer[3] = corr3;
return true;
}
}
}

View File

@ -1,4 +1,7 @@
using System.Collections.Generic;
// Magica Cloth 2.
// Copyright (c) 2025 MagicaSoft.
// https://magicasoft.jp
using System.Collections.Generic;
using UnityEngine;
namespace MagicaCloth2
@ -83,8 +86,57 @@ namespace MagicaCloth2
/// </summary>
public List<Renderer> cameraCullingRenderers = new List<Renderer>();
/// <summary>
/// 距離カリングの状態と距離
/// Distance Culling State and Distance.
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
public CheckSliderSerializeData distanceCullingLength;
/// <summary>
/// 距離カリングのフェード割合(0.0 ~ 1.0)
/// Distance culling fade rate (0.0 to 1.0).
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
[Range(0.0f, 1.0f)]
public float distanceCullingFadeRatio;
/// <summary>
/// 距離カリングの測定対象(None=メインカメラ)
/// Distance culling measurement target (None = main camera).
/// [OK] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>
public GameObject distanceCullingReferenceObject;
//=========================================================================================
public struct CullingParams
{
public bool useDistanceCulling;
public float distanceCullingLength;
public float distanceCullingFadeRatio;
public void Convert(CullingSettings cullingSettings)
{
useDistanceCulling = cullingSettings.distanceCullingLength.use;
distanceCullingLength = cullingSettings.distanceCullingLength.value;
distanceCullingFadeRatio = cullingSettings.distanceCullingFadeRatio;
}
}
//=========================================================================================
public CullingSettings()
{
distanceCullingLength = new CheckSliderSerializeData(false, 30.0f);
distanceCullingFadeRatio = 0.2f;
}
public void DataValidate()
{
distanceCullingLength.DataValidate(0.0f, Define.System.DistanceCullingMaxLength);
distanceCullingFadeRatio = Mathf.Clamp01(distanceCullingFadeRatio);
}
public CullingSettings Clone()
@ -94,6 +146,9 @@ namespace MagicaCloth2
cameraCullingMode = cameraCullingMode,
cameraCullingMethod = cameraCullingMethod,
cameraCullingRenderers = new List<Renderer>(cameraCullingRenderers),
distanceCullingLength = distanceCullingLength.Clone(),
distanceCullingFadeRatio = distanceCullingFadeRatio,
distanceCullingReferenceObject = distanceCullingReferenceObject,
};
}

View File

@ -19,10 +19,8 @@ namespace MagicaCloth2
public bool enable = false;
/// <summary>
/// bones for skinning.
/// Calculated from the parent-child structure line of bones registered here.
/// スキニング用ボーン
/// ここに登録されたボーンの親子構造ラインから算出される
/// Bones for custom skinning.
/// カスタムスキニング用ボーン
/// [NG] Runtime changes.
/// [NG] Export/Import with Presets
/// </summary>

View File

@ -42,7 +42,14 @@ namespace MagicaCloth2
/// General processing.
/// </summary>
private ClothProcess process = new ClothProcess();
public ClothProcess Process { get { process.cloth = this; return process; } }
public ClothProcess Process
{
get
{
process.cloth = this;
return process;
}
}
/// <summary>
/// Cloth component transform.
@ -53,7 +60,14 @@ namespace MagicaCloth2
/// <summary>
/// Synchronization target.
/// </summary>
public MagicaCloth SyncCloth => SerializeData.IsBoneSpring() ? null : SerializeData.selfCollisionConstraint.GetSyncPartner();
public MagicaCloth SyncPartnerCloth
{
get
{
var syncCloth = SerializeData.IsBoneSpring() ? null : SerializeData.selfCollisionConstraint.GetSyncPartner();
return syncCloth == this ? null : syncCloth;
}
}
/// <summary>
/// Check if the cloth component is in a valid state.
@ -66,6 +80,14 @@ namespace MagicaCloth2
}
//=========================================================================================
private void Reset()
{
#if UNITY_EDITOR
// Automatically generate pre-build ID
serializeData2.preBuildData.buildId = PreBuildSerializeData.GenerateBuildID();
#endif
}
private void OnValidate()
{
Process.DataUpdate();
@ -73,10 +95,11 @@ namespace MagicaCloth2
private void Awake()
{
Process.Init();
// If Awake() is called, OnDestroy() will also be called, so remove it from monitoring.
MagicaManager.Team.RemoveMonitoringProcess(Process);
if (MagicaManager.initializationLocation == MagicaManager.InitializationLocation.Awake)
{
Process.Init();
MagicaManager.Team.RemoveMonitoringProcess(Process);
}
}
private void OnEnable()
@ -91,6 +114,12 @@ namespace MagicaCloth2
void Start()
{
if (MagicaManager.initializationLocation == MagicaManager.InitializationLocation.Start)
{
Process.Init();
MagicaManager.Team.RemoveMonitoringProcess(Process);
}
Process.AutoBuild();
}

View File

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
executionOrder: 5
icon: {fileID: 2800000, guid: cf7e3400035e987478c3a19e57b4cf75, type: 3}
userData:
assetBundleName:

View File

@ -29,8 +29,14 @@ namespace MagicaCloth2
/// Event after completion of cloth data construction.
/// (true = Success, false = Failure)
/// </summary>
public Action<bool> OnBuildComplete;
public Action<MagicaCloth, bool> OnBuildComplete;
/// <summary>
/// レンダラーメッシュ変更後イベント
/// Renderer mesh change event.
/// (true = Change to custom mesh, false = Change to original mesh)
/// </summary>
public Action<MagicaCloth, Renderer, bool> OnRendererMeshChange;
/// <summary>
/// 初期化を実行します
@ -67,41 +73,74 @@ namespace MagicaCloth2
/// <returns>true=start build. false=build failed.</returns>
public bool BuildAndRun()
{
if (Application.isPlaying == false)
return false;
bool ret = false;
bool buildComplate = true;
DisableAutoBuild();
if (Process.IsState(ClothProcess.State_Build))
try
{
Develop.LogError($"Already built.:{this.name}");
return false;
}
if (Application.isPlaying == false)
throw new MagicaClothProcessingException();
// initialize generated data.
if (Process.GenerateInitialization() == false)
return false;
DisableAutoBuild();
// setting by type.
switch (serializeData.clothType)
{
case ClothProcess.ClothType.BoneCloth:
case ClothProcess.ClothType.BoneSpring:
// BoneCloth用のセレクションデータの作成
// ただしセレクションデータが存在し、かつユーザー定義されている場合は作成しない
var nowSelection = serializeData2.selectionData;
if (nowSelection == null || nowSelection.IsValid() == false || nowSelection.IsUserEdit() == false)
if (Process.IsState(ClothProcess.State_Build))
{
Develop.LogError($"Already built.:{this.name}");
throw new MagicaClothProcessingException();
}
// initialize generated data.
if (Process.GenerateInitialization() == false)
throw new MagicaClothProcessingException();
// check Pre-Build
bool usePreBuildData = serializeData2.preBuildData.UsePreBuild();
if (usePreBuildData == false)
{
// Runtime Build.
// setting by type.
switch (serializeData.clothType)
{
if (Process.GenerateBoneClothSelection() == false)
return false;
case ClothProcess.ClothType.BoneCloth:
case ClothProcess.ClothType.BoneSpring:
// BoneCloth用のセレクションデータの作成
// ただしセレクションデータが存在し、かつユーザー定義されている場合は作成しない
var nowSelection = serializeData2.selectionData;
if (nowSelection == null || nowSelection.IsValid() == false || nowSelection.IsUserEdit() == false)
{
if (Process.GenerateBoneClothSelection() == false)
throw new MagicaClothProcessingException();
}
break;
}
break;
// build and run.
ret = Process.StartRuntimeBuild();
if (ret)
buildComplate = false; // OnBuildCompleteはランタイム構築後に呼ばれる
}
else
{
// pre-build
ret = Process.PreBuildDataConstruction();
}
}
catch (MagicaClothProcessingException)
{
}
catch (Exception exception)
{
Debug.LogException(exception);
}
finally
{
// ビルド完了イベント
if (buildComplate)
OnBuildComplete?.Invoke(this, ret);
}
// build and run.
Process.StartBuild();
return true;
return ret;
}
/// <summary>
@ -123,7 +162,7 @@ namespace MagicaCloth2
var replaceDict = new Dictionary<int, Transform>();
foreach (var t in useTransformSet)
{
if (targetTransformDict.ContainsKey(t.name))
if (t && targetTransformDict.ContainsKey(t.name))
{
replaceDict.Add(t.GetInstanceID(), targetTransformDict[t.name]);
}
@ -133,6 +172,18 @@ namespace MagicaCloth2
Process.ReplaceTransform(replaceDict);
}
/// <summary>
/// コンポーネントが保持するすべてのトランスフォームを取得します。
/// Gets all the transforms held by the component.
/// </summary>
/// <returns></returns>
public HashSet<Transform> GetUsedTransform()
{
var useTransformSet = new HashSet<Transform>();
Process.GetUsedTransform(useTransformSet);
return useTransformSet;
}
/// <summary>
/// パラメータの変更を通知
@ -197,8 +248,8 @@ namespace MagicaCloth2
// Reset
tdata.flag.SetBits(TeamManager.Flag_Reset, true);
tdata.flag.SetBits(TeamManager.Flag_TimeReset, true);
tdata.flag.SetBits(TeamManager.Flag_CullingKeep, false);
Process.SetState(ClothProcess.State_CullingKeep, false);
tdata.flag.SetBits(TeamManager.Flag_CameraCullingKeep, false);
Process.SetState(ClothProcess.State_CameraCullingKeep, false);
Process.UpdateRendererUse();
}
}
@ -251,5 +302,50 @@ namespace MagicaCloth2
Process.SetSkipWriting(sw);
}
}
private RenderData GetRenderData(Renderer ren)
{
if (IsValid() == false || ren == null)
return null;
int handle = ren.GetInstanceID();
return MagicaManager.Render.GetRendererData(handle);
}
/// <summary>
/// MeshClothのオリジナルメッシュを取得します
/// Get the original mesh of MeshCloth.
/// </summary>
/// <param name="ren"></param>
/// <returns>null if not found</returns>
public Mesh GetOriginalMesh(Renderer ren)
{
return GetRenderData(ren)?.originalMesh ?? null;
}
/// <summary>
/// MeshClothのカスタムメッシュを取得します
/// Get the custom mesh for MeshCloth.
/// </summary>
/// <param name="ren"></param>
/// <returns>null if not found</returns>
public Mesh GetCustomMesh(Renderer ren)
{
return GetRenderData(ren)?.customMesh ?? null;
}
/// <summary>
/// MeshClothのSkinnedMeshRendererに設定されているカスタムボーンリストを取得します
/// カスタムボーンリストはオリジナルのBonesからスキニングに不要なTransformをnullに設定し、
/// また最後にレンダラーのTransformが追加されるなど加工されているので注意してください。
/// Gets the custom bone list set for the SkinnedMeshRenderer of MeshCloth.
/// Please note that the custom bone list has been processed by setting Transforms
/// that are not necessary for skinning to null from the original Bones, and adding the renderer Transform at the end.
/// </summary>
/// <param name="ren"></param>
/// <returns>null if not found</returns>
public List<Transform> GetCustomBones(Renderer ren)
{
return GetRenderData(ren)?.transformList ?? null;
}
}
}

View File

@ -212,13 +212,7 @@ namespace MagicaCloth2
public void Fill(VertexAttribute attr)
{
#if UNITY_2020
int cnt = Count;
for (int i = 0; i < cnt; i++)
attributes[i] = attr;
#else
Array.Fill(attributes, attr);
#endif
}
//=========================================================================================

View File

@ -200,8 +200,9 @@ namespace MagicaCloth2
public void SetWindDirection(Vector3 dir, bool localSpace = false)
{
Vector3 lv = localSpace ? dir : transform.InverseTransformDirection(dir);
directionAngleX = Mathf.Atan2(lv.z, lv.x) * Mathf.Rad2Deg;
directionAngleY = Mathf.Atan2(lv.z, lv.y) * Mathf.Rad2Deg;
var angles = Quaternion.FromToRotation(Vector3.forward, lv).eulerAngles;
directionAngleX = angles.x > 180 ? angles.x - 360 : angles.x;
directionAngleY = angles.y > 180 ? angles.y - 360 : angles.y;
}
}
}

View File

@ -34,6 +34,8 @@ namespace MagicaCloth2
RenderMesh_UnknownWarning = 10100,
RenderMesh_VertexWeightIs5BonesOrMore,
Init_NonUniformScale = 10200,
///////////////////////////////////////////////////////////////////
// Error(20000 - )
///////////////////////////////////////////////////////////////////
@ -42,11 +44,15 @@ namespace MagicaCloth2
// Validating serialized data
SerializeData_InvalidData = 20050,
SerializeData_Over31Renderers,
SerializeData_DuplicateRootBone,
SerializeData_DuplicateRenderer,
// init
Init_InvalidData = 20100,
Init_InvalidPaintMap,
Init_PaintMapNotReadable,
Init_ScaleIsZero,
Init_NegativeScale,
// RenderSetup
RenderSetup_Exception = 20200,
@ -76,6 +82,11 @@ namespace MagicaCloth2
CreateCloth_InvalidPaintMap,
CreateCloth_PaintMapNotReadable,
CreateCloth_PaintMapCountMismatch,
CreateCloth_CanNotStart,
CreateCloth_VertexAttributeListCountMismatch,
CreateCloth_VertexAttributeListIsNull,
CreateCloth_VertexAttributeListDataMismatch,
CreateCloth_InvalidVertexAttributeData,
// Reduction
Reduction_Exception = 20500,
@ -131,6 +142,41 @@ namespace MagicaCloth2
MagicaMesh_Invalid,
MagicaMesh_InvalidRenderer,
MagicaMesh_InvalidMeshFilter,
// PreBuildData
PreBuildData_UnknownError = 22600,
PreBuildData_MagicaClothException,
PreBuildData_VirtualMeshDeserializationException,
PreBuildData_VerificationResult,
PreBuildData_VersionMismatch,
PreBuildData_InvalidClothData,
PreBuildData_Empty,
PreBuildData_InvalidScale,
// PreBuild
PreBuild_UnknownError = 22700,
PreBuild_Exception,
PreBuild_InvalidPreBuildData,
PreBuild_InvalidRenderSetupData,
PreBuild_SetupDeserializationError,
// PreBuild Deserialization
Deserialization_UnknownError = 22800,
Deserialization_Exception,
// Init SerializeData
InitSerializeData_UnknownError = 22900,
InitSerializeData_InvalidHash,
InitSerializeData_InvalidVersion,
InitSerializeData_InvalidSetupData,
InitSerializeData_ClothTypeMismatch,
InitSerializeData_SetupCountMismatch,
InitSerializeData_CustomSkinningBoneCountMismatch,
InitSerializeData_MeshClothSetupValidationError,
InitSerializeData_BoneClothSetupValidationError,
InitSerializeData_BoneSpringSetupValidationError,
InitSerializeData_DeserializationError,
InitSerializeData_InvalidCloneMesh,
}
}
}

View File

@ -17,6 +17,11 @@ namespace MagicaCloth2
/// </summary>
public const string DefineSymbol = "MAGICACLOTH2";
/// <summary>
/// 現在有効なPreBuildの最新バージョン
/// </summary>
public const int LatestPreBuildVersion = 2;
/// <summary>
/// 計算を省略する最小の浮動小数点数
/// </summary>
@ -72,6 +77,16 @@ namespace MagicaCloth2
/// </summary>
public const float SameSurfaceAngle = 80.0f;
/// <summary>
/// 未来予測時のルートからの距離制限倍率
/// </summary>
public const float MaxDistanceRatioFutuerPrediction = 1.3f;
/// <summary>
/// 分割ジョブを適用するプロキシメッシュメッシュの頂点数
/// </summary>
public const int SplitProxyMeshVertexCount = 300;
/// <summary>
/// [Reduction]
/// 有効フラグ。常にtrueとする。
@ -319,7 +334,19 @@ namespace MagicaCloth2
/// [Self Collision]
/// 反復回数
/// </summary>
public const int SelfCollisionSolverIteration = 4;
public const int SelfCollisionSolverIteration = 4; // 4
/// <summary>
/// [Self Collision]
/// 無効グリッド座標
/// </summary>
public const int SelfCollisionIgnoreGrid = 1000000;
/// <summary>
/// [Self Collision]
/// 交差判定の分割数
/// </summary>
public const int SelfCollisionIntersectDiv = 2; // 8
/// <summary>
/// [Self Collision]
@ -351,12 +378,6 @@ namespace MagicaCloth2
/// </summary>
public static readonly float SelfCollisionPointTriangleAngleCos = math.cos(math.radians(60.0f));
/// <summary>
/// [Self Collision]
/// 交差判定の分割数
/// </summary>
public const int SelfCollisionIntersectDiv = 8;
/// <summary>
/// [Self Collision]
/// Thicknessの最小値(m)
@ -398,6 +419,12 @@ namespace MagicaCloth2
/// BoneSpring利用時のfriction値
/// </summary>
public const float BoneSpringCollisionFriction = 0.5f;
/// <summary>
/// [Culling]
/// 距離カリングの最大距離
/// </summary>
public const float DistanceCullingMaxLength = 100.0f;
}
}
}

View File

@ -15,7 +15,7 @@ namespace MagicaCloth2
public class ClothManager : IManager, IValid
{
// すべて
internal HashSet<ClothProcess> clothSet = new HashSet<ClothProcess>();
internal HashSet<ClothProcess> clothSet = new HashSet<ClothProcess>(256);
// BoneCloth,BoneSpring
internal HashSet<ClothProcess> boneClothSet = new HashSet<ClothProcess>();
@ -50,6 +50,7 @@ namespace MagicaCloth2
// 更新処理
MagicaManager.afterEarlyUpdateDelegate -= OnEarlyClothUpdate;
MagicaManager.firstPreUpdateDelegate -= OnFirstPreUpdate;
MagicaManager.afterLateUpdateDelegate -= OnAfterLateUpdate;
MagicaManager.beforeLateUpdateDelegate -= OnBeforeLateUpdate;
}
@ -69,6 +70,7 @@ namespace MagicaCloth2
// 更新処理
MagicaManager.afterEarlyUpdateDelegate += OnEarlyClothUpdate;
MagicaManager.firstPreUpdateDelegate += OnFirstPreUpdate;
MagicaManager.afterLateUpdateDelegate += OnAfterLateUpdate;
MagicaManager.beforeLateUpdateDelegate += OnBeforeLateUpdate;
@ -118,6 +120,9 @@ namespace MagicaCloth2
break;
}
// チームマネージャの作業バッファへ登録
MagicaManager.Team.comp2TeamIdMap.Add(cprocess.cloth.GetInstanceID(), teamId);
return teamId;
}
@ -140,11 +145,15 @@ namespace MagicaCloth2
/// </summary>
void OnEarlyClothUpdate()
{
if (MagicaManager.Team.ActiveTeamCount > 0)
//Debug.Log($"OnEarlyClothUpdate. F:{Time.frameCount}");
if (MagicaManager.Team.TrueTeamCount > 0) // カリング判定があるのでDisableチームもまわす必要がある
{
//Debug.Log($"TransformRestoreUpdate. F:{Time.frameCount}");
// チームカリング更新
MagicaManager.Team.TeamCullingUpdate();
// カメラカリング更新
if (MagicaManager.Team.ActiveTeamCount > 0)
{
// この更新は次のTransform復元の前に行う必要がある
MagicaManager.Team.CameraCullingPostProcess();
}
// BoneClothのTransform復元更新
ClearMasterJob();
@ -153,6 +162,25 @@ namespace MagicaCloth2
}
}
/// <summary>
/// PreUpdate開始時に実行される更新処理
/// </summary>
void OnFirstPreUpdate()
{
//Debug.Log($"OnFirstPreUpdate. F:{Time.frameCount}");
if (MagicaManager.Team.TrueTeamCount > 0) // カリング判定があるのでDisableチームもまわす必要がある
{
//Debug.Log($"existFixedTeam:{MagicaManager.Bone.existFixedTeam.Value}");
// FixedUpdateが回かつFixedTeamが存在する場合のみ
if (MagicaManager.Time.FixedUpdateCount == 0 && MagicaManager.Bone.existFixedTeam.Value)
{
ClearMasterJob();
masterJob = MagicaManager.Bone.RestoreBaseTransform(masterJob);
CompleteMasterJob();
}
}
}
void OnBeforeLateUpdate()
{
if (MagicaManager.Time.updateLocation == TimeManager.UpdateLocation.BeforeLateUpdate)
@ -166,7 +194,9 @@ namespace MagicaCloth2
}
//=========================================================================================
static readonly ProfilerMarker startClothUpdateMainProfiler = new ProfilerMarker("StartClothUpdate.Main");
static readonly ProfilerMarker startClothUpdateTimeProfiler = new ProfilerMarker("StartClothUpdate.Time");
static readonly ProfilerMarker startClothUpdateTeamProfiler = new ProfilerMarker("StartClothUpdate.Team");
static readonly ProfilerMarker startClothUpdatePrePareProfiler = new ProfilerMarker("StartClothUpdate.Prepare");
static readonly ProfilerMarker startClothUpdateScheduleProfiler = new ProfilerMarker("StartClothUpdate.Schedule");
/// <summary>
@ -177,13 +207,16 @@ namespace MagicaCloth2
if (MagicaManager.IsPlaying() == false)
return;
// ■コンポーネント0なら終了
var tm = MagicaManager.Team;
if (tm.TrueTeamCount == 0)
return;
//-----------------------------------------------------------------
// シミュレーション開始イベント
MagicaManager.OnPreSimulation?.Invoke();
//-----------------------------------------------------------------
var tm = MagicaManager.Team;
var vm = MagicaManager.VMesh;
var sm = MagicaManager.Simulation;
var bm = MagicaManager.Bone;
var wm = MagicaManager.Wind;
@ -192,22 +225,21 @@ namespace MagicaCloth2
//Develop.DebugLog($"StartClothUpdate. F:{Time.frameCount}, dtime:{Time.deltaTime}, stime:{Time.smoothDeltaTime}");
//-----------------------------------------------------------------
startClothUpdateMainProfiler.Begin();
// ■時間マネージャ更新
startClothUpdateTimeProfiler.Begin();
MagicaManager.Time.FrameUpdate();
startClothUpdateTimeProfiler.End();
// ■常に実行するチーム更新
startClothUpdateTeamProfiler.Begin();
tm.AlwaysTeamUpdate();
startClothUpdateTeamProfiler.End();
// ■ここで実行チーム数が0ならば終了
if (tm.ActiveTeamCount == 0)
{
startClothUpdateMainProfiler.End();
return;
}
int maxUpdateCount = tm.maxUpdateCount.Value;
//Debug.Log($"maxUpdateCount:{maxUpdateCount}");
startClothUpdatePrePareProfiler.Begin();
// ■常に実行する風ゾーン更新
wm.AlwaysWindUpdate();
@ -215,106 +247,32 @@ namespace MagicaCloth2
// ■作業バッファ更新
sm.WorkBufferUpdate();
startClothUpdateMainProfiler.End();
startClothUpdatePrePareProfiler.End();
//-----------------------------------------------------------------
#if true
startClothUpdateScheduleProfiler.Begin();
// マスタージョブ初期化
ClearMasterJob();
// ■トランスフォーム情報の読み込み
masterJob = bm.ReadTransform(masterJob);
masterJob = bm.ReadTransformSchedule(masterJob);
// ■プロキシメッシュをスキニングし基本姿勢を求める
masterJob = vm.PreProxyMeshUpdate(masterJob);
//-----------------------------------------------------------------
// チームのセンター姿勢の決定と慣性用の移動量計算
masterJob = tm.CalcCenterAndInertiaAndWind(masterJob);
// パーティクルリセットの適用
masterJob = sm.PreSimulationUpdate(masterJob);
// ■コライダーのローカル姿勢を求める
masterJob = MagicaManager.Collider.PreSimulationUpdate(masterJob);
//-----------------------------------------------------------------
// ■クロスシミュレーション実行
// ステップ実行
for (int i = 0; i < maxUpdateCount; i++)
{
masterJob = sm.SimulationStepUpdate(maxUpdateCount, i, masterJob);
}
//-----------------------------------------------------------------
// 表示位置の決定
masterJob = sm.CalcDisplayPosition(masterJob);
//-----------------------------------------------------------------
// ■クロスシミュレーション後の頂点姿勢計算
// プロキシメッシュの頂点から法線接線を求め姿勢を確定させる
// ラインがある場合はベースラインごとに姿勢を整える
// BoneClothの場合は頂点姿勢を連動するトランスフォームデータにコピーする
masterJob = vm.PostProxyMeshUpdate(masterJob);
// マッピングメッシュ
int mappingCount = tm.MappingCount;
if (mappingCount > 0)
{
// マッピングメッシュ頂点姿勢をプロキシメッシュからスキニングし求める
// マッピングメッシュのローカル空間に座標変換する
masterJob = vm.PostMappingMeshUpdate(masterJob);
// レンダーデータへ反映する
foreach (var cprocess in meshClothSet)
{
if (cprocess == null || cprocess.IsValid() == false || cprocess.IsEnable == false)
continue;
// カリングによる非表示中ならば書き込まない
if (cprocess.IsCullingInvisible())
continue;
int cnt = cprocess.renderMeshInfoList.Count;
for (int i = 0; i < cnt; i++)
{
var info = cprocess.renderMeshInfoList[i];
var renderData = MagicaManager.Render.GetRendererData(info.renderHandle);
// Position/Normal書き込み
masterJob = renderData.UpdatePositionNormal(info.mappingChunk, masterJob);
// BoneWeight書き込み
if (renderData.ChangeCustomMesh)
{
masterJob = renderData.UpdateBoneWeight(info.mappingChunk, masterJob);
}
}
}
}
//-----------------------------------------------------------------
// ■BoneClothのTransformへの書き込み
masterJob = bm.WriteTransform(masterJob);
//-----------------------------------------------------------------
// ■コライダー更新後処理
masterJob = MagicaManager.Collider.PostSimulationUpdate(masterJob);
// ■チーム更新後処理
masterJob = tm.PostTeamUpdate(masterJob);
// ■シミュレーションジョブ
masterJob = sm.ClothSimulationSchedule(masterJob);
startClothUpdateScheduleProfiler.End();
//-----------------------------------------------------------------
//JobHandle.ScheduleBatchedJobs();
//-----------------------------------------------------------------
// ジョブを即実行
//JobHandle.ScheduleBatchedJobs();
// ■ジョブ完了待ちの間に行う処理
// カメラカリングの準備
tm.CameraCullingPreProcess();
//-----------------------------------------------------------------
// ■現在は即時実行のためここでジョブの完了待ちを行う
CompleteMasterJob();
#endif
//-----------------------------------------------------------------
// シミュレーション終了イベント

View File

@ -40,6 +40,7 @@ namespace MagicaCloth2
public static SimulationManager Simulation => managers?[6] as SimulationManager;
public static ColliderManager Collider => managers?[7] as ColliderManager;
public static WindManager Wind => managers?[8] as WindManager;
public static PreBuildManager PreBuild => managers?[9] as PreBuildManager;
//=========================================================================================
// player loop delegate
@ -55,6 +56,11 @@ namespace MagicaCloth2
/// </summary>
public static UpdateMethod afterFixedUpdateDelegate;
/// <summary>
/// PreUpdate()の開始直後
/// </summary>
public static UpdateMethod firstPreUpdateDelegate;
/// <summary>
/// Update()の後
/// </summary>
@ -91,8 +97,6 @@ namespace MagicaCloth2
//=========================================================================================
static volatile bool isPlaying = false;
//static bool isValid = false;
//=========================================================================================
/// <summary>
/// Reload Domain 対策
@ -122,6 +126,7 @@ namespace MagicaCloth2
managers.Add(new SimulationManager()); // [6]
managers.Add(new ColliderManager()); // [7]
managers.Add(new WindManager()); // [8]
managers.Add(new PreBuildManager()); // [9]
foreach (var manager in managers)
manager.Initialize();
@ -321,7 +326,7 @@ namespace MagicaCloth2
type = typeof(MagicaManager),
updateDelegate = () => afterEarlyUpdateDelegate?.Invoke()
};
AddPlayerLoop(afterEarlyUpdate, ref playerLoop, "EarlyUpdate", string.Empty, last: true);
AddPlayerLoop(afterEarlyUpdate, ref playerLoop, "EarlyUpdate", string.Empty, firstLast: 1);
// after fixed update
// FixedUpdate()の後
@ -335,6 +340,17 @@ namespace MagicaCloth2
};
AddPlayerLoop(afterFixedUpdate, ref playerLoop, "FixedUpdate", "ScriptRunBehaviourFixedUpdate");
// first pre update
PlayerLoopSystem firstPreUpdate = new PlayerLoopSystem()
{
type = typeof(MagicaManager),
updateDelegate = () =>
{
firstPreUpdateDelegate?.Invoke();
}
};
AddPlayerLoop(firstPreUpdate, ref playerLoop, "PreUpdate", string.Empty, firstLast: -1);
// after update
// Update()の後
PlayerLoopSystem afterUpdate = new PlayerLoopSystem()
@ -403,13 +419,18 @@ namespace MagicaCloth2
/// <param name="playerLoop"></param>
/// <param name="categoryName"></param>
/// <param name="systemName"></param>
static void AddPlayerLoop(PlayerLoopSystem method, ref PlayerLoopSystem playerLoop, string categoryName, string systemName, bool last = false, bool before = false)
static void AddPlayerLoop(PlayerLoopSystem method, ref PlayerLoopSystem playerLoop, string categoryName, string systemName, int firstLast = 0, bool before = false)
{
int sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == categoryName);
PlayerLoopSystem category = playerLoop.subSystemList[sysIndex];
var systemList = new List<PlayerLoopSystem>(category.subSystemList);
if (last)
if (firstLast < 0)
{
// 最初に追加
systemList.Insert(0, method);
}
else if (firstLast > 0)
{
// 最後に追加
systemList.Add(method);

View File

@ -33,7 +33,6 @@ namespace MagicaCloth2
{
if (IsPlaying())
{
//Team.globalTimeScale = Mathf.Clamp01(timeScale);
Time.GlobalTimeScale = Mathf.Clamp01(timeScale);
}
}
@ -47,7 +46,6 @@ namespace MagicaCloth2
{
if (IsPlaying())
{
//return Team.globalTimeScale;
return Time.GlobalTimeScale;
}
else
@ -153,5 +151,74 @@ namespace MagicaCloth2
return TimeManager.UpdateLocation.AfterLateUpdate;
}
}
/// <summary>
/// 未使用のデータをすべて解放します
/// Free all unused data.
/// - Unused PreBuild data
/// </summary>
public static void UnloadUnusedData()
{
if (IsPlaying())
{
PreBuild.UnloadUnusedData();
}
}
/// <summary>
/// MonoBehaviourでのMagicaClothの初期化場所
/// MagicaCloth initialization location in MonoBehaviour.
/// </summary>
public enum InitializationLocation
{
/// <summary>
/// Initialize with MonoBehaviour.Start().
/// (Default)
/// </summary>
Start = 0,
/// <summary>
/// Initialize with MonoBehaviour.Awake().
/// </summary>
Awake = 1,
}
internal static InitializationLocation initializationLocation = InitializationLocation.Start;
/// <summary>
/// MonoBehaviourでのMagicaClothの初期化場所を設定する
/// Setting MagicaCloth initialization location in MonoBehaviour.
/// </summary>
/// <param name="initLocation"></param>
public static void SetInitializationLocation(InitializationLocation initLocation)
{
initializationLocation = initLocation;
}
/// <summary>
/// 分割ジョブを適用するプロキシメッシュの頂点数を設定する
/// Sets the number of vertices for the proxy mesh to which the split job will be applied.
/// </summary>
/// <param name="vertexCount"></param>
public static void SetSplitProxyMeshVertexCount(int vertexCount)
{
if (IsPlaying())
Simulation.splitProxyMeshVertexCount = vertexCount;
}
/// <summary>
/// 分割ジョブを適用するプロキシメッシュの頂点数を取得する
/// Gets the vertex count of the proxy mesh to which the split job is applied.
/// </summary>
/// <returns></returns>
public static int GetSplitProxyMeshVertexCount()
{
if (IsPlaying())
return Simulation.splitProxyMeshVertexCount;
else
{
Develop.LogError("MagicaManager is not starting!");
return 0;
}
}
}
}

View File

@ -15,8 +15,8 @@ namespace MagicaCloth2
public enum RefreshMode
{
/// <summary>
/// コンポーネント生成時に1度だけ送信する
/// Send only once when the component is created.
/// コンポーネントのAwake()で1度だけ送信する
/// Send once at the component's Awake()
/// </summary>
OnAwake = 0,
@ -25,6 +25,18 @@ namespace MagicaCloth2
/// Send content every frame.
/// </summary>
EveryFrame = 1,
/// <summary>
/// コンポーネントのStart()で一度だけ送信する
/// Send once at the component's Start().
/// </summary>
OnStart = 2,
/// <summary>
/// コンポーネントは何もしません。送信には手動でRefresh()を呼ぶ必要があります
/// The component does nothing. You must manually call Refresh() to submit.
/// </summary>
Manual = 3,
}
/// <summary>
@ -64,6 +76,12 @@ namespace MagicaCloth2
[Range(Define.System.MaxSimulationCountPerFrame_Low, Define.System.MaxSimulationCountPerFrame_Hi)]
public int maxSimulationCountPerFrame = Define.System.DefaultMaxSimulationCountPerFrame;
/// <summary>
/// MonoBehaviourでのMagicaClothの初期化場所。
/// MagicaCloth initialization location in MonoBehaviour.
/// </summary>
public MagicaManager.InitializationLocation initializationLocation = MagicaManager.InitializationLocation.Start;
/// <summary>
/// シミュレーションの更新場所
/// BeforeLateUpdate : LateUpdate()の前に実行します。これはUnity 2D Animationで利用する場合に必要です。
@ -75,6 +93,27 @@ namespace MagicaCloth2
/// </summary>
public TimeManager.UpdateLocation updateLocation = TimeManager.UpdateLocation.AfterLateUpdate;
/// <summary>
/// PlayerLoopの監視
/// MagicaClothのシステムはUnityのPlayerLoopに登録することで動作します
/// この登録が他の外部アセットにより上書きされてしまうと、MagicaClothのシステムが停止してしまいます
/// このフラグを有効にすると、PlayerLoopを監視して、上書きされていた場合は再度システムを登録するようになります
///
/// PlayerLoop monitoring.
/// The MagicaCloth system works by registering it in Unity's PlayerLoop.
/// If this registration is overwritten by other external assets, the MagicaCloth system will stop working.
/// When this flag is enabled, the PlayerLoop will be monitored, and the system will be registered again if it has been overwritten.
/// </summary>
public bool monitorPlayerLoop = false;
/// <summary>
/// ジョブ分割を適用するプロキシメッシュの頂点数
///
/// The number of proxy mesh vertices to apply job splitting to.
/// </summary>
[Min(0)]
public int splitProxyMeshVertexCount = Define.System.SplitProxyMeshVertexCount;
//=========================================================================================
public void Awake()
{
@ -82,6 +121,12 @@ namespace MagicaCloth2
Refresh();
}
public void Start()
{
if (refreshMode == RefreshMode.OnStart)
Refresh();
}
public void Update()
{
if (refreshMode == RefreshMode.EveryFrame)
@ -108,7 +153,12 @@ namespace MagicaCloth2
MagicaManager.SetSimulationFrequency(simulationFrequency);
MagicaManager.SetMaxSimulationCountPerFrame(maxSimulationCountPerFrame);
MagicaManager.SetInitializationLocation(initializationLocation);
MagicaManager.SetUpdateLocation(updateLocation);
MagicaManager.SetSplitProxyMeshVertexCount(splitProxyMeshVertexCount);
if (monitorPlayerLoop)
MagicaManager.InitCustomGameLoop();
}
else
{

View File

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
executionOrder: -50
icon: {fileID: 2800000, guid: 4b0d1adb21ff82e4e8e9ea02f486a85f, type: 3}
userData:
assetBundleName:

View File

@ -5,10 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
@ -37,6 +34,7 @@ namespace MagicaCloth2
//=========================================================================================
// セットアップデータ
internal RenderSetupData setupData;
internal RenderSetupData.UniqueSerializationData preBuildUniqueSerializeData;
internal string Name => setupData?.name ?? "(empty)";
@ -44,40 +42,27 @@ namespace MagicaCloth2
internal bool HasBoneWeight => setupData?.hasBoneWeight ?? false;
//=========================================================================================
// カスタムメッシュ情報
Mesh customMesh;
NativeArray<Vector3> localPositions;
NativeArray<Vector3> localNormals;
NativeArray<BoneWeight> boneWeights;
BoneWeight centerBoneWeight;
// オリジナル情報
internal Mesh originalMesh { get; private set; }
private Renderer renderer;
private SkinnedMeshRenderer skinnedMeshRendere;
private MeshFilter meshFilter;
internal List<Transform> transformList { get; private set; }
internal Mesh customMesh { get; private set; }
/// <summary>
/// カスタムメッシュの使用フラグ
/// </summary>
public bool UseCustomMesh { get; private set; }
/// <summary>
/// カスタムメッシュの変更フラグ
/// </summary>
public bool ChangeCustomMesh { get; private set; }
public bool ChangePositionNormal { get; private set; }
public bool ChangeBoneWeight { get; private set; }
// RenderDataWorkバッファへのインデックス(RenderManagerが管理)
internal int renderDataWorkIndex { get; private set; } = -1;
//=========================================================================================
public void Dispose()
{
// オリジナルメッシュに戻す
SwapOriginalMesh();
SwapOriginalMesh(null);
setupData?.Dispose();
preBuildUniqueSerializeData = null;
if (localPositions.IsCreated)
localPositions.Dispose();
if (localNormals.IsCreated)
localNormals.Dispose();
if (boneWeights.IsCreated)
boneWeights.Dispose();
MagicaManager.Render.RemoveRenderDataWork(renderDataWorkIndex);
if (customMesh)
GameObject.Destroy(customMesh);
@ -98,17 +83,42 @@ namespace MagicaCloth2
/// この処理はスレッド化できないので少し負荷がかかるが即時実行する
/// </summary>
/// <param name="ren"></param>
internal void Initialize(Renderer ren)
internal void Initialize(
Renderer ren,
RenderSetupData referenceSetupData,
RenderSetupData.UniqueSerializationData referencePreBuildUniqueSetupData,
RenderSetupSerializeData referenceInitSetupData
)
{
Debug.Assert(ren);
// セットアップデータ作成
setupData = new RenderSetupData(ren);
// PreBuildでは外部から受け渡される
if (referenceSetupData != null && referencePreBuildUniqueSetupData != null)
{
setupData = referenceSetupData;
preBuildUniqueSerializeData = referencePreBuildUniqueSetupData;
// センタートランスフォーム用ボーンウエイト
centerBoneWeight = new BoneWeight();
centerBoneWeight.boneIndex0 = setupData.renderTransformIndex;
centerBoneWeight.weight0 = 1.0f;
originalMesh = preBuildUniqueSerializeData.originalMesh;
renderer = preBuildUniqueSerializeData.renderer;
skinnedMeshRendere = preBuildUniqueSerializeData.skinRenderer;
meshFilter = preBuildUniqueSerializeData.meshFilter;
transformList = preBuildUniqueSerializeData.transformList;
}
else
{
setupData = new RenderSetupData(referenceInitSetupData, ren);
preBuildUniqueSerializeData = null;
originalMesh = setupData.originalMesh;
renderer = setupData.renderer;
skinnedMeshRendere = setupData.skinRenderer;
meshFilter = setupData.meshFilter;
transformList = setupData.transformList;
}
// レンダーデータワークを確保
renderDataWorkIndex = MagicaManager.Render.AddRenderDataWork(this);
}
internal ResultCode Result => setupData?.result ?? ResultCode.None;
@ -127,41 +137,39 @@ namespace MagicaCloth2
}
//=========================================================================================
void SwapCustomMesh()
void SwapCustomMesh(ClothProcess process)
{
Debug.Assert(setupData != null);
if (setupData.IsFaild())
return;
if (setupData.originalMesh == null)
if (originalMesh == null)
return;
if (MagicaManager.Render.IsSetRenderDataWorkFlag(renderDataWorkIndex, RenderManager.RenderDataFlag_UseCustomMesh))
return;
// カスタムメッシュの作成
if (customMesh == null)
{
Debug.Assert(setupData.originalMesh);
//Debug.Assert(setupData.originalMesh);
// クローン作成
customMesh = GameObject.Instantiate(setupData.originalMesh);
customMesh = GameObject.Instantiate(originalMesh);
customMesh.MarkDynamic();
// 作業配列
int vertexCount = setupData.vertexCount;
localPositions = new NativeArray<Vector3>(vertexCount, Allocator.Persistent);
localNormals = new NativeArray<Vector3>(vertexCount, Allocator.Persistent);
if (HasBoneWeight)
boneWeights = new NativeArray<BoneWeight>(vertexCount, Allocator.Persistent);
// bind pose
if (HasBoneWeight)
{
int transformCount = setupData.TransformCount;
int transformCount = preBuildUniqueSerializeData != null ? preBuildUniqueSerializeData.transformList.Count : setupData.TransformCount;
var bindPoseList = new List<Matrix4x4>(transformCount);
bindPoseList.AddRange(setupData.bindPoseList);
// rootBone/skinning bones
while (bindPoseList.Count < transformCount)
bindPoseList.Add(Matrix4x4.identity);
customMesh.bindposes = bindPoseList.ToArray();
// スキニング用ボーンを書き換える
// このリストにはオリジナルのスキニングボーン+レンダラーのトランスフォームが含まれている
skinnedMeshRendere.bones = transformList.ToArray();
}
}
@ -170,45 +178,76 @@ namespace MagicaCloth2
// カスタムメッシュに表示切り替え
SetMesh(customMesh);
MagicaManager.Render.SetBitsRenderDataWorkFlag(renderDataWorkIndex, RenderManager.RenderDataFlag_UseCustomMesh, true);
// スキニング用ボーンを書き換える
if (HasBoneWeight)
{
// このリストにはオリジナルのスキニングボーン+レンダラーのトランスフォームが含まれている
setupData.skinRenderer.bones = setupData.transformList.ToArray();
}
UseCustomMesh = true;
// Event
if (process != null)
process.cloth?.OnRendererMeshChange?.Invoke(process.cloth, renderer, true);
}
void ResetCustomMeshWorkData()
{
var rm = MagicaManager.Render;
ref var wdata = ref rm.GetRenderDataWorkRef(renderDataWorkIndex);
int vcnt = setupData.vertexCount;
// オリジナルデータをコピーする
var meshData = setupData.meshDataArray[0];
meshData.GetVertices(localPositions);
meshData.GetNormals(localNormals);
if (HasBoneWeight)
if (setupData.HasMeshDataArray)
{
var meshData = setupData.meshDataArray[0];
using var localPositions = new NativeArray<Vector3>(vcnt, Allocator.TempJob);
using var localNormals = new NativeArray<Vector3>(vcnt, Allocator.TempJob);
meshData.GetVertices(localPositions);
meshData.GetNormals(localNormals);
rm.renderMeshPositions.CopyFrom(localPositions, wdata.renderMeshPositionAndNormalChunk.startIndex, vcnt);
rm.renderMeshNormals.CopyFrom(localNormals, wdata.renderMeshPositionAndNormalChunk.startIndex, vcnt);
if (wdata.HasMeshTangent)
{
using var localTangents = new NativeArray<Vector4>(vcnt, Allocator.TempJob);
meshData.GetTangents(localTangents);
rm.renderMeshTangents.CopyFrom(localTangents, wdata.renderMeshTangentChunk.startIndex, vcnt);
wdata.flag.SetBits(RenderManager.RenderDataFlag_HasTangent, true); // 最終的な接線あり
}
}
else
{
rm.renderMeshPositions.CopyFrom(setupData.localPositions, wdata.renderMeshPositionAndNormalChunk.startIndex, vcnt);
rm.renderMeshNormals.CopyFrom(setupData.localNormals, wdata.renderMeshPositionAndNormalChunk.startIndex, vcnt);
if (wdata.HasMeshTangent && setupData.HasTangent)
{
rm.renderMeshTangents.CopyFrom(setupData.localTangents, wdata.renderMeshTangentChunk.startIndex, vcnt);
wdata.flag.SetBits(RenderManager.RenderDataFlag_HasTangent, true); // 最終的な接線あり
}
}
if (HasBoneWeight && wdata.HasBoneWeight)
{
using var boneWeights = new NativeArray<BoneWeight>(vcnt, Allocator.TempJob);
setupData.GetBoneWeightsRun(boneWeights);
rm.renderMeshBoneWeights.CopyFrom(boneWeights, wdata.renderMeshBoneWeightChunk.startIndex, vcnt);
}
}
/// <summary>
/// オリジナルメッシュに戻す
/// </summary>
void SwapOriginalMesh()
void SwapOriginalMesh(ClothProcess process)
{
if (UseCustomMesh && setupData != null)
{
SetMesh(setupData.originalMesh);
var rm = MagicaManager.Render;
if (setupData.skinRenderer != null)
if (rm.IsSetRenderDataWorkFlag(renderDataWorkIndex, RenderManager.RenderDataFlag_UseCustomMesh) && setupData != null)
{
SetMesh(originalMesh);
if (skinnedMeshRendere != null)
{
setupData.skinRenderer.bones = setupData.transformList.ToArray();
skinnedMeshRendere.bones = transformList.ToArray();
}
}
rm.SetBitsRenderDataWorkFlag(renderDataWorkIndex, RenderManager.RenderDataFlag_UseCustomMesh, false);
UseCustomMesh = false;
// Event
if (process != null)
process.cloth?.OnRendererMeshChange?.Invoke(process.cloth, renderer, false);
}
/// <summary>
@ -222,13 +261,13 @@ namespace MagicaCloth2
if (setupData != null)
{
if (setupData.meshFilter != null)
if (meshFilter != null)
{
setupData.meshFilter.mesh = mesh;
meshFilter.mesh = mesh;
}
else if (setupData.skinRenderer != null)
else if (skinnedMeshRendere != null)
{
setupData.skinRenderer.sharedMesh = mesh;
skinnedMeshRendere.sharedMesh = mesh;
}
}
}
@ -251,7 +290,7 @@ namespace MagicaCloth2
/// </summary>
public void EndUse(ClothProcess cprocess)
{
Debug.Assert(useProcessSet.Count > 0);
//Debug.Assert(useProcessSet.Count > 0);
UpdateUse(cprocess, -1);
}
@ -263,36 +302,62 @@ namespace MagicaCloth2
}
else if (add < 0)
{
Debug.Assert(useProcessSet.Count > 0);
useProcessSet.Remove(cprocess);
//Debug.Assert(useProcessSet.Count > 0);
if (useProcessSet.Contains(cprocess))
useProcessSet.Remove(cprocess);
else
return;
}
// Invisible状態
bool invisible = useProcessSet.Any(x => x.IsCullingInvisible() && x.IsCullingKeep() == false);
bool invisible = useProcessSet.Any(x => (x.IsCameraCullingInvisible() && x.IsCameraCullingKeep() == false) || x.IsDistanceCullingInvisible());
// 状態変更
bool modifyBoneWeight = false;
if (invisible || useProcessSet.Count == 0)
{
// 利用停止
// オリジナルメッシュに切り替え
SwapOriginalMesh();
ChangeCustomMesh = true;
SwapOriginalMesh(cprocess);
}
else if (useProcessSet.Count == 1)
else if (add == 0 && useProcessSet.Count > 0)
{
// カリング復帰
// カスタムメッシュに切り替え、および作業バッファ作成
// すでにカスタムメッシュが存在する場合は作業バッファのみ再初期化する
SwapCustomMesh(cprocess);
modifyBoneWeight = true;
}
else if (add > 0 && useProcessSet.Count == 1)
{
// 利用開始
// カスタムメッシュに切り替え、および作業バッファ作成
// すでにカスタムメッシュが存在する場合は作業バッファのみ最初期化する
SwapCustomMesh();
ChangeCustomMesh = true;
// すでにカスタムメッシュが存在する場合は作業バッファのみ初期化する
SwapCustomMesh(cprocess);
modifyBoneWeight = true;
}
else if (add != 0)
{
// 複数から利用されている状態で1つが停止した。
// バッファを最初期化する
ResetCustomMeshWorkData();
ChangeCustomMesh = true;
modifyBoneWeight = true;
}
// BoneWeight変更を連動するマッピングに指示する
if (modifyBoneWeight)
{
ref var wdata = ref MagicaManager.Render.GetRenderDataWorkRef(renderDataWorkIndex);
int mcnt = wdata.mappingDataIndexList.Length;
for (int i = 0; i < mcnt; i++)
{
int mindex = wdata.mappingDataIndexList[i];
ref var mdata = ref MagicaManager.Team.GetMappingDataRef(mindex);
mdata.flag.SetBits(TeamManager.MappingDataFlag_ModifyBoneWeight, true);
}
}
//Debug.Log($"add:{add}, invisible:{invisible}, useCount:{useProcessSet.Count}, ModifyBoneWeight = {flag.IsSet(Flag_ModifyBoneWeight)}");
}
//=========================================================================================
@ -312,177 +377,39 @@ namespace MagicaCloth2
//=========================================================================================
internal void WriteMesh()
{
if (UseCustomMesh == false || useProcessSet.Count == 0)
var rm = MagicaManager.Render;
ref var wdata = ref rm.GetRenderDataWorkRef(renderDataWorkIndex);
if (wdata.UseCustomMesh == false || useProcessSet.Count == 0)
return;
// 書き込み停止中ならスキップ
if (isSkipWriting)
return;
//Debug.Log($"WriteMesh [{Name}] ChangePositionNormal:{flag.IsSet(Flag_ChangePositionNormal)}, ChangeBoneWeight:{flag.IsSet(Flag_ChangeBoneWeight)}");
// メッシュに反映
if (ChangePositionNormal)
if (wdata.flag.IsSet(RenderManager.RenderDataFlag_WritePositionNormal))
{
customMesh.SetVertices(localPositions);
customMesh.SetNormals(localNormals);
customMesh.SetVertices(rm.renderMeshPositions.GetNativeArray(), wdata.renderMeshPositionAndNormalChunk.startIndex, wdata.renderMeshPositionAndNormalChunk.dataLength);
customMesh.SetNormals(rm.renderMeshNormals.GetNativeArray(), wdata.renderMeshPositionAndNormalChunk.startIndex, wdata.renderMeshPositionAndNormalChunk.dataLength);
wdata.flag.SetBits(RenderManager.RenderDataFlag_WritePositionNormal, false);
//Debug.Log($"[{customMesh.name}] Write Position+Normal");
}
if (ChangeBoneWeight && HasBoneWeight)
if (wdata.flag.IsSet(RenderManager.RenderDataFlag_WriteTangent))
{
customMesh.boneWeights = boneWeights.ToArray();
customMesh.SetTangents(rm.renderMeshTangents.GetNativeArray(), wdata.renderMeshTangentChunk.startIndex, wdata.renderMeshTangentChunk.dataLength);
wdata.flag.SetBits(RenderManager.RenderDataFlag_WriteTangent, false);
//Debug.Log($"[{customMesh.name}] Write Tangent");
}
// 完了
ChangeCustomMesh = false;
ChangePositionNormal = false;
ChangeBoneWeight = false;
}
//=========================================================================================
/// <summary>
/// メッシュの位置法線を更新
/// </summary>
/// <param name="mappingChunk"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
internal JobHandle UpdatePositionNormal(DataChunk mappingChunk, JobHandle jobHandle = default)
{
if (UseCustomMesh == false)
return jobHandle;
var vm = MagicaManager.VMesh;
// 座標・法線の差分書き換え
var job = new UpdatePositionNormalJob2()
if (wdata.flag.IsSet(RenderManager.RenderDataFlag_WriteBoneWeight))
{
startIndex = mappingChunk.startIndex,
meshLocalPositions = localPositions.Reinterpret<float3>(),
meshLocalNormals = localNormals.Reinterpret<float3>(),
mappingReferenceIndices = vm.mappingReferenceIndices.GetNativeArray(),
mappingAttributes = vm.mappingAttributes.GetNativeArray(),
mappingPositions = vm.mappingPositions.GetNativeArray(),
mappingNormals = vm.mappingNormals.GetNativeArray(),
};
jobHandle = job.Schedule(mappingChunk.dataLength, 32, jobHandle);
ChangePositionNormal = true;
return jobHandle;
}
[BurstCompile]
struct UpdatePositionNormalJob2 : IJobParallelFor
{
public int startIndex;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<float3> meshLocalPositions;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<float3> meshLocalNormals;
// mapping mesh
[Unity.Collections.ReadOnly]
public NativeArray<int> mappingReferenceIndices;
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> mappingAttributes;
[Unity.Collections.ReadOnly]
public NativeArray<float3> mappingPositions;
[Unity.Collections.ReadOnly]
public NativeArray<float3> mappingNormals;
public void Execute(int index)
{
int vindex = index + startIndex;
// 無効頂点なら書き込まない
var attr = mappingAttributes[vindex];
if (attr.IsInvalid())
return;
// 固定も書き込まない(todo:一旦こうする)
if (attr.IsFixed())
return;
// 書き込む頂点インデックス
int windex = mappingReferenceIndices[vindex];
// 座標書き込み
meshLocalPositions[windex] = mappingPositions[vindex];
// 法線書き込み
meshLocalNormals[windex] = mappingNormals[vindex];
}
}
/// <summary>
/// メッシュのボーンウエイト書き込み
/// </summary>
/// <param name="vmesh"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
internal JobHandle UpdateBoneWeight(DataChunk mappingChunk, JobHandle jobHandle = default)
{
if (UseCustomMesh == false)
return jobHandle;
// ボーンウエイトの差分書き換え
if (HasBoneWeight)
{
var vm = MagicaManager.VMesh;
var job = new UpdateBoneWeightJob2()
{
startIndex = mappingChunk.startIndex,
centerBoneWeight = centerBoneWeight,
meshBoneWeights = boneWeights,
mappingReferenceIndices = vm.mappingReferenceIndices.GetNativeArray(),
mappingAttributes = vm.mappingAttributes.GetNativeArray(),
};
jobHandle = job.Schedule(mappingChunk.dataLength, 32, jobHandle);
ChangeBoneWeight = true;
}
return jobHandle;
}
[BurstCompile]
struct UpdateBoneWeightJob2 : IJobParallelFor
{
public int startIndex;
public BoneWeight centerBoneWeight;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<BoneWeight> meshBoneWeights;
// mapping mesh
[Unity.Collections.ReadOnly]
public NativeArray<int> mappingReferenceIndices;
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> mappingAttributes;
public void Execute(int index)
{
int vindex = index + startIndex;
// 無効頂点なら書き込まない
var attr = mappingAttributes[vindex];
if (attr.IsInvalid())
return;
// 固定も書き込まない(todo:一旦こうする)
if (attr.IsFixed())
return;
// 書き込む頂点インデックス
int windex = mappingReferenceIndices[vindex];
// 使用頂点のウエイトはcenterTransform100%で書き込む
meshBoneWeights[windex] = centerBoneWeight;
// BoneWeightはNativeArrayの区間指定ができない
var boneWeightsSlice = new NativeSlice<BoneWeight>(rm.renderMeshBoneWeights.GetNativeArray(), wdata.renderMeshBoneWeightChunk.startIndex, wdata.renderMeshBoneWeightChunk.dataLength);
customMesh.boneWeights = boneWeightsSlice.ToArray();
wdata.flag.SetBits(RenderManager.RenderDataFlag_WriteBoneWeight, false);
//Debug.Log($"[{customMesh.name}] Write BoneWeight");
}
}

View File

@ -3,6 +3,9 @@
// https://magicasoft.jp
using System.Collections.Generic;
using System.Text;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Profiling;
using UnityEngine;
namespace MagicaCloth2
@ -17,6 +20,78 @@ namespace MagicaCloth2
/// </summary>
Dictionary<int, RenderData> renderDataDict = new Dictionary<int, RenderData>();
//=========================================================================================
// ■RenderData
//=========================================================================================
/// <summary>
/// RenderDataの状態フラグ(32bit)
/// </summary>
public const int RenderDataFlag_UseCustomMesh = 0; // カスタムメッシュの利用
public const int RenderDataFlag_WritePositionNormal = 1; // 座標および法線の書き込み
public const int RenderDataFlag_WriteBoneWeight = 2; // ボーンウエイトの書き込み
//public const int RenderDataFlag_ModifyBoneWeight = 3; // ボーンウエイトの変更
public const int RenderDataFlag_HasMeshTangent = 4; // オリジナルメッシュが接線を持っているかどうか
public const int RenderDataFlag_HasTangent = 5; // 最終的に接線情報を持っているかどうか
public const int RenderDataFlag_WriteTangent = 6; // 接線の書き込み
public const int RenderDataFlag_HasSkinnedMesh = 7;
public const int RenderDataFlag_HasBoneWeight = 8;
public struct RenderDataWork : IValid
{
/// <summary>
/// RenderData状態フラグ
/// </summary>
public BitField32 flag;
/// <summary>
/// バッファへの格納先情報
/// </summary>
public DataChunk renderMeshPositionAndNormalChunk;
public DataChunk renderMeshTangentChunk;
public DataChunk renderMeshBoneWeightChunk;
/// <summary>
/// 制御頂点に設定するボーンウエイト
/// </summary>
public BoneWeight centerBoneWeight;
/// <summary>
/// 紐づけられているマッピングメッシュデータへのインデックスリスト
/// </summary>
public FixedList32Bytes<short> mappingDataIndexList;
public bool IsValid()
{
return renderMeshPositionAndNormalChunk.IsValid;
}
public bool UseCustomMesh => flag.IsSet(RenderDataFlag_UseCustomMesh);
public bool HasMeshTangent => flag.IsSet(RenderDataFlag_HasMeshTangent);
public bool HasTangent => flag.IsSet(RenderDataFlag_HasTangent);
public bool HasBoneWeight => flag.IsSet(RenderDataFlag_HasBoneWeight);
public void AddMappingIndex(int mindex)
{
mappingDataIndexList.Add((short)mindex);
}
public void RemoveMappingIndex(int mindex)
{
mappingDataIndexList.MC2RemoveItemAtSwapBack((short)mindex);
}
}
public ExNativeArray<RenderDataWork> renderDataWorkArray;
public int RenderDataWorkCount => renderDataWorkArray?.Count ?? 0;
//=========================================================================================
// ■RenderMesh
//=========================================================================================
public ExNativeArray<float3> renderMeshPositions;
public ExNativeArray<float3> renderMeshNormals;
public ExNativeArray<float4> renderMeshTangents;
public ExNativeArray<BoneWeight> renderMeshBoneWeights;
bool isValid = false;
//=========================================================================================
@ -24,6 +99,15 @@ namespace MagicaCloth2
{
Dispose();
// 作業バッファ
const int capacity = 0;
const bool create = true;
renderDataWorkArray = new ExNativeArray<RenderDataWork>(capacity, create);
renderMeshPositions = new ExNativeArray<float3>(capacity, create);
renderMeshNormals = new ExNativeArray<float3>(capacity, create);
renderMeshTangents = new ExNativeArray<float4>(capacity, create);
renderMeshBoneWeights = new ExNativeArray<BoneWeight>(capacity, create);
// 更新処理
MagicaManager.afterDelayedDelegate += PreRenderingUpdate;
@ -48,6 +132,18 @@ namespace MagicaCloth2
}
renderDataDict.Clear();
// 作業バッファ
renderDataWorkArray?.Dispose();
renderMeshPositions?.Dispose();
renderMeshNormals?.Dispose();
renderMeshTangents?.Dispose();
renderMeshBoneWeights?.Dispose();
renderDataWorkArray = null;
renderMeshPositions = null;
renderMeshNormals = null;
renderMeshBoneWeights = null;
renderMeshTangents = null;
// 更新処理
MagicaManager.afterDelayedDelegate -= PreRenderingUpdate;
}
@ -63,7 +159,12 @@ namespace MagicaCloth2
/// </summary>
/// <param name="ren"></param>
/// <returns></returns>
public int AddRenderer(Renderer ren)
public int AddRenderer(
Renderer ren,
RenderSetupData referenceSetupData,
RenderSetupData.UniqueSerializationData referenceUniqueSetupData,
RenderSetupSerializeData referenceInitSetupData
)
{
if (isValid == false)
return 0;
@ -78,7 +179,7 @@ namespace MagicaCloth2
{
// 新規
var rdata = new RenderData();
rdata.Initialize(ren);
rdata.Initialize(ren, referenceSetupData, referenceUniqueSetupData, referenceInitSetupData);
renderDataDict.Add(handle, rdata);
}
@ -96,8 +197,6 @@ namespace MagicaCloth2
bool delete = false;
//if(renderDataDict.ContainsKey(handle) == )
//Debug.Log($"RemoveRenderer:{handle}");
//Debug.Assert(ren);
Debug.Assert(renderDataDict.ContainsKey(handle));
@ -137,6 +236,92 @@ namespace MagicaCloth2
}
}
//=========================================================================================
public int AddRenderDataWork(RenderData rdata)
{
if (isValid == false)
return -1;
var wdata = new RenderDataWork();
// オリジナルメッシュの接線情報を確認
wdata.flag.SetBits(RenderDataFlag_HasMeshTangent, rdata.originalMesh.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.Tangent));
//Debug.Log($"OriginalMesh[{originalMesh.name}] hasTangent:{originalMesh.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.Tangent)}");
// Flag
wdata.flag.SetBits(RenderDataFlag_HasSkinnedMesh, rdata.HasSkinnedMesh);
wdata.flag.SetBits(RenderDataFlag_HasBoneWeight, rdata.HasBoneWeight);
// センタートランスフォーム用ボーンウエイト
var centerBoneWeight = new BoneWeight();
centerBoneWeight.boneIndex0 = rdata.setupData.renderTransformIndex;
centerBoneWeight.weight0 = 1.0f;
wdata.centerBoneWeight = centerBoneWeight;
// レンダーメッシュ用バッファ確保
int vcnt = rdata.originalMesh.vertexCount;
wdata.renderMeshPositionAndNormalChunk = renderMeshPositions.AddRange(vcnt);
renderMeshNormals.AddRange(vcnt);
if (wdata.HasMeshTangent)
wdata.renderMeshTangentChunk = renderMeshTangents.AddRange(vcnt);
if (wdata.HasBoneWeight)
{
wdata.renderMeshBoneWeightChunk = renderMeshBoneWeights.AddRange(vcnt);
}
// 格納
var c = renderDataWorkArray.Add(wdata);
return c.startIndex;
}
public void RemoveRenderDataWork(int index)
{
if (isValid == false)
return;
if (index < 0)
return;
// バッファ削除
ref var wdata = ref GetRenderDataWorkRef(index);
if (wdata.renderMeshPositionAndNormalChunk.IsValid)
{
renderMeshPositions.Remove(wdata.renderMeshPositionAndNormalChunk);
renderMeshNormals.Remove(wdata.renderMeshPositionAndNormalChunk);
}
if (wdata.renderMeshTangentChunk.IsValid)
{
renderMeshTangents.Remove(wdata.renderMeshTangentChunk);
}
if (wdata.renderMeshBoneWeightChunk.IsValid)
{
renderMeshBoneWeights.Remove(wdata.renderMeshBoneWeightChunk);
}
wdata.renderMeshPositionAndNormalChunk.Clear();
wdata.renderMeshTangentChunk.Clear();
wdata.renderMeshBoneWeightChunk.Clear();
wdata.flag.Clear();
// 削除
renderDataWorkArray.RemoveAndFill(new DataChunk(index));
}
public ref RenderDataWork GetRenderDataWorkRef(int index)
{
return ref renderDataWorkArray.GetRef(index);
}
public bool IsSetRenderDataWorkFlag(int index, int flag)
{
ref var wdata = ref GetRenderDataWorkRef(index);
return wdata.flag.IsSet(flag);
}
public void SetBitsRenderDataWorkFlag(int index, int flag, bool sw)
{
ref var wdata = ref GetRenderDataWorkRef(index);
wdata.flag.SetBits(flag, sw);
}
//=========================================================================================
/// <summary>
/// 有効化
@ -157,14 +342,21 @@ namespace MagicaCloth2
}
//=========================================================================================
static readonly ProfilerMarker writeMeshTimeProfiler = new ProfilerMarker("WriteMesh");
/// <summary>
/// レンダリング前更新
/// </summary>
void PreRenderingUpdate()
{
if (renderDataDict.Count == 0)
return;
// メッシュへの反映
writeMeshTimeProfiler.Begin();
foreach (var rdata in renderDataDict.Values)
rdata?.WriteMesh();
writeMeshTimeProfiler.End();
}
//=========================================================================================
@ -179,7 +371,25 @@ namespace MagicaCloth2
else
{
sb.AppendLine($"Render Manager. Count({renderDataDict.Count})");
sb.AppendLine($" [RenderMeshBuffer]");
sb.AppendLine($" -renderMeshPositions:{renderMeshPositions.ToSummary()}");
sb.AppendLine($" -renderMeshNormals:{renderMeshNormals.ToSummary()}");
sb.AppendLine($" -renderMeshTangents:{renderMeshTangents.ToSummary()}");
sb.AppendLine($" [RenderDataWork]");
sb.AppendLine($" -Count:{renderDataWorkArray.Count}");
if (renderDataWorkArray.Count > 0)
{
for (int j = 0; j < renderDataWorkArray.Count; j++)
{
var wdata = renderDataWorkArray[j];
if (wdata.IsValid() == false)
continue;
sb.AppendLine($" [{j}] MappingListCount:{wdata.mappingDataIndexList.Length}");
}
}
sb.AppendLine($" [RenderData]");
foreach (var kv in renderDataDict)
{
sb.Append(kv.Value.ToString());

View File

@ -7,6 +7,7 @@ using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.Jobs;
@ -19,10 +20,11 @@ namespace MagicaCloth2
/// またこの情報はキャラクターが動き出す前に取得しておく必要がある
/// そのためAwake()などで実行する
/// </summary>
public class RenderSetupData : IDisposable, ITransform
public partial class RenderSetupData : IDisposable, ITransform
{
public ResultCode result;
public string name = string.Empty;
public bool isManaged; // pre-build DeserializeManager管理
// タイプ
public enum SetupType
@ -45,11 +47,17 @@ namespace MagicaCloth2
public Mesh.MeshDataArray meshDataArray; // Jobで利用するためのMeshData
public int skinRootBoneIndex;
public int skinBoneCount;
// MeshDataでは取得できないメッシュ情報
public List<Matrix4x4> bindPoseList;
public NativeArray<byte> bonesPerVertexArray;
public NativeArray<BoneWeight1> boneWeightArray;
// PreBuild時のみ保持する情報.逆にmeshDataArrayは持たない
public NativeArray<Vector3> localPositions;
public NativeArray<Vector3> localNormals;
public NativeArray<Vector4> localTangents;
// Bone ---------------------------------------------------------------
public List<int> rootTransformIdList;
public enum BoneConnectionMode
@ -82,7 +90,7 @@ namespace MagicaCloth2
public List<FixedList512Bytes<int>> transformChildIdList; // 子IDリスト
public NativeArray<float3> transformPositions;
public NativeArray<quaternion> transformRotations;
public NativeArray<float3> transformLocalPositins;
public NativeArray<float3> transformLocalPositions;
public NativeArray<quaternion> transformLocalRotations;
public NativeArray<float3> transformScales;
public NativeArray<quaternion> transformInverseRotations;
@ -95,16 +103,21 @@ namespace MagicaCloth2
public bool IsSuccess() => result.IsSuccess();
public bool IsFaild() => result.IsFaild();
public int TransformCount => transformList?.Count ?? 0;
public bool HasMeshDataArray => meshDataArray.Length > 0;
public bool HasLocalPositions => localPositions.IsCreated;
public bool HasTangent => localTangents.IsCreated && localTangents.Length > 0;
//static readonly ProfilerMarker initProfiler = new ProfilerMarker("Render Setup");
static readonly ProfilerMarker readTransformProfiler = new ProfilerMarker("readTransform");
//=========================================================================================
public RenderSetupData() { }
/// <summary>
/// レンダラーから基本情報を作成する(メインスレッドのみ)
/// タイプはMeshになる
/// </summary>
/// <param name="ren"></param>
public RenderSetupData(Renderer ren)
public RenderSetupData(RenderSetupSerializeData referenceInitSetupData, Renderer ren)
{
//using (initProfiler.Auto())
{
@ -119,6 +132,9 @@ namespace MagicaCloth2
return;
}
// 初期化データの有無
bool useInitData = referenceInitSetupData != null;
name = ren.name;
var sren = ren as SkinnedMeshRenderer;
@ -133,7 +149,36 @@ namespace MagicaCloth2
// 描画の基準トランスフォーム
var renderTransform = ren.transform;
if (sren)
if (useInitData)
{
// 初期化データがある場合はコピーして終わり
skinBoneCount = referenceInitSetupData.skinBoneCount;
skinRootBoneIndex = referenceInitSetupData.skinRootBoneIndex;
renderTransformIndex = referenceInitSetupData.renderTransformIndex;
hasBoneWeight = referenceInitSetupData.hasBoneWeight;
// transformList復元
transformList = new List<Transform>(new Transform[referenceInitSetupData.transformCount]);
int ucnt = referenceInitSetupData.useTransformCount;
for (int i = 0; i < ucnt; i++)
{
int tindex = referenceInitSetupData.useTransformIndexArray[i];
transformList[tindex] = referenceInitSetupData.transformArray[i];
}
// SkinnedMeshRendererかつオリジナルメッシュが存在する場合はSkinnedMeshRenererを復元する
// 実行時キャラクターコピーへの対応
if (sren && referenceInitSetupData.originalMesh && sren.sharedMesh != referenceInitSetupData.originalMesh && skinBoneCount > 0)
{
sren.sharedMesh = referenceInitSetupData.originalMesh;
var newBones = new Transform[skinBoneCount];
transformList.CopyTo(0, newBones, 0, skinBoneCount);
sren.bones = newBones;
sren.rootBone = transformList[skinRootBoneIndex];
//Debug.Log($"★SkinnedMeshRenderer再構成");
}
}
else if (sren)
{
// bones
// このスキニングボーンの取得が特に重くメモリアロケーションも頻発する問題児
@ -188,7 +233,7 @@ namespace MagicaCloth2
}
// トランスフォーム情報の読み取り
ReadTransformInformation(includeChilds: false);
ReadTransformInformation(includeChilds: false, referenceInitSetupData, ren.transform);
// bindpose / weights
if (sren)
@ -207,12 +252,14 @@ namespace MagicaCloth2
// どうもコピーを作らないとダメらしい..
// ※具体的にはメッシュのクローンを作成したときに壊れる
var weightArray = mesh.GetAllBoneWeights();
var perVertexArray = mesh.GetBonesPerVertex();
using var weightArray = mesh.GetAllBoneWeights();
using var perVertexArray = mesh.GetBonesPerVertex();
boneWeightArray = new NativeArray<BoneWeight1>(weightArray, Allocator.Persistent);
bonesPerVertexArray = new NativeArray<byte>(perVertexArray, Allocator.Persistent);
#if UNITY_EDITOR
// 5ボーン以上を利用する頂点ウエイトは警告とする。一応無効となるだけで動くのでエラーにはしない。
// なおこの検証はビルド環境では行わない
int vcnt = mesh.vertexCount;
using var bonesPerVertexResult = new NativeReference<Define.Result>(Allocator.TempJob);
var job = new VertexWeight5BoneCheckJob()
@ -223,6 +270,7 @@ namespace MagicaCloth2
};
job.Run();
result.SetWarning(bonesPerVertexResult.Value);
#endif
}
else
{
@ -276,6 +324,10 @@ namespace MagicaCloth2
}
}
#if UNITY_EDITOR
/// <summary>
/// 5ウエイト以上の検出
/// </summary>
[BurstCompile]
struct VertexWeight5BoneCheckJob : IJob
{
@ -301,6 +353,7 @@ namespace MagicaCloth2
}
}
}
#endif
/// <summary>
/// ルートボーンリストから基本情報を作成する(メインスレッドのみ)
@ -309,6 +362,7 @@ namespace MagicaCloth2
/// <param name="renderTransform"></param>
/// <param name="rootTransforms"></param>
public RenderSetupData(
RenderSetupSerializeData referenceInitSetupData,
SetupType setType,
Transform renderTransform,
List<Transform> rootTransforms,
@ -322,6 +376,8 @@ namespace MagicaCloth2
//using (initProfiler.Auto())
try
{
bool useInitData = referenceInitSetupData != null;
// Boneタイプに設定
setupType = setType;
@ -344,30 +400,47 @@ namespace MagicaCloth2
this.name = name;
// 必要なトランスフォーム情報
var indexDict = new Dictionary<Transform, int>(256);
transformList = new List<Transform>(256);
// root以下をすべて登録する
var stack = new Stack<Transform>(256);
foreach (var t in rootTransforms)
stack.Push(t);
while (stack.Count > 0)
if (useInitData)
{
var t = stack.Pop();
if (indexDict.ContainsKey(t))
continue;
// 初期化データがある場合はコピーして終わり
transformList = new List<Transform>(referenceInitSetupData.transformArray);
skinBoneCount = referenceInitSetupData.skinBoneCount;
renderTransformIndex = referenceInitSetupData.renderTransformIndex;
}
else
{
var indexDict = new Dictionary<Transform, int>(256);
transformList = new List<Transform>(256);
// 登録
int index = transformList.Count;
transformList.Add(t);
indexDict.Add(t, index);
// child
int cnt = t.childCount;
for (int i = 0; i < cnt; i++)
// root以下をすべて登録する
var stack = new Stack<Transform>(256);
foreach (var t in rootTransforms)
stack.Push(t);
while (stack.Count > 0)
{
stack.Push(t.GetChild(i));
var t = stack.Pop();
if (indexDict.ContainsKey(t))
continue;
// 登録
int index = transformList.Count;
transformList.Add(t);
indexDict.Add(t, index);
// child
int cnt = t.childCount;
for (int i = 0; i < cnt; i++)
{
stack.Push(t.GetChild(i));
}
}
// スキニングボーン数
skinBoneCount = transformList.Count;
// レンダートランスフォームを最後に追加
renderTransformIndex = transformList.Count;
transformList.Add(renderTransform);
}
// root transform id
@ -392,15 +465,8 @@ namespace MagicaCloth2
}
}
// スキニングボーン数
skinBoneCount = transformList.Count;
// レンダートランスフォームを最後に追加
renderTransformIndex = transformList.Count;
transformList.Add(renderTransform);
// トランスフォーム情報の読み取り
ReadTransformInformation(includeChilds: true);
ReadTransformInformation(includeChilds: true, referenceInitSetupData, renderTransform);
// 完了
result.SetSuccess();
@ -422,42 +488,117 @@ namespace MagicaCloth2
/// トランスフォーム情報の読み取り(メインスレッドのみ)
/// この情報だけはキャラクターが動く前に取得する必要がある
/// </summary>
void ReadTransformInformation(bool includeChilds)
void ReadTransformInformation(bool includeChilds, RenderSetupSerializeData referenceInitSetupData, Transform rendererTransform)
{
readTransformProfiler.Begin();
int tcnt = transformList.Count;
using var transformArray = new TransformAccessArray(transformList.ToArray());
bool useInitData = referenceInitSetupData != null;
// バッファ作成
transformPositions = new NativeArray<float3>(tcnt, Allocator.Persistent);
transformRotations = new NativeArray<quaternion>(tcnt, Allocator.Persistent);
transformLocalPositins = new NativeArray<float3>(tcnt, Allocator.Persistent);
transformLocalPositions = new NativeArray<float3>(tcnt, Allocator.Persistent);
transformLocalRotations = new NativeArray<quaternion>(tcnt, Allocator.Persistent);
transformScales = new NativeArray<float3>(tcnt, Allocator.Persistent);
transformInverseRotations = new NativeArray<quaternion>(tcnt, Allocator.Persistent);
var job = new ReadTransformJob()
// 読み取り
if (useInitData)
{
positions = transformPositions,
rotations = transformRotations,
scales = transformScales,
localPositions = transformLocalPositins,
localRotations = transformLocalRotations,
inverseRotations = transformInverseRotations,
};
// シミュレーション以外でワーカーを消費したくないのでRun()版にしておく
job.RunReadOnly(transformArray);
Debug.Assert(tcnt == referenceInitSetupData.transformCount);
// 初期センタートランスフォームを別途コピーしておく
initRenderLocalToWorld = GetRendeerLocalToWorldMatrix();
initRenderWorldtoLocal = math.inverse(initRenderLocalToWorld);
initRenderRotation = transformRotations[renderTransformIndex];
initRenderScale = transformScales[renderTransformIndex];
// 初期化データがある場合はコピーして終わり
int ucnt = referenceInitSetupData.useTransformCount;
// id
if (ucnt == tcnt)
{
// 全体コピー
NativeArray<float3>.Copy(referenceInitSetupData.transformPositions, 0, transformPositions, 0, ucnt);
NativeArray<quaternion>.Copy(referenceInitSetupData.transformRotations, 0, transformRotations, 0, ucnt);
NativeArray<float3>.Copy(referenceInitSetupData.transformLocalPositions, 0, transformLocalPositions, 0, ucnt);
NativeArray<quaternion>.Copy(referenceInitSetupData.transformLocalRotations, 0, transformLocalRotations, 0, ucnt);
NativeArray<float3>.Copy(referenceInitSetupData.transformScales, 0, transformScales, 0, ucnt);
}
else
{
// 差分
for (int i = 0; i < ucnt; i++)
{
int tindex = referenceInitSetupData.useTransformIndexArray[i];
transformPositions[tindex] = referenceInitSetupData.transformPositions[i];
transformRotations[tindex] = referenceInitSetupData.transformRotations[i];
transformLocalPositions[tindex] = referenceInitSetupData.transformLocalPositions[i];
transformLocalRotations[tindex] = referenceInitSetupData.transformLocalRotations[i];
transformScales[tindex] = referenceInitSetupData.transformScales[i];
}
}
// 逆回転のみ計算で求める
var job = new CalcInverseRotationJob()
{
rotations = transformRotations,
inverseRotations = transformInverseRotations,
};
job.Run(tcnt);
// 初期センタートランスフォームを別途コピーしておく
initRenderLocalToWorld = referenceInitSetupData.initRenderLocalToWorld;
initRenderWorldtoLocal = referenceInitSetupData.initRenderWorldtoLocal;
initRenderRotation = referenceInitSetupData.initRenderRotation;
initRenderScale = referenceInitSetupData.initRenderScale;
}
else
{
//using var transformArray = new TransformAccessArray(transformList.ToArray());
// Unity6.1対応
using var transformArray = new TransformAccessArray(transformList.Count);
int cnt = transformList.Count;
for (int i = 0; i < cnt; i++)
{
var t = transformList[i];
if (t == null)
t = rendererTransform;
//transformArray[i] = t;
transformArray.Add(t);
}
var job = new ReadTransformJob()
{
positions = transformPositions,
rotations = transformRotations,
scales = transformScales,
localPositions = transformLocalPositions,
localRotations = transformLocalRotations,
inverseRotations = transformInverseRotations
};
// シミュレーション以外でワーカーを消費したくないのでRun()版にしておく
job.RunReadOnly(transformArray);
// 初期センタートランスフォームを別途コピーしておく
initRenderLocalToWorld = GetRendeerLocalToWorldMatrix();
initRenderWorldtoLocal = math.inverse(initRenderLocalToWorld);
initRenderRotation = transformRotations[renderTransformIndex];
initRenderScale = transformScales[renderTransformIndex];
}
// id / parent id
transformIdList = new List<int>(tcnt);
transformParentIdList = new List<int>(tcnt);
for (int i = 0; i < tcnt; i++)
{
int id = 0, pid = 0;
var t = transformList[i];
transformIdList.Add(t?.GetInstanceID() ?? 0);
transformParentIdList.Add(t?.parent?.GetInstanceID() ?? 0);
if (t)
{
id = t.GetInstanceID();
if (includeChilds && t.parent)
pid = t.parent.GetInstanceID();
}
transformIdList.Add(id);
transformParentIdList.Add(pid);
}
// child id
@ -468,7 +609,7 @@ namespace MagicaCloth2
{
var t = transformList[i];
var clist = new FixedList512Bytes<int>();
if (t?.childCount > 0)
if (t && t.childCount > 0)
{
for (int j = 0; j < t.childCount; j++)
{
@ -479,6 +620,32 @@ namespace MagicaCloth2
transformChildIdList.Add(clist);
}
}
readTransformProfiler.End();
}
/// <summary>
/// 逆回転を計算で求める
/// </summary>
[BurstCompile]
struct CalcInverseRotationJob : IJobParallelFor
{
[Unity.Collections.ReadOnly]
public NativeArray<quaternion> rotations;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> inverseRotations;
public void Execute(int index)
{
var rot = rotations[index];
if (math.any(rot.value))
{
// どれか1つでも != 0 なら実行する
var irot = math.inverse(rot);
inverseRotations[index] = irot;
}
}
}
/// <summary>
@ -487,16 +654,22 @@ namespace MagicaCloth2
[BurstCompile]
struct ReadTransformJob : IJobParallelForTransform
{
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<float3> positions;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> rotations;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<float3> scales;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<float3> localPositions;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> localRotations;
[NativeDisableParallelForRestriction]
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> inverseRotations;
@ -527,23 +700,22 @@ namespace MagicaCloth2
public void Dispose()
{
if (bonesPerVertexArray.IsCreated)
bonesPerVertexArray.Dispose();
if (boneWeightArray.IsCreated)
boneWeightArray.Dispose();
// Pre-Build DeserializeManager管理中は破棄させない
if (isManaged)
return;
if (transformPositions.IsCreated)
transformPositions.Dispose();
if (transformRotations.IsCreated)
transformRotations.Dispose();
if (transformLocalPositins.IsCreated)
transformLocalPositins.Dispose();
if (transformLocalRotations.IsCreated)
transformLocalRotations.Dispose();
if (transformScales.IsCreated)
transformScales.Dispose();
if (transformInverseRotations.IsCreated)
transformInverseRotations.Dispose();
bonesPerVertexArray.MC2DisposeSafe();
boneWeightArray.MC2DisposeSafe();
localPositions.MC2DisposeSafe();
localNormals.MC2DisposeSafe();
localTangents.MC2DisposeSafe();
transformPositions.MC2DisposeSafe();
transformRotations.MC2DisposeSafe();
transformLocalPositions.MC2DisposeSafe();
transformLocalRotations.MC2DisposeSafe();
transformScales.MC2DisposeSafe();
transformInverseRotations.MC2DisposeSafe();
// MeshDataArrayはメインスレッドのみDispose()可能
if (setupType == SetupType.MeshCloth)
@ -555,8 +727,12 @@ namespace MagicaCloth2
public void GetUsedTransform(HashSet<Transform> transformSet)
{
foreach (var t in transformList)
transformSet.Add(t);
if (transformList != null)
{
foreach (var t in transformList)
if (t)
transformSet.Add(t);
}
}
public void ReplaceTransform(Dictionary<int, Transform> replaceDict)
@ -622,9 +798,10 @@ namespace MagicaCloth2
public float4x4 GetRendeerLocalToWorldMatrix()
{
var pos = transformPositions[renderTransformIndex];
var rot = transformRotations[renderTransformIndex];
var scl = transformScales[renderTransformIndex];
int index = renderTransformIndex;
var pos = transformPositions[index];
var rot = transformRotations[index];
var scl = transformScales[index];
return float4x4.TRS(pos, rot, scl);
}

View File

@ -96,7 +96,7 @@ namespace MagicaCloth2
//=========================================================================================
void AfterFixedUpdate()
{
//Debug.Log($"AF. F:{Time.frameCount}");
//Debug.Log($"AfterFixedUpdate. F:{Time.frameCount}");
FixedUpdateCount++;
}
@ -147,7 +147,7 @@ namespace MagicaCloth2
sb.AppendLine($"MaxSimulationCountPerFrame:{maxSimulationCountPerFrame}");
sb.AppendLine($"GlobalTimeScale:{GlobalTimeScale}");
sb.AppendLine($"SimulationDeltaTime:{SimulationDeltaTime}");
sb.AppendLine($"MaxDeltaTime:{MaxDeltaTime}");
//sb.AppendLine($"MaxDeltaTime:{MaxDeltaTime}");
sb.AppendLine($"SimulationPower:{SimulationPower}");
}
sb.AppendLine();

File diff suppressed because it is too large Load Diff

View File

@ -86,6 +86,12 @@ namespace MagicaCloth2
}
}
public void CopyFrom(in TeamWindData wdata)
{
windZoneList = wdata.windZoneList;
movingWind = wdata.movingWind;
}
//public void DebugLog(int teamId)
//{
// Debug.Log($"TeamWindData:{teamId}, zoneCnt:{ZoneCount}");

View File

@ -3,6 +3,7 @@
// https://magicasoft.jp
using System;
using System.Collections.Generic;
using System.Text;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
@ -16,7 +17,7 @@ namespace MagicaCloth2
/// TransformAccessArrayを中心とした一連のTransform管理クラス
/// スレッドで利用できるように様々な工夫を行っている
/// </summary>
public class TransformData : IDisposable
public partial class TransformData : IDisposable
{
internal List<Transform> transformList;
@ -101,10 +102,7 @@ namespace MagicaCloth2
Queue<int> emptyStack;
//=========================================================================================
public TransformData()
{
Init(100);
}
public TransformData() { }
public TransformData(int capacity)
{
@ -133,7 +131,7 @@ namespace MagicaCloth2
public void Dispose()
{
transformList.Clear();
transformList?.Clear();
idArray?.Dispose();
parentIdArray?.Dispose();
flagArray?.Dispose();
@ -145,15 +143,11 @@ namespace MagicaCloth2
localPositionArray?.Dispose();
localRotationArray?.Dispose();
inverseRotationArray?.Dispose();
emptyStack.Clear();
emptyStack?.Clear();
// transformAccessArrayはメインスレッドのみ
if (transformAccessArray.isCreated)
{
//if (MagicaManager.Discard != null)
// MagicaManager.Discard.AddMain(transformAccessArray);
//else
// transformAccessArray.Dispose();
transformAccessArray.Dispose();
}
}
@ -161,6 +155,7 @@ namespace MagicaCloth2
public int Count => transformList.Count;
public int RootCount => rootIdList?.Count ?? 0;
public bool IsDirty => isDirty;
public bool IsEmpty => transformList == null;
//=========================================================================================
/// <summary>
@ -951,5 +946,19 @@ namespace MagicaCloth2
{
return math.inverse(GetLocalToWorldMatrix(index));
}
//=========================================================================================
public override string ToString()
{
int transformListCount = transformList?.Count ?? 0;
StringBuilder sb = new StringBuilder();
sb.AppendLine("==== TransformData ====");
sb.AppendLine($"isDirty:{isDirty}");
sb.AppendLine($"transformList:{transformListCount}");
sb.AppendLine($"flagArray:{flagArray.Length}");
return sb.ToString();
}
}
}

View File

@ -31,6 +31,16 @@ namespace MagicaCloth2
/// </summary>
internal ExNativeArray<quaternion> initLocalRotationArray;
/// <summary>
/// シミュレーションの基準となるlocalPosition
/// </summary>
internal ExNativeArray<float3> baseLocalPositionArray;
/// <summary>
/// シミュレーションの基準となるlocalRotation
/// </summary>
internal ExNativeArray<quaternion> baseLocalRotationArray;
/// <summary>
/// ワールド座標
/// </summary>
@ -44,7 +54,7 @@ namespace MagicaCloth2
/// <summary>
/// ワールド逆回転
/// </summary>
internal ExNativeArray<quaternion> inverseRotationArray;
//internal ExNativeArray<quaternion> inverseRotationArray;
/// <summary>
/// ワールドスケール
@ -62,6 +72,16 @@ namespace MagicaCloth2
/// </summary>
internal ExNativeArray<quaternion> localRotationArray;
/// <summary>
/// ローカルスケール
/// </summary>
internal ExNativeArray<float3> localScaleArray;
/// <summary>
/// ワールド変換マトリックス
/// </summary>
internal ExNativeArray<float4x4> localToWorldMatrixArray;
/// <summary>
/// 接続チームID(0=なし)
/// </summary>
@ -78,15 +98,16 @@ namespace MagicaCloth2
//=========================================================================================
/// <summary>
/// 書き込み用トランスフォームのデータ参照インデックス
/// つまり上記配列へのインデックス
/// コンポーネント用ワールド座標
/// </summary>
//internal ExNativeArray<short> writeIndexArray;
internal ExNativeArray<float3> componentPositionArray;
/// <summary>
/// 書き込み用トランスフォームアクセス配列
/// コンポーネント用トランスフォーム
/// </summary>
//internal TransformAccessArray writeTransformAccessArray;
internal TransformAccessArray componentTransformAccessArray;
internal NativeReference<bool> existFixedTeam;
bool isValid;
@ -98,24 +119,32 @@ namespace MagicaCloth2
flagArray?.Dispose();
initLocalPositionArray?.Dispose();
initLocalRotationArray?.Dispose();
baseLocalPositionArray?.Dispose();
baseLocalRotationArray?.Dispose();
positionArray?.Dispose();
rotationArray?.Dispose();
inverseRotationArray?.Dispose();
//inverseRotationArray?.Dispose();
scaleArray?.Dispose();
localPositionArray?.Dispose();
localRotationArray?.Dispose();
localScaleArray?.Dispose();
localToWorldMatrixArray?.Dispose();
teamIdArray?.Dispose();
//writeIndexArray?.Dispose();
flagArray = null;
initLocalPositionArray = null;
initLocalRotationArray = null;
baseLocalPositionArray = null;
baseLocalRotationArray = null;
positionArray = null;
rotationArray = null;
inverseRotationArray = null;
//inverseRotationArray = null;
scaleArray = null;
localPositionArray = null;
localRotationArray = null;
localScaleArray = null;
localToWorldMatrixArray = null;
teamIdArray = null;
//writeIndexArray = null;
@ -123,6 +152,13 @@ namespace MagicaCloth2
transformAccessArray.Dispose();
//if (writeTransformAccessArray.isCreated)
// writeTransformAccessArray.Dispose();
componentPositionArray?.Dispose();
if (componentTransformAccessArray.isCreated)
componentTransformAccessArray.Dispose();
if (existFixedTeam.IsCreated)
existFixedTeam.Dispose();
}
public void EnterdEditMode()
@ -138,16 +174,25 @@ namespace MagicaCloth2
flagArray = new ExNativeArray<ExBitFlag8>(capacity);
initLocalPositionArray = new ExNativeArray<float3>(capacity);
initLocalRotationArray = new ExNativeArray<quaternion>(capacity);
baseLocalPositionArray = new ExNativeArray<float3>(capacity);
baseLocalRotationArray = new ExNativeArray<quaternion>(capacity);
positionArray = new ExNativeArray<float3>(capacity);
rotationArray = new ExNativeArray<quaternion>(capacity);
inverseRotationArray = new ExNativeArray<quaternion>(capacity);
//inverseRotationArray = new ExNativeArray<quaternion>(capacity);
scaleArray = new ExNativeArray<float3>(capacity);
localPositionArray = new ExNativeArray<float3>(capacity);
localRotationArray = new ExNativeArray<quaternion>(capacity);
localScaleArray = new ExNativeArray<float3>(capacity);
localToWorldMatrixArray = new ExNativeArray<float4x4>(capacity);
teamIdArray = new ExNativeArray<short>(capacity);
transformAccessArray = new TransformAccessArray(capacity);
componentPositionArray = new ExNativeArray<float3>(capacity);
componentTransformAccessArray = new TransformAccessArray(capacity);
existFixedTeam = new NativeReference<bool>(Allocator.Persistent);
isValid = true;
}
@ -158,30 +203,34 @@ namespace MagicaCloth2
//=========================================================================================
/// <summary>
/// TransformDataを追加する
/// VirtualMeshのTransformDataを追加する
/// </summary>
/// <param name="tdata"></param>
/// <returns></returns>
internal DataChunk AddTransform(TransformData tdata, int teamId)
internal DataChunk AddTransform(VirtualMeshContainer cmesh, int teamId)
{
if (isValid == false)
return default;
Debug.Assert(tdata != null);
int cnt = tdata.Count;
Debug.Assert(cmesh != null);
int cnt = cmesh.GetTransformCount();
// データコピー追加
var c = flagArray.AddRange(tdata.flagArray);
initLocalPositionArray.AddRange(tdata.initLocalPositionArray);
initLocalRotationArray.AddRange(tdata.initLocalRotationArray);
var c = flagArray.AddRange(cmesh.shareVirtualMesh.transformData.flagArray);
initLocalPositionArray.AddRange(cmesh.shareVirtualMesh.transformData.initLocalPositionArray);
initLocalRotationArray.AddRange(cmesh.shareVirtualMesh.transformData.initLocalRotationArray);
baseLocalPositionArray.AddRange(cmesh.shareVirtualMesh.transformData.initLocalPositionArray);
baseLocalRotationArray.AddRange(cmesh.shareVirtualMesh.transformData.initLocalRotationArray);
// 領域のみ追加
positionArray.AddRange(cnt);
rotationArray.AddRange(cnt);
inverseRotationArray.AddRange(cnt);
//inverseRotationArray.AddRange(cnt);
scaleArray.AddRange(cnt);
localPositionArray.AddRange(cnt);
localRotationArray.AddRange(cnt);
localScaleArray.AddRange(cnt);
localToWorldMatrixArray.AddRange(cnt);
// チームID
teamIdArray.AddRange(cnt, (short)teamId);
@ -190,16 +239,19 @@ namespace MagicaCloth2
int nowcnt = transformAccessArray.length;
// データチャンクの開始まで埋める
var meshT = cmesh.GetCenterTransform();
int start = c.startIndex;
while (nowcnt < start)
{
transformAccessArray.Add(null);
transformAccessArray.Add(meshT);
nowcnt++;
}
for (int i = 0; i < cnt; i++)
{
var t = tdata.transformList[i];
Transform t = cmesh.GetTransformFromIndex(i);
if (t == null)
t = meshT;
int index = c.startIndex + i;
if (index < nowcnt)
transformAccessArray[index] = t;
@ -215,7 +267,7 @@ namespace MagicaCloth2
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
internal DataChunk AddTransform(int count, int teamId)
internal DataChunk AddTransform(int count, int teamId, Transform t)
{
if (isValid == false)
return default;
@ -224,30 +276,34 @@ namespace MagicaCloth2
var c = flagArray.AddRange(count);
initLocalPositionArray.AddRange(count);
initLocalRotationArray.AddRange(count);
baseLocalPositionArray.AddRange(count);
baseLocalRotationArray.AddRange(count);
positionArray.AddRange(count);
rotationArray.AddRange(count);
inverseRotationArray.AddRange(count);
//inverseRotationArray.AddRange(count);
scaleArray.AddRange(count);
localPositionArray.AddRange(count);
localRotationArray.AddRange(count);
localScaleArray.AddRange(count);
localToWorldMatrixArray.AddRange(count);
// チームID
teamIdArray.AddRange(count, (short)teamId);
// トランスフォームはすべてnullで登録する
// トランスフォームはすべてnullで登録する(Unity6.1でnull登録はNGになった)
int nowcnt = transformAccessArray.length;
// データチャンクの開始まで埋める
int start = c.startIndex;
while (nowcnt < start)
{
transformAccessArray.Add(null);
transformAccessArray.Add(t);
nowcnt++;
}
for (int i = 0; i < count; i++)
{
Transform t = null;
//Transform t = null;
int index = c.startIndex + i;
if (index < nowcnt)
transformAccessArray[index] = t;
@ -273,12 +329,16 @@ namespace MagicaCloth2
var c = flagArray.Add(flag);
initLocalPositionArray.Add(t.localPosition);
initLocalRotationArray.Add(t.localRotation);
baseLocalPositionArray.Add(t.localPosition);
baseLocalRotationArray.Add(t.localRotation);
positionArray.Add(t.position);
rotationArray.Add(t.rotation);
inverseRotationArray.Add(math.inverse(t.rotation));
//inverseRotationArray.Add(math.inverse(t.rotation));
scaleArray.Add(t.lossyScale);
localPositionArray.Add(t.localPosition);
localRotationArray.Add(t.localRotation);
localScaleArray.Add(t.localScale);
localToWorldMatrixArray.Add(float4x4.identity); // ここは単位行列
// チームID
teamIdArray.Add((short)teamId);
@ -311,12 +371,16 @@ namespace MagicaCloth2
flagArray[index] = flag;
initLocalPositionArray[index] = t.localPosition;
initLocalRotationArray[index] = t.localRotation;
baseLocalPositionArray[index] = t.localPosition;
baseLocalRotationArray[index] = t.localRotation;
positionArray[index] = t.position;
rotationArray[index] = t.rotation;
inverseRotationArray[index] = math.inverse(t.rotation);
//inverseRotationArray[index] = math.inverse(t.rotation);
scaleArray[index] = t.lossyScale;
localPositionArray[index] = t.localPosition;
localRotationArray[index] = t.localRotation;
localScaleArray[index] = t.localScale;
//localToWorldMatrix[index] = t.localToWorldMatrix; // ここは不要
teamIdArray[index] = (short)teamId;
transformAccessArray[index] = t;
}
@ -342,12 +406,16 @@ namespace MagicaCloth2
flagArray[toIndex] = flagArray[fromIndex];
initLocalPositionArray[toIndex] = initLocalPositionArray[fromIndex];
initLocalRotationArray[toIndex] = initLocalRotationArray[fromIndex];
baseLocalPositionArray[toIndex] = baseLocalPositionArray[fromIndex];
baseLocalRotationArray[toIndex] = baseLocalRotationArray[fromIndex];
positionArray[toIndex] = positionArray[fromIndex];
rotationArray[toIndex] = rotationArray[fromIndex];
inverseRotationArray[toIndex] = inverseRotationArray[fromIndex];
//inverseRotationArray[toIndex] = inverseRotationArray[fromIndex];
scaleArray[toIndex] = scaleArray[fromIndex];
localPositionArray[toIndex] = localPositionArray[fromIndex];
localRotationArray[toIndex] = localRotationArray[fromIndex];
localScaleArray[toIndex] = localScaleArray[fromIndex];
//localToWorldMatrix[toIndex] = localToWorldMatrix[fromIndex]; // ここは不要
transformAccessArray[toIndex] = transformAccessArray[fromIndex];
teamIdArray[toIndex] = teamIdArray[fromIndex];
}
@ -366,12 +434,16 @@ namespace MagicaCloth2
flagArray.RemoveAndFill(c);
initLocalPositionArray.Remove(c);
initLocalRotationArray.Remove(c);
baseLocalPositionArray.Remove(c);
baseLocalRotationArray.Remove(c);
positionArray.Remove(c);
rotationArray.Remove(c);
inverseRotationArray.Remove(c);
//inverseRotationArray.Remove(c);
scaleArray.Remove(c);
localPositionArray.Remove(c);
localRotationArray.Remove(c);
localScaleArray.Remove(c);
localToWorldMatrixArray.Remove(c);
teamIdArray.RemoveAndFill(c, 0);
// トランスフォーム削除
@ -454,12 +526,16 @@ namespace MagicaCloth2
var nc = flagArray.Expand(c, newLength);
initLocalPositionArray.Expand(c, newLength);
initLocalRotationArray.Expand(c, newLength);
baseLocalPositionArray.Expand(c, newLength);
baseLocalRotationArray.Expand(c, newLength);
positionArray.Expand(c, newLength);
rotationArray.Expand(c, newLength);
inverseRotationArray.Expand(c, newLength);
//inverseRotationArray.Expand(c, newLength);
scaleArray.Expand(c, newLength);
localPositionArray.Expand(c, newLength);
localRotationArray.Expand(c, newLength);
localScaleArray.Expand(c, newLength);
localToWorldMatrixArray.Expand(c, newLength);
// チームID
teamIdArray.Expand(c, newLength);
@ -467,8 +543,11 @@ namespace MagicaCloth2
// トランスフォームアクセス配列の拡張
if (c.startIndex != nc.startIndex)
{
// 旧領域の先頭Transform
Transform frontT = transformAccessArray[c.startIndex];
while (transformAccessArray.length < (nc.startIndex + nc.dataLength))
transformAccessArray.Add(null);
transformAccessArray.Add(frontT);
for (int i = 0; i < c.dataLength; i++)
{
@ -490,11 +569,14 @@ namespace MagicaCloth2
/// <returns></returns>
public JobHandle RestoreTransform(JobHandle jobHandle)
{
existFixedTeam.Value = false;
if (Count > 0)
{
//Debug.Log("RestoreTransform");
var job = new RestoreTransformJob()
{
existFixedTeam = existFixedTeam,
flagList = flagArray.GetNativeArray(),
localPositionArray = initLocalPositionArray.GetNativeArray(),
localRotationArray = initLocalRotationArray.GetNativeArray(),
@ -511,6 +593,9 @@ namespace MagicaCloth2
[BurstCompile]
struct RestoreTransformJob : IJobParallelForTransform
{
[NativeDisableParallelForRestriction]
public NativeReference<bool> existFixedTeam;
[Unity.Collections.ReadOnly]
public NativeArray<ExBitFlag8> flagList;
@ -531,19 +616,102 @@ namespace MagicaCloth2
if (transform.isValid == false)
return;
var flag = flagList[index];
if (flag.IsSet(Flag_Enable) == false)
return;
if (flag.IsSet(Flag_Restore) == false)
return;
// Keepカリング時はスキップする
int teamId = teamIdArray[index];
var tdata = teamDataArray[teamId];
if (tdata.IsCullingInvisible && tdata.IsCullingKeep)
// 一度のみ復元フラグが立っている場合は実行する
if (flag.IsSet(Flag_Enable) == false && tdata.flag.IsSet(TeamManager.Flag_RestoreTransformOnlyOnec) == false)
return;
// Keepカリング時はスキップする
if ((tdata.IsCameraCullingInvisible && tdata.IsCameraCullingKeep) || tdata.IsDistanceCullingInvisible)
return;
transform.localPosition = localPositionArray[index];
transform.localRotation = localRotationArray[index];
// 物理更新チームの存在
if (tdata.IsFixedUpdate)
existFixedTeam.Value = true;
//Debug.Log($"RestoreTransform [{index}] lpos:{localPositionArray[index]}, lrot:{localRotationArray[index]}");
}
}
//=========================================================================================
/// <summary>
/// UnityPhysicsチームかつFixedUpdateが実行されなかったフレームは退避させておいたベース姿勢で再度復元させる
/// </summary>
/// <param name="count"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
public JobHandle RestoreBaseTransform(JobHandle jobHandle)
{
if (Count > 0)
{
//Debug.Log("RestoreTransform");
var job = new RestoreBaseTransformJob()
{
flagList = flagArray.GetNativeArray(),
baseLocalPositionArray = baseLocalPositionArray.GetNativeArray(),
baseLocalRotationArray = baseLocalRotationArray.GetNativeArray(),
teamIdArray = teamIdArray.GetNativeArray(),
teamDataArray = MagicaManager.Team.teamDataArray.GetNativeArray(),
};
jobHandle = job.Schedule(transformAccessArray, jobHandle);
}
return jobHandle;
}
[BurstCompile]
struct RestoreBaseTransformJob : IJobParallelForTransform
{
[Unity.Collections.ReadOnly]
public NativeArray<ExBitFlag8> flagList;
[Unity.Collections.ReadOnly]
public NativeArray<float3> baseLocalPositionArray;
[Unity.Collections.ReadOnly]
public NativeArray<quaternion> baseLocalRotationArray;
[Unity.Collections.ReadOnly]
public NativeArray<short> teamIdArray;
// team
[Unity.Collections.ReadOnly]
public NativeArray<TeamManager.TeamData> teamDataArray;
public void Execute(int index, TransformAccess transform)
{
if (transform.isValid == false)
return;
var flag = flagList[index];
if (flag.IsSet(Flag_Restore) == false)
return;
int teamId = teamIdArray[index];
var tdata = teamDataArray[teamId];
// Fixed更新チームのみ
if (tdata.IsFixedUpdate == false)
return;
// 一度のみ復元フラグが立っている場合は実行する
if (flag.IsSet(Flag_Enable) == false && tdata.flag.IsSet(TeamManager.Flag_RestoreTransformOnlyOnec) == false)
return;
// Keepカリング時はスキップする
if ((tdata.IsCameraCullingInvisible && tdata.IsCameraCullingKeep) || tdata.IsDistanceCullingInvisible)
return;
transform.localPosition = baseLocalPositionArray[index];
transform.localRotation = baseLocalRotationArray[index];
//Debug.Log($"RestoreTransform [{index}] lpos:{baseLocalPositionArray[index]}, lrot:{baseLocalRotationArray[index].value}");
}
}
@ -553,21 +721,25 @@ namespace MagicaCloth2
/// </summary>
/// <param name="jobHandle"></param>
/// <returns></returns>
public JobHandle ReadTransform(JobHandle jobHandle)
public JobHandle ReadTransformSchedule(JobHandle jobHandle)
{
if (Count > 0)
{
// todo:未来予測などがあると色々複雑化するところ
var job = new ReadTransformJob()
{
fixedUpdateCount = MagicaManager.Time.FixedUpdateCount,
flagList = flagArray.GetNativeArray(),
positionArray = positionArray.GetNativeArray(),
rotationArray = rotationArray.GetNativeArray(),
scaleList = scaleArray.GetNativeArray(),
localPositionArray = localPositionArray.GetNativeArray(),
localRotationArray = localRotationArray.GetNativeArray(),
inverseRotationArray = inverseRotationArray.GetNativeArray(),
localScaleArray = localScaleArray.GetNativeArray(),
//inverseRotationArray = inverseRotationArray.GetNativeArray(),
localToWorldMatrixArray = localToWorldMatrixArray.GetNativeArray(),
baseLocalPositionArray = baseLocalPositionArray.GetNativeArray(),
baseLocalRotationArray = baseLocalRotationArray.GetNativeArray(),
teamIdArray = teamIdArray.GetNativeArray(),
@ -582,6 +754,8 @@ namespace MagicaCloth2
[BurstCompile]
struct ReadTransformJob : IJobParallelForTransform
{
public int fixedUpdateCount;
[Unity.Collections.ReadOnly]
public NativeArray<ExBitFlag8> flagList;
@ -596,7 +770,15 @@ namespace MagicaCloth2
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> localRotationArray;
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> inverseRotationArray;
public NativeArray<float3> localScaleArray;
//[Unity.Collections.WriteOnly]
//public NativeArray<quaternion> inverseRotationArray;
[Unity.Collections.WriteOnly]
public NativeArray<float4x4> localToWorldMatrixArray;
[Unity.Collections.WriteOnly]
public NativeArray<float3> baseLocalPositionArray;
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> baseLocalRotationArray;
[Unity.Collections.ReadOnly]
public NativeArray<short> teamIdArray;
@ -615,7 +797,7 @@ namespace MagicaCloth2
if (flag.IsSet(Flag_Read) == false)
return;
// カリング時は書き込まない
// カリング時は読み込まない
int teamId = teamIdArray[index];
var tdata = teamDataArray[teamId];
if (tdata.IsCullingInvisible)
@ -629,6 +811,7 @@ namespace MagicaCloth2
rotationArray[index] = rot;
localPositionArray[index] = transform.localPosition;
localRotationArray[index] = transform.localRotation;
localScaleArray[index] = transform.localScale;
// マトリックスから正確なスケール値を算出するこれはTransform.lossyScaleと等価
var irot = math.inverse(rot);
@ -636,8 +819,24 @@ namespace MagicaCloth2
var scl = new float3(m2.c0.x, m2.c1.y, m2.c2.z);
scaleList[index] = scl;
//Debug.Log($"ReadTransform [{index}] pos:{pos}, rot:{rot}, scl:{scl}");
//Debug.Log($"LtoW:\n{LtoW}");
// ワールド->ローカル変換用の逆クォータニオン
inverseRotationArray[index] = math.inverse(rot);
//inverseRotationArray[index] = math.inverse(rot);
// ワールド変換マトリックス
localToWorldMatrixArray[index] = LtoW;
// 今回の姿勢を基本姿勢として退避させる
// Fixed更新チームかつFixedUpdateが実行されている場合、そもそもFixed更新出ない場合は毎回
if ((tdata.IsFixedUpdate && fixedUpdateCount > 0) || tdata.IsFixedUpdate == false)
{
baseLocalPositionArray[index] = transform.localPosition;
baseLocalRotationArray[index] = transform.localRotation;
}
//Debug.Log($"ReadTransform [{index}] pos:{pos}, rot:{rot}");
}
}
@ -648,7 +847,7 @@ namespace MagicaCloth2
/// <param name="count"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
public JobHandle WriteTransform(JobHandle jobHandle)
public JobHandle WriteTransformSchedule(JobHandle jobHandle)
{
var job = new WriteTransformJob()
{
@ -700,6 +899,7 @@ namespace MagicaCloth2
// カリング時は書き込まない
int teamId = teamIdArray[index];
var tdata = teamDataArray[teamId];
//if (tdata.IsCameraCullingInvisible)
if (tdata.IsCullingInvisible)
return;
@ -711,20 +911,101 @@ namespace MagicaCloth2
{
// ワールド回転
transform.rotation = worldRotations[index];
//Debug.Log($"WriteTransform [{index}] (World!) rot:{worldRotations[index]}");
// BoneSpringのみワールド座標を書き込む
if (tdata.IsSpring)
{
transform.position = worldPositions[index];
}
}
else if (flag.IsSet(Flag_LocalPosRotWrite))
{
// ローカル座標・回転を書き込む
transform.localPosition = localPositions[index];
transform.localRotation = localRotations[index];
//Debug.Log($"WriteTransform [{index}] (local!) lpos:{localPositions[index]}, lrot:{localRotations[index]}");
}
}
}
//=========================================================================================
/// <summary>
/// コンポーネント用トランスフォームの登録
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
internal int AddComponentTransform(Transform t)
{
if (isValid == false)
return -1;
Debug.Assert(t);
int index = componentPositionArray.Add(float3.zero).startIndex;
// トランスフォーム
int nowcnt = componentTransformAccessArray.length;
if (index < nowcnt)
componentTransformAccessArray[index] = t;
else
componentTransformAccessArray.Add(t);
return index;
}
/// <summary>
/// コンポーネント用トランスフォームの削除
/// </summary>
/// <param name="index"></param>
internal void RemoveComponentTransform(int index)
{
if (isValid == false)
return;
if (index < 0)
return;
componentPositionArray.Remove(index);
// トランスフォーム削除
componentTransformAccessArray[index] = null;
}
/// <summary>
/// トランスフォームを読み込むジョブを発行する
/// </summary>
/// <param name="jobHandle"></param>
/// <returns></returns>
internal JobHandle ReadComponentTransform(JobHandle jobHandle)
{
if (componentPositionArray.Count > 0)
{
var job = new ReadComponentTransformJob()
{
positionArray = componentPositionArray.GetNativeArray(),
};
jobHandle = job.ScheduleReadOnly(componentTransformAccessArray, 16, jobHandle);
}
return jobHandle;
}
[BurstCompile]
struct ReadComponentTransformJob : IJobParallelForTransform
{
[Unity.Collections.WriteOnly]
public NativeArray<float3> positionArray;
public void Execute(int index, TransformAccess transform)
{
if (transform.isValid == false)
return;
positionArray[index] = transform.position;
}
}
//=========================================================================================
public void InformationLog(StringBuilder allsb)
{
@ -741,12 +1022,16 @@ namespace MagicaCloth2
sb.AppendLine($" -flagArray:{flagArray.ToSummary()}");
sb.AppendLine($" -initLocalPositionArray:{initLocalPositionArray.ToSummary()}");
sb.AppendLine($" -initLocalRotationArray:{initLocalRotationArray.ToSummary()}");
sb.AppendLine($" -baseLocalPositionArray:{baseLocalPositionArray.ToSummary()}");
sb.AppendLine($" -baseLocalRotationArray:{baseLocalRotationArray.ToSummary()}");
sb.AppendLine($" -positionArray:{positionArray.ToSummary()}");
sb.AppendLine($" -rotationArray:{rotationArray.ToSummary()}");
sb.AppendLine($" -inverseRotationArray:{inverseRotationArray.ToSummary()}");
//sb.AppendLine($" -inverseRotationArray:{inverseRotationArray.ToSummary()}");
sb.AppendLine($" -scaleArray:{scaleArray.ToSummary()}");
sb.AppendLine($" -localPositionArray:{localPositionArray.ToSummary()}");
sb.AppendLine($" -localRotationArray:{localRotationArray.ToSummary()}");
sb.AppendLine($" -localScaleArray:{localScaleArray.ToSummary()}");
sb.AppendLine($" -localToWorldMatirxArray:{localToWorldMatrixArray.ToSummary()}");
sb.AppendLine($" -teamIdArray:{teamIdArray.ToSummary()}");
if (transformAccessArray.isCreated)

View File

@ -22,19 +22,23 @@ namespace MagicaCloth2
public Matrix4x4 worldToLocalMatrix;
public int pid;
public TransformRecord(Transform t)
public TransformRecord(Transform t, bool read)
{
if (t)
{
transform = t;
id = t.GetInstanceID();
localPosition = t.localPosition;
localRotation = t.localRotation;
position = t.position;
rotation = t.rotation;
scale = t.lossyScale;
localToWorldMatrix = t.localToWorldMatrix;
worldToLocalMatrix = t.worldToLocalMatrix;
if (read)
{
localPosition = t.localPosition;
localRotation = t.localRotation;
position = t.position;
rotation = t.rotation;
scale = t.lossyScale;
localToWorldMatrix = t.localToWorldMatrix;
worldToLocalMatrix = t.worldToLocalMatrix;
}
if (t.parent)
pid = t.parent.GetInstanceID();

View File

@ -26,7 +26,8 @@ namespace MagicaCloth2
//=========================================================================================
GridMap<int> gridMap;
NativeParallelHashSet<int2> joinPairSet;
//NativeParallelHashSet<int2> joinPairSet;
NativeParallelMultiHashMap<ushort, ushort> joinPairMap;
NativeReference<int> resultRef;
//=========================================================================================
@ -48,8 +49,10 @@ namespace MagicaCloth2
public virtual void Dispose()
{
if (joinPairSet.IsCreated)
joinPairSet.Dispose();
//if (joinPairSet.IsCreated)
// joinPairSet.Dispose();
if (joinPairMap.IsCreated)
joinPairMap.Dispose();
if (resultRef.IsCreated)
resultRef.Dispose();
gridMap?.Dispose();
@ -77,7 +80,8 @@ namespace MagicaCloth2
// 頂点ごとの接続マップ
// インデックスが若い方が記録する
joinPairSet = new NativeParallelHashSet<int2>(vmesh.VertexCount / 4, Allocator.Persistent);
//joinPairSet = new NativeParallelHashSet<int2>(vmesh.VertexCount / 4, Allocator.Persistent);
joinPairMap = new NativeParallelMultiHashMap<ushort, ushort>(vmesh.VertexCount, Allocator.Persistent);
resultRef = new NativeReference<int>(Allocator.Persistent);
// ポイントをグリッドに登録
@ -100,14 +104,18 @@ namespace MagicaCloth2
localPositions = vmesh.localPositions.GetNativeArray(),
joinIndices = workData.vertexJoinIndices,
gridMap = gridMap.GetMultiHashMap(),
joinPairSet = joinPairSet,
//joinPairSet = joinPairSet,
joinPairMap = joinPairMap,
};
searchJoinJob.Run();
// 結合する
#if false
var joinJob = new JoinJob()
{
joinPairSet = joinPairSet,
vertexCount = vmesh.VertexCount,
//joinPairSet = joinPairSet,
joinPairMap = joinPairMap,
joinIndices = workData.vertexJoinIndices,
vertexToVertexMap = workData.vertexToVertexMap,
boneWeights = vmesh.boneWeights.GetNativeArray(),
@ -115,6 +123,25 @@ namespace MagicaCloth2
result = resultRef,
};
joinJob.Run();
#endif
#if true
// 高速化および詳細メッシュがある場合に1つの頂点に大量に結合しオーバーフローが発生する問題を回避したもの
// 以前はA->B->C結合時にA->C間が接続距離以上でもA->Cが結合されてしまったが、この改良版ではそれがおこならない
// そのため以前とは結果がことなることに注意!
using var tempList = new NativeList<ushort>(2048, Allocator.Persistent);
var joinJob2 = new JoinJob2()
{
vertexCount = vmesh.VertexCount,
joinPairMap = joinPairMap,
joinIndices = workData.vertexJoinIndices,
vertexToVertexMap = workData.vertexToVertexMap,
boneWeights = vmesh.boneWeights.GetNativeArray(),
attributes = vmesh.attributes.GetNativeArray(),
result = resultRef,
tempList = tempList,
};
joinJob2.Run();
#endif
// 頂点の接続状態を最新に更新する。すべて最新の生存ポイントを指すように変更する
UpdateJoinAndLink();
@ -191,7 +218,8 @@ namespace MagicaCloth2
[Unity.Collections.ReadOnly]
public NativeParallelMultiHashMap<int3, int> gridMap;
public NativeParallelHashSet<int2> joinPairSet;
//public NativeParallelHashSet<int2> joinPairSet;
public NativeParallelMultiHashMap<ushort, ushort> joinPairMap;
public void Execute()
{
@ -222,14 +250,91 @@ namespace MagicaCloth2
continue;
// 結合登録
int2 ehash = DataUtility.PackInt2(vindex, tvindex);
joinPairSet.Add(ehash);
//int2 ehash = DataUtility.PackInt2(vindex, tvindex);
//joinPairSet.Add(ehash);
joinPairMap.Add((ushort)vindex, (ushort)tvindex);
}
}
}
}
}
[BurstCompile]
struct JoinJob2 : IJob
{
public int vertexCount;
[Unity.Collections.ReadOnly]
public NativeParallelMultiHashMap<ushort, ushort> joinPairMap;
public NativeArray<int> joinIndices;
public NativeParallelMultiHashMap<ushort, ushort> vertexToVertexMap;
public NativeArray<VirtualMeshBoneWeight> boneWeights;
public NativeArray<VertexAttribute> attributes;
public NativeReference<int> result;
public NativeList<ushort> tempList;
public void Execute()
{
int cnt = 0;
for (ushort vindexLive = 0; vindexLive < vertexCount; vindexLive++)
{
// すでに結合済みならスキップ
if (joinIndices[vindexLive] >= 0)
continue;
// 対象ループ
foreach (ushort vindexDead in joinPairMap.GetValuesForKey(vindexLive))
{
// 対象がすでに結合ずみならスキップ
if (joinIndices[vindexDead] >= 0)
continue;
// 結合(vertexDead -> vertexLive)
joinIndices[vindexDead] = vindexLive;
cnt++;
vertexToVertexMap.MC2RemoveValue(vindexLive, vindexDead);
tempList.Clear();
foreach (ushort i in vertexToVertexMap.GetValuesForKey(vindexDead))
{
tempList.Add(i);
}
foreach (ushort i in tempList)
{
if (joinIndices[i] >= 0)
continue;
if (i == vindexLive || i == vindexDead)
continue;
vertexToVertexMap.MC2RemoveValue(i, vindexDead);
vertexToVertexMap.MC2UniqueAdd(vindexLive, i);
vertexToVertexMap.MC2UniqueAdd(i, vindexLive);
// p2にBoneWeightを結合
var bw = boneWeights[vindexLive];
bw.AddWeight(boneWeights[vindexDead]);
boneWeights[vindexLive] = bw;
// 属性
var attr1 = attributes[vindexDead];
var attr2 = attributes[vindexLive];
attributes[vindexLive] = VertexAttribute.JoinAttribute(attr1, attr2);
attributes[vindexDead] = VertexAttribute.Invalid; // 削除頂点は無効にする
}
}
}
// 削除頂点数記録
result.Value = cnt;
}
}
#if false // old
[BurstCompile]
struct JoinJob : IJob
{
@ -245,32 +350,31 @@ namespace MagicaCloth2
public void Execute()
{
var workSet = new FixedList512Bytes<ushort>();
int cnt = 0;
foreach (var ehash in joinPairSet)
{
int vindex2 = ehash[0]; // 生存側
int vindex1 = ehash[1]; // 削除側
int vindexLive = ehash[0]; // 生存側
int vindexDead = ehash[1]; // 削除側
// 両方とも生存インデックスに変換する
while (joinIndices[vindex1] >= 0)
while (joinIndices[vindexDead] >= 0)
{
vindex1 = joinIndices[vindex1];
vindexDead = joinIndices[vindexDead];
}
while (joinIndices[vindex2] >= 0)
while (joinIndices[vindexLive] >= 0)
{
vindex2 = joinIndices[vindex2];
vindexLive = joinIndices[vindexLive];
}
if (vindex1 == vindex2)
if (vindexDead == vindexLive)
continue;
// 結合(vertex1 -> vertex2)
joinIndices[vindex1] = vindex2;
joinIndices[vindexDead] = vindexLive;
cnt++;
// 接続数を結合する(重複は弾かれる)
workSet.Clear();
foreach (ushort i in vertexToVertexMap.GetValuesForKey((ushort)vindex1))
foreach (ushort i in vertexToVertexMap.GetValuesForKey((ushort)vindexDead))
{
int index = i;
// 生存インデックス
@ -278,10 +382,10 @@ namespace MagicaCloth2
{
index = joinIndices[index];
}
if (index != vindex1 && index != vindex2)
workSet.Set((ushort)index);
if (index != vindexDead && index != vindexLive)
workSet.MC2Set((ushort)index);
}
foreach (ushort i in vertexToVertexMap.GetValuesForKey((ushort)vindex2))
foreach (ushort i in vertexToVertexMap.GetValuesForKey((ushort)vindexLive))
{
int index = i;
// 生存インデックス
@ -289,32 +393,33 @@ namespace MagicaCloth2
{
index = joinIndices[index];
}
if (index != vindex1 && index != vindex2)
workSet.Set((ushort)index);
if (index != vindexDead && index != vindexLive)
workSet.MC2Set((ushort)index);
}
vertexToVertexMap.Remove((ushort)vindex2);
vertexToVertexMap.Remove((ushort)vindexLive);
for (int i = 0; i < workSet.Length; i++)
{
vertexToVertexMap.Add((ushort)vindex2, workSet[i]);
vertexToVertexMap.Add((ushort)vindexLive, workSet[i]);
}
//Debug.Assert(workSet.Length > 0);
// p2にBoneWeightを結合
var bw = boneWeights[vindex2];
bw.AddWeight(boneWeights[vindex1]);
boneWeights[vindex2] = bw;
var bw = boneWeights[vindexLive];
bw.AddWeight(boneWeights[vindexDead]);
boneWeights[vindexLive] = bw;
// 属性
var attr1 = attributes[vindex1];
var attr2 = attributes[vindex2];
attributes[vindex2] = VertexAttribute.JoinAttribute(attr1, attr2);
attributes[vindex1] = VertexAttribute.Invalid; // 削除頂点は無効にする
var attr1 = attributes[vindexDead];
var attr2 = attributes[vindexLive];
attributes[vindexLive] = VertexAttribute.JoinAttribute(attr1, attr2);
attributes[vindexDead] = VertexAttribute.Invalid; // 削除頂点は無効にする
}
// 削除頂点数記録
result.Value = cnt;
}
}
#endif
//=========================================================================================
/// <summary>
@ -395,7 +500,7 @@ namespace MagicaCloth2
if (tvindex == vindex)
continue;
newLinkSet.Set((ushort)tvindex);
newLinkSet.MC2Set((ushort)tvindex);
}
// 生存のみの新しいセットに入れ替え
vertexToVertexMap.Remove((ushort)vindex);

View File

@ -450,12 +450,12 @@ namespace MagicaCloth2
foreach (ushort nindex in vertexToVertexMap.GetValuesForKey((ushort)vindex1))
{
if (nindex != vindex1 && nindex != vindex2)
newLink.Set(nindex);
newLink.MC2Set(nindex);
}
foreach (ushort nindex in vertexToVertexMap.GetValuesForKey((ushort)vindex2))
{
if (nindex != vindex1 && nindex != vindex2)
newLink.Set(nindex);
newLink.MC2Set(nindex);
}
vertexToVertexMap.Remove((ushort)vindex2);
for (int i = 0; i < newLink.Length; i++)
@ -558,7 +558,7 @@ namespace MagicaCloth2
if (tvindex == vindex)
continue;
newLinkSet.Set((ushort)tvindex);
newLinkSet.MC2Set((ushort)tvindex);
}
// 生存のみの新しいセットに入れ替え
vertexToVertexMap.Remove((ushort)vindex);
@ -640,12 +640,12 @@ namespace MagicaCloth2
foreach (ushort index in vertexToVertexMap.GetValuesForKey((ushort)vindex))
{
if (index != vindex && index != tvindex)
joinVlink.Set(index);
joinVlink.MC2Set(index);
}
foreach (ushort index in vertexToVertexMap.GetValuesForKey((ushort)tvindex))
{
if (index != vindex && index != tvindex)
joinVlink.Set(index);
joinVlink.MC2Set(index);
}
// 点になるのはNG
@ -662,20 +662,20 @@ namespace MagicaCloth2
// 結合後のトライアングルの外周をひと筆書きし、すべての外周頂点が使われているならOK!
// 外周頂点が一筆書きできない場合はつ以上のグループに分かれいるX型になる
var stack = new FixedList512Bytes<ushort>();
stack.Push(joinVlink[0]);
stack.MC2Push(joinVlink[0]);
while (stack.Length > 0)
{
ushort index = stack.Pop();
ushort index = stack.MC2Pop();
if (joinVlink.Contains(index) == false)
continue;
joinVlink.RemoveItemAtSwapBack(index);
joinVlink.MC2RemoveItemAtSwapBack(index);
foreach (ushort nindex in vertexToVertexMap.GetValuesForKey((ushort)index))
{
if (joinVlink.Contains(nindex))
{
// next
stack.Push(nindex);
stack.MC2Push(nindex);
}
}
}
@ -716,13 +716,13 @@ namespace MagicaCloth2
{
int index = vlist[i];
if (index != vindex && index != tvindex)
joinVlink.SetLimit((ushort)index);
joinVlink.MC2SetLimit((ushort)index);
}
for (int i = 0; i < tvlist.Length; i++)
{
int index = tvlist[i];
if (index != vindex && index != tvindex)
joinVlink.SetLimit((ushort)index);
joinVlink.MC2SetLimit((ushort)index);
}
// 点になるのはNG
@ -739,13 +739,13 @@ namespace MagicaCloth2
// 結合後のトライアングルの外周をひと筆書きし、すべての外周頂点が使われているならOK!
// 外周頂点が一筆書きできない場合はつ以上のグループに分かれいるX型になる
var stack = new FixedList512Bytes<ushort>();
stack.Push(joinVlink[0]);
stack.MC2Push(joinVlink[0]);
while (stack.Length > 0)
{
ushort index = stack.Pop();
ushort index = stack.MC2Pop();
if (joinVlink.Contains(index) == false)
continue;
joinVlink.RemoveItemAtSwapBack(index);
joinVlink.MC2RemoveItemAtSwapBack(index);
var link = vertexToVertexArray[index];
for (int i = 0; i < link.Length; i++)
@ -754,7 +754,7 @@ namespace MagicaCloth2
if (joinVlink.Contains(nindex))
{
// next
stack.Push(nindex);
stack.MC2Push(nindex);
}
}
}

View File

@ -35,9 +35,7 @@ namespace MagicaCloth2
public DebugAxis animatedAxis = DebugAxis.None;
public bool animatedShape = false;
public bool inertiaCenter = true;
//public bool basicPosition = false;
//public DebugAxis basicAxis = DebugAxis.None;
//public bool basicShape = false;
public bool customSkinningBone = true;
//=====================================================================
// ■デバッグ用
@ -60,6 +58,7 @@ namespace MagicaCloth2
//public bool horizontalDistanceConstraint = false;
public bool collisionNormal = false;
public bool cellCube = false;
public bool baseLinePos = false;
public int vertexMinIndex = 0;
public int vertexMaxIndex = 100000;
public int triangleMinIndex = 0;
@ -103,16 +102,11 @@ namespace MagicaCloth2
#endif
}
public float GetLineSize()
{
//return 0.03f; // 固定
return 0.05f; // 固定
}
public float GetLineSize() => 0.05f; // 固定
public float GetInertiaCenterRadius()
{
return 0.01f; // 固定
}
public float GetInertiaCenterRadius() => 0.01f; // 固定
public float GetCustomSkinningRadius() => 0.02f; // 固定
public bool IsReferOldPos()
{

View File

@ -1,6 +1,7 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Runtime.CompilerServices;
using Unity.Mathematics;
using UnityEngine;
@ -428,7 +429,7 @@ namespace MagicaCloth2
{
float time = i / 15.0f;
float val = curve.Evaluate(time);
m.SetValue(i, val);
m.MC2SetValue(i, val);
}
return m;
@ -447,32 +448,81 @@ namespace MagicaCloth2
int index = (int)(math.saturate(time) * 15);
time -= index * interval;
float t = time / interval;
return math.lerp(curve.GetValue(index), curve.GetValue(index + 1), t);
return math.lerp(curve.MC2GetValue(index), curve.MC2GetValue(index + 1), t);
}
//=========================================================================================
/// <summary>
/// 8bitフラグからコライダータイプを取得する
/// </summary>
/// <param name="flag"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ColliderManager.ColliderType GetColliderType(in ExBitFlag8 flag)
{
return (ColliderManager.ColliderType)(flag.Value & 0x0f);
}
/// <summary>
/// 8bitフラグにコライダータイプを設定する
/// 16bitフラグにコライダータイプを設定する
/// </summary>
/// <param name="flag"></param>
/// <param name="ctype"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ExBitFlag8 SetColliderType(ExBitFlag8 flag, ColliderManager.ColliderType ctype)
public static ExBitFlag16 SetColliderType(ExBitFlag16 flag, ColliderManager.ColliderType ctype)
{
flag.Value = (byte)(flag.Value & 0xf0 | (byte)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

@ -17,7 +17,7 @@ namespace MagicaCloth2
/// GridSize>0である必要あり!
/// </summary>
/// <typeparam name="T"></typeparam>
public class GridMap<T> : IDisposable where T : unmanaged, IEquatable<T>
public class GridMap<T> : IDisposable where T : unmanaged // , IEquatable<T>
{
private NativeParallelMultiHashMap<int3, T> gridMap;

View File

@ -3,10 +3,6 @@
// https://magicasoft.jp
using System.Runtime.CompilerServices;
using System.Threading;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
namespace MagicaCloth2
@ -19,13 +15,12 @@ namespace MagicaCloth2
/// <summary>
/// 固定小数点への変換倍率
/// </summary>
const int ToFixed = 100000;
internal const int ToFixed = 1000000;
/// <summary>
/// 少数への復元倍率
/// </summary>
const float ToFloat = 0.00001f;
internal const float ToFloat = 0.000001f;
//=========================================================================================
/// <summary>
@ -65,17 +60,6 @@ namespace MagicaCloth2
}
}
/// <summary>
/// 集計バッファのカウンタのみインクリメントする
/// </summary>
/// <param name="index"></param>
/// <param name="cntPt"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe internal static void Increment(int index, int* cntPt)
{
Interlocked.Increment(ref cntPt[index]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe internal static void Max(int index, float value, int* pt)
{
@ -91,16 +75,16 @@ namespace MagicaCloth2
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float3 ReadAverageFloat3(int index, in NativeArray<int> countArray, in NativeArray<int> sumArray)
unsafe internal static float3 ReadAverageFloat3(int index, int* cntPt, int* sumPt)
{
int count = countArray[index];
int count = cntPt[index];
if (count == 0)
return 0;
int dataIndex = index * 3;
// 集計
float3 add = new float3(sumArray[dataIndex], sumArray[dataIndex + 1], sumArray[dataIndex + 2]);
float3 add = new float3(sumPt[dataIndex], sumPt[dataIndex + 1], sumPt[dataIndex + 2]);
add /= count;
// データは固定小数点なので戻す
@ -110,10 +94,10 @@ namespace MagicaCloth2
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float3 ReadFloat3(int index, in NativeArray<int> bufferArray)
unsafe internal static float3 ReadFloat3(int index, int* vecPt)
{
int dataIndex = index * 3;
float3 v = new float3(bufferArray[dataIndex], bufferArray[dataIndex + 1], bufferArray[dataIndex + 2]);
float3 v = new float3(vecPt[dataIndex], vecPt[dataIndex + 1], vecPt[dataIndex + 2]);
// データは固定小数点なので戻す
v *= ToFloat;
@ -122,351 +106,9 @@ namespace MagicaCloth2
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float ReadFloat(int index, in NativeArray<int> bufferArray)
unsafe internal static float ReadFloat(int index, int* floatPt)
{
return bufferArray[index] * ToFloat;
}
#if false
/// <summary>
/// 指定アドレスにfloat値を加算する
/// </summary>
/// <param name="pt"></param>
/// <param name="index"></param>
/// <param name="value"></param>
unsafe public static void AddFloat(int* pt, int index, float value)
{
float current = UnsafeUtility.ReadArrayElement<float>(pt, index);
int currenti = math.asint(current);
while (true)
{
float next = current + value;
int nexti = math.asint(next);
int prev = Interlocked.CompareExchange(ref pt[index], nexti, currenti);
if (prev == currenti)
return;
else
{
currenti = prev;
current = math.asfloat(prev);
}
}
}
#endif
//=========================================================================================
/// <summary>
/// 加算集計バッファを平均化してnextPosに加算する
/// </summary>
/// <param name="particleList"></param>
/// <param name="jobHandle"></param>
/// <returns></returns>
internal static JobHandle SolveAggregateBufferAndClear(in NativeList<int> particleList, float velocityAttenuation, JobHandle jobHandle)
{
var sm = MagicaManager.Simulation;
if (velocityAttenuation > 1e-06f)
{
// 速度影響あり
var job = new AggregateWithVelocityJob()
{
jobParticleIndexList = particleList,
nextPosArray = sm.nextPosArray.GetNativeArray(),
velocityPosArray = sm.velocityPosArray.GetNativeArray(),
velocityAttenuation = velocityAttenuation,
countArray = sm.countArray,
sumArray = sm.sumArray,
};
jobHandle = job.Schedule(particleList, 16, jobHandle);
}
else
{
// 速度影響なし
var job = new AggregateJob()
{
//velocityLimit = velocityLimit,
jobParticleIndexList = particleList,
nextPosArray = sm.nextPosArray.GetNativeArray(),
countArray = sm.countArray,
sumArray = sm.sumArray,
};
jobHandle = job.Schedule(particleList, 16, jobHandle);
}
return jobHandle;
}
[BurstCompile]
struct AggregateJob : IJobParallelForDefer
{
// 速度制限
//public float velocityLimit;
[Unity.Collections.ReadOnly]
public NativeList<int> jobParticleIndexList;
// particle
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
// aggregate
[NativeDisableParallelForRestriction]
public NativeArray<int> countArray;
[NativeDisableParallelForRestriction]
public NativeArray<int> sumArray;
// 集計パーティクルごと
public void Execute(int index)
{
int pindex = jobParticleIndexList[index];
int count = countArray[pindex];
if (count == 0)
return;
int dataIndex = pindex * 3;
// 集計
float3 add = new float3(sumArray[dataIndex], sumArray[dataIndex + 1], sumArray[dataIndex + 2]);
add /= count;
// データは固定小数点なので戻す
add *= ToFloat;
// 速度制限
//add = MathUtility.ClampVector(add, velocityLimit);
// 書き出し
nextPosArray[pindex] = nextPosArray[pindex] + add;
// 集計バッファクリア
countArray[pindex] = 0;
sumArray[dataIndex] = 0;
sumArray[dataIndex + 1] = 0;
sumArray[dataIndex + 2] = 0;
}
}
/// <summary>
/// 速度影響あり
/// </summary>
[BurstCompile]
struct AggregateWithVelocityJob : IJobParallelForDefer
{
[Unity.Collections.ReadOnly]
public NativeList<int> jobParticleIndexList;
// particle
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> velocityPosArray;
// aggregate
public float velocityAttenuation;
[NativeDisableParallelForRestriction]
public NativeArray<int> countArray;
[NativeDisableParallelForRestriction]
public NativeArray<int> sumArray;
// 集計パーティクルごと
public void Execute(int index)
{
int pindex = jobParticleIndexList[index];
int count = countArray[pindex];
if (count == 0)
return;
int dataIndex = pindex * 3;
// 集計
float3 add = new float3(sumArray[dataIndex], sumArray[dataIndex + 1], sumArray[dataIndex + 2]);
add /= count;
// データは固定小数点なので戻す
add *= ToFloat;
// 書き出し
nextPosArray[pindex] = nextPosArray[pindex] + add;
// 速度影響
velocityPosArray[pindex] = velocityPosArray[pindex] + add * velocityAttenuation;
// 集計バッファクリア
countArray[pindex] = 0;
sumArray[dataIndex] = 0;
sumArray[dataIndex + 1] = 0;
sumArray[dataIndex + 2] = 0;
}
}
internal static JobHandle SolveAggregateBufferAndClear(in ExProcessingList<int> processingList, float velocityAttenuation, JobHandle jobHandle)
{
return SolveAggregateBufferAndClear(processingList.Buffer, processingList.Counter, velocityAttenuation, jobHandle);
}
unsafe internal static JobHandle SolveAggregateBufferAndClear(in NativeArray<int> particleArray, in NativeReference<int> counter, float velocityAttenuation, JobHandle jobHandle)
{
var sm = MagicaManager.Simulation;
if (velocityAttenuation > 1e-06f)
{
// 速度影響あり
var job = new AggregateWithVelocityJob2()
{
particleIndexArray = particleArray,
nextPosArray = sm.nextPosArray.GetNativeArray(),
velocityPosArray = sm.velocityPosArray.GetNativeArray(),
velocityAttenuation = velocityAttenuation,
countArray = sm.countArray,
sumArray = sm.sumArray,
};
jobHandle = job.Schedule((int*)counter.GetUnsafePtrWithoutChecks(), 16, jobHandle);
}
else
{
// 速度影響なし
var job = new AggregateJob2()
{
//velocityLimit = velocityLimit,
particleIndexArray = particleArray,
nextPosArray = sm.nextPosArray.GetNativeArray(),
countArray = sm.countArray,
sumArray = sm.sumArray,
};
jobHandle = job.Schedule((int*)counter.GetUnsafePtrWithoutChecks(), 16, jobHandle);
}
return jobHandle;
}
[BurstCompile]
struct AggregateJob2 : IJobParallelForDefer
{
// 速度制限
//public float velocityLimit;
[Unity.Collections.ReadOnly]
public NativeArray<int> particleIndexArray;
// particle
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
// aggregate
[NativeDisableParallelForRestriction]
public NativeArray<int> countArray;
[NativeDisableParallelForRestriction]
public NativeArray<int> sumArray;
// 集計パーティクルごと
public void Execute(int index)
{
int pindex = particleIndexArray[index];
int count = countArray[pindex];
if (count == 0)
return;
int dataIndex = pindex * 3;
// 集計
float3 add = new float3(sumArray[dataIndex], sumArray[dataIndex + 1], sumArray[dataIndex + 2]);
add /= count;
// データは固定小数点なので戻す
add *= ToFloat;
// 速度制限
//add = MathUtility.ClampVector(add, velocityLimit);
// 書き出し
nextPosArray[pindex] = nextPosArray[pindex] + add;
// 集計バッファクリア
countArray[pindex] = 0;
sumArray[dataIndex] = 0;
sumArray[dataIndex + 1] = 0;
sumArray[dataIndex + 2] = 0;
}
}
/// <summary>
/// 速度影響あり
/// </summary>
[BurstCompile]
struct AggregateWithVelocityJob2 : IJobParallelForDefer
{
[Unity.Collections.ReadOnly]
public NativeArray<int> particleIndexArray;
// particle
[NativeDisableParallelForRestriction]
public NativeArray<float3> nextPosArray;
[NativeDisableParallelForRestriction]
public NativeArray<float3> velocityPosArray;
// aggregate
public float velocityAttenuation;
[NativeDisableParallelForRestriction]
public NativeArray<int> countArray;
[NativeDisableParallelForRestriction]
public NativeArray<int> sumArray;
// 集計パーティクルごと
public void Execute(int index)
{
int pindex = particleIndexArray[index];
int count = countArray[pindex];
if (count == 0)
return;
int dataIndex = pindex * 3;
// 集計
float3 add = new float3(sumArray[dataIndex], sumArray[dataIndex + 1], sumArray[dataIndex + 2]);
add /= count;
// データは固定小数点なので戻す
add *= ToFloat;
// 書き出し
nextPosArray[pindex] = nextPosArray[pindex] + add;
// 速度影響
velocityPosArray[pindex] = velocityPosArray[pindex] + add * velocityAttenuation;
// 集計バッファクリア
countArray[pindex] = 0;
sumArray[dataIndex] = 0;
sumArray[dataIndex + 1] = 0;
sumArray[dataIndex + 2] = 0;
}
}
/// <summary>
/// 集計バッファのカウンタのみゼロクリアする
/// </summary>
/// <param name="jobHandle"></param>
/// <returns></returns>
internal static JobHandle ClearCountArray(JobHandle jobHandle)
{
var sm = MagicaManager.Simulation;
return JobUtility.Fill(sm.countArray, sm.countArray.Length, 0, jobHandle);
return floatPt[index] * ToFloat;
}
}
}

View File

@ -15,7 +15,7 @@ namespace MagicaCloth2
/// <param name="index"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float GetValue(in this float4x4 m, int index)
public static float MC2GetValue(in this float4x4 m, int index)
{
index = math.clamp(index, 0, 15);
return m[index / 4][index % 4];
@ -28,7 +28,7 @@ namespace MagicaCloth2
/// <param name="index"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetValue(ref this float4x4 m, int index, float value)
public static void MC2SetValue(ref this float4x4 m, int index, float value)
{
index = math.clamp(index, 0, 15);
m[index / 4][index % 4] = value;
@ -41,7 +41,7 @@ namespace MagicaCloth2
/// <param name="time">0.0 ~ 1.0</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float EvaluateCurveClamp01(in this float4x4 m, float time)
public static float MC2EvaluateCurveClamp01(in this float4x4 m, float time)
{
return math.saturate(DataUtility.EvaluateCurve(m, time));
}
@ -53,7 +53,7 @@ namespace MagicaCloth2
/// <param name="time">0.0 ~ 1.0</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float EvaluateCurve(in this float4x4 m, float time)
public static float MC2EvaluateCurve(in this float4x4 m, float time)
{
return DataUtility.EvaluateCurve(m, time);
}

View File

@ -223,6 +223,45 @@ namespace MagicaCloth2
return quaternion.AxisAngle(math.normalize(axis), angle * t);
}
/// <summary>
/// fromからtoへ回転させるクォータニオンを返します(単位化なし)
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <param name="t">補間率(0.0-1.0)</param>
/// <returns></returns>
public static quaternion FromToRotationWithoutNormalize(in float3 v1, in float3 v2, float t = 1.0f)
{
//float3 v1 = math.normalize(from);
//float3 v2 = math.normalize(to);
float c = Clamp1(math.dot(v1, v2));
float angle = math.acos(c);
float3 axis = math.cross(v1, v2);
if (math.abs(1.0f + c) < 1e-06f)
{
angle = (float)math.PI;
if (v1.x > v1.y && v1.x > v1.z)
{
axis = math.cross(v1, new float3(0, 1, 0));
}
else
{
axis = math.cross(v1, new float3(1, 0, 0));
}
}
else if (math.abs(1.0f - c) < 1e-06f)
{
//angle = 0.0f;
//axis = new float3(1, 0, 0);
return quaternion.identity;
}
return quaternion.AxisAngle(math.normalize(axis), angle * t);
}
/// <summary>
/// fromからtoへ回転させるクォータニオンを返します
/// </summary>
@ -287,13 +326,15 @@ namespace MagicaCloth2
public static quaternion ToRotation(in float3 nor, in float3 tan)
{
#if MC2_DEBUG
// 安全性確認
float ln = math.length(nor);
float lt = math.length(tan);
Develop.Assert(ln > 0.0f);
Develop.Assert(lt > 0.0f);
Develop.Assert(ln > 0.99f && ln < 1.01f);
Develop.Assert(lt > 0.99f && lt < 1.01f);
float dot = math.dot(nor / ln, tan / lt);
Develop.Assert(dot != 1.0f && dot != -1.0f);
#endif
// 2 つの入力ベクトルは単位長であり、同一直線上にないことが前提となります。
return quaternion.LookRotation(tan, nor);
}
@ -402,6 +443,45 @@ namespace MagicaCloth2
axis = q.value.xyz / s;
}
/// <summary>
/// クォータニオンからオイラー角度を計算して返す
/// </summary>
/// <param name="q"></param>
/// <returns>(Deg)角度</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 ToEuler(in quaternion q)
{
float3 angles = 0;
// クォータニオンの成分
float qx = q.value.x;
float qy = q.value.y;
float qz = q.value.z;
float qw = q.value.w;
// ピッチ (x軸回転)
float sinX = 2f * (qw * qx - qz * qy);
if (math.abs(sinX) >= 0.99999f) // ジンバルロックの検出
{
angles.x = math.sign(sinX) * 90f; // ±90度(deg)
angles.y = math.atan2(2f * (qw * qy + qx * qz), 1f - 2f * (qx * qx + qy * qy));
angles.y = math.degrees(angles.y);
angles.z = 0f; // ジンバルロックでロールは不定
}
else
{
angles.x = math.asin(sinX);
// ヨー (y軸回転)
angles.y = math.atan2(2f * (qw * qy + qx * qz), 1f - 2f * (qx * qx + qy * qy));
// ロール (z軸回転)
//angles.z = math.atan2(2f * (qw * qz + qx * qy), 1f - 2f * (qy * qy + qz * qz));
angles.z = math.atan2(2f * (qw * qz + qx * qy), 1f - 2f * (qx * qx + qz * qz)); // どうやらこれっぽい
angles = math.degrees(angles);
}
return angles;
}
/// <summary>
/// 与えられた線分abおよび点cに対して、ab上の最近接点t(0.0-1.0)を計算して返す
/// </summary>
@ -570,6 +650,82 @@ namespace MagicaCloth2
return math.dot(c1 - c2, c1 - c2);
}
/// <summary>
/// 2つの線分(p1-q1)(p2-q2)の最近接点(s, t)を計算する
/// この関数ではs/tのみで接点と距離は計算しない
/// </summary>
/// <param name="p1">線分1の始点</param>
/// <param name="q1">線分1の終点</param>
/// <param name="p2">線分2の始点</param>
/// <param name="q2">線分2の終点</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ClosestPtSegmentSegment2(in float3 p1, in float3 q1, in float3 p2, in float3 q2, out float s, out float t)
{
//s = 0.0f;
//t = 0.0f;
float3 d1 = q1 - p1; // 線分s1の方向ベクトル
float3 d2 = q2 - p2; // 線分s2の方向ベクトル
float3 r = p1 - p2;
float a = math.dot(d1, d1); // 線分s1の距離の平方、常に正
float e = math.dot(d2, d2); // 線分s2の距離の平方、常に正
float f = math.dot(d2, r);
// 片方あるいは両方の線分が点に縮退しているかどうかチェック
if (a <= 1e-8f && e <= 1e-8f)
{
// 両方の線分が点に縮退
s = t = 0.0f;
}
else if (a <= 1e-8f)
{
// 最初の線分が点に縮退
s = 0.0f;
t = math.saturate(f / e);
}
else
{
float c = math.dot(d1, r);
if (e <= 1e-8f)
{
// 2番目の線分が点に縮退
t = 0.0f;
s = math.saturate(-c / a);
}
else
{
// ここから一般的な縮退の場合を開始
float b = math.dot(d1, d2);
float denom = a * e - b * b; // 常に正
// 線分が平行でない場合、L1上のL2に対する最近接点を計算、そして
// 線分s1に対してクランプ。そうでない場合は任意s(ここでは0)を選択
if (denom != 0.0f)
{
s = math.saturate((b * f - c * e) / denom);
}
else
{
s = 0.0f;
}
// L2上のs1(s)に対する最近接点を以下を用いて計算
// t = dot((p1 + d1 * s) - p2, d2) / dot(d2, d2) = (b * s + f) / e
t = (b * s + f) / e;
// tが[0,1]の中にあれば終了。
// そうでなければtをクランプ、sをtの新しい値に対して以下を用いて再計算
// s = dot((p2 + d2 * t) - p1, d1) / dot(d1, d1) = (t * b - c) / a
// そしてsを[0,1]にクランプ
if (t < 0.0f)
{
t = 0.0f;
s = math.saturate(-c / a);
}
else if (t > 1.0f)
{
t = 1.0f;
s = math.saturate((b - c) / a);
}
}
}
}
/// <summary>
/// 三角形(abc)から点(p)への最近接点とその重心座標uvwを返す
/// </summary>
@ -780,6 +936,7 @@ namespace MagicaCloth2
return tan;
}
#if false
/// <summary>
/// トライアングルの回転姿勢を返す
/// 法線と(重心-p0)の軸からなるクォータニオン
@ -815,6 +972,7 @@ namespace MagicaCloth2
var tan = math.normalize(p3 - p2);
return quaternion.LookRotation(tan, n);
}
#endif
/// <summary>
/// トライアングルペアのなす角を返す(ラジアン)
@ -1064,30 +1222,45 @@ namespace MagicaCloth2
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 TransformPoint(in float3 pos, in float4x4 localToWorldMatrix)
public static float3 TransformPoint(in float3 pos, in float3 wpos, in quaternion wrot, in float3 wscl)
{
return math.transform(localToWorldMatrix, pos);
return math.transform(Matrix4x4.TRS(wpos, wrot, wscl), pos);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 TransformVector(in float3 vec, in float4x4 localToWorldMatrix)
public static float3 TransformPoint(in float3 pos, in float4x4 m)
{
return math.mul(localToWorldMatrix, new float4(vec, 0)).xyz;
return math.transform(m, pos);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 TransformDirection(in float3 dir, in float4x4 localToWorldMatrix)
public static float3 TransformVector(in float3 vec, in float4x4 m)
{
return math.mul(m, new float4(vec, 0)).xyz;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 TransformDirection(in float3 dir, in float4x4 m)
{
float len = math.length(dir);
if (len > 0.0f)
return math.normalize(TransformVector(dir, localToWorldMatrix)) * len;
return math.normalize(TransformVector(dir, m)) * len;
else
return dir;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static quaternion TransformRotation(in quaternion rot, in float4x4 m, in float3 normalTangentFlip)
{
ToNormalTangent(rot, out float3 nor, out float3 tan);
nor = math.mul(m, new float4(nor, 0)).xyz * normalTangentFlip.y;
tan = math.mul(m, new float4(tan, 0)).xyz * normalTangentFlip.z;
return quaternion.LookRotation(tan, nor);
}
/// <summary>
/// 距離を空間変換する
/// 不均等スケールを考慮して各軸の平均値を返す
/// 非一様スケールを考慮して各軸の平均値を返す
/// </summary>
/// <param name="dist"></param>
/// <param name="localToWorldMatrix"></param>
@ -1139,6 +1312,12 @@ namespace MagicaCloth2
return math.transform(worldToLocalMatrix, pos);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 InverseTransformPoint(in float3 pos, in float3 wpos, in quaternion wrot, in float3 wscl)
{
return math.transform(math.inverse(Matrix4x4.TRS(wpos, wrot, wscl)), pos);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 InverseTransformVector(in float3 vec, in float4x4 worldToLocalMatrix)
{
@ -1364,6 +1543,7 @@ namespace MagicaCloth2
if (math.dot(planeDir, v) < 0.0f)
{
// 押し出し発生
// 押出し座標
outPos = pos - gv;
@ -1373,9 +1553,10 @@ namespace MagicaCloth2
}
else
{
// 押し出し不要。何もしない
outPos = pos;
// 面までの距離を返す
// 面までの距離を返す(+)
return len;
}
}
@ -1554,5 +1735,57 @@ namespace MagicaCloth2
Develop.Assert(mass > 0.0f);
return 1.0f / mass;
}
/// <summary>
/// 数をn分割して、指定インデックスの範囲を返します。
/// 数は整数のみ。
/// 例100(dataLength)を5(divCount)分割した結果
/// divIndex(0):0~20
/// divIndex(1):20~40
/// divIndex(2):40~60
/// divIndex(3):60~80
/// divIndex(4):80~100
///
/// ただし、数が分割数を下回る場合は範囲外のインデックスは-1となる
/// 例3(dataLength)を5(divCount)分割した結果
/// divIndex(0):0~1
/// divIndex(1):1~2
/// divIndex(2):2~3
/// divIndex(3):-1~-1
/// divIndex(4):-1~-1
/// </summary>
/// <param name="dataLength"></param>
/// <param name="divCount"></param>
/// <param name="divIndex"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int2 CalcSplitRange(int dataLength, int divCount, int divIndex)
{
if (dataLength <= 0)
return -1;
if (dataLength < divCount)
{
if (divIndex < dataLength)
return new int2(divIndex, divIndex + 1);
else
return -1;
}
float segment = (float)dataLength / divCount;
int start = (int)(segment * divIndex);
int end = (divIndex == divCount - 1) ? dataLength : (int)(segment * (divIndex + 1));
return new int2(start, end);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static DataChunk GetWorkerChunk(int dataLenght, int workerCount, int workerIndex)
{
int2 range = CalcSplitRange(dataLenght, workerCount, workerIndex);
if (range.x < 0)
return DataChunk.Empty;
else
return new DataChunk(range.x, range.y - range.x);
}
}
}

View File

@ -7,6 +7,7 @@ using System.Text;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
namespace MagicaCloth2
{
@ -389,6 +390,12 @@ namespace MagicaCloth2
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);
@ -399,11 +406,22 @@ namespace MagicaCloth2
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[]など
@ -474,6 +492,11 @@ namespace MagicaCloth2
}
}
public void Remove(int index)
{
Remove(new DataChunk(index));
}
public void RemoveAndFill(DataChunk chunk, T clearData = default(T))
{
Remove(chunk);

View File

@ -67,6 +67,11 @@ namespace MagicaCloth2
count = length;
}
public ExSimpleNativeArray(SerializationData sdata)
{
Deserialize(sdata);
}
public void Dispose()
{
if (nativeArray.IsCreated)
@ -108,11 +113,13 @@ namespace MagicaCloth2
//=========================================================================================
/// <summary>
/// 領域のみ拡張する
/// すでにその長さの領域が確保されている場合は何もしない
/// </summary>
/// <param name="capacity"></param>
public void AddCapacity(int capacity)
/// <param name="newLength"></param>
public void SetLength(int newLength)
{
Expand(capacity, true);
if (newLength > length)
Expand(newLength - length, force: true, copy: true);
}
/// <summary>
@ -216,6 +223,16 @@ namespace MagicaCloth2
}
}
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);
@ -481,7 +498,8 @@ namespace MagicaCloth2
/// </summary>
/// <param name="dataLength"></param>
/// <param name="force">強制的に領域を追加</param>
void Expand(int dataLength, bool force = false)
/// <param name="copy">古いデータをコピーするかどうか</param>
void Expand(int dataLength, bool force = false, bool copy = true)
{
int newlength = force ? length + dataLength : count + dataLength;
@ -499,8 +517,11 @@ namespace MagicaCloth2
var newNativeArray = new NativeArray<T>(newlength, Allocator.Persistent);
// copy
// コピーは使用分だけ
NativeArray<T>.Copy(nativeArray, newNativeArray, count);
if (copy)
{
// コピーは使用分だけ
NativeArray<T>.Copy(nativeArray, newNativeArray, count);
}
nativeArray.Dispose();
nativeArray = newNativeArray;
@ -512,8 +533,8 @@ namespace MagicaCloth2
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"ExNativeArray Length:{Length} Count:{Count} IsValid:{IsValid}");
sb.AppendLine("---- Datas[100] ----");
sb.AppendLine($"ExSimpleNativeArray Length:{Length} Count:{Count} IsValid:{IsValid}");
sb.AppendLine("---- Datas[~100] ----");
if (IsValid)
{
for (int i = 0; i < Length && i < 100; i++)
@ -524,5 +545,59 @@ namespace MagicaCloth2
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

@ -15,7 +15,7 @@ namespace MagicaCloth2
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCapacity<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static bool MC2IsCapacity<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
@ -30,7 +30,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2Set<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
@ -46,7 +46,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetLimit<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2SetLimit<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
@ -67,7 +67,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveItemAtSwapBack<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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++)
{
@ -83,13 +83,13 @@ namespace MagicaCloth2
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Push<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Pop<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Pop<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
@ -101,13 +101,13 @@ namespace MagicaCloth2
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Enqueue<T>(ref this FixedList128Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Dequque<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Dequque<T>(ref this FixedList128Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);

View File

@ -8,14 +8,13 @@ using UnityEngine;
namespace MagicaCloth2
{
//[BurstCompatible]
public static class FixedList32BytesExtensions
{
//=====================================================================
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCapacity<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static bool MC2IsCapacity<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
@ -31,7 +30,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2Set<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
@ -47,7 +46,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetLimit<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2SetLimit<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
@ -68,7 +67,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveItemAtSwapBack<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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++)
{
@ -84,13 +83,13 @@ namespace MagicaCloth2
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Push<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Pop<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Pop<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
@ -102,13 +101,13 @@ namespace MagicaCloth2
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Enqueue<T>(ref this FixedList32Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Dequque<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Dequque<T>(ref this FixedList32Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);

View File

@ -15,7 +15,7 @@ namespace MagicaCloth2
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCapacity<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static bool MC2IsCapacity<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
@ -30,7 +30,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2Set<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
@ -46,7 +46,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetLimit<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2SetLimit<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
@ -67,7 +67,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveItemAtSwapBack<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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++)
{
@ -83,13 +83,13 @@ namespace MagicaCloth2
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Push<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Pop<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Pop<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
@ -101,13 +101,13 @@ namespace MagicaCloth2
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Enqueue<T>(ref this FixedList4096Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Dequque<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Dequque<T>(ref this FixedList4096Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);

View File

@ -15,7 +15,7 @@ namespace MagicaCloth2
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCapacity<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static bool MC2IsCapacity<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
@ -30,7 +30,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2Set<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
@ -46,7 +46,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetLimit<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2SetLimit<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
@ -67,7 +67,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveItemAtSwapBack<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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++)
{
@ -83,13 +83,13 @@ namespace MagicaCloth2
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Push<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Pop<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Pop<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
@ -101,13 +101,13 @@ namespace MagicaCloth2
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Enqueue<T>(ref this FixedList512Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Dequque<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Dequque<T>(ref this FixedList512Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);

View File

@ -15,7 +15,7 @@ namespace MagicaCloth2
// Common
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCapacity<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static bool MC2IsCapacity<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
return fixedList.Length >= fixedList.Capacity;
}
@ -30,7 +30,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2Set<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Contains(item) == false)
{
@ -46,7 +46,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetLimit<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
public static void MC2SetLimit<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
{
if (fixedList.Length >= fixedList.Capacity)
{
@ -67,7 +67,7 @@ namespace MagicaCloth2
/// <param name="fixedList"></param>
/// <param name="item"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveItemAtSwapBack<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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++)
{
@ -83,13 +83,13 @@ namespace MagicaCloth2
// Stack
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Push<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Pop<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Pop<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
int index = fixedList.Length - 1;
T item = fixedList[index];
@ -101,13 +101,13 @@ namespace MagicaCloth2
// Queue
//=====================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Enqueue<T>(ref this FixedList64Bytes<T> fixedList, T item) where T : unmanaged, IEquatable<T>
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 Dequque<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
public static T MC2Dequque<T>(ref this FixedList64Bytes<T> fixedList) where T : unmanaged, IEquatable<T>
{
T item = fixedList[0];
fixedList.RemoveAt(0);

View File

@ -8,14 +8,14 @@ namespace MagicaCloth2
/// <summary>
/// NativeArrayの拡張メソッド
/// </summary>
static class NativeArrayExtensions
public static class NativeArrayExtensions
{
/// <summary>
/// NativeArrayが確保されている場合のみDispose()する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
public static void DisposeSafe<T>(ref this NativeArray<T> array) where T : unmanaged
public static void MC2DisposeSafe<T>(ref this NativeArray<T> array) where T : unmanaged
{
if (array.IsCreated)
array.Dispose();
@ -31,13 +31,55 @@ namespace MagicaCloth2
/// <param name="size"></param>
/// <param name="allocator"></param>
/// <param name="options"></param>
public static void Resize<T>(ref this NativeArray<T> array, int size, Allocator allocator = Allocator.Persistent, NativeArrayOptions options = NativeArrayOptions.ClearMemory) where T : unmanaged
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.DisposeSafe();
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

@ -2,17 +2,21 @@
// 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>
static class NativeMultiHashMapExtensions
public static class NativeMultiHashMapExtensions
{
/// <summary>
/// NativeMultiHashMapのキーに指定データが存在するか判定する
/// NativeParallelMultiHashMapのキーに指定データが存在するか判定する
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
@ -21,9 +25,9 @@ namespace MagicaCloth2
/// <param name="value"></param>
/// <returns></returns>
#if MC2_COLLECTIONS_200
public static bool Contains<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
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 Contains<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue>
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))
@ -35,7 +39,7 @@ namespace MagicaCloth2
}
/// <summary>
/// NativeMultiHashMapキーに対して重複なしのデータを追加する
/// NativeParallelMultiHashMapキーに対して重複なしのデータを追加する
/// すでにキーに同じデータが存在する場合は追加しない。
/// </summary>
/// <typeparam name="TKey"></typeparam>
@ -44,26 +48,59 @@ namespace MagicaCloth2
/// <param name="key"></param>
/// <param name="value"></param>
#if MC2_COLLECTIONS_200
public static void UniqueAdd<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
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 UniqueAdd<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key, TValue value) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue>
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.Contains(key, value) == false)
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> ToFixedList512Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
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> ToFixedList512Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : struct, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
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>();
@ -84,9 +121,9 @@ namespace MagicaCloth2
/// <param name="index"></param>
/// <returns></returns>
#if MC2_COLLECTIONS_200
public static FixedList128Bytes<TValue> ToFixedList128Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
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> ToFixedList128Bytes<TKey, TValue>(ref this NativeParallelMultiHashMap<TKey, TValue> map, TKey key) where TKey : struct, IEquatable<TKey> where TValue : unmanaged, IEquatable<TValue>
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>();
@ -100,5 +137,91 @@ namespace MagicaCloth2
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

@ -18,7 +18,7 @@ namespace MagicaCloth2
/// <param name="dataCount"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe public static int InterlockedStartIndex(ref this NativeReference<int> counter, int dataCount)
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;

View File

@ -2,25 +2,31 @@
// 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)
{
@ -119,9 +125,13 @@ namespace MagicaCloth2
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";
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;
}
@ -133,6 +143,8 @@ namespace MagicaCloth2
{
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;
}

View File

@ -13,7 +13,7 @@ namespace MagicaCloth2
string name = string.Empty;
DateTime stime;
DateTime etime;
//bool isFinish;
bool isFinish;
public TimeSpan() { }
@ -21,21 +21,23 @@ namespace MagicaCloth2
{
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;
//}
//etime = DateTime.Now;
if (isFinish == false)
{
etime = DateTime.Now;
isFinish = true;
}
}
public double TotalSeconds()
@ -60,5 +62,10 @@ namespace MagicaCloth2
{
Develop.DebugLog(this);
}
public void Log()
{
Develop.Log(this);
}
}
}

View File

@ -19,7 +19,7 @@ namespace MagicaCloth2
/// <param name="rsetup"></param>
/// <param name="ct"></param>
/// <returns></returns>
public void ImportFrom(RenderSetupData rsetup)
public void ImportFrom(RenderSetupData rsetup, int uvChannel)
{
try
{
@ -44,7 +44,7 @@ namespace MagicaCloth2
rsetup.transformIdList,
rsetup.transformParentIdList,
rsetup.rootTransformIdList,
rsetup.transformLocalPositins,
rsetup.transformLocalPositions,
rsetup.transformLocalRotations,
rsetup.transformPositions,
rsetup.transformRotations,
@ -68,7 +68,7 @@ namespace MagicaCloth2
// メッシュタイプ
meshType = MeshType.NormalMesh;
isBoneCloth = false;
ImportMeshType(rsetup, indices);
ImportMeshType(rsetup, indices, uvChannel);
// スキニングメッシュでは1回スキニングを行いクロスローカル空間に姿勢を変換する
if (rsetup.hasBoneWeight)
@ -129,7 +129,7 @@ namespace MagicaCloth2
/// </summary>
/// <param name="rsetup"></param>
/// <param name="transformIndices"></param>
void ImportMeshType(RenderSetupData rsetup, int[] transformIndices)
void ImportMeshType(RenderSetupData rsetup, int[] transformIndices, int uvChannel)
{
// root bone
skinRootIndex = transformIndices[rsetup.skinRootBoneIndex];
@ -155,6 +155,7 @@ namespace MagicaCloth2
meshData.GetNormals(localNormals.GetNativeArray<Vector3>());
if (meshData.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.Tangent))
{
// 接線情報がメッシュに存在する
using var tangents = new NativeArray<Vector4>(vcnt, Allocator.TempJob);
meshData.GetTangents(tangents);
// tangent変換(Vector4->float3)
@ -162,6 +163,7 @@ namespace MagicaCloth2
}
else
{
// 接線情報がメッシュに存在しない
Develop.DebugLogWarning($"[{name}] Tangents not found!");
// tangentを生成する
// このtangentは描画用では無く姿勢制御用なのである意味適当でも大丈夫
@ -174,11 +176,19 @@ namespace MagicaCloth2
}
if (meshData.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.TexCoord0))
{
meshData.GetUVs(0, uv.GetNativeArray<Vector2>());
uvChannel = Mathf.Clamp(uvChannel, 0, 7);
UnityEngine.Rendering.VertexAttribute useTexCoord = UnityEngine.Rendering.VertexAttribute.TexCoord0 + uvChannel;
if (meshData.HasVertexAttribute(useTexCoord) == false)
{
Develop.LogWarning($"[{name}] UV{uvChannel} not found! => Use UV0.");
uvChannel = 0;
}
//Debug.Log($"Fetch UV:{uvChannel}");
meshData.GetUVs(uvChannel, uv.GetNativeArray<Vector2>());
}
else
{
Debug.LogWarning($"[{name}] UV not found!");
Develop.LogWarning($"[{name}] UV0 not found!");
}
// 属性
@ -439,7 +449,7 @@ namespace MagicaCloth2
skinBoneTransformIndices.AddRange(transformIndices, rsetup.skinBoneCount);
skinBoneBindPoses.AddRange(vcnt);
// Transformの情報をローカル空間に変換し頂点情報に割り当てる
// Transformの情報をクロスローカル空間に変換し頂点情報に割り当てる
// およびバインドポーズの算出
var WtoL = rsetup.initRenderWorldtoLocal;
var LtoW = rsetup.initRenderLocalToWorld;
@ -899,13 +909,17 @@ namespace MagicaCloth2
quaternion rot = transformRotations[vindex];
float3 scl = transformScales[vindex];
// トランスフォーム姿勢をローカル空間に変換する
// トランスフォーム姿勢をクロスローカル空間に変換する
// オリジナル
#if true
float3 lpos = MathUtility.InverseTransformPoint(pos, WtoL);
float3 lnor, ltan;
lnor = math.mul(rot, math.up());
ltan = math.mul(rot, math.forward());
lnor = MathUtility.InverseTransformDirection(lnor, WtoL);
ltan = MathUtility.InverseTransformDirection(ltan, WtoL);
#endif
//Debug.Log($"Import [{vindex}] lpos:{lpos}, lnor:{lnor}, ltan:{ltan}");
localPositions[vindex] = lpos;
localNormals[vindex] = lnor;
@ -928,7 +942,7 @@ namespace MagicaCloth2
/// レンダーデータからインポートする
/// </summary>
/// <param name="renderData"></param>
public void ImportFrom(RenderData renderData)
public void ImportFrom(RenderData renderData, int uvChannel)
{
try
{
@ -938,7 +952,7 @@ namespace MagicaCloth2
throw new MagicaClothProcessingException();
}
ImportFrom(renderData.setupData);
ImportFrom(renderData.setupData, uvChannel);
}
catch (MagicaClothProcessingException)
{
@ -1764,16 +1778,6 @@ namespace MagicaCloth2
return transformData.GetTransformFromIndex(centerTransformIndex);
}
public float4x4 GetCenterLocalToWorldMatrix()
{
return transformData.GetLocalToWorldMatrix(centerTransformIndex);
}
public float4x4 GetCenterWorldToLocalMatrix()
{
return transformData.GetWorldToLocalMatrix(centerTransformIndex);
}
/// <summary>
/// カスタムスキニング用ボーンを登録する
/// </summary>
@ -1828,6 +1832,7 @@ namespace MagicaCloth2
}
//=========================================================================================
#if false
/// <summary>
/// UnityMeshに出力するメインスレッドのみ
/// ※ほぼデバッグ用
@ -1893,93 +1898,6 @@ namespace MagicaCloth2
return mesh;
}
/// <summary>
/// メッシュの基準トランスフォームを返す
/// 通常はレンダラーのtransform
/// </summary>
/// <returns></returns>
public Transform ExportCenterTransform()
{
return transformData.GetTransformFromIndex(centerTransformIndex);
}
public Transform ExportSkinRootBone()
{
return transformData.GetTransformFromIndex(skinRootIndex);
}
/// <summary>
/// メッシュのスキニング用ボーンリストを返す
/// </summary>
/// <returns></returns>
public List<Transform> ExportSkinningBones()
{
var sbones = new List<Transform>(SkinBoneCount);
for (int i = 0; i < SkinBoneCount; i++)
{
sbones.Add(transformData.GetTransformFromIndex(skinBoneTransformIndices[i]));
}
return sbones;
}
/// <summary>
/// メッシュのバウンディングボックスを返す
/// スキニングの場合はスキニングルートボーンからのバウンディングボックスとなる
/// それ以外はセンターボーンからのバウンディングボックスとなる
/// </summary>
/// <returns></returns>
/*public Bounds ExportBounds()
{
float3 offset = 0;
if (skinRootIndex >= 0 && centerTransformIndex != skinRootIndex)
{
// スキニングのルートボーンが別の場合
// ちょっと面倒
float3 wmin = transformData.TransformPoint(centerTransformIndex, boundingBox.Value.Min);
float3 wmax = transformData.TransformPoint(centerTransformIndex, boundingBox.Value.Max);
float3 lmin = transformData.InverseTransformPoint(skinRootIndex, wmin);
float3 lmax = transformData.InverseTransformPoint(skinRootIndex, wmax);
float3 cen = (lmax + lmin) * 0.5f;
float3 size = math.abs(lmax - lmin);
return new Bounds(cen, size);
}
else
{
return new Bounds(boundingBox.Value.Center, boundingBox.Value.Extents);
}
}*/
/// <summary>
/// 現在のメッシュをレンダラーに反映させる(主にデバッグ用)
/// </summary>
/// <param name="ren"></param>
public Mesh ToRenderer(Renderer ren)
{
Mesh mesh = null;
if (IsSuccess == false)
return mesh;
if (ren is MeshRenderer)
{
mesh = ExportToMesh();
var filter = ren.GetComponent<MeshFilter>();
filter.mesh = mesh;
}
else if (ren is SkinnedMeshRenderer)
{
var sren = ren as SkinnedMeshRenderer;
mesh = ExportToMesh(true);
var rootBone = ExportSkinRootBone();
var skinBones = ExportSkinningBones().ToArray();
sren.rootBone = rootBone;
sren.bones = skinBones;
sren.sharedMesh = mesh;
}
return mesh;
}
#endif
}
}

View File

@ -237,6 +237,7 @@ namespace MagicaCloth2
}
}
[BurstCompile]
struct Mapping_CalcDirectWeightJob : IJob
{
// data
@ -282,10 +283,10 @@ namespace MagicaCloth2
// ウエイトバッファ
var weights = new ExCostSortedList4(-1);
stack.Push(pindex);
stack.MC2Push(pindex);
while (stack.IsEmpty == false)
{
pindex = stack.Pop();
pindex = stack.MC2Pop();
if (useSet.Contains(pindex))
continue;
@ -305,7 +306,7 @@ namespace MagicaCloth2
// 次の接続
DataUtility.Unpack12_20(proxyVertexToVertexIndexArray[pindex], out var dcnt, out var dstart);
for (int i = 0; i < dcnt && stack.IsCapacity() == false; i++)
for (int i = 0; i < dcnt && stack.MC2IsCapacity() == false; i++)
{
ushort tindex = proxyVertexToVertexDataArray[dstart + i];
@ -317,7 +318,7 @@ namespace MagicaCloth2
if (dist > weightLength)
continue;
stack.Push(tindex);
stack.MC2Push(tindex);
}
}

View File

@ -94,13 +94,13 @@ namespace MagicaCloth2
if (edgeToTriangleList.ContainsKey(edge))
{
var tlist = edgeToTriangleList[edge];
tlist.Set(i);
tlist.MC2Set(i);
edgeToTriangleList[edge] = tlist;
}
else
{
var tlist = new FixedList128Bytes<int>();
tlist.Set(i);
tlist.MC2Set(i);
edgeToTriangleList.Add(edge, tlist);
}
}

View File

@ -346,9 +346,6 @@ namespace MagicaCloth2
{
center = center,
localPositions = localPositions.GetNativeArray(),
vertexParentIndices = vertexParentIndices,
vertexChildIndexArray = vertexChildIndexArray,
vertexChildDataArray = vertexChildDataArray,
localNormals = localNormals.GetNativeArray(),
localTangents = localTangents.GetNativeArray(),
@ -356,109 +353,14 @@ namespace MagicaCloth2
};
job1.Run(vcnt);
}
#if false // ★どうもうまくいかないので一旦停止!
// 頂点ウエイトから
else if (mode == NormalAlignmentSettings.AlignmentMode.BoneWeight)
{
var job2 = new ProxyNormalWeightAdjustmentJob()
{
WtoL = initWorldToLocal,
localPositions = localPositions.GetNativeArray(),
boneWeights = boneWeights.GetNativeArray(),
transformPositionArray = transformData.positionArray.GetNativeArray(),
localNormals = localNormals.GetNativeArray(),
localTangents = localTangents.GetNativeArray(),
normalAdjustmentRotations = normalAdjustmentRotations,
};
job2.Run(vcnt);
}
#endif
}
#if false
[BurstCompile]
struct ProxyNormalWeightAdjustmentJob : IJobParallelFor
{
public float4x4 WtoL;
[Unity.Collections.ReadOnly]
public NativeArray<float3> localPositions;
[Unity.Collections.ReadOnly]
public NativeArray<VirtualMeshBoneWeight> boneWeights;
// transform
[Unity.Collections.ReadOnly]
public NativeArray<float3> transformPositionArray;
// out
//[Unity.Collections.WriteOnly]
public NativeArray<float3> localNormals;
//[Unity.Collections.WriteOnly]
public NativeArray<float3> localTangents;
[Unity.Collections.WriteOnly]
public NativeArray<quaternion> normalAdjustmentRotations;
public void Execute(int vindex)
{
var lpos = localPositions[vindex];
var bw = boneWeights[vindex];
// 現在の回転
var lrot = MathUtility.ToRotation(localNormals[vindex], localTangents[vindex]);
// 影響するボーンへの方向をまとめる
float3 bv = 0;
int bcnt = bw.Count;
if (bcnt == 0)
return;
for (int i = 0; i < bcnt; i++)
{
var bonePos = math.transform(WtoL, transformPositionArray[bw.boneIndices[i]]);
//var v = transformPositionArray[bw.boneIndices[i]] - lpos;
var v = lpos - bonePos;
v = math.normalizesafe(v, float3.zero);
v *= bw.weights[i];
bv += v;
}
if (math.lengthsq(bv) < Define.System.Epsilon)
return;
// 法線確定
float3 n = math.normalize(bv);
// 法線をもとに接線を計算する
var lnor = localNormals[vindex];
var ltan = localTangents[vindex];
var dotNormal = math.dot(n, lnor);
var dotTangent = math.dot(n, ltan);
float3 tv = dotNormal < dotTangent ? lnor : ltan;
float3 tan = math.cross(tv, n);
//localNormals[vindex] = tan;
//localTangents[vindex] = n;
localNormals[vindex] = n;
localTangents[vindex] = tan;
var nrot = MathUtility.ToRotation(n, tan);
// 補正用回転を算出し格納する
normalAdjustmentRotations[vindex] = math.mul(math.inverse(lrot), nrot);
}
}
#endif
[BurstCompile]
struct ProxyNormalRadiationAdjustmentJob : IJobParallelFor
{
public float3 center;
[Unity.Collections.ReadOnly]
public NativeArray<float3> localPositions;
[Unity.Collections.ReadOnly]
public NativeArray<int> vertexParentIndices;
[Unity.Collections.ReadOnly]
public NativeArray<uint> vertexChildIndexArray;
[Unity.Collections.ReadOnly]
public NativeArray<ushort> vertexChildDataArray;
// out
public NativeArray<float3> localNormals;
@ -472,58 +374,33 @@ namespace MagicaCloth2
var v = lpos - center;
if (math.length(v) < Define.System.Epsilon)
return;
v = math.normalize(v);
// 指定されたセンターからの放射方向に法線を変更する
var n = math.normalize(v);
// 現在の回転
var lrot = MathUtility.ToRotation(localNormals[vindex], localTangents[vindex]);
var ln = localNormals[vindex];
var lt = localTangents[vindex];
var lrot = MathUtility.ToRotation(ln, lt);
// 子がいる場合は子へのベクトルから算出
var nrot = lrot;
int pindex = vertexParentIndices[vindex];
var pack = vertexChildIndexArray[vindex];
DataUtility.Unpack12_20(pack, out var dcnt, out var dstart);
if (dcnt > 0)
// 接線を補正する
float dot = math.dot(n, lt);
if (dot < 0.99f)
{
float3 cv = 0;
for (int i = 0; i < dcnt; i++)
{
int cindex = vertexChildDataArray[dstart + i];
cv += localPositions[cindex] - lpos;
}
if (math.lengthsq(cv) > Define.System.Epsilon)
{
cv = math.normalize(cv);
float3 n = math.cross(cv, v);
n = math.cross(n, cv);
if (math.lengthsq(n) > Define.System.Epsilon)
{
n = math.normalize(n);
localNormals[vindex] = n;
localTangents[vindex] = cv;
nrot = MathUtility.ToRotation(n, cv);
}
}
float3 bn = math.normalize(math.cross(n, lt));
lt = math.normalize(math.cross(bn, n));
}
// 子がいなく親がいる場合は親からのベクトルから算出
else if (pindex >= 0)
else
{
var ppos = localPositions[pindex];
var w = lpos - ppos;
w = math.normalize(w);
float3 n = math.cross(w, v);
n = math.cross(n, w);
if (math.lengthsq(n) > Define.System.Epsilon)
{
n = math.normalize(n);
localNormals[vindex] = n;
localTangents[vindex] = w;
nrot = MathUtility.ToRotation(n, w);
}
// 元の接線と新しい法線が同じベクトルの場合は、従法線から新しい接線を算出する
var lbn = math.normalize(math.cross(ln, lt));
lt = math.normalize(math.cross(lbn, n));
}
localNormals[vindex] = n;
localTangents[vindex] = lt;
var nrot = MathUtility.ToRotation(n, lt);
// 補正用回転を算出し格納する
normalAdjustmentRotations[vindex] = math.mul(math.inverse(lrot), nrot);
}
@ -837,11 +714,11 @@ namespace MagicaCloth2
var vset_z = (ptr + tri.z);
if (vset_x->Length < 7)
vset_x->Set(tindex);
vset_x->MC2Set(tindex);
if (vset_y->Length < 7)
vset_y->Set(tindex);
vset_y->MC2Set(tindex);
if (vset_z->Length < 7)
vset_z->Set(tindex);
vset_z->MC2Set(tindex);
}
}
}
@ -1103,7 +980,10 @@ namespace MagicaCloth2
// 頂点のローカル回転
var vrot = MathUtility.ToRotation(localNormals[vindex], localTangents[vindex]);
vertexToTransformRotations[vindex] = math.mul(math.inverse(vrot), trot);
// 頂点ローカル回転をトランスフォームローカル回転に復元する回転を求める
var toRot = math.mul(math.inverse(vrot), trot);
vertexToTransformRotations[vindex] = toRot;
}
}
@ -1131,7 +1011,7 @@ namespace MagicaCloth2
for (int j = 0; j < 3; j++)
{
int2 edge = edges[j];
edgeToTriangles.UniqueAdd(edge, (ushort)i);
edgeToTriangles.MC2UniqueAdd(edge, (ushort)i);
}
}
}
@ -1157,13 +1037,13 @@ namespace MagicaCloth2
public void Execute(int vindex)
{
float3 pos = localPositions[vindex];
var nor = localNormals[vindex];
var tan = localTangents[vindex];
float3 lpos = localPositions[vindex];
var lnor = localNormals[vindex];
var ltan = localTangents[vindex];
// マッピング用の頂点バインドポーズを求める
quaternion rot = MathUtility.ToRotation(nor, tan);
vertexBindPosePositions[vindex] = -pos;
quaternion rot = MathUtility.ToRotation(lnor, ltan);
vertexBindPosePositions[vindex] = -lpos;
vertexBindPoseRotations[vindex] = math.inverse(rot);
}
}
@ -1192,12 +1072,12 @@ namespace MagicaCloth2
ushort y = (ushort)tri.y;
ushort z = (ushort)tri.z;
vertexToVertexMap.UniqueAdd(tri.x, y);
vertexToVertexMap.UniqueAdd(tri.x, z);
vertexToVertexMap.UniqueAdd(tri.y, x);
vertexToVertexMap.UniqueAdd(tri.y, z);
vertexToVertexMap.UniqueAdd(tri.z, x);
vertexToVertexMap.UniqueAdd(tri.z, y);
vertexToVertexMap.MC2UniqueAdd(tri.x, y);
vertexToVertexMap.MC2UniqueAdd(tri.x, z);
vertexToVertexMap.MC2UniqueAdd(tri.y, x);
vertexToVertexMap.MC2UniqueAdd(tri.y, z);
vertexToVertexMap.MC2UniqueAdd(tri.z, x);
vertexToVertexMap.MC2UniqueAdd(tri.z, y);
edgeSet.Add(DataUtility.PackInt2(tri.xy));
edgeSet.Add(DataUtility.PackInt2(tri.yz));
@ -1226,8 +1106,8 @@ namespace MagicaCloth2
{
int2 line = lines[i];
vertexToVertexMap.UniqueAdd(line.x, (ushort)line.y);
vertexToVertexMap.UniqueAdd(line.y, (ushort)line.x);
vertexToVertexMap.MC2UniqueAdd(line.x, (ushort)line.y);
vertexToVertexMap.MC2UniqueAdd(line.y, (ushort)line.x);
edgeSet.Add(DataUtility.PackInt2(line));
}
@ -1268,10 +1148,10 @@ namespace MagicaCloth2
struct SkinningBoneInfo
{
//public int transformIndex;
public int startTransformIndex;
public float3 startPos;
public int endTransformIndex;
public float3 endPos;
public int parentTransformIndex;
public float3 parentPos;
public int childTransformIndex;
public float3 childPos;
}
/// <summary>
@ -1282,39 +1162,6 @@ namespace MagicaCloth2
if (CustomSkinningBoneCount == 0)
return;
#if false
// ボーン情報の構築
using var boneInfoList = new NativeList<SkinningBoneInfo>(CustomSkinningBoneCount, Allocator.Persistent);
for (int i = 0; i < CustomSkinningBoneCount; i++)
{
int tindex = customSkinningBoneIndices[i];
if (tindex == -1)
continue;
// 登録
var info = new SkinningBoneInfo();
info.transformIndex = tindex;
info.startPos = bones[i].localPosition;
boneInfoList.Add(info);
Debug.Log($"[{boneInfoList.Length - 1}] {i}, tindex:{tindex}");
}
if (boneInfoList.Length == 0)
return;
// 頂点ごとにカスタムスキニングウエイトを算出
var job = new Proxy_CalcCustomSkinningWeightsJob2()
{
distanceReduction = setting.distanceReduction,
distancePow = setting.distancePow,
//attributes = attributes.GetNativeArray(),
localPositions = localPositions.GetNativeArray(),
boneInfoList = boneInfoList,
boneWeights = boneWeights.GetNativeArray(),
};
job.Run(VertexCount);
#endif
#if true
// ボーン情報の構築
using var boneInfoList = new NativeList<SkinningBoneInfo>(CustomSkinningBoneCount * 2, Allocator.Persistent);
for (int i = 0; i < CustomSkinningBoneCount; i++)
@ -1322,6 +1169,9 @@ namespace MagicaCloth2
int tindex = customSkinningBoneIndices[i];
if (tindex == -1)
continue;
#if MC2_CUSTOM_SKINNING_V1
// 旧
int pid = bones[i].pid;
if (pid == 0)
continue;
@ -1330,16 +1180,22 @@ namespace MagicaCloth2
continue;
// ボーンライン情報の作成
// localPositonはクロス空間での座標に変換済み
var info = new SkinningBoneInfo();
//info.transformIndex = customSkinningBoneIndices[pindex];
info.startTransformIndex = customSkinningBoneIndices[pindex];
info.startPos = bones[pindex].localPosition;
info.endTransformIndex = tindex;
info.endPos = bones[i].localPosition;
info.parentTransformIndex = customSkinningBoneIndices[pindex];
info.parentPos = bones[pindex].localPosition;
info.childTransformIndex = tindex;
info.childPos = bones[i].localPosition;
// 距離がほぼ0なら無効
if (math.distance(info.startPos, info.endPos) < Define.System.Epsilon)
if (math.distance(info.parentPos, info.childPos) < Define.System.Epsilon)
continue;
#else
// V2
var info = new SkinningBoneInfo();
info.childTransformIndex = tindex;
info.childPos = bones[i].localPosition;
#endif
// 登録
boneInfoList.Add(info);
@ -1349,12 +1205,11 @@ namespace MagicaCloth2
return;
// 頂点ごとにカスタムスキニングウエイトを算出
#if MC2_CUSTOM_SKINNING_V1
// 旧
var job = new Proxy_CalcCustomSkinningWeightsJob()
{
isBoneCloth = isBoneCloth,
//angularAttenuation = setting.angularAttenuation,
//distanceReduction = setting.distanceReduction,
//distancePow = setting.distancePow,
angularAttenuation = Define.System.CustomSkinningAngularAttenuation,
distanceReduction = Define.System.CustomSkinningDistanceReduction,
distancePow = Define.System.CustomSkinningDistancePow,
@ -1364,19 +1219,36 @@ namespace MagicaCloth2
boneInfoList = boneInfoList,
boneWeights = boneWeights.GetNativeArray(),
};
job.Run(VertexCount);
#else
// V2
var job = new Proxy_CalcCustomSkinningWeightsJobV2()
{
isBoneCloth = isBoneCloth,
angularAttenuation = Define.System.CustomSkinningAngularAttenuation,
distanceReduction = Define.System.CustomSkinningDistanceReduction,
distancePow = Define.System.CustomSkinningDistancePow,
attributes = attributes.GetNativeArray(),
localPositions = localPositions.GetNativeArray(),
boneInfoList = boneInfoList,
boneWeights = boneWeights.GetNativeArray(),
};
#endif
job.Run(VertexCount);
}
#if false
#if !MC2_CUSTOM_SKINNING_V1
// V2
[BurstCompile]
struct Proxy_CalcCustomSkinningWeightsJob2 : IJobParallelFor
struct Proxy_CalcCustomSkinningWeightsJobV2 : IJobParallelFor
{
public bool isBoneCloth;
public float angularAttenuation;
public float distanceReduction;
public float distancePow;
//[Unity.Collections.ReadOnly]
//public NativeArray<VertexAttribute> attributes;
[Unity.Collections.ReadOnly]
public NativeArray<VertexAttribute> attributes;
[Unity.Collections.ReadOnly]
public NativeArray<float3> localPositions;
[Unity.Collections.ReadOnly]
@ -1384,12 +1256,12 @@ namespace MagicaCloth2
[Unity.Collections.WriteOnly]
public NativeArray<VirtualMeshBoneWeight> boneWeights;
// プロキシメッシュ頂点ごと
public void Execute(int vindex)
{
// 固定は無効(※この時点ではまだ属性がない!)
//var attr = attributes[vindex];
//if (attr.IsMove() == false)
// return;
// 移動属性のみ
if (attributes[vindex].IsDontMove())
return;
var lpos = localPositions[vindex];
@ -1398,12 +1270,14 @@ namespace MagicaCloth2
for (int i = 0; i < bcnt; i++)
{
var binfo = boneInfoList[i];
var bpos = binfo.childPos;
int boneIndex = binfo.childTransformIndex;
// 距離
float dist = math.distance(lpos, binfo.startPos);
var v = lpos - bpos;
float dist = math.length(v);
// 登録。すでに登録済みならばdistがより小さい場合のみ再登録
int boneIndex = binfo.transformIndex;
int nowIndex = costList.indexOf(boneIndex);
if (nowIndex >= 0)
{
@ -1417,59 +1291,67 @@ namespace MagicaCloth2
costList.Add(dist, boneIndex);
}
// ウエイト算出
// (0)最小距離のn%を減算する
// (1)最小距離のn%を減算する
int cnt = costList.Count;
//const float lengthWeight = 0.8f;
float mindist = costList.MinCost * distanceReduction;
costList.costs -= mindist;
// (1)distanceをn乗する
//const float pow = 2.0f;
costList.costs = math.pow(costList.costs, distancePow);
// (2)最小値の逆数にする
float min = math.max(costList.MinCost, 1e-06f);
float sum = 0;
float mindist = costList.MinCost * distanceReduction; // 0.6
for (int i = 0; i < cnt; i++)
costList.costs[i] = costList.costs[i] - mindist;
// (2)distanceをn乗する
for (int i = 0; i < cnt; i++)
costList.costs[i] = math.pow(costList.costs[i], distancePow); // 2.0
// ウエイト算出
if (costList.MinCost < Define.System.Epsilon)
{
costList.costs[i] = min / costList.costs[i];
sum += costList.costs[i];
costList.costs = new float4(1, 0, 0, 0);
costList.data = new int4(costList.data[0], 0, 0, 0);
}
// (3)割合を出す
costList.costs /= sum;
// (4)極小のウエイトは削除する
sum = 0;
for (int i = 0; i < 4; i++)
else
{
if (costList.costs[i] < 0.01f || i >= cnt)
// コストの逆数の合計
cnt = costList.Count;
float sum = 0;
for (int i = 0; i < cnt; i++)
{
// 打ち切り
costList.costs[i] = 0.0f;
costList.data[i] = 0;
sum += 1.0f / costList.costs[i];
}
else
// 1.0fに正規化
for (int i = 0; i < cnt; i++)
{
sum += costList.costs[i];
costList.costs[i] = (1.0f / costList.costs[i]) / sum;
}
// 極小のウエイトは削除する
const float InvalidWeight = 0.001f; // 0.1%
sum = 0;
for (int i = 0; i < 4; i++)
{
if (costList.costs[i] < InvalidWeight || i >= cnt)
{
// 打ち切り
costList.costs[i] = 0.0f;
costList.data[i] = 0;
}
else
{
sum += costList.costs[i];
}
}
Debug.Assert(sum > 0);
costList.costs /= sum; // 再度1.0正規化
}
Debug.Assert(sum > 0);
// (5)再度1.0に平均化
costList.costs /= sum;
//Debug.Log($"[{vindex}] :{costList}");
// ウエイト作成
// ウエイト構造体に変換して格納
var bw = new VirtualMeshBoneWeight(costList.data, costList.costs);
boneWeights[vindex] = bw;
}
}
#endif
#if true
#if MC2_CUSTOM_SKINNING_V1
[BurstCompile]
struct Proxy_CalcCustomSkinningWeightsJob : IJobParallelFor
{
@ -1487,7 +1369,7 @@ namespace MagicaCloth2
[Unity.Collections.WriteOnly]
public NativeArray<VirtualMeshBoneWeight> boneWeights;
// プロキシメッシュ頂点ごと
public void Execute(int vindex)
{
// BoneClothカスタムスキニングでは固定は動かさない
@ -1501,21 +1383,21 @@ namespace MagicaCloth2
for (int i = 0; i < bcnt; i++)
{
var binfo = boneInfoList[i];
float3 d = MathUtility.ClosestPtPointSegment(lpos, binfo.startPos, binfo.endPos);
float3 d = MathUtility.ClosestPtPointSegment(lpos, binfo.parentPos, binfo.childPos);
//float dist = math.distance(lpos, d);
// ボーンラインとの角度により判定距離を調整する
// ラインと水平になるほど影響がよわくなる
var v = lpos - d;
var bv = binfo.endPos - binfo.startPos;
var bv = binfo.childPos - binfo.parentPos;
float dot = math.dot(math.normalize(v), math.normalize(bv));
float ratio = 1.0f + math.abs(dot) * angularAttenuation;
float ratio = 1.0f + math.abs(dot) * angularAttenuation; // 1.0
// 登録。すでに登録済みならばdistがより小さい場合のみ再登録
for (int j = 0; j < 2; j++)
{
int boneIndex = j == 0 ? binfo.startTransformIndex : binfo.endTransformIndex;
float dist = j == 0 ? math.distance(lpos, binfo.startPos) : math.distance(lpos, binfo.endPos);
int boneIndex = j == 0 ? binfo.parentTransformIndex : binfo.childTransformIndex;
float dist = j == 0 ? math.distance(lpos, binfo.parentPos) : math.distance(lpos, binfo.childPos);
dist *= ratio;
int nowIndex = costList.indexOf(boneIndex);
@ -1535,11 +1417,11 @@ namespace MagicaCloth2
// ウエイト算出
// (0)最小距離のn%を減算する
int cnt = costList.Count;
float mindist = costList.MinCost * distanceReduction;
float mindist = costList.MinCost * distanceReduction; // 0.6
costList.costs -= mindist;
// (1)distanceをn乗する
costList.costs = math.pow(costList.costs, distancePow);
costList.costs = math.pow(costList.costs, distancePow); // 2.0
// (2)最小値の逆数にする
float min = math.max(costList.MinCost, 1e-06f);
@ -1950,7 +1832,7 @@ namespace MagicaCloth2
int pindex = vertexParentIndices[vindex];
if (pindex >= 0)
{
vertexChildMap.UniqueAdd(pindex, (ushort)vindex);
vertexChildMap.MC2UniqueAdd(pindex, (ushort)vindex);
}
}
@ -2261,16 +2143,21 @@ namespace MagicaCloth2
float3 tan = localTangents[vindex];
quaternion rot = MathUtility.ToRotation(nor, tan);
float3 lpos = math.mul(iprot, pos - ppos);
quaternion lrot = math.mul(iprot, rot);
vertexLocalPositions[vindex] = lpos;
vertexLocalRotations[vindex] = lrot;
//Debug.Log($"vertexLocalPositions [{vindex}] : {lpos}");
//Debug.Log($"vertexLocalRotations [{vindex}] : {lrot}");
}
else
{
vertexLocalPositions[vindex] = 0;
vertexLocalRotations[vindex] = quaternion.identity;
//Debug.Log($"vertexLocalPositions [{vindex}] : 0");
//Debug.Log($"vertexLocalRotations [{vindex}] : (0, 0, 0, 1)");
}
}
}
@ -2379,7 +2266,7 @@ namespace MagicaCloth2
}
//=========================================================================================
#if false // pitch/yaw個別制限はv1.0では実装しないので一旦停止
#if false // pitch/yaw個別制限はv1.0では実装しないので一旦停止
/// <summary>
/// 角度制限計算用ローカル回転の算出
/// </summary>

View File

@ -48,7 +48,7 @@ namespace MagicaCloth2
float sameDistance = maxSideLength * math.saturate(Define.System.ReductionSameDistance);
float simpleDistance = maxSideLength * math.saturate(settings.simpleDistance);
float shapeDistance = maxSideLength * math.saturate(settings.shapeDistance);
//Develop.DebugLog($"ReductionDista. maxSideLength:{maxSideLength}, same:{sameDistance}, simple:{simpleDistance}, shape:{shapeDistance}");
Develop.DebugLog($"ReductionDista. maxSideLength:{maxSideLength}, same:{sameDistance}, simple:{simpleDistance}, shape:{shapeDistance}");
// 同一距離リダクション
ct.ThrowIfCancellationRequested();
@ -659,7 +659,7 @@ namespace MagicaCloth2
{
int newIndex2 = vertexRemapIndices[oldVertexIndex];
//Debug.Assert(newIndex != newIndex2);
newVertexToVertexMap.UniqueAdd((ushort)newIndex, (ushort)newIndex2);
newVertexToVertexMap.MC2UniqueAdd((ushort)newIndex, (ushort)newIndex2);
}
//Debug.Log($"[{newIndex}] cnt:{newVertexToVertexMap.CountValuesForKey((ushort)newIndex)}");
@ -764,7 +764,7 @@ namespace MagicaCloth2
if (vindex == edge.x || vindex == edge.y)
continue;
if (newVertexToVertexMap.Contains((ushort)edge.y, vindex))
if (newVertexToVertexMap.MC2Contains((ushort)edge.y, vindex))
{
// トライアングル生成
int3 tri = DataUtility.PackInt3(edge.x, edge.y, vindex);

View File

@ -14,7 +14,7 @@ namespace MagicaCloth2
{
//=========================================================================================
/// <summary>
/// 頂点間の平均/最大距離を調べる(スレッド可)
/// 頂点間の平均/最大距離を調べる(スレッド可)
/// 結果はaverageVertexDistance/maxVertexDistanceに格納される
/// </summary>
internal void CalcAverageAndMaxVertexDistanceRun()

View File

@ -22,6 +22,8 @@ namespace MagicaCloth2
public ResultCode result = new ResultCode();
public bool isManaged; // PreBuild DeserializeManager管理
/// <summary>
/// メッシュタイプ
/// </summary>
@ -360,26 +362,34 @@ namespace MagicaCloth2
public int mappingId;
//=========================================================================================
public VirtualMesh()
public VirtualMesh() { }
public VirtualMesh(bool initialize)
{
transformData = new TransformData();
if (initialize)
{
transformData = new TransformData(100);
// 最小限のデータ
averageVertexDistance = new NativeReference<float>(0.0f, Allocator.Persistent);
maxVertexDistance = new NativeReference<float>(0.0f, Allocator.Persistent);
// 最小限のデータ
averageVertexDistance = new NativeReference<float>(0.0f, Allocator.Persistent);
maxVertexDistance = new NativeReference<float>(0.0f, Allocator.Persistent);
// 作業中にしておく
result.SetProcess();
// 作業中にしておく
result.SetProcess();
}
}
public VirtualMesh(string name) : this()
public VirtualMesh(string name) : this(true)
{
this.name = name;
}
public void Dispose()
{
// PreBuild DeserializeManager管理中は破棄させない
if (isManaged)
return;
result.Clear();
referenceIndices.Dispose();
attributes.Dispose();
@ -471,7 +481,8 @@ namespace MagicaCloth2
return false;
// レンダラーが存在する場合はその存在を確認する
if (centerTransformIndex >= 0 && transformData.GetTransformFromIndex(centerTransformIndex) == null)
// ただしPreBuildではtransformListは空なのでスキップする
if (centerTransformIndex >= 0 && transformData.IsEmpty == false && transformData.GetTransformFromIndex(centerTransformIndex) == null)
return false;
return true;
@ -483,23 +494,38 @@ namespace MagicaCloth2
StringBuilder sb = new StringBuilder();
sb.AppendLine($"===== {name} =====");
sb.Append($"Result:{result}");
sb.Append($", Type:{meshType}");
sb.Append($", Vertex:{VertexCount}");
sb.Append($", Line:{LineCount}");
sb.Append($", Triangle:{TriangleCount}");
sb.Append($", Edge:{EdgeCount}");
sb.Append($", SkinBone:{SkinBoneCount}");
sb.Append($", Transform:{transformData?.Count}");
sb.Append($", BaseLine:{BaseLineCount}");
sb.AppendLine($"Result:{result.GetResultString()}");
sb.AppendLine($"Type:{meshType}");
sb.AppendLine($"Vertex:{VertexCount}");
sb.AppendLine($"Line:{LineCount}");
sb.AppendLine($"Triangle:{TriangleCount}");
sb.AppendLine($"Edge:{EdgeCount}");
sb.AppendLine($"SkinBone:{SkinBoneCount}");
sb.AppendLine($"Transform:{TransformCount}");
if (averageVertexDistance.IsCreated)
sb.AppendLine($"avgDist:{averageVertexDistance.Value}");
if (maxVertexDistance.IsCreated)
sb.AppendLine($"maxDist:{maxVertexDistance.Value}");
if (boundingBox.IsCreated)
sb.AppendLine($"AABB:{boundingBox.Value}");
sb.AppendLine();
if (averageVertexDistance.IsCreated)
sb.Append($"avgDist:{averageVertexDistance.Value}");
if (maxVertexDistance.IsCreated)
sb.Append($", maxDist:{maxVertexDistance.Value}");
if (boundingBox.IsCreated)
sb.Append($", AABB:{boundingBox.Value}");
sb.AppendLine($"<<< Proxy >>>");
sb.AppendLine($"BaseLine:{BaseLineCount}");
sb.AppendLine($"EdgeCount:{EdgeCount}");
int edgeToTrianglesCnt = edgeToTriangles.IsCreated ? edgeToTriangles.Count() : 0;
sb.AppendLine($"edgeToTriangles:{edgeToTrianglesCnt}");
sb.AppendLine($"CustomSkinningBoneCount:{CustomSkinningBoneCount}");
sb.AppendLine($"CenterFixedPointCount:{CenterFixedPointCount}");
sb.AppendLine($"NormalAdjustmentRotationCount:{NormalAdjustmentRotationCount}");
sb.AppendLine();
sb.AppendLine($"<<< Mapping >>>");
sb.AppendLine($"centerWorldPosition:{centerWorldPosition}");
sb.AppendLine();
//sb.AppendLine($"<<< TransformData >>>");
sb.AppendLine(transformData?.ToString() ?? "(none)");
sb.AppendLine();
return sb.ToString();

Some files were not shown because too many files have changed in this diff Show More