mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-26 04:39:51 -05:00
Merge branch 'master' into fix/10_show_select_springbone
This commit is contained in:
29
.github/workflows/create-unitypackage.yml
vendored
29
.github/workflows/create-unitypackage.yml
vendored
@@ -14,23 +14,16 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
checkout:
|
||||
create-unitypackage:
|
||||
runs-on: [self-hosted, Windows, X64, Unity]
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- id: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
lfs: true
|
||||
|
||||
detect-unity-version:
|
||||
needs: checkout
|
||||
runs-on: [self-hosted, Windows, X64, Unity]
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
unity-editor-executable: ${{ steps.detect-unity-version.outputs.unity-editor-executable }}
|
||||
steps:
|
||||
|
||||
- name: Detect Unity Version
|
||||
id: detect-unity-version
|
||||
run: |
|
||||
@@ -58,18 +51,13 @@ jobs:
|
||||
echo "${UNITY_EDITOR_EXECUTABLE} is installed."
|
||||
echo "unity-editor-executable=${UNITY_EDITOR_EXECUTABLE}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
run-edit-mode-tests:
|
||||
needs: detect-unity-version
|
||||
runs-on: [self-hosted, Windows, X64, Unity]
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Run EditMode Tests
|
||||
id: run-edit-mode-tests
|
||||
run: |
|
||||
echo "Run EditMode Tests..."
|
||||
# RunEditModeTests の実行の結果、終了コードが 0 でない場合でもテストの結果を表示したいので set +e して一時的に回避する
|
||||
set +e
|
||||
"${{ needs.detect-unity-version.outputs.unity-editor-executable }}" \
|
||||
"${{ steps.detect-unity-version.outputs.unity-editor-executable }}" \
|
||||
-batchmode \
|
||||
-silent-crashes \
|
||||
-projectPath "${{ env.UNITY_PROJECT_PATH }}" \
|
||||
@@ -91,7 +79,7 @@ jobs:
|
||||
echo "Test failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -99,15 +87,10 @@ jobs:
|
||||
name: run-edit-mode-tests.xml
|
||||
path: ${{ env.UNITY_PROJECT_PATH }}/run-edit-mode-tests.xml
|
||||
|
||||
create-unitypackage:
|
||||
needs: [detect-unity-version, run-edit-mode-tests]
|
||||
runs-on: [self-hosted, Windows, X64, Unity]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Create UnityPackage
|
||||
id: create-unitypackage
|
||||
run: |
|
||||
"${{ needs.detect-unity-version.outputs.unity-editor-executable }}" \
|
||||
"${{ steps.detect-unity-version.outputs.unity-editor-executable }}" \
|
||||
-batchmode \
|
||||
-silent-crashes \
|
||||
-projectPath "${{ env.UNITY_PROJECT_PATH }}" \
|
||||
|
||||
@@ -6,18 +6,9 @@ namespace UniGLTF
|
||||
{
|
||||
public static class GltfImportMenu
|
||||
{
|
||||
public const string MENU_NAME = "Import glTF... (*.gltf|*.glb|*.zip)";
|
||||
public static void ImportGltfFileToGameObject()
|
||||
{
|
||||
var path = EditorUtility.OpenFilePanel(MENU_NAME + ": open glb", "",
|
||||
#if UNITY_EDITOR_OSX
|
||||
// https://github.com/vrm-c/UniVRM/issues/1837
|
||||
"glb"
|
||||
#else
|
||||
"gltf,glb,zip"
|
||||
#endif
|
||||
);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
if(!UniGltfEditorDialog.TryOpenFilePanel("", out var path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace UniGLTF
|
||||
private static void ExportGameObjectToGltf() => GltfExportWindow.ExportGameObjectToGltfFile();
|
||||
|
||||
|
||||
[MenuItem(UserGltfMenuPrefix + "/" + GltfImportMenu.MENU_NAME, priority = 2)]
|
||||
[MenuItem(UserGltfMenuPrefix + "/" + UniGltfEditorDialog.IMPORT_MENU_NAME, priority = 2)]
|
||||
private static void ImportGltfFile() => GltfImportMenu.ImportGltfFileToGameObject();
|
||||
|
||||
|
||||
|
||||
@@ -156,8 +156,7 @@ namespace UniGLTF
|
||||
|
||||
if (GUILayout.Button("Export", GUILayout.MinWidth(100)))
|
||||
{
|
||||
var path = SaveFileDialog.GetPath(SaveTitle, SaveName, SaveExtensions);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
if (UniGltfEditorDialog.TrySaveFilePanel(SaveTitle, SaveName, SaveExtensions, out var path))
|
||||
{
|
||||
ExportPath(path);
|
||||
// close
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class SaveFileDialog
|
||||
{
|
||||
static string m_lastExportDir;
|
||||
|
||||
static string extensionString(string[] extensions)
|
||||
{
|
||||
#if UNITY_EDITOR_OSX
|
||||
// in OSX multi extension cause exception.
|
||||
// https://github.com/vrm-c/UniVRM/issues/1837
|
||||
return extensions.Length > 0 ? extensions[0] : "";
|
||||
#else
|
||||
return string.Join(",", extensions);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string GetPath(string title, string name, params string[] extensions)
|
||||
{
|
||||
string directory = m_lastExportDir;
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
directory = Directory.GetParent(Application.dataPath).ToString();
|
||||
}
|
||||
|
||||
var path = EditorUtility.SaveFilePanel(title, directory, name, extensionString(extensions));
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string GetDir(string title, string dir = null)
|
||||
{
|
||||
string directory = string.IsNullOrEmpty(dir) ? m_lastExportDir : dir;
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
directory = Directory.GetParent(Application.dataPath).ToString();
|
||||
}
|
||||
|
||||
var path = EditorUtility.SaveFolderPanel(title, directory, null);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Assets/UniGLTF/Editor/UniGltfEditorDialog.cs
Normal file
78
Assets/UniGLTF/Editor/UniGltfEditorDialog.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class UniGltfEditorDialog
|
||||
{
|
||||
public const string IMPORT_MENU_NAME = "Import glTF... (*.gltf|*.glb|*.zip)";
|
||||
|
||||
static string extensionString(string[] extensions)
|
||||
{
|
||||
if (Application.platform == RuntimePlatform.WindowsEditor)
|
||||
{
|
||||
return string.Join(",", extensions);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in OSX multi extension cause exception.
|
||||
// https://github.com/vrm-c/UniVRM/issues/1837
|
||||
// in Linux
|
||||
// https://github.com/vrm-c/UniVRM/issues/2515
|
||||
return extensions.Length > 0 ? extensions[0] : "";
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryOpenFilePanel(string directory, out string path)
|
||||
{
|
||||
path = EditorUtility.OpenFilePanel(IMPORT_MENU_NAME, directory,
|
||||
// https://github.com/vrm-c/UniVRM/issues/1837
|
||||
Application.platform == RuntimePlatform.WindowsEditor ? "gltf,glb,zip" : "glb"
|
||||
);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string s_lastExportDir;
|
||||
public static bool TrySaveFilePanel(string title, string name, string[] extensions, out string path)
|
||||
{
|
||||
string directory = s_lastExportDir;
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
directory = Directory.GetParent(Application.dataPath).ToString();
|
||||
}
|
||||
|
||||
path = EditorUtility.SaveFilePanel(title, directory, name, extensionString(extensions));
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
s_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetDir(string title, string dir, out string path)
|
||||
{
|
||||
string directory = string.IsNullOrEmpty(dir) ? s_lastExportDir : dir;
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
directory = Directory.GetParent(Application.dataPath).ToString();
|
||||
}
|
||||
|
||||
path = EditorUtility.SaveFolderPanel(title, directory, null);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
s_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35dc28badc82c5e4a8aa813ddd3369fe
|
||||
guid: 20581dc53a52e174cbaec605148d3e78
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -172,7 +172,14 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
|
||||
for (int i = offset; i < end; ++i)
|
||||
{
|
||||
// mark velocity zero
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
currentTails.GetSubArray(offset, Logics.Length).AsSpan().Fill(new Vector3(float.NaN, float.NaN, float.NaN));
|
||||
#else
|
||||
var subArray = currentTails.GetSubArray(offset, Logics.Length);
|
||||
var value = new Vector3(float.NaN, float.NaN, float.NaN);
|
||||
for (int a = 0; a < subArray.Length; ++a)
|
||||
subArray[a] = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,17 @@ namespace UniGLTF
|
||||
[Serializable]
|
||||
public class glTFPrimitives
|
||||
{
|
||||
public enum Mode
|
||||
{
|
||||
POINTS = 0,
|
||||
LINES = 1,
|
||||
LINE_LOOP = 2,
|
||||
LINE_STRIP = 3,
|
||||
TRIANGLES = 4,
|
||||
TRIANGLE_STRIP = 5,
|
||||
TRIANGLE_FAN = 6,
|
||||
}
|
||||
|
||||
[JsonSchema(EnumValues = new object[] { 0, 1, 2, 3, 4, 5, 6 })]
|
||||
public int mode;
|
||||
|
||||
|
||||
@@ -43,22 +43,18 @@ namespace UniGLTF
|
||||
|
||||
public glTFBufferView Extend(IntPtr p, int bytesLength, int stride, glBufferTarget target)
|
||||
{
|
||||
var tmp = m_bytes;
|
||||
// alignment
|
||||
var padding = m_used % stride == 0 ? 0 : stride - m_used % stride;
|
||||
var requiredLength = m_used + padding + bytesLength;
|
||||
|
||||
if (m_bytes == null || m_used + padding + bytesLength > m_bytes.Length)
|
||||
if (m_bytes == null || requiredLength > m_bytes.Length)
|
||||
{
|
||||
// recreate buffer
|
||||
m_bytes = new Byte[m_used + padding + bytesLength];
|
||||
var newLength = Math.Max(requiredLength, m_bytes?.Length * 2 ?? 256);
|
||||
var newBuffer = new Byte[newLength];
|
||||
if (m_used > 0)
|
||||
{
|
||||
Buffer.BlockCopy(tmp, 0, m_bytes, 0, m_used);
|
||||
}
|
||||
}
|
||||
if (m_used + padding + bytesLength > m_bytes.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
Buffer.BlockCopy(m_bytes, 0, newBuffer, 0, m_used);
|
||||
m_bytes = newBuffer;
|
||||
}
|
||||
|
||||
Marshal.Copy(p, m_bytes, m_used + padding, bytesLength);
|
||||
@@ -70,7 +66,7 @@ namespace UniGLTF
|
||||
byteStride = stride,
|
||||
target = target,
|
||||
};
|
||||
m_used = m_used + padding + bytesLength;
|
||||
m_used = requiredLength;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,20 +14,22 @@ namespace UniGLTF
|
||||
private readonly MaterialDescriptor m_defaultMaterialParams;
|
||||
private readonly List<MaterialLoadInfo> m_materials = new List<MaterialLoadInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// gltfPritmitive.material が無い場合のデフォルトマテリアル
|
||||
/// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#default-material
|
||||
///
|
||||
/// </summary>
|
||||
private Material m_defaultMaterial;
|
||||
|
||||
public IReadOnlyList<MaterialLoadInfo> Materials => m_materials;
|
||||
|
||||
|
||||
public MaterialFactory(IReadOnlyDictionary<SubAssetKey, Material> externalMaterialMap, MaterialDescriptor defaultMaterialParams)
|
||||
{
|
||||
m_externalMap = externalMaterialMap;
|
||||
m_defaultMaterialParams = defaultMaterialParams;
|
||||
m_defaultMaterialParams = new MaterialDescriptor(
|
||||
m_defaultMaterialKey.Name,
|
||||
defaultMaterialParams.Shader,
|
||||
defaultMaterialParams.RenderQueue,
|
||||
defaultMaterialParams.TextureSlots,
|
||||
defaultMaterialParams.FloatValues,
|
||||
defaultMaterialParams.Colors,
|
||||
defaultMaterialParams.Vectors,
|
||||
defaultMaterialParams.Actions,
|
||||
defaultMaterialParams.AsyncActions
|
||||
);
|
||||
}
|
||||
|
||||
public struct MaterialLoadInfo
|
||||
@@ -56,11 +58,6 @@ namespace UniGLTF
|
||||
UnityObjectDestroyer.DestroyRuntimeOrEditor(x.Asset);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_defaultMaterial != null)
|
||||
{
|
||||
UnityObjectDestroyer.DestroyRuntimeOrEditor(m_defaultMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,12 +81,6 @@ namespace UniGLTF
|
||||
m_materials.Remove(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_defaultMaterial != null)
|
||||
{
|
||||
take(m_defaultMaterialKey, m_defaultMaterial);
|
||||
m_defaultMaterial = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Material GetMaterial(int index)
|
||||
@@ -99,22 +90,16 @@ namespace UniGLTF
|
||||
return m_materials[index].Asset;
|
||||
}
|
||||
|
||||
public async Task<Material> GetDefaultMaterialAsync(IAwaitCaller awaitCaller)
|
||||
/// <summary>
|
||||
/// gltfPritmitive.material が無い場合のデフォルトマテリアル
|
||||
/// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#default-material
|
||||
///
|
||||
/// </summary>
|
||||
public Task<Material> GetDefaultMaterialAsync(IAwaitCaller awaitCaller)
|
||||
{
|
||||
if (m_externalMap.ContainsKey(m_defaultMaterialKey))
|
||||
{
|
||||
m_defaultMaterial = m_externalMap[m_defaultMaterialKey];
|
||||
return m_externalMap[m_defaultMaterialKey];
|
||||
}
|
||||
|
||||
if (m_defaultMaterial == null)
|
||||
{
|
||||
m_defaultMaterial = await LoadAsync(m_defaultMaterialParams, (_, _) => null, awaitCaller);
|
||||
}
|
||||
return m_defaultMaterial;
|
||||
return LoadAsync(m_defaultMaterialParams, (_, _) => null, awaitCaller);
|
||||
}
|
||||
|
||||
|
||||
public async Task<Material> LoadAsync(MaterialDescriptor matDesc, GetTextureAsyncFunc getTexture, IAwaitCaller awaitCaller)
|
||||
{
|
||||
if (m_externalMap.TryGetValue(matDesc.SubAssetKey, out Material material))
|
||||
|
||||
@@ -114,52 +114,7 @@ namespace UniGLTF
|
||||
attributes.JOINTS_0 = jointsAccessorIndex;
|
||||
}
|
||||
|
||||
var gltfMesh = new glTFMesh(mesh.name);
|
||||
var indices = new List<uint>();
|
||||
for (int j = 0; j < mesh.subMeshCount; ++j)
|
||||
{
|
||||
indices.Clear();
|
||||
|
||||
var triangles = mesh.GetIndices(j);
|
||||
if (triangles.Length == 0)
|
||||
{
|
||||
// https://github.com/vrm-c/UniVRM/issues/664
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < triangles.Length; i += 3)
|
||||
{
|
||||
var i0 = triangles[i];
|
||||
var i1 = triangles[i + 1];
|
||||
var i2 = triangles[i + 2];
|
||||
|
||||
// flip triangle
|
||||
indices.Add((uint)i2);
|
||||
indices.Add((uint)i1);
|
||||
indices.Add((uint)i0);
|
||||
}
|
||||
|
||||
var indicesAccessorIndex = data.ExtendBufferAndGetAccessorIndex(indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER);
|
||||
if (indicesAccessorIndex < 0)
|
||||
{
|
||||
// https://github.com/vrm-c/UniVRM/issues/664
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
if (j >= materials.Length)
|
||||
{
|
||||
Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Mesh.name);
|
||||
break;
|
||||
}
|
||||
|
||||
gltfMesh.primitives.Add(new glTFPrimitives
|
||||
{
|
||||
attributes = attributes,
|
||||
indices = indicesAccessorIndex,
|
||||
mode = 4, // triangles ?
|
||||
material = unityMaterials.IndexOf(materials[j])
|
||||
});
|
||||
}
|
||||
var gltfMesh = CreateGLTFMesh(attributes, data, unityMesh, unityMaterials);
|
||||
|
||||
var blendShapeIndexMap = new Dictionary<int, int>();
|
||||
{
|
||||
@@ -201,6 +156,119 @@ namespace UniGLTF
|
||||
return (gltfMesh, blendShapeIndexMap);
|
||||
}
|
||||
|
||||
private static glTFMesh CreateGLTFMesh(glTFAttributes attributes, ExportingGltfData data, MeshExportInfo unityMesh, List<Material> unityMaterials)
|
||||
{
|
||||
var mesh = unityMesh.Mesh;
|
||||
var materials = unityMesh.Materials;
|
||||
var gltfMesh = new glTFMesh(mesh.name);
|
||||
|
||||
var indices = new List<uint>();
|
||||
for (int j = 0; j < mesh.subMeshCount; ++j)
|
||||
{
|
||||
indices.Clear();
|
||||
if (j >= materials.Length)
|
||||
{
|
||||
Debug.LogWarningFormat("{0}.materials is not enough", mesh.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
var subMesh = mesh.GetSubMesh(j);
|
||||
var topologyType = subMesh.topology;
|
||||
var materialIndex = unityMaterials.IndexOf(materials[j]);
|
||||
|
||||
var submeshIndices = mesh.GetIndices(j);
|
||||
if (submeshIndices.Length == 0)
|
||||
{
|
||||
// https://github.com/vrm-c/UniVRM/issues/664
|
||||
break;
|
||||
}
|
||||
else if (submeshIndices.Length < 3)
|
||||
{
|
||||
Debug.LogWarningFormat("Invalid primitive of type {0} found", topologyType);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add indices considering the topology type
|
||||
switch (topologyType)
|
||||
{
|
||||
case MeshTopology.Triangles:
|
||||
if (submeshIndices.Length % 3 != 0)
|
||||
Debug.LogWarningFormat("triangle indices is not multiple of 3");
|
||||
GetTriangleIndices(indices, submeshIndices);
|
||||
break;
|
||||
case MeshTopology.Quads:
|
||||
if (submeshIndices.Length % 4 != 0)
|
||||
Debug.LogWarningFormat("quad indices is not multiple of 4");
|
||||
GetQuadIndices(indices, submeshIndices);
|
||||
break;
|
||||
default:
|
||||
case MeshTopology.Lines:
|
||||
case MeshTopology.LineStrip:
|
||||
case MeshTopology.Points:
|
||||
Debug.LogWarningFormat("Mesh {0} has unsupported topology type {1}.", mesh.name, topologyType);
|
||||
continue;
|
||||
}
|
||||
|
||||
var primitive = CreatePrimitives(attributes, data, indices, materialIndex);
|
||||
gltfMesh.primitives.Add(primitive);
|
||||
}
|
||||
|
||||
return gltfMesh;
|
||||
}
|
||||
|
||||
private static glTFPrimitives CreatePrimitives(glTFAttributes attributes, ExportingGltfData data, List<uint> indices, int materialIndex)
|
||||
{
|
||||
var indicesAccessorIndex = data.ExtendBufferAndGetAccessorIndex(indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER);
|
||||
if (indicesAccessorIndex < 0)
|
||||
{
|
||||
// https://github.com/vrm-c/UniVRM/issues/664
|
||||
throw new Exception();
|
||||
}
|
||||
var primitive = new glTFPrimitives
|
||||
{
|
||||
attributes = attributes,
|
||||
indices = indicesAccessorIndex,
|
||||
mode = (int)glTFPrimitives.Mode.TRIANGLES, // triangles ?
|
||||
material = materialIndex
|
||||
};
|
||||
return primitive;
|
||||
}
|
||||
|
||||
private static void GetQuadIndices(List<uint> indices, int[] quadIndices)
|
||||
{
|
||||
for (int i = 0; i < quadIndices.Length - 3; i += 4)
|
||||
{
|
||||
var i0 = quadIndices[i];
|
||||
var i1 = quadIndices[i + 1];
|
||||
var i2 = quadIndices[i + 2];
|
||||
var i3 = quadIndices[i + 3];
|
||||
|
||||
// flip triangles
|
||||
indices.Add((uint)i2);
|
||||
indices.Add((uint)i1);
|
||||
indices.Add((uint)i0);
|
||||
|
||||
indices.Add((uint)i3);
|
||||
indices.Add((uint)i2);
|
||||
indices.Add((uint)i0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetTriangleIndices(List<uint> indices, int[] triangleIndices)
|
||||
{
|
||||
for (int i = 0; i < triangleIndices.Length - 2; i += 3)
|
||||
{
|
||||
var i0 = triangleIndices[i];
|
||||
var i1 = triangleIndices[i + 1];
|
||||
var i2 = triangleIndices[i + 2];
|
||||
|
||||
// flip triangle
|
||||
indices.Add((uint)i2);
|
||||
indices.Add((uint)i1);
|
||||
indices.Add((uint)i0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool UseSparse(
|
||||
bool usePosition, Vector3 position,
|
||||
bool useNormal, Vector3 normal,
|
||||
|
||||
@@ -69,8 +69,8 @@ namespace UniGLTF
|
||||
public NativeArray<T> CreateNativeArray<T>(ArraySegment<T> data) where T : struct
|
||||
{
|
||||
var array = CreateNativeArray<T>(data.Count);
|
||||
// TODO: remove ToArray
|
||||
array.CopyFrom(data.ToArray());
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
array[i] = data.Array[data.Offset + i];
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 976e99d37c093ce4c9b249c81c2cbdd5
|
||||
timeCreated: 1520401720
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace VRM
|
||||
[LangMsg(Languages.en, "Model's normalization (bake to remove roation and scaling from the hierarchy)")]
|
||||
NORMALIZE,
|
||||
|
||||
[LangMsg(Languages.ja, "正規化するときににブレンドシェイプによる変形をベイク処理します。")]
|
||||
[LangMsg(Languages.en, "Bake the blendshape deformations as the base model when normalizing.")]
|
||||
FREEZE_BLENDSHAPE,
|
||||
|
||||
[LangMsg(Languages.ja, "エクスポート時に新しいJsonSerializerを使う")]
|
||||
[LangMsg(Languages.en, "The new version of JsonSerializer for model export")]
|
||||
USE_GENERATED_SERIALIZER,
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace VRM
|
||||
/// FreezeBlendShape
|
||||
/// </summary>
|
||||
[Tooltip("when freeze mesh, blendShpae base use current weight")]
|
||||
public bool FreezeMeshUseCurrentBlendShapeWeight = false;
|
||||
public bool FreezeMeshUseCurrentBlendShapeWeight = true;
|
||||
|
||||
/// <summary>
|
||||
/// BlendShapeのシリアライズにSparseAccessorを使う
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace VRM
|
||||
private void OnEnable()
|
||||
{
|
||||
m_checkbox_list.Add(new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.PoseFreeze)), VRMExportOptions.NORMALIZE));
|
||||
m_checkbox_list.Add(new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.FreezeMeshUseCurrentBlendShapeWeight)), VRMExportOptions.FREEZE_BLENDSHAPE));
|
||||
m_checkbox_list.Add(new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.UseSparseAccessor)), VRMExportOptions.BLENDSHAPE_USE_SPARSE));
|
||||
m_checkbox_list.Add(new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.OnlyBlendshapePosition)), VRMExportOptions.BLENDSHAPE_EXCLUDE_NORMAL_AND_TANGENT));
|
||||
m_checkbox_list.Add(new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.ReduceBlendshape)), VRMExportOptions.BLENDSHAPE_ONLY_CLIP_USE));
|
||||
|
||||
8
Assets/VRM/Icons.meta
Normal file
8
Assets/VRM/Icons.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e811516644fdd954f866e2f48a257a16
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/VRM/Icons/vrm0x-48x48.png
Normal file
BIN
Assets/VRM/Icons/vrm0x-48x48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
114
Assets/VRM/Icons/vrm0x-48x48.png.meta
Normal file
114
Assets/VRM/Icons/vrm0x-48x48.png.meta
Normal file
@@ -0,0 +1,114 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7656e3d0da852c54e979d2887196ef75
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,13 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 329dca3bf78fcdd42b2df941673db76f
|
||||
timeCreated: 1519195979
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 7656e3d0da852c54e979d2887196ef75, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37562b39ff933b245ac2f35d87edbcd6
|
||||
timeCreated: 1517402750
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 7656e3d0da852c54e979d2887196ef75, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b678c1df50cfb547990db24a32856da
|
||||
timeCreated: 1517467747
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 7656e3d0da852c54e979d2887196ef75, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dedba1309bdf12b42af2362f52eea134
|
||||
timeCreated: 1519218333
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 7656e3d0da852c54e979d2887196ef75, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 690ea0146224b8b4694a1925dddeb352
|
||||
timeCreated: 1522391118
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 7656e3d0da852c54e979d2887196ef75, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63b589176a34b344b9ccbee2b7e7114a
|
||||
timeCreated: 1522391129
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 7656e3d0da852c54e979d2887196ef75, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00ea06e1753e16f4ca870c39c067c86b
|
||||
timeCreated: 1517224588
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 7656e3d0da852c54e979d2887196ef75, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
[CustomEditor(typeof(VRM10SpringBoneCollider))]
|
||||
@@ -9,32 +10,41 @@ namespace UniVRM10
|
||||
VRM10SpringBoneCollider _target;
|
||||
Vrm10Instance _vrm;
|
||||
|
||||
SerializedProperty _script;
|
||||
SerializedProperty _colliderType;
|
||||
SerializedProperty _offset;
|
||||
SerializedProperty _tail;
|
||||
|
||||
static VRM10SpringBoneCollider s_selected;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_target = (VRM10SpringBoneCollider)target;
|
||||
if(_target!=null)
|
||||
if (_target != null)
|
||||
{
|
||||
_vrm = _target.GetComponentInParent<Vrm10Instance>();
|
||||
}
|
||||
|
||||
_script = serializedObject.FindProperty("m_Script");
|
||||
_colliderType = serializedObject.FindProperty(nameof(_target.ColliderType));
|
||||
_offset = serializedObject.FindProperty(nameof(_target.Offset));
|
||||
_tail = serializedObject.FindProperty(nameof(_target.Tail));
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
// if (VRM10Window.Active == target)
|
||||
// {
|
||||
// GUI.backgroundColor = Color.cyan;
|
||||
// Repaint();
|
||||
// }
|
||||
var property = serializedObject.FindProperty("m_Script");
|
||||
var component = (VRM10SpringBoneCollider)target;
|
||||
EditorGUILayout.PropertyField(property, new GUIContent("Script (" + component.GetIdentificationName() + ")"));
|
||||
switch (component.ColliderType)
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
EditorGUILayout.PropertyField(_script);
|
||||
}
|
||||
|
||||
switch (_target.ColliderType)
|
||||
{
|
||||
case VRM10SpringBoneColliderTypes.Plane:
|
||||
// radius: x
|
||||
// tail: x
|
||||
// normal: o
|
||||
DrawPropertiesExcluding(serializedObject, "m_Script", nameof(component.Tail), nameof(component.Radius));
|
||||
DrawPropertiesExcluding(serializedObject, "m_Script", nameof(_target.Tail), nameof(_target.Radius));
|
||||
break;
|
||||
|
||||
case VRM10SpringBoneColliderTypes.Sphere:
|
||||
@@ -42,7 +52,7 @@ namespace UniVRM10
|
||||
// radius: o
|
||||
// tail: x
|
||||
// normal: x
|
||||
DrawPropertiesExcluding(serializedObject, nameof(component.Tail), nameof(component.Normal), "m_Script");
|
||||
DrawPropertiesExcluding(serializedObject, nameof(_target.Tail), nameof(_target.Normal), "m_Script");
|
||||
break;
|
||||
|
||||
case VRM10SpringBoneColliderTypes.Capsule:
|
||||
@@ -50,21 +60,74 @@ namespace UniVRM10
|
||||
// radius: o
|
||||
// tail: o
|
||||
// normal: x
|
||||
DrawPropertiesExcluding(serializedObject, nameof(component.Normal), "m_Script");
|
||||
DrawPropertiesExcluding(serializedObject, nameof(_target.Normal), "m_Script");
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
{
|
||||
if (GUILayout.Button("Drag handle"))
|
||||
{
|
||||
s_selected = _target;
|
||||
}
|
||||
|
||||
if (_target.transform.childCount > 0)
|
||||
{
|
||||
if (GUILayout.Button("Fit head-tail capsule"))
|
||||
{
|
||||
_colliderType.enumValueFlag = (int)VRM10SpringBoneColliderTypes.Capsule;
|
||||
_offset.vector3Value = Vector3.zero;
|
||||
var tail = _target.transform.GetChild(0);
|
||||
_tail.vector3Value = _target.transform.worldToLocalMatrix.MultiplyPoint(
|
||||
tail.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (serializedObject.ApplyModifiedProperties())
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
// UniGLTF.UniGLTFLogger.Log("invaliate");
|
||||
if(_vrm!=null)
|
||||
if (_vrm != null)
|
||||
{
|
||||
_vrm.Runtime.SpringBone.ReconstructSpringBone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSceneGUI()
|
||||
{
|
||||
HandleUtility.Repaint();
|
||||
|
||||
if (s_selected == _target)
|
||||
{
|
||||
var c = _target;
|
||||
Handles.matrix = c.transform.localToWorldMatrix;
|
||||
if (_target.ColliderType == VRM10SpringBoneColliderTypes.Capsule
|
||||
|| _target.ColliderType == VRM10SpringBoneColliderTypes.CapsuleInside)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var newTargetPosition = Handles.PositionHandle(c.Tail, Quaternion.identity);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(c, "collider");
|
||||
c.Tail = newTargetPosition;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var newTargetPosition = Handles.PositionHandle(c.Offset, Quaternion.identity);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(c, "collider");
|
||||
c.Offset = newTargetPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
[CustomEditor(typeof(VRM10SpringBoneColliderGroup))]
|
||||
class VRM10SpringBoneColliderGroupEditor : Editor
|
||||
{
|
||||
ListView m_colliders;
|
||||
|
||||
static VRM10SpringBoneCollider s_collider;
|
||||
|
||||
public override VisualElement CreateInspectorGUI()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
// root.TrackSerializedObjectValue(serializedObject, OnValueChanged);
|
||||
|
||||
root.Bind(serializedObject);
|
||||
|
||||
{
|
||||
var s = new PropertyField { bindingPath = "m_Script" };
|
||||
s.SetEnabled(false);
|
||||
root.Add(s);
|
||||
}
|
||||
|
||||
|
||||
root.Add(new PropertyField { bindingPath = nameof(VRM10SpringBoneColliderGroup.Name) });
|
||||
|
||||
{
|
||||
m_colliders = new ListView
|
||||
{
|
||||
bindingPath = nameof(VRM10SpringBoneColliderGroup.Colliders)
|
||||
};
|
||||
m_colliders.selectionChanged += (e) =>
|
||||
{
|
||||
var item = (SerializedProperty)e.FirstOrDefault();
|
||||
if (item == null)
|
||||
{
|
||||
s_collider = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_collider = (VRM10SpringBoneCollider)item.objectReferenceValue;
|
||||
}
|
||||
};
|
||||
m_colliders.showAddRemoveFooter = true;
|
||||
m_colliders.showFoldoutHeader = true;
|
||||
m_colliders.headerTitle = "Colliders";
|
||||
root.Add(m_colliders);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public void OnSceneGUI()
|
||||
{
|
||||
HandleUtility.Repaint();
|
||||
if (m_colliders == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_collider is VRM10SpringBoneCollider c)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Handles.matrix = c.transform.localToWorldMatrix;
|
||||
var newTargetPosition = Handles.PositionHandle(c.Offset, Quaternion.identity);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(c, "collider");
|
||||
c.Offset = newTargetPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ccae4c83e5d3a6438402cdd307f4ad2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
63
Assets/VRM10/Editor/ScriptedImporter/VrmaScriptedImporter.cs
Normal file
63
Assets/VRM10/Editor/ScriptedImporter/VrmaScriptedImporter.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using UnityEditor.AssetImporters;
|
||||
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
[ScriptedImporter(1, "vrma")]
|
||||
public class VrmaScriptedImporter : ScriptedImporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Vrm-1.0 の Asset にアイコンを付与する
|
||||
/// </summary>
|
||||
static Texture2D _AssetIcon = null;
|
||||
static Texture2D AssetIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_AssetIcon == null)
|
||||
{
|
||||
// try package
|
||||
_AssetIcon = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.vrmc.vrm/Icons/vrm-48x48.png");
|
||||
}
|
||||
if (_AssetIcon == null)
|
||||
{
|
||||
// try assets
|
||||
_AssetIcon = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/VRM10/Icons/vrm-48x48.png");
|
||||
}
|
||||
return _AssetIcon;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnImportAsset(AssetImportContext context)
|
||||
{
|
||||
// 2 回目以降の Asset Import において、 Importer の設定で Extract した UnityEngine.Object が入る
|
||||
var extractedObjects = GetExternalObjectMap()
|
||||
.Where(x => x.Value != null)
|
||||
.ToDictionary(kv => new SubAssetKey(kv.Value.GetType(), kv.Key.name), kv => kv.Value);
|
||||
|
||||
using (var data = new AutoGltfFileParser(assetPath).Parse())
|
||||
using (var loader = new VrmAnimationImporter(data, extractedObjects))
|
||||
{
|
||||
var loaded = loader.Load();
|
||||
|
||||
loaded.TransferOwnership((k, o) =>
|
||||
{
|
||||
context.AddObjectToAsset(k.Name, o);
|
||||
});
|
||||
|
||||
var root = loaded.Root;
|
||||
GameObject.DestroyImmediate(loaded);
|
||||
|
||||
// var vrma = root.GetComponent<Vrm10AnimationInstance>();
|
||||
// context.AddObjectToAsset("__boxman_mesh__", vrma.BoxMan.sharedMesh);
|
||||
// context.AddObjectToAsset("__boxman_mesh__material__", vrma.BoxMan.sharedMaterial);
|
||||
|
||||
context.AddObjectToAsset(root.name, root, AssetIcon);
|
||||
context.SetMainObject(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19427481f19aca9429da3548d28da6f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -190,8 +190,7 @@ namespace UniVRM10
|
||||
if (GUILayout.Button("Create new VRM10Object and default Expressions. select target folder"))
|
||||
{
|
||||
var saveName = GetSaveName(instance);
|
||||
var dir = SaveFileDialog.GetDir(SaveTitle, System.IO.Path.GetDirectoryName(saveName));
|
||||
if (!string.IsNullOrEmpty(dir))
|
||||
if (UniGltfEditorDialog.TryGetDir(SaveTitle, System.IO.Path.GetDirectoryName(saveName), out var dir))
|
||||
{
|
||||
var expressions = new Dictionary<ExpressionPreset, VRM10Expression>();
|
||||
foreach (ExpressionPreset expression in CachedEnum.GetValues<ExpressionPreset>())
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -231,29 +231,6 @@ namespace UniVRM10
|
||||
}
|
||||
}
|
||||
|
||||
// UVアクセスするテクスチャーのScaleOffsetプロパティの一覧
|
||||
static Dictionary<string, string[]> UVPropMap = new Dictionary<string, string[]>
|
||||
{
|
||||
{"Standard", new string[]{
|
||||
"_MainTex_ST",
|
||||
}},
|
||||
{"VRM10/MToon10", new string[]{
|
||||
"_MainTex_ST",
|
||||
}},
|
||||
};
|
||||
static string[] DefaultProps = { "_MainTex_ST" };
|
||||
public static String[] GetUVProps(string shaderName)
|
||||
{
|
||||
if (UVPropMap.TryGetValue(shaderName, out string[] props))
|
||||
{
|
||||
return props;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DefaultProps;
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<MaterialTarget> m_used = new HashSet<MaterialTarget>();
|
||||
public void Apply()
|
||||
{
|
||||
@@ -302,10 +279,8 @@ namespace UniVRM10
|
||||
//
|
||||
// Standard and MToon use _MainTex_ST as uv0 scale/offset
|
||||
//
|
||||
foreach (var prop in GetUVProps(item.Material.shader.name))
|
||||
{
|
||||
item.Material.SetVector(prop, kv.Value);
|
||||
}
|
||||
item.Material.mainTextureScale = new Vector2(kv.Value.x, kv.Value.y);
|
||||
item.Material.mainTextureOffset = new Vector2(kv.Value.z, kv.Value.w);
|
||||
}
|
||||
}
|
||||
m_materialUVMap.Clear();
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -54,21 +54,23 @@ namespace UniVRM10
|
||||
|
||||
public void DrawGizmos()
|
||||
{
|
||||
var backup = Gizmos.matrix;
|
||||
Gizmos.matrix = Matrix4x4.identity;
|
||||
VRM10SpringBoneJoint lastJoint = Joints[0];
|
||||
for (int i = 1; i < Joints.Count; ++i)
|
||||
if (Joints.Count > 0)
|
||||
{
|
||||
var joint = Joints[i];
|
||||
Gizmos.color = JointColor(lastJoint);
|
||||
if (joint != null && lastJoint != null)
|
||||
var backup = Gizmos.matrix;
|
||||
Gizmos.matrix = Matrix4x4.identity;
|
||||
VRM10SpringBoneJoint lastJoint = Joints[0];
|
||||
for (int i = 1; i < Joints.Count; ++i)
|
||||
{
|
||||
Gizmos.DrawLine(lastJoint.transform.position, joint.transform.position);
|
||||
var joint = Joints[i];
|
||||
Gizmos.color = JointColor(lastJoint);
|
||||
if (joint != null && lastJoint != null)
|
||||
{
|
||||
Gizmos.DrawLine(lastJoint.transform.position, joint.transform.position);
|
||||
}
|
||||
lastJoint = joint;
|
||||
}
|
||||
lastJoint = joint;
|
||||
Gizmos.matrix = backup;
|
||||
}
|
||||
|
||||
Gizmos.matrix = backup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace UniVRM10
|
||||
m_fastSpringBoneService.BufferCombiner.Unregister(m_fastSpringBoneBuffer);
|
||||
}
|
||||
|
||||
m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(new ImmediateCaller(), m_instance, m_fastSpringBoneBuffer);
|
||||
m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(awaitCaller, m_instance, m_fastSpringBoneBuffer);
|
||||
|
||||
// 登録
|
||||
m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer);
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace UniVRM10
|
||||
m_bufferCombiner.Unregister(m_fastSpringBoneBuffer);
|
||||
}
|
||||
|
||||
m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(new ImmediateCaller(), m_instance, m_fastSpringBoneBuffer);
|
||||
m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(awaitCaller, m_instance, m_fastSpringBoneBuffer);
|
||||
|
||||
// 登録
|
||||
m_bufferCombiner.Register(m_fastSpringBoneBuffer);
|
||||
|
||||
@@ -5,7 +5,7 @@ MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: ad4861e134018c948ac79793d290f48b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -7,12 +7,15 @@ using UniGLTF.Extensions.VRMC_vrm_animation;
|
||||
using UniHumanoid;
|
||||
using UniJSON;
|
||||
using UnityEngine;
|
||||
using VrmLib;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
public class VrmAnimationImporter : UniGLTF.ImporterContext
|
||||
{
|
||||
VRMC_vrm_animation m_vrma;
|
||||
ExpressionInfo[] m_expressions;
|
||||
Material m_defaultMaterial;
|
||||
|
||||
public VrmAnimationImporter(GltfData data,
|
||||
IReadOnlyDictionary<SubAssetKey, UnityEngine.Object> externalObjectMap = null,
|
||||
@@ -230,8 +233,8 @@ namespace UniVRM10
|
||||
{
|
||||
// Expression は AnimationClip を分ける。
|
||||
// glTFData から関連 Animation を取り除いて、取っておく。
|
||||
var expressions = IterateExpressions().ToArray();
|
||||
foreach (var channelIndex in expressions.Select(x => x.ChannelIndex).OrderByDescending(x => x))
|
||||
m_expressions = IterateExpressions().ToArray();
|
||||
foreach (var channelIndex in m_expressions.Select(x => x.ChannelIndex).OrderByDescending(x => x))
|
||||
{
|
||||
var nodeIndex = Data.GLTF.animations[0].channels[channelIndex].target.node;
|
||||
Data.GLTF.nodes.RemoveAt(nodeIndex);
|
||||
@@ -243,11 +246,16 @@ namespace UniVRM10
|
||||
Data.GLTF.scenes[0].nodes = Data.GLTF.scenes[0].nodes.Take(1).ToArray();
|
||||
|
||||
// 可視化メッシュ用マテリアル。base.LoadAsync を呼ぶ前に生成する。
|
||||
var defaultMaterial = await MaterialFactory.GetDefaultMaterialAsync(awaitCaller);
|
||||
m_defaultMaterial = await MaterialFactory.GetDefaultMaterialAsync(awaitCaller);
|
||||
|
||||
// Humanoid Animation が Gltf アニメーションとしてロードされる
|
||||
var instance = await base.LoadAsync(awaitCaller, measureTime);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected override Task OnLoadHierarchy(IAwaitCaller awaitCaller, Func<string, IDisposable> MeasureTime)
|
||||
{
|
||||
// setup humanoid
|
||||
var humanMap = GetHumanMap();
|
||||
if (humanMap.Count > 0)
|
||||
@@ -256,22 +264,23 @@ namespace UniVRM10
|
||||
//
|
||||
// avatar
|
||||
//
|
||||
var avatar = description.CreateAvatar(instance.Root.transform);
|
||||
var avatar = description.CreateAvatar(Root.transform);
|
||||
avatar.name = "Avatar";
|
||||
// AvatarDescription = description;
|
||||
var animator = instance.gameObject.AddComponent<Animator>();
|
||||
var animator = Root.AddComponent<Animator>();
|
||||
animator.avatar = avatar;
|
||||
}
|
||||
|
||||
if (expressions.Length > 0)
|
||||
if (m_expressions.Length > 0)
|
||||
{
|
||||
var animation = instance.GetComponentOrThrow<Animation>();
|
||||
var animation = Root.GetComponentOrThrow<Animation>();
|
||||
var clip = animation.clip;
|
||||
// m_expressionClip.name = "__expression__";
|
||||
|
||||
// Expression の float カーブを追加する
|
||||
// VrmAnimationInstance の "preset_xx" field に連動する
|
||||
var gltfAnimation = Data.GLTF.animations[0];
|
||||
foreach (var expression in expressions)
|
||||
foreach (var expression in m_expressions)
|
||||
{
|
||||
var channel = expression.Channel;
|
||||
var sampler = gltfAnimation.samplers[channel.sampler];
|
||||
@@ -284,11 +293,22 @@ namespace UniVRM10
|
||||
}
|
||||
}
|
||||
|
||||
// VRMA-animation solver
|
||||
var animationInstance = instance.gameObject.AddComponent<Vrm10AnimationInstance>();
|
||||
animationInstance.Initialize(expressions.Select(x => x.Key), defaultMaterial);
|
||||
var animationInstance = Root.AddComponent<Vrm10AnimationInstance>();
|
||||
|
||||
return instance;
|
||||
animationInstance.Initialize(m_expressions.Select(x => x.Key), m_defaultMaterial);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override void TransferOwnership(TakeResponsibilityForDestroyObjectFunc take)
|
||||
{
|
||||
var animationInstance = Root.GetComponent<Vrm10AnimationInstance>();
|
||||
take(SubAssetKey.Create(animationInstance.BoxMan.sharedMesh), animationInstance.BoxMan.sharedMesh);
|
||||
|
||||
var animator = Root.GetComponent<Animator>();
|
||||
take(SubAssetKey.Create(animator.avatar), animator.avatar);
|
||||
|
||||
base.TransferOwnership(take);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ namespace UniVRM10.Cloth.Viewer
|
||||
var warp = childchild.gameObject.AddComponent<ClothWarpRoot>();
|
||||
// Name = name,
|
||||
// CollisionMask = mask,
|
||||
warp.BaseSettings.radius = 0.02f;
|
||||
warp.BaseSettings.Radius = 0.02f;
|
||||
// Connection = type
|
||||
transforms.Add(warp);
|
||||
break;
|
||||
@@ -179,7 +179,7 @@ namespace UniVRM10.Cloth.Viewer
|
||||
if (warp != null)
|
||||
{
|
||||
// CollisionMask = mask,
|
||||
warp.BaseSettings.radius = 0.02f;
|
||||
warp.BaseSettings.Radius = 0.02f;
|
||||
// Connection = type
|
||||
transforms.Add(warp);
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ MonoBehaviour:
|
||||
onValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_IsOn: 0
|
||||
m_IsOn: 1
|
||||
--- !u!1 &153452228
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -686,6 +686,92 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 168425994}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &172483632
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 172483633}
|
||||
- component: {fileID: 172483634}
|
||||
m_Layer: 5
|
||||
m_Name: AddClothToHips
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &172483633
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 172483632}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1472853923}
|
||||
- {fileID: 1724807119}
|
||||
m_Father: {fileID: 339774397}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 162, y: 20}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &172483634
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 172483632}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Highlighted
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 1472853924}
|
||||
toggleTransition: 1
|
||||
graphic: {fileID: 1236232219}
|
||||
m_Group: {fileID: 0}
|
||||
onValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_IsOn: 1
|
||||
--- !u!1 &175751362
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1407,6 +1493,7 @@ RectTransform:
|
||||
- {fileID: 1767706907}
|
||||
- {fileID: 947409974}
|
||||
- {fileID: 135168672}
|
||||
- {fileID: 172483633}
|
||||
- {fileID: 2144476967}
|
||||
- {fileID: 1194499280}
|
||||
- {fileID: 153452229}
|
||||
@@ -5027,6 +5114,81 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1215781541}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &1236232217
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1236232218}
|
||||
- component: {fileID: 1236232220}
|
||||
- component: {fileID: 1236232219}
|
||||
m_Layer: 5
|
||||
m_Name: Checkmark
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1236232218
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1236232217}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1472853923}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 20, y: 20}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1236232219
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1236232217}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!222 &1236232220
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1236232217}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &1242458542
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -6228,6 +6390,82 @@ MonoBehaviour:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_IsOn: 0
|
||||
--- !u!1 &1472853922
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1472853923}
|
||||
- component: {fileID: 1472853925}
|
||||
- component: {fileID: 1472853924}
|
||||
m_Layer: 5
|
||||
m_Name: Background
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1472853923
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1472853922}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1236232218}
|
||||
m_Father: {fileID: 172483633}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 10, y: -10}
|
||||
m_SizeDelta: {x: 20, y: 20}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1472853924
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1472853922}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!222 &1472853925
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1472853922}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &1476033060
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -6774,6 +7012,85 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1684483641}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &1724807118
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1724807119}
|
||||
- component: {fileID: 1724807121}
|
||||
- component: {fileID: 1724807120}
|
||||
m_Layer: 5
|
||||
m_Name: Label
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1724807119
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1724807118}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 172483633}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 9, y: -0.5}
|
||||
m_SizeDelta: {x: -28, y: -3}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1724807120
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1724807118}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_FontSize: 14
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 10
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 0
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: Add cloth to Hips
|
||||
--- !u!222 &1724807121
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1724807118}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &1761414315
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -7224,6 +7541,7 @@ MonoBehaviour:
|
||||
m_openModel: {fileID: 2009818433}
|
||||
m_showBoxMan: {fileID: 1767706908}
|
||||
m_useJob: {fileID: 135168673}
|
||||
m_addClothToHips: {fileID: 172483634}
|
||||
m_reconstructSprngBone: {fileID: 2144476968}
|
||||
m_resetSpringBone: {fileID: 1194499281}
|
||||
m_pauseSpringBone: {fileID: 153452230}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace UniVRM10.Cloth.Viewer
|
||||
|
||||
[Header("Cloth")]
|
||||
[SerializeField] Toggle m_useJob = default;
|
||||
[SerializeField] Toggle m_addClothToHips = default;
|
||||
[SerializeField] Button m_reconstructSprngBone = default;
|
||||
[SerializeField] Button m_resetSpringBone = default;
|
||||
[SerializeField] Toggle m_pauseSpringBone = default;
|
||||
@@ -61,6 +62,7 @@ namespace UniVRM10.Cloth.Viewer
|
||||
m_showBoxMan = map.Get<Toggle>("ShowBoxMan");
|
||||
|
||||
m_useJob = map.Get<Toggle>("UseJob");
|
||||
m_addClothToHips = map.Get<Toggle>("AddClothToHips");
|
||||
m_reconstructSprngBone = map.Get<Button>("ReconstcutSpringBone");
|
||||
m_resetSpringBone = map.Get<Button>("ResetSpringBone");
|
||||
m_pauseSpringBone = map.Get<Toggle>("PauseSpringBone");
|
||||
@@ -402,18 +404,20 @@ namespace UniVRM10.Cloth.Viewer
|
||||
else
|
||||
{
|
||||
ClothWarpRuntimeProvider.FromVrm10(vrm,
|
||||
go => go.AddComponent<ClothWarpRoot>(),
|
||||
o => GameObject.DestroyImmediate(o));
|
||||
go => go.AddComponent<ClothWarpRoot>());
|
||||
}
|
||||
|
||||
if (animator.GetBoneTransform(HumanBodyBones.Hips) is var hips)
|
||||
if (m_addClothToHips.isOn)
|
||||
{
|
||||
var cloth = hips.GetComponent<ClothGrid>();
|
||||
if (cloth == null)
|
||||
if (animator.GetBoneTransform(HumanBodyBones.Hips) is var hips)
|
||||
{
|
||||
cloth = hips.gameObject.AddComponent<ClothGrid>();
|
||||
cloth.Reset();
|
||||
cloth.LoopIsClosed = true;
|
||||
var cloth = hips.GetComponent<ClothGrid>();
|
||||
if (cloth == null)
|
||||
{
|
||||
cloth = hips.gameObject.AddComponent<ClothGrid>();
|
||||
cloth.Reset();
|
||||
cloth.LoopIsClosed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"references": [
|
||||
"GUID:308b348fb80d89d42a9620951b0f60db",
|
||||
"GUID:e47c917724578cc43b5506c17a27e9a0",
|
||||
"GUID:3e5d614bc16b50d41bd94c8d7444ca46"
|
||||
"GUID:3e5d614bc16b50d41bd94c8d7444ca46",
|
||||
"GUID:8d76e605759c3f64a957d63ef96ada7c",
|
||||
"GUID:5f875fdc81c40184c8333b9d63c6ddd5"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UniVRM10;
|
||||
using UniGLTF;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Components
|
||||
{
|
||||
[CustomEditor(typeof(ClothWarpRoot))]
|
||||
class WarpRootEditor : Editor
|
||||
class ClothWarpRootEditor : Editor
|
||||
{
|
||||
private ClothWarpRoot m_target;
|
||||
private Vrm10Instance m_vrm;
|
||||
private MultiColumnTreeView m_treeview;
|
||||
VisualElement m_body;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
@@ -27,37 +28,20 @@ namespace UniVRM10.ClothWarp.Components
|
||||
m_vrm = m_target.GetComponentInParent<Vrm10Instance>();
|
||||
}
|
||||
|
||||
// public override void OnInspectorGUI()
|
||||
// {
|
||||
// var n = EditorUtility.GetDirtyCount(m_target.GetInstanceID());
|
||||
// base.OnInspectorGUI();
|
||||
// if (n != EditorUtility.GetDirtyCount(m_target.GetInstanceID()))
|
||||
// {
|
||||
// if (m_vrm != null)
|
||||
// {
|
||||
// if (Application.isPlaying)
|
||||
// {
|
||||
// m_vrm.Runtime.SpringBone.SetJointLevel(m_target.transform, m_target.BaseSettings);
|
||||
// foreach (var p in m_target.Particles)
|
||||
// {
|
||||
// m_vrm.Runtime.SpringBone.SetJointLevel(p.Transform, p.GetSettings(m_target.BaseSettings));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
void BindColumn<T>(string title, int width, Func<T> makeVisualELmeent, Func<int, bool> enableFunc, string subpath) where T : BindableElement
|
||||
void BindColumn<T>(MultiColumnTreeView tree, string title,
|
||||
int width, Func<T> makeVisualELment,
|
||||
Func<int, bool> enableFunc, string subpath) where T : BindableElement
|
||||
{
|
||||
m_treeview.columns.Add(new Column
|
||||
{
|
||||
title = title,
|
||||
width = width,
|
||||
makeCell = makeVisualELmeent,
|
||||
bindCell = (v, i) =>
|
||||
makeCell = makeVisualELment,
|
||||
bindCell = (v, index) =>
|
||||
{
|
||||
if (v is T prop)
|
||||
{
|
||||
var i = tree.GetIdForIndex(index);
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append("m_particles.Array.data[");
|
||||
sb.Append(i);
|
||||
@@ -85,8 +69,24 @@ namespace UniVRM10.ClothWarp.Components
|
||||
s.SetEnabled(false);
|
||||
root.Add(s);
|
||||
}
|
||||
root.Add(new PropertyField { bindingPath = nameof(ClothWarpRoot.BaseSettings) });
|
||||
root.Add(new PropertyField { bindingPath = nameof(ClothWarpRoot.Center) });
|
||||
|
||||
root.Add(new IMGUIContainer(() =>
|
||||
{
|
||||
foreach (var v in m_target.Validations)
|
||||
{
|
||||
v.DrawGUI();
|
||||
}
|
||||
}));
|
||||
|
||||
m_body = new VisualElement();
|
||||
root.Add(m_body);
|
||||
m_body.style.display = m_target.Validations.All(x => x.ErrorLevel < ErrorLevels.Warning)
|
||||
? DisplayStyle.Flex
|
||||
: DisplayStyle.None
|
||||
;
|
||||
|
||||
m_body.Add(new PropertyField { bindingPath = nameof(ClothWarpRoot.BaseSettings) });
|
||||
m_body.Add(new PropertyField { bindingPath = nameof(ClothWarpRoot.Center) });
|
||||
|
||||
// root.Add(new PropertyField { bindingPath = "m_particles" });
|
||||
{
|
||||
@@ -96,35 +96,43 @@ namespace UniVRM10.ClothWarp.Components
|
||||
};
|
||||
|
||||
m_treeview = new MultiColumnTreeView();
|
||||
BindColumn("Transform", 120, () => new ObjectField(), (_) => false, "Transform");
|
||||
BindColumn("Mode", 40, () => new EnumField(), (_) => true, "Mode");
|
||||
BindColumn("stiffnessForce", 40, () => new FloatField(), isCustom, "Settings.stiffnessForce");
|
||||
BindColumn("gravityPower", 40, () => new FloatField(), isCustom, "Settings.gravityPower");
|
||||
BindColumn("gravityDir", 120, () => new Vector3Field(), isCustom, "Settings.gravityDir");
|
||||
BindColumn("dragForce", 40, () => new FloatField(), isCustom, "Settings.dragForce");
|
||||
BindColumn("radius", 40, () => new FloatField(), isCustom, "Settings.radius");
|
||||
BindColumn(m_treeview, "Transform", 120, () => new ObjectField(), (_) => false, "Transform");
|
||||
BindColumn(m_treeview, "Mode", 40, () => new EnumField(), (_) => true, "Mode");
|
||||
BindColumn(m_treeview, "Stiffness", 40, () => new FloatField(), isCustom, "Settings.Stiffness");
|
||||
BindColumn(m_treeview, "Gravity", 120, () => new Vector3Field(), isCustom, "Settings.Gravity");
|
||||
BindColumn(m_treeview, "Deceleration", 40, () => new FloatField(), isCustom, "Settings.Deceleration");
|
||||
BindColumn(m_treeview, "Radius", 40, () => new FloatField(), isCustom, "Settings.Radius");
|
||||
|
||||
m_treeview.autoExpand = true;
|
||||
m_treeview.SetRootItems(m_target.m_rootitems);
|
||||
root.Add(m_treeview);
|
||||
m_body.Add(m_treeview);
|
||||
}
|
||||
|
||||
root.Add(new PropertyField { bindingPath = nameof(ClothWarpRoot.ColliderGroups) });
|
||||
m_body.Add(new PropertyField { bindingPath = nameof(ClothWarpRoot.ColliderGroups) });
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private void OnValueChanged(SerializedObject so)
|
||||
{
|
||||
Debug.Log("Name changed: " + so.targetObject.name);
|
||||
// var nameProperty = so.FindProperty("m_Name");
|
||||
if (m_vrm != null)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
m_vrm.Runtime.SpringBone.SetJointLevel(m_target.transform, m_target.BaseSettings.ToBlittableJointMutable());
|
||||
foreach (var p in m_target.Particles)
|
||||
{
|
||||
m_vrm.Runtime.SpringBone.SetJointLevel(p.Transform, p.Settings.ToBlittableJointMutable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (nameProperty.stringValue.Contains(" "))
|
||||
// _textField.style.backgroundColor = Color.red;
|
||||
// else
|
||||
// _textField.style.backgroundColor = StyleKeyword.Null;
|
||||
m_treeview.RefreshItems();
|
||||
// m_treeview.SetRootItems(m_target.m_rootitems);
|
||||
|
||||
m_body.style.display = m_target.Validations.All(x => x.ErrorLevel < ErrorLevels.Warning)
|
||||
? DisplayStyle.Flex
|
||||
: DisplayStyle.None
|
||||
;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
@@ -147,7 +155,7 @@ namespace UniVRM10.ClothWarp.Components
|
||||
p = m_target.GetParticleFromTransform(p.Transform);
|
||||
var t = p.Transform;
|
||||
Handles.color = Color.green;
|
||||
Handles.SphereHandleCap(t.GetInstanceID(), t.position, t.rotation, p.Settings.radius * 2, EventType.Repaint);
|
||||
Handles.SphereHandleCap(t.GetInstanceID(), t.position, t.rotation, p.Settings.Radius * 2, EventType.Repaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Linq;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UniVRM10;
|
||||
using UnityEditor.UIElements;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Components
|
||||
@@ -9,60 +8,109 @@ namespace UniVRM10.ClothWarp.Components
|
||||
[CustomEditor(typeof(ClothWarpRuntimeProvider))]
|
||||
public class RotateParticleRuntimeProviderEditor : Editor
|
||||
{
|
||||
const string FROM_VRM10_MENU = "Replace VRM10 Springs to ClothWarp Warps";
|
||||
ClothWarpRuntimeProvider _target;
|
||||
Vrm10Instance _vrm;
|
||||
|
||||
[MenuItem(FROM_VRM10_MENU, true)]
|
||||
public static bool IsFromVrm10()
|
||||
void OnEnable()
|
||||
{
|
||||
var go = Selection.activeGameObject;
|
||||
if (go == null)
|
||||
_target = (ClothWarpRuntimeProvider)target;
|
||||
if (_target != null)
|
||||
{
|
||||
return false;
|
||||
_vrm = _target.GetComponent<Vrm10Instance>();
|
||||
}
|
||||
return go.GetComponent<Vrm10Instance>() != null;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
public override VisualElement CreateInspectorGUI()
|
||||
{
|
||||
var provider = target as ClothWarpRuntimeProvider;
|
||||
if (provider == null)
|
||||
var root = new VisualElement();
|
||||
root.Bind(serializedObject);
|
||||
{
|
||||
return;
|
||||
var s = new PropertyField { bindingPath = "m_Script" };
|
||||
s.SetEnabled(false);
|
||||
root.Add(s);
|
||||
}
|
||||
var instance = provider.GetComponent<Vrm10Instance>();
|
||||
using (new EditorGUI.DisabledScope(instance == null))
|
||||
root.Add(new PropertyField { bindingPath = nameof(_target.UseJob) });
|
||||
root.Add(new PropertyField { bindingPath = nameof(_target.Warps) });
|
||||
root.Add(new PropertyField { bindingPath = nameof(_target.Cloths) });
|
||||
|
||||
{
|
||||
if (GUILayout.Button("Replace VRM10 Springs to ClothWarp Warps"))
|
||||
var setup = new Foldout { text = "Setup" };
|
||||
|
||||
var from_vrm10 = new Button { text = "Load VRM10 Springs to ClothWarp Warps" };
|
||||
setup.Add(from_vrm10);
|
||||
from_vrm10.RegisterCallback<ClickEvent>(e =>
|
||||
{
|
||||
Undo.IncrementCurrentGroup();
|
||||
Undo.SetCurrentGroupName(FROM_VRM10_MENU);
|
||||
Undo.SetCurrentGroupName("Load Vrm-1.0 Springs to ClothWarp Warps");
|
||||
var undo = Undo.GetCurrentGroup();
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(instance, "RegisterCompleteObjectUndo");
|
||||
ClothWarpRuntimeProvider.FromVrm10(instance, Undo.AddComponent<ClothWarpRoot>, Undo.DestroyObjectImmediate);
|
||||
Undo.RegisterFullObjectHierarchyUndo(instance.gameObject, "RegisterFullObjectHierarchyUndo");
|
||||
// attach ClothWarp from VRM10Instance.Springs
|
||||
ClothWarpRuntimeProvider.FromVrm10(_vrm, Undo.AddComponent<ClothWarpRoot>);
|
||||
Undo.RegisterFullObjectHierarchyUndo(_vrm.gameObject, "RegisterFullObjectHierarchyUndo");
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(provider, "RegisterCompleteObjectUndo");
|
||||
provider.Reset();
|
||||
// update ClothWarpRuntimeProvider
|
||||
Undo.RegisterCompleteObjectUndo(_target, "RegisterCompleteObjectUndo");
|
||||
_target.Reset();
|
||||
|
||||
Undo.CollapseUndoOperations(undo);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
using (new EditorGUI.DisabledScope(instance == null || !Application.isPlaying))
|
||||
{
|
||||
if (GUILayout.Button("RestoreInitialTransform"))
|
||||
var clear_vrm10_springs = new Button { text = "Clear Vrm-1.0 springs" };
|
||||
setup.Add(clear_vrm10_springs);
|
||||
clear_vrm10_springs.RegisterCallback<ClickEvent>(e =>
|
||||
{
|
||||
instance.Runtime.SpringBone.RestoreInitialTransform();
|
||||
}
|
||||
Undo.IncrementCurrentGroup();
|
||||
Undo.SetCurrentGroupName("Clear VRM10 Srpings");
|
||||
var undo = Undo.GetCurrentGroup();
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(_vrm, "RegisterCompleteObjectUndo");
|
||||
foreach (var spring in _vrm.SpringBone.Springs)
|
||||
{
|
||||
if (spring != null)
|
||||
{
|
||||
foreach (var joint in spring.Joints)
|
||||
{
|
||||
if (joint != null)
|
||||
{
|
||||
Undo.DestroyObjectImmediate(joint);
|
||||
}
|
||||
}
|
||||
}
|
||||
spring.Joints.Clear();
|
||||
}
|
||||
_vrm.SpringBone.Springs.Clear();
|
||||
Undo.RegisterFullObjectHierarchyUndo(_vrm.gameObject, "RegisterFullObjectHierarchyUndo");
|
||||
|
||||
Undo.CollapseUndoOperations(undo);
|
||||
});
|
||||
|
||||
var reload = new Button { text = "Reload" };
|
||||
setup.Add(reload);
|
||||
reload.RegisterCallback<ClickEvent>(e =>
|
||||
{
|
||||
_target.Reset();
|
||||
});
|
||||
|
||||
root.Add(setup);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Reset"))
|
||||
{
|
||||
provider.Reset();
|
||||
// runtime: reset button
|
||||
var runtime = new Foldout { text = "Runtime" };
|
||||
root.Add(runtime);
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
text = "RestoreInitialTransform",
|
||||
};
|
||||
runtime.Add(button);
|
||||
button.RegisterCallback<ClickEvent>((e) =>
|
||||
{
|
||||
_vrm.Runtime.SpringBone.RestoreInitialTransform();
|
||||
});
|
||||
}
|
||||
|
||||
base.OnInspectorGUI();
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ Base 設定を変更する場合は変える方を列挙設定できる。
|
||||
Custom と Disable を選択できる。
|
||||
|
||||
- ゆれものの根元にアタッチする
|
||||
- [ ] 子孫に HumanoidBone がある場合にアタッチ不可
|
||||
- [x] 子孫に HumanoidBone がある場合にアタッチ不可
|
||||
- [ ] 枝分かれ
|
||||
- [ ] WarpRoot らからデフォルト以外の Warp を選び出す
|
||||
- [ ] Center
|
||||
@@ -126,4 +126,4 @@ WarpRoot2 o=o=o
|
||||
|
||||
### Optimize
|
||||
|
||||
- [ ] 衝突グループ(現状総当たり)
|
||||
- [x] 衝突グループ
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace UniVRM10.ClothWarp
|
||||
class ClothRectList
|
||||
{
|
||||
readonly List<Transform> _particles;
|
||||
|
||||
public List<(SpringConstraint, ClothRect)> List = new();
|
||||
public readonly ClothGrid[] ClothGrids;
|
||||
public List<(int, SpringConstraint, ClothRect)> List = new();
|
||||
public readonly bool[] ClothUsedParticles;
|
||||
|
||||
public ClothRectList(List<Transform> particles, Vrm10Instance vrm)
|
||||
@@ -19,15 +19,16 @@ namespace UniVRM10.ClothWarp
|
||||
_particles = particles;
|
||||
ClothUsedParticles = new bool[_particles.Count];
|
||||
|
||||
var cloths = vrm.GetComponentsInChildren<ClothGrid>();
|
||||
foreach (var cloth in cloths)
|
||||
ClothGrids = vrm.GetComponentsInChildren<ClothGrid>();
|
||||
for (int i = 0; i < ClothGrids.Length; ++i)
|
||||
{
|
||||
AddCloth(cloth, vrm);
|
||||
AddCloth(i, vrm);
|
||||
}
|
||||
}
|
||||
|
||||
void AddCloth(ClothGrid cloth, Vrm10Instance vrm)
|
||||
void AddCloth(int clothGridIndex, Vrm10Instance vrm)
|
||||
{
|
||||
var cloth = ClothGrids[clothGridIndex];
|
||||
for (int i = 1; i < cloth.Warps.Count; ++i)
|
||||
{
|
||||
var s0 = cloth.Warps[i - 1];
|
||||
@@ -53,6 +54,7 @@ namespace UniVRM10.ClothWarp
|
||||
(c, d) = (d, c);
|
||||
}
|
||||
List.Add((
|
||||
clothGridIndex,
|
||||
new SpringConstraint(
|
||||
_particles.IndexOf(a),
|
||||
_particles.IndexOf(b),
|
||||
@@ -91,6 +93,7 @@ namespace UniVRM10.ClothWarp
|
||||
(c, d) = (d, c);
|
||||
}
|
||||
List.Add((
|
||||
clothGridIndex,
|
||||
new SpringConstraint(
|
||||
_particles.IndexOf(a),
|
||||
_particles.IndexOf(b),
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"GUID:3e5d614bc16b50d41bd94c8d7444ca46",
|
||||
"GUID:e47c917724578cc43b5506c17a27e9a0",
|
||||
"GUID:8d76e605759c3f64a957d63ef96ada7c",
|
||||
"GUID:1cd941934d098654fa21a13f28346412"
|
||||
"GUID:1cd941934d098654fa21a13f28346412",
|
||||
"GUID:5f875fdc81c40184c8333b9d63c6ddd5"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -11,6 +11,9 @@ using UnityEngine;
|
||||
|
||||
namespace UniVRM10.ClothWarp
|
||||
{
|
||||
/// <summary>
|
||||
/// プロトタイプ。非 job
|
||||
/// </summary>
|
||||
public class ClothWarpRuntime : IVrm10SpringBoneRuntime
|
||||
{
|
||||
Vrm10Instance _vrm;
|
||||
@@ -58,7 +61,7 @@ namespace UniVRM10.ClothWarp
|
||||
return Color.gray;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync(Vrm10Instance vrm, IAwaitCaller awaitCaller)
|
||||
public Task InitializeAsync(Vrm10Instance vrm, IAwaitCaller awaitCaller)
|
||||
{
|
||||
_building = true;
|
||||
_vrm = vrm;
|
||||
@@ -102,7 +105,7 @@ namespace UniVRM10.ClothWarp
|
||||
_clothRectCollisions = new();
|
||||
for (int i = 0; i < _clothRects.List.Count; ++i)
|
||||
{
|
||||
var (s, r) = _clothRects.List[i];
|
||||
var (grid, s, r) = _clothRects.List[i];
|
||||
_clothRectCollisions.Add(new());
|
||||
var c = _clothRectCollisions.Last();
|
||||
c.InitializeColliderSide(_newPos, _colliderGroups, r);
|
||||
@@ -112,6 +115,8 @@ namespace UniVRM10.ClothWarp
|
||||
|
||||
_initialized = true;
|
||||
_building = false;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -167,7 +172,7 @@ namespace UniVRM10.ClothWarp
|
||||
// verlet 積分
|
||||
var time = new FrameTime(deltaTime);
|
||||
_list.BeginFrame(Env, time, _restPositions);
|
||||
foreach (var (spring, collision) in _clothRects.List)
|
||||
foreach (var (gridIndex, spring, collision) in _clothRects.List)
|
||||
{
|
||||
// cloth constraint
|
||||
spring.Resolve(time, _clothFactor, _list._particles);
|
||||
@@ -189,7 +194,7 @@ namespace UniVRM10.ClothWarp
|
||||
|
||||
for (int j = 0; j < _clothRects.List.Count; ++j)
|
||||
{
|
||||
var (spring, rect) = _clothRects.List[j];
|
||||
var (gridIndex, spring, rect) = _clothRects.List[j];
|
||||
var collision = _clothRectCollisions[j];
|
||||
// using var prof = new ProfileSample("Collision: Cloth");
|
||||
// 頂点 abcd は同じ CollisionMask
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF.SpringBoneJobs.Blittables;
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UniVRM10;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Components
|
||||
@@ -17,18 +16,6 @@ namespace UniVRM10.ClothWarp.Components
|
||||
/// </summary>
|
||||
public class ClothWarpRoot : MonoBehaviour
|
||||
{
|
||||
public static BlittableJointMutable DefaultSetting()
|
||||
{
|
||||
return new BlittableJointMutable
|
||||
{
|
||||
stiffnessForce = 1.0f,
|
||||
gravityPower = 0,
|
||||
gravityDir = new Vector3(0, -1.0f, 0),
|
||||
dragForce = 0.4f,
|
||||
radius = 0.02f,
|
||||
};
|
||||
}
|
||||
|
||||
public enum ParticleMode
|
||||
{
|
||||
/// <summary>
|
||||
@@ -53,28 +40,28 @@ namespace UniVRM10.ClothWarp.Components
|
||||
{
|
||||
public Transform Transform;
|
||||
public ParticleMode Mode;
|
||||
public BlittableJointMutable Settings;
|
||||
public Jobs.ParticleSettings Settings;
|
||||
|
||||
public Particle(Transform t, ParticleMode mode, BlittableJointMutable settings)
|
||||
public Particle(Transform t, ParticleMode mode, Jobs.ParticleSettings settings)
|
||||
{
|
||||
Transform = t;
|
||||
Mode = mode;
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
public Particle(Transform t, BlittableJointMutable settings)
|
||||
public Particle(Transform t, Jobs.ParticleSettings settings)
|
||||
: this(t, ParticleMode.Custom, settings)
|
||||
{
|
||||
}
|
||||
|
||||
public Particle(Transform t)
|
||||
: this(t, ParticleMode.Base, DefaultSetting())
|
||||
: this(t, ParticleMode.Base, Jobs.ParticleSettings.Default)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
public BlittableJointMutable BaseSettings = DefaultSetting();
|
||||
public Jobs.ParticleSettings BaseSettings = Jobs.ParticleSettings.Default;
|
||||
|
||||
/// <summary>
|
||||
/// null のときは world root ではなく model root で処理
|
||||
@@ -98,9 +85,59 @@ namespace UniVRM10.ClothWarp.Components
|
||||
// 逆引き
|
||||
Dictionary<Transform, int> m_map = new();
|
||||
|
||||
public readonly List<Validation> Validations = new();
|
||||
|
||||
bool HasHumanoidBonesInChildren(Animator animator, out Transform t)
|
||||
{
|
||||
foreach (HumanBodyBones bone in Enum.GetValues(typeof(HumanBodyBones)))
|
||||
{
|
||||
if (bone == HumanBodyBones.LastBone)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var b = animator.GetBoneTransform(bone);
|
||||
if (b != null)
|
||||
{
|
||||
for (var parent = b.parent; parent != null; parent = parent.parent)
|
||||
{
|
||||
if (parent == transform)
|
||||
{
|
||||
t = transform;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
t = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
m_particles = GetComponentsInChildren<Transform>().Skip(1).Select(x => new Particle(x)).ToList();
|
||||
Validations.Clear();
|
||||
if (GetComponentInParent<Animator>() is var animator)
|
||||
{
|
||||
if (HasHumanoidBonesInChildren(animator, out var t))
|
||||
{
|
||||
Validations.Add(Validation.Error(
|
||||
"アタッチできません。子孫にHumanoidBoneがあります",
|
||||
ValidationContext.Create(t)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
var backup = m_particles.ToDictionary(x => x.Transform, x => x);
|
||||
m_particles = GetComponentsInChildren<Transform>().Skip(1).Select(x =>
|
||||
{
|
||||
foreach (var particle in m_particles)
|
||||
{
|
||||
if (particle.Transform == x)
|
||||
{
|
||||
return particle;
|
||||
}
|
||||
}
|
||||
return new Particle(x);
|
||||
}).ToList();
|
||||
m_map.Clear();
|
||||
for (int i = 0; i < m_particles.Count; ++i)
|
||||
{
|
||||
@@ -164,7 +201,7 @@ namespace UniVRM10.ClothWarp.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSettings(Transform t, BlittableJointMutable settings)
|
||||
public void SetSettings(Transform t, Jobs.ParticleSettings settings)
|
||||
{
|
||||
if (t == null) return;
|
||||
for (int i = 0; i < m_particles.Count; ++i)
|
||||
@@ -182,7 +219,7 @@ namespace UniVRM10.ClothWarp.Components
|
||||
|
||||
public void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.DrawSphere(transform.position, BaseSettings.radius);
|
||||
Gizmos.DrawSphere(transform.position, BaseSettings.Radius);
|
||||
|
||||
foreach (var p in Particles)
|
||||
{
|
||||
@@ -190,7 +227,7 @@ namespace UniVRM10.ClothWarp.Components
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Gizmos.DrawWireSphere(p.Transform.position, p.Settings.radius);
|
||||
Gizmos.DrawWireSphere(p.Transform.position, p.Settings.Radius);
|
||||
|
||||
if (TryGetClosestParent(p.Transform, out var parent))
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UniVRM10;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Components
|
||||
{
|
||||
@@ -17,7 +17,7 @@ namespace UniVRM10.ClothWarp.Components
|
||||
public List<ClothGrid> Cloths = new();
|
||||
|
||||
[SerializeField]
|
||||
public bool UseJob;
|
||||
public bool UseJob = true;
|
||||
|
||||
IVrm10SpringBoneRuntime m_runtime;
|
||||
public IVrm10SpringBoneRuntime CreateSpringBoneRuntime()
|
||||
@@ -45,8 +45,7 @@ namespace UniVRM10.ClothWarp.Components
|
||||
}
|
||||
|
||||
public static void FromVrm10(Vrm10Instance instance,
|
||||
Func<GameObject, ClothWarpRoot> addWarp,
|
||||
Action<UnityEngine.Object> deleteObject)
|
||||
Func<GameObject, ClothWarpRoot> addWarp)
|
||||
{
|
||||
foreach (var spring in instance.SpringBone.Springs)
|
||||
{
|
||||
@@ -64,29 +63,30 @@ namespace UniVRM10.ClothWarp.Components
|
||||
var warp = root_joint.GetComponent<ClothWarpRoot>();
|
||||
if (warp == null)
|
||||
{
|
||||
// var warp = Undo.AddComponent<Warp>(root_joint);
|
||||
warp = addWarp(root_joint);
|
||||
var joints = spring.Joints.Where(x => x != null).ToArray();
|
||||
for (int i = 0; i < joints.Length; ++i)
|
||||
{
|
||||
var joint = joints[i];
|
||||
var settings = new UniGLTF.SpringBoneJobs.Blittables.BlittableJointMutable
|
||||
|
||||
// mod ?
|
||||
var stiffness = Mathf.Min(0.08f, joint.m_stiffnessForce * 0.1f);
|
||||
|
||||
var settings = new Jobs.ParticleSettings
|
||||
{
|
||||
dragForce = joint.m_dragForce,
|
||||
gravityDir = joint.m_gravityDir,
|
||||
gravityPower = joint.m_gravityPower,
|
||||
// mod
|
||||
stiffnessForce = joint.m_stiffnessForce * 6,
|
||||
Deceleration = joint.m_dragForce,
|
||||
Gravity = joint.m_gravityDir * joint.m_gravityPower,
|
||||
Stiffness = stiffness,
|
||||
};
|
||||
if (i == 0)
|
||||
{
|
||||
settings.radius = joints[0].m_jointRadius;
|
||||
settings.Radius = joints[0].m_jointRadius;
|
||||
warp.BaseSettings = settings;
|
||||
}
|
||||
else
|
||||
{
|
||||
// breaking change from vrm-1.0
|
||||
settings.radius = joints[i - 1].m_jointRadius;
|
||||
settings.Radius = joints[i - 1].m_jointRadius;
|
||||
var useInheritSettings = warp.BaseSettings.Equals(settings);
|
||||
if (useInheritSettings)
|
||||
{
|
||||
@@ -97,14 +97,10 @@ namespace UniVRM10.ClothWarp.Components
|
||||
warp.SetSettings(joint.transform, settings);
|
||||
}
|
||||
}
|
||||
// Undo.DestroyObjectImmediate(joint);
|
||||
deleteObject(joint);
|
||||
}
|
||||
spring.Joints.Clear();
|
||||
warp.ColliderGroups = spring.ColliderGroups.ToList();
|
||||
}
|
||||
}
|
||||
instance.SpringBone.Springs.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,13 @@ using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Jobs;
|
||||
using UniVRM10;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
/// <summary>
|
||||
/// Job 版
|
||||
/// </summary>
|
||||
public class ClothWarpJobRuntime : IVrm10SpringBoneRuntime
|
||||
{
|
||||
Vrm10Instance _vrm;
|
||||
@@ -19,12 +21,25 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
bool _building = false;
|
||||
|
||||
//
|
||||
// collider
|
||||
// colliderTransform
|
||||
//
|
||||
List<Transform> _colliderTransforms;
|
||||
TransformAccessArray _colliderTransformAccessArray;
|
||||
NativeArray<Matrix4x4> _currentColliders;
|
||||
NativeArray<BlittableCollider> _colliders;
|
||||
|
||||
//
|
||||
// collider
|
||||
//
|
||||
List<VRM10SpringBoneCollider> _colliders;
|
||||
NativeArray<BlittableCollider> _colliderInfo;
|
||||
|
||||
//
|
||||
// colliderGroup
|
||||
//
|
||||
List<VRM10SpringBoneColliderGroup> _colliderGroups;
|
||||
NativeArray<int> _colliderRef;
|
||||
NativeArray<ArrayRange> _colliderGroup;
|
||||
NativeArray<int> _colliderGroupRef;
|
||||
|
||||
//
|
||||
// particle
|
||||
@@ -38,10 +53,10 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
NativeArray<Vector3> _nextPositions;
|
||||
NativeArray<Quaternion> _nextRotations;
|
||||
|
||||
NativeArray<Vector3> _strandCollision;
|
||||
NativeArray<int> _clothCollisionCount;
|
||||
NativeArray<Vector3> _clothCollisionDelta;
|
||||
NativeArray<Vector3> _forces;
|
||||
NativeArray<Vector3> _warpCollision;
|
||||
NativeArray<int> _rectCollisionCount;
|
||||
NativeArray<Vector3> _rectCollisionDelta;
|
||||
NativeArray<Vector3> _impulsiveForces;
|
||||
|
||||
//
|
||||
// warp
|
||||
@@ -49,16 +64,26 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
NativeArray<WarpInfo> _warps;
|
||||
|
||||
//
|
||||
// cloth
|
||||
// cloth rect
|
||||
//
|
||||
NativeArray<bool> _clothUsedParticles;
|
||||
NativeArray<(SpringConstraint, SphereTriangle.ClothRect)> _clothRects;
|
||||
NativeArray<(int ClothGridIndex, SpringConstraint SpringConstraint, SphereTriangle.ClothRect Rect)> _clothRects;
|
||||
NativeArray<(Vector3, Vector3, Vector3, Vector3)> _clothRectResults;
|
||||
|
||||
//
|
||||
// cloth grid
|
||||
//
|
||||
NativeArray<ClothInfo> _cloths;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_colliderTransformAccessArray.isCreated) _colliderTransformAccessArray.Dispose();
|
||||
if (_currentColliders.IsCreated) _currentColliders.Dispose();
|
||||
|
||||
if (_colliderRef.IsCreated) _colliderRef.Dispose();
|
||||
if (_colliderGroup.IsCreated) _colliderGroup.Dispose();
|
||||
if (_colliderGroupRef.IsCreated) _colliderGroupRef.Dispose();
|
||||
|
||||
if (_warps.IsCreated) _warps.Dispose();
|
||||
if (_transformAccessArray.isCreated) _transformAccessArray.Dispose();
|
||||
if (_inputData.IsCreated) _inputData.Dispose();
|
||||
@@ -67,15 +92,17 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
if (_prevPositions.IsCreated) _prevPositions.Dispose();
|
||||
if (_nextPositions.IsCreated) _nextPositions.Dispose();
|
||||
if (_nextRotations.IsCreated) _nextRotations.Dispose();
|
||||
if (_strandCollision.IsCreated) _strandCollision.Dispose();
|
||||
if (_clothCollisionCount.IsCreated) _clothCollisionCount.Dispose();
|
||||
if (_clothCollisionDelta.IsCreated) _clothCollisionDelta.Dispose();
|
||||
if (_forces.IsCreated) _forces.Dispose();
|
||||
if (_warpCollision.IsCreated) _warpCollision.Dispose();
|
||||
if (_rectCollisionCount.IsCreated) _rectCollisionCount.Dispose();
|
||||
if (_rectCollisionDelta.IsCreated) _rectCollisionDelta.Dispose();
|
||||
if (_impulsiveForces.IsCreated) _impulsiveForces.Dispose();
|
||||
|
||||
if (_warps.IsCreated) _warps.Dispose();
|
||||
if (_cloths.IsCreated) _cloths.Dispose();
|
||||
|
||||
if (_clothUsedParticles.IsCreated) _clothUsedParticles.Dispose();
|
||||
if (_clothRects.IsCreated) _clothRects.Dispose();
|
||||
if (_clothRectResults.IsCreated) _clothRectResults.Dispose();
|
||||
}
|
||||
|
||||
(int index, bool isNew) GetTransformIndex(Transform t,
|
||||
@@ -97,6 +124,33 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
return (i, true);
|
||||
}
|
||||
|
||||
Transform GetParent(Transform t, Transform root)
|
||||
{
|
||||
for (var parent = t.parent; parent != null; parent = parent.parent)
|
||||
{
|
||||
if (_transforms.Contains(parent))
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
if (parent == root)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
int GetOrAddColliderTransform(Transform t)
|
||||
{
|
||||
var index = _colliderTransforms.IndexOf(t);
|
||||
if (index == -1)
|
||||
{
|
||||
index = _colliderTransforms.Count;
|
||||
_colliderTransforms.Add(t);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync(Vrm10Instance vrm, IAwaitCaller awaitCaller)
|
||||
{
|
||||
_vrm = vrm;
|
||||
@@ -111,30 +165,67 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
//
|
||||
// colliders
|
||||
//
|
||||
_colliders = new();
|
||||
_colliderGroups = new();
|
||||
_colliderTransforms = new();
|
||||
List<BlittableCollider> colliders = new();
|
||||
foreach (var collider in vrm.GetComponentsInChildren<VRM10SpringBoneCollider>())
|
||||
List<BlittableCollider> colliderInfo = new();
|
||||
List<int> colliderRef = new();
|
||||
List<ArrayRange> colliderGroups = new();
|
||||
foreach (var colliderGroup in vrm.GetComponentsInChildren<VRM10SpringBoneColliderGroup>())
|
||||
{
|
||||
colliders.Add(new BlittableCollider
|
||||
if (colliderGroup == null)
|
||||
{
|
||||
offset = collider.Offset,
|
||||
radius = collider.Radius,
|
||||
tailOrNormal = collider.TailOrNormal,
|
||||
colliderType = TranslateColliderType(collider.ColliderType)
|
||||
continue;
|
||||
}
|
||||
|
||||
var startColliderRef = colliderRef.Count;
|
||||
foreach (var collider in colliderGroup.Colliders)
|
||||
{
|
||||
if (collider == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_colliders.Contains(collider))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var colliderTransformIndex = GetOrAddColliderTransform(collider.transform);
|
||||
|
||||
colliderRef.Add(colliderInfo.Count);
|
||||
colliderInfo.Add(new BlittableCollider
|
||||
{
|
||||
offset = collider.Offset,
|
||||
radius = collider.Radius,
|
||||
tailOrNormal = collider.TailOrNormal,
|
||||
colliderType = TranslateColliderType(collider.ColliderType),
|
||||
transformIndex = colliderTransformIndex,
|
||||
});
|
||||
_colliders.Add(collider);
|
||||
}
|
||||
|
||||
_colliderGroups.Add(colliderGroup);
|
||||
colliderGroups.Add(new ArrayRange
|
||||
{
|
||||
Start = startColliderRef,
|
||||
End = colliderRef.Count,
|
||||
});
|
||||
_colliderTransforms.Add(collider.transform);
|
||||
}
|
||||
_colliderTransformAccessArray = new(_colliderTransforms.ToArray(), 128);
|
||||
_colliders = new(colliders.ToArray(), Allocator.Persistent);
|
||||
_currentColliders = new(_colliderTransforms.Count, Allocator.Persistent);
|
||||
_colliderInfo = new(colliderInfo.ToArray(), Allocator.Persistent);
|
||||
|
||||
_colliderRef = new(colliderRef.ToArray(), Allocator.Persistent);
|
||||
_colliderGroup = new(colliderGroups.ToArray(), Allocator.Persistent);
|
||||
|
||||
//
|
||||
// warps
|
||||
// warps => particles
|
||||
//
|
||||
_transforms = new();
|
||||
List<TransformInfo> info = new();
|
||||
List<Vector3> positions = new();
|
||||
List<WarpInfo> warps = new();
|
||||
List<int> colliderGroupRef = new();
|
||||
var warpSrcs = vrm.GetComponentsInChildren<Components.ClothWarpRoot>();
|
||||
for (int warpIndex = 0; warpIndex < warpSrcs.Length; ++warpIndex)
|
||||
{
|
||||
@@ -145,14 +236,16 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
GetTransformIndex(warp.Center, new TransformInfo
|
||||
{
|
||||
TransformType = TransformType.Center
|
||||
TransformType = TransformType.Center,
|
||||
WarpIndex = warpIndex,
|
||||
}, info, positions);
|
||||
start += 1;
|
||||
}
|
||||
|
||||
var warpRootParentTransformIndex = GetTransformIndex(warp.transform.parent, new TransformInfo
|
||||
{
|
||||
TransformType = TransformType.WarpRootParent
|
||||
TransformType = TransformType.WarpRootParent,
|
||||
WarpIndex = warpIndex,
|
||||
}, info, positions);
|
||||
Debug.Assert(warpRootParentTransformIndex.index != -1);
|
||||
if (warpRootParentTransformIndex.isNew)
|
||||
@@ -166,31 +259,81 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
ParentIndex = warpRootParentTransformIndex.index,
|
||||
InitLocalPosition = vrm.DefaultTransformStates[warp.transform].LocalPosition,
|
||||
InitLocalRotation = vrm.DefaultTransformStates[warp.transform].LocalRotation,
|
||||
Settings = warp.BaseSettings,
|
||||
WarpIndex = warpIndex,
|
||||
}, info, positions);
|
||||
Debug.Assert(warpRootTransformIndex.index != -1);
|
||||
Debug.Assert(warpRootTransformIndex.isNew);
|
||||
|
||||
var parentIndex = warpRootTransformIndex.index;
|
||||
foreach (var particle in warp.Particles)
|
||||
var colliderGroupRefStart = colliderGroupRef.Count;
|
||||
if (warpRootTransformIndex.isNew)
|
||||
{
|
||||
if (particle.Transform != null && particle.Mode != Components.ClothWarpRoot.ParticleMode.Disabled)
|
||||
// var parentIndex = warpRootTransformIndex.index;
|
||||
|
||||
Func<int, int> GetFirstSiblingIndex = (parent) =>
|
||||
{
|
||||
var outputParticleTransformIndex = GetTransformIndex((Transform)particle.Transform, new TransformInfo
|
||||
for (int i = 0; i < info.Count; ++i)
|
||||
{
|
||||
TransformType = TransformType.Particle,
|
||||
ParentIndex = parentIndex,
|
||||
InitLocalPosition = vrm.DefaultTransformStates[(Transform)particle.Transform].LocalPosition,
|
||||
InitLocalRotation = vrm.DefaultTransformStates[(Transform)particle.Transform].LocalRotation,
|
||||
Settings = particle.Settings,
|
||||
}, info, positions);
|
||||
parentIndex = outputParticleTransformIndex.index;
|
||||
if (info[i].ParentIndex == parent)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new Exception();
|
||||
};
|
||||
|
||||
HashSet<int> parentIndexSet = new();
|
||||
foreach (var particle in warp.Particles)
|
||||
{
|
||||
if (particle.Transform != null && particle.Mode != Components.ClothWarpRoot.ParticleMode.Disabled)
|
||||
{
|
||||
var parentIndex = _transforms.IndexOf(GetParent(particle.Transform, warp.transform));
|
||||
BranchInfo? branch = default;
|
||||
if (parentIndexSet.Contains(parentIndex))
|
||||
{
|
||||
branch = new BranchInfo
|
||||
{
|
||||
FirstSiblingIndex = GetFirstSiblingIndex(parentIndex),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
parentIndexSet.Add(parentIndex);
|
||||
}
|
||||
|
||||
var outputParticleTransformIndex = GetTransformIndex(particle.Transform, new TransformInfo
|
||||
{
|
||||
TransformType = TransformType.Particle,
|
||||
ParentIndex = parentIndex,
|
||||
InitLocalPosition = vrm.DefaultTransformStates[particle.Transform].LocalPosition,
|
||||
InitLocalRotation = vrm.DefaultTransformStates[particle.Transform].LocalRotation,
|
||||
Settings = particle.Settings,
|
||||
WarpIndex = warpIndex,
|
||||
Branch = branch,
|
||||
}, info, positions);
|
||||
// parentIndex = outputParticleTransformIndex.index;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var group in warp.ColliderGroups)
|
||||
{
|
||||
if (group != null)
|
||||
{
|
||||
colliderGroupRef.Add(_colliderGroups.IndexOf(group));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
warps.Add(new WarpInfo
|
||||
{
|
||||
StartIndex = start,
|
||||
EndIndex = _transforms.Count,
|
||||
PrticleRange = new ArrayRange
|
||||
{
|
||||
Start = start,
|
||||
End = _transforms.Count,
|
||||
},
|
||||
ColliderGroupRefRange = new ArrayRange
|
||||
{
|
||||
Start = colliderGroupRefStart,
|
||||
End = colliderGroupRef.Count,
|
||||
},
|
||||
});
|
||||
|
||||
await awaitCaller.NextFrame();
|
||||
@@ -204,18 +347,48 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
_nextPositions = new(pos.Length, Allocator.Persistent);
|
||||
_nextRotations = new(pos.Length, Allocator.Persistent);
|
||||
_info = new(info.ToArray(), Allocator.Persistent);
|
||||
_strandCollision = new(pos.Length, Allocator.Persistent);
|
||||
_clothCollisionCount = new(pos.Length, Allocator.Persistent);
|
||||
_clothCollisionDelta = new(pos.Length, Allocator.Persistent);
|
||||
_forces = new(pos.Length, Allocator.Persistent);
|
||||
_warpCollision = new(pos.Length, Allocator.Persistent);
|
||||
_rectCollisionCount = new(pos.Length, Allocator.Persistent);
|
||||
_rectCollisionDelta = new(pos.Length, Allocator.Persistent);
|
||||
_impulsiveForces = new(pos.Length, Allocator.Persistent);
|
||||
|
||||
//
|
||||
// cloths
|
||||
//
|
||||
var clothRects = new ClothRectList(_transforms, vrm);
|
||||
_clothRects = new(clothRects.List.ToArray(), Allocator.Persistent);
|
||||
_clothRectResults = new(clothRects.List.Count, Allocator.Persistent);
|
||||
_clothUsedParticles = new(clothRects.ClothUsedParticles, Allocator.Persistent);
|
||||
_building = false;
|
||||
|
||||
List<ClothInfo> cloths = new();
|
||||
foreach (var grid in clothRects.ClothGrids)
|
||||
{
|
||||
var colliderGroupRefStart = colliderGroupRef.Count;
|
||||
HashSet<VRM10SpringBoneColliderGroup> groups = new();
|
||||
foreach (var warp in grid.Warps)
|
||||
{
|
||||
foreach (var group in warp.ColliderGroups)
|
||||
{
|
||||
if (group != null && !groups.Contains(group))
|
||||
{
|
||||
groups.Add(group);
|
||||
colliderGroupRef.Add(_colliderGroups.IndexOf(group));
|
||||
}
|
||||
}
|
||||
}
|
||||
cloths.Add(new ClothInfo
|
||||
{
|
||||
ColliderGroupRefRange = new ArrayRange
|
||||
{
|
||||
Start = colliderGroupRefStart,
|
||||
End = colliderGroupRef.Count,
|
||||
},
|
||||
});
|
||||
}
|
||||
_cloths = new(cloths.ToArray(), Allocator.Persistent);
|
||||
|
||||
_colliderGroupRef = new(colliderGroupRef.ToArray(), Allocator.Persistent);
|
||||
}
|
||||
|
||||
private static BlittableColliderType TranslateColliderType(VRM10SpringBoneColliderTypes colliderType)
|
||||
@@ -259,10 +432,10 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
Info = _info,
|
||||
InputData = _inputData,
|
||||
CurrentPositions = _currentPositions,
|
||||
Forces = _forces,
|
||||
ImpulsiveForces = _impulsiveForces,
|
||||
|
||||
CollisionCount = _clothCollisionCount,
|
||||
CollisionDelta = _clothCollisionDelta,
|
||||
CollisionCount = _rectCollisionCount,
|
||||
CollisionDelta = _rectCollisionDelta,
|
||||
}.Schedule(_transformAccessArray, handle);
|
||||
|
||||
// spring(cloth weft)
|
||||
@@ -271,8 +444,8 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
ClothRects = _clothRects,
|
||||
CurrentPositions = _currentPositions,
|
||||
|
||||
Force = _forces,
|
||||
}.Schedule(_clothRects.Length, 128, handle);
|
||||
ImpulsiveForces = _impulsiveForces,
|
||||
}.Schedule(_clothRects.Length, 1, handle);
|
||||
|
||||
// verlet
|
||||
handle = new VerletJob
|
||||
@@ -282,58 +455,86 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
CurrentTransforms = _inputData,
|
||||
PrevPositions = _prevPositions,
|
||||
CurrentPositions = _currentPositions,
|
||||
Forces = _forces,
|
||||
ImpulsiveForces = _impulsiveForces,
|
||||
|
||||
NextPositions = _nextPositions,
|
||||
NextRotations = _nextRotations,
|
||||
}.Schedule(_info.Length, 128, handle);
|
||||
}.Schedule(_info.Length, 1, handle);
|
||||
|
||||
// 親子の長さで拘束
|
||||
handle = new ParentLengthConstraintJob
|
||||
{
|
||||
Warps = _warps,
|
||||
Info = _info,
|
||||
Data = _inputData,
|
||||
NextPositions = _nextPositions,
|
||||
}.Schedule(_warps.Length, 16, handle);
|
||||
}.Schedule(_warps.Length, 1, handle);
|
||||
|
||||
// collision
|
||||
{
|
||||
var handle0 = new StrandCollisionJob
|
||||
var handle0 = new WarpCollisionJob
|
||||
{
|
||||
Colliders = _colliders,
|
||||
Colliders = _colliderInfo,
|
||||
CurrentColliders = _currentColliders,
|
||||
|
||||
Info = _info,
|
||||
NextPositions = _nextPositions,
|
||||
ClothUsedParticles = _clothUsedParticles,
|
||||
StrandCollision = _strandCollision,
|
||||
}.Schedule(_info.Length, 128, handle);
|
||||
StrandCollision = _warpCollision,
|
||||
|
||||
var handle1 = new ClothCollisionJob
|
||||
Warps = _warps,
|
||||
ColliderGroupRef = _colliderGroupRef,
|
||||
ColliderGroup = _colliderGroup,
|
||||
ColliderRef = _colliderRef,
|
||||
}.Schedule(_info.Length, 1, handle);
|
||||
|
||||
var handle1 = new RectCollisionJob
|
||||
{
|
||||
Colliders = _colliders,
|
||||
ClothRects = _clothRects,
|
||||
ClothRectResults = _clothRectResults,
|
||||
|
||||
Colliders = _colliderInfo,
|
||||
CurrentColliders = _currentColliders,
|
||||
|
||||
Info = _info,
|
||||
NextPositions = _nextPositions,
|
||||
CollisionCount = _clothCollisionCount,
|
||||
CollisionDelta = _clothCollisionDelta,
|
||||
|
||||
Cloths = _cloths,
|
||||
ColliderGroupRef = _colliderGroupRef,
|
||||
ColliderGroup = _colliderGroup,
|
||||
ColliderRef = _colliderRef,
|
||||
}.Schedule(_clothRects.Length, 1, handle);
|
||||
|
||||
handle1 = new RectCollisionReduceJob
|
||||
{
|
||||
ClothRects = _clothRects,
|
||||
}.Schedule(_clothRects.Length, 128, handle);
|
||||
ClothRectResults = _clothRectResults,
|
||||
NextPositions = _nextPositions,
|
||||
|
||||
RectCollisionCount = _rectCollisionCount,
|
||||
RectCollisionDelta = _rectCollisionDelta,
|
||||
}.Schedule(handle1);
|
||||
|
||||
handle = JobHandle.CombineDependencies(handle0, handle1);
|
||||
|
||||
handle = new CollisionApplyJob
|
||||
{
|
||||
ClothUsedParticles = _clothUsedParticles,
|
||||
StrandCollision = _strandCollision,
|
||||
ClothCollisionCount = _clothCollisionCount,
|
||||
ClothCollisionDelta = _clothCollisionDelta,
|
||||
StrandCollision = _warpCollision,
|
||||
RectCollisionCount = _rectCollisionCount,
|
||||
RectCollisionDelta = _rectCollisionDelta,
|
||||
NextPosition = _nextPositions,
|
||||
}.Schedule(_info.Length, 128, handle);
|
||||
}.Schedule(_info.Length, 1, handle);
|
||||
}
|
||||
|
||||
// 親子の長さで拘束. TODO: ApplyRotationJob と合体
|
||||
handle = new ParentLengthConstraintJob
|
||||
{
|
||||
Warps = _warps,
|
||||
Info = _info,
|
||||
Data = _inputData,
|
||||
NextPositions = _nextPositions,
|
||||
}.Schedule(_warps.Length, 1, handle);
|
||||
// NextPositions から NextRotations を作る
|
||||
handle = new ApplyRotationJob
|
||||
{
|
||||
@@ -342,7 +543,7 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
CurrentTransforms = _inputData,
|
||||
NextPositions = _nextPositions,
|
||||
NextRotations = _nextRotations,
|
||||
}.Schedule(_warps.Length, 16, handle);
|
||||
}.Schedule(_warps.Length, 1, handle);
|
||||
|
||||
// output
|
||||
handle = new OutputTransformJob
|
||||
@@ -362,7 +563,7 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
foreach (var warp in _warps)
|
||||
{
|
||||
for (int i = warp.StartIndex; i < warp.EndIndex; ++i)
|
||||
for (int i = warp.PrticleRange.Start; i < warp.PrticleRange.End; ++i)
|
||||
{
|
||||
var p = _info[i];
|
||||
var t = _transforms[i];
|
||||
@@ -392,11 +593,11 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
break;
|
||||
case TransformType.ClothWarp:
|
||||
Gizmos.color = Color.white;
|
||||
Gizmos.DrawSphere(v, info.Settings.radius);
|
||||
Gizmos.DrawSphere(v, info.Settings.Radius);
|
||||
break;
|
||||
case TransformType.Particle:
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(v, info.Settings.radius);
|
||||
Gizmos.DrawWireSphere(v, info.Settings.Radius);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -408,7 +609,7 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
if (i != -1)
|
||||
{
|
||||
var info = _info[i];
|
||||
info.Settings = jointSettings;
|
||||
info.Settings.FromBlittableJointMutable(jointSettings);
|
||||
_info[i] = info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SphereTriangle;
|
||||
using UniGLTF.SpringBoneJobs.Blittables;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Jobs;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
public struct InputColliderJob : IJobParallelForTransform
|
||||
{
|
||||
[WriteOnly] public NativeArray<Matrix4x4> CurrentCollider;
|
||||
|
||||
public void Execute(int colliderIndex, TransformAccess transform)
|
||||
{
|
||||
CurrentCollider[colliderIndex] = transform.localToWorldMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
public struct StrandCollisionJob : IJobParallelFor
|
||||
{
|
||||
// collider
|
||||
[ReadOnly] public NativeArray<BlittableCollider> Colliders;
|
||||
[ReadOnly] public NativeArray<Matrix4x4> CurrentColliders;
|
||||
|
||||
// particle
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<Vector3> NextPositions;
|
||||
[ReadOnly] public NativeArray<bool> ClothUsedParticles;
|
||||
[WriteOnly] public NativeArray<Vector3> StrandCollision;
|
||||
|
||||
public void Execute(int particleIndex)
|
||||
{
|
||||
if (!ClothUsedParticles[particleIndex])
|
||||
{
|
||||
var info = Info[particleIndex];
|
||||
var pos = NextPositions[particleIndex];
|
||||
for (int colliderIndex = 0; colliderIndex < Colliders.Length; ++colliderIndex)
|
||||
{
|
||||
var c = Colliders[colliderIndex];
|
||||
var m = CurrentColliders[colliderIndex];
|
||||
|
||||
if (c.colliderType == BlittableColliderType.Capsule)
|
||||
{
|
||||
if (TryCollideCapsuleAndSphere(m.MultiplyPoint(c.offset), m.MultiplyPoint(c.tailOrNormal), c.radius,
|
||||
pos, info.Settings.radius, out var l))
|
||||
{
|
||||
pos += l.GetDelta(c.radius);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryCollideSphereAndSphere(m.MultiplyPoint(c.offset), c.radius,
|
||||
pos, info.Settings.radius, out var l))
|
||||
{
|
||||
pos += l.GetDelta(c.radius);
|
||||
}
|
||||
}
|
||||
StrandCollision[particleIndex] = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// collide sphere a and sphere b.
|
||||
/// move sphere b to resolved if collide.
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="ra"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <param name="ba"></param>
|
||||
/// <param name="resolved"></param>
|
||||
/// <returns></returns>
|
||||
static bool TryCollideSphereAndSphere(
|
||||
in Vector3 from, float ra,
|
||||
in Vector3 to, float rb,
|
||||
out LineSegment resolved
|
||||
)
|
||||
{
|
||||
var d = Vector3.Distance(from, to);
|
||||
if (d > (ra + rb))
|
||||
{
|
||||
resolved = default;
|
||||
return false;
|
||||
}
|
||||
Vector3 normal = (to - from).normalized;
|
||||
resolved = new(from, from + normal * (d - rb));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// collide capsule and sphere b.
|
||||
/// move sphere b to resolved if collide.
|
||||
/// </summary>
|
||||
/// <param name="capsuleHead"></param>
|
||||
/// <param name="capsuleTail"></param>
|
||||
/// <param name="capsuleRadius"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="rb"></param>
|
||||
static bool TryCollideCapsuleAndSphere(
|
||||
in Vector3 capsuleHead,
|
||||
in Vector3 capsuleTail,
|
||||
float capsuleRadius,
|
||||
in Vector3 b,
|
||||
float rb,
|
||||
out LineSegment resolved
|
||||
)
|
||||
{
|
||||
var P = (capsuleTail - capsuleHead).normalized;
|
||||
var Q = b - capsuleHead;
|
||||
var dot = Vector3.Dot(P, Q);
|
||||
if (dot <= 0)
|
||||
{
|
||||
// head側半球の球判定
|
||||
return TryCollideSphereAndSphere(capsuleHead, capsuleRadius, b, rb, out resolved);
|
||||
}
|
||||
|
||||
var t = dot / P.magnitude;
|
||||
if (t >= 1.0f)
|
||||
{
|
||||
// tail側半球の球判定
|
||||
return TryCollideSphereAndSphere(capsuleTail, capsuleRadius, b, rb, out resolved);
|
||||
}
|
||||
|
||||
// head-tail上の m_transform.position との最近点
|
||||
var p = capsuleHead + P * t;
|
||||
return TryCollideSphereAndSphere(p, capsuleRadius, b, rb, out resolved);
|
||||
}
|
||||
}
|
||||
|
||||
public struct ClothCollisionJob : IJobParallelFor
|
||||
{
|
||||
// collider
|
||||
[ReadOnly] public NativeArray<BlittableCollider> Colliders;
|
||||
[ReadOnly] public NativeArray<Matrix4x4> CurrentColliders;
|
||||
|
||||
// particle
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<Vector3> NextPositions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> CollisionCount;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<Vector3> CollisionDelta;
|
||||
|
||||
// cloth
|
||||
[ReadOnly] public NativeArray<(SpringConstraint, ClothRect)> ClothRects;
|
||||
|
||||
private void CollisionMove(int particleIndex, Vector3 delta)
|
||||
{
|
||||
CollisionCount[particleIndex] += 1;
|
||||
CollisionDelta[particleIndex] += delta;
|
||||
}
|
||||
|
||||
public void Execute(int rectIndex)
|
||||
{
|
||||
var (spring, rect) = ClothRects[rectIndex];
|
||||
|
||||
// using (new ProfileSample("Rect: Prepare"))
|
||||
// _s0.BeginFrame();
|
||||
// _s1.BeginFrame();
|
||||
|
||||
var a = NextPositions[rect._a];
|
||||
var b = NextPositions[rect._b];
|
||||
var c = NextPositions[rect._c];
|
||||
var d = NextPositions[rect._d];
|
||||
var aabb = GetBoundsFrom4(a, b, c, d);
|
||||
|
||||
// d x-x c
|
||||
// |/
|
||||
// a x
|
||||
var _triangle1 = new Triangle(c, d, a);
|
||||
// x c
|
||||
// /|
|
||||
// a x-x b
|
||||
var _triangle0 = new Triangle(a, b, c);
|
||||
|
||||
for (int colliderIndex = 0; colliderIndex < Colliders.Length; ++colliderIndex)
|
||||
{
|
||||
var collider = Colliders[colliderIndex];
|
||||
var collider_matrix = CurrentColliders[colliderIndex];
|
||||
if (!aabb.Intersects(GetBounds(collider, collider_matrix)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 面の片側だけにヒットさせる
|
||||
// 行き過ぎて戻るときに素通りする
|
||||
// var p = _triangle0.Plane.ClosestPointOnPlane(col_pos);
|
||||
// var dot = Vector3.Dot(_triangle0.Plane.normal, col_pos - p);
|
||||
// if (_initialColliderNormalSide[collider] * dot < 0)
|
||||
// {
|
||||
// // 片側
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (TryCollide(collider, collider_matrix, _triangle0, out var l0))
|
||||
{
|
||||
CollisionMove(rect._a, l0.GetDelta(collider.radius));
|
||||
CollisionMove(rect._b, l0.GetDelta(collider.radius));
|
||||
CollisionMove(rect._c, l0.GetDelta(collider.radius));
|
||||
}
|
||||
if (TryCollide(collider, collider_matrix, _triangle1, out var l1))
|
||||
{
|
||||
CollisionMove(rect._c, l1.GetDelta(collider.radius));
|
||||
CollisionMove(rect._d, l1.GetDelta(collider.radius));
|
||||
CollisionMove(rect._a, l1.GetDelta(collider.radius));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool TryCollide(BlittableCollider collider, in Matrix4x4 colliderMatrix, in Triangle t, out LineSegment l)
|
||||
{
|
||||
var col_pos = colliderMatrix.MultiplyPoint(collider.offset);
|
||||
if (collider.colliderType == BlittableColliderType.Capsule)
|
||||
{
|
||||
// capsule
|
||||
var tail_pos = colliderMatrix.MultiplyPoint(collider.tailOrNormal);
|
||||
var result = TriangleCapsuleCollisionSolver.Collide(t, new LineSegment(col_pos, tail_pos), collider.radius);
|
||||
var type = result.TryGetClosest(out l);
|
||||
return type.HasValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// sphere
|
||||
return TryCollideSphere(t, col_pos, collider.radius, out l);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="triangle"></param>
|
||||
/// <param name="collider"></param>
|
||||
/// <param name="radius"></param>
|
||||
/// <returns>collider => 衝突点 への線分を返す</returns>
|
||||
static bool TryCollideSphere(in Triangle triangle, in Vector3 collider, float radius, out LineSegment l)
|
||||
{
|
||||
var p = triangle.Plane.ClosestPointOnPlane(collider);
|
||||
var distance = Vector3.Distance(p, collider);
|
||||
if (distance > radius)
|
||||
{
|
||||
l = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (triangle.IsSameSide(p))
|
||||
{
|
||||
l = new LineSegment(collider, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
var (closestPoint, d) = triangle.GetClosest(collider);
|
||||
if (d > radius)
|
||||
{
|
||||
l = default;
|
||||
return false;
|
||||
}
|
||||
l = new LineSegment(collider, closestPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Bounds GetBoundsFrom4(in Vector3 a, in Vector3 b, in Vector3 c, in Vector3 d)
|
||||
{
|
||||
var aabb = new Bounds(a, Vector3.zero);
|
||||
aabb.Encapsulate(b);
|
||||
aabb.Encapsulate(c);
|
||||
aabb.Encapsulate(d);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
public static Bounds GetBounds(BlittableCollider collider, Matrix4x4 m)
|
||||
{
|
||||
switch (collider.colliderType)
|
||||
{
|
||||
case BlittableColliderType.Capsule:
|
||||
{
|
||||
var h = m.MultiplyPoint(collider.offset);
|
||||
var t = m.MultiplyPoint(collider.tailOrNormal);
|
||||
var d = h - t;
|
||||
var aabb = new Bounds((h + t) * 0.5f, new Vector3(Mathf.Abs(d.x), Mathf.Abs(d.y), Mathf.Abs(d.z)));
|
||||
aabb.Expand(collider.radius * 2);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
case BlittableColliderType.Sphere:
|
||||
return new Bounds(m.MultiplyPoint(collider.offset), new Vector3(collider.radius, collider.radius, collider.radius));
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct CollisionApplyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<bool> ClothUsedParticles;
|
||||
[ReadOnly] public NativeArray<Vector3> StrandCollision;
|
||||
[ReadOnly] public NativeArray<int> ClothCollisionCount;
|
||||
[ReadOnly] public NativeArray<Vector3> ClothCollisionDelta;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<Vector3> NextPosition;
|
||||
|
||||
public void Execute(int particleIndex)
|
||||
{
|
||||
if (ClothUsedParticles[particleIndex])
|
||||
{
|
||||
if (ClothCollisionCount[particleIndex] > 0)
|
||||
{
|
||||
NextPosition[particleIndex] += (ClothCollisionDelta[particleIndex] / ClothCollisionCount[particleIndex]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NextPosition[particleIndex] = StrandCollision[particleIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Jobs;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
public struct ClothInfo
|
||||
{
|
||||
public ArrayRange ColliderGroupRefRange;
|
||||
}
|
||||
|
||||
public struct InputColliderJob : IJobParallelForTransform
|
||||
{
|
||||
[WriteOnly] public NativeArray<Matrix4x4> CurrentCollider;
|
||||
|
||||
public void Execute(int colliderIndex, TransformAccess transform)
|
||||
{
|
||||
CurrentCollider[colliderIndex] = transform.localToWorldMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
public struct CollisionApplyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<bool> ClothUsedParticles;
|
||||
[ReadOnly] public NativeArray<Vector3> StrandCollision;
|
||||
|
||||
[ReadOnly] public NativeArray<int> RectCollisionCount;
|
||||
[ReadOnly] public NativeArray<Vector3> RectCollisionDelta;
|
||||
public NativeArray<Vector3> NextPosition;
|
||||
|
||||
public void Execute(int particleIndex)
|
||||
{
|
||||
if (ClothUsedParticles[particleIndex])
|
||||
{
|
||||
var count = RectCollisionCount[particleIndex];
|
||||
if (count > 0)
|
||||
{
|
||||
// 一つの頂点が最大で近接する4つの rect で当たり判定をされる
|
||||
// 衝突結果を足して割る
|
||||
NextPosition[particleIndex] += RectCollisionDelta[particleIndex] / count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NextPosition[particleIndex] = StrandCollision[particleIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,31 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
[ReadOnly] public NativeArray<WarpInfo> Warps;
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<TransformData> Data;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<Vector3> NextPositions;
|
||||
public void Execute(int warpIndex)
|
||||
{
|
||||
var warp = Warps[warpIndex];
|
||||
for (int particleIndex = warp.StartIndex; particleIndex < warp.EndIndex - 1; ++particleIndex)
|
||||
for (int particleIndex = warp.PrticleRange.Start; particleIndex < warp.PrticleRange.End - 1; ++particleIndex)
|
||||
{
|
||||
// 位置を長さで拘束
|
||||
NextPositions[particleIndex + 1] = NextPositions[particleIndex] +
|
||||
(NextPositions[particleIndex + 1] - NextPositions[particleIndex]).normalized
|
||||
* Info[particleIndex + 1].InitLocalPosition.magnitude;
|
||||
var particle = Info[particleIndex + 1];
|
||||
if (particle.Branch.HasValue)
|
||||
{
|
||||
var branch = particle.Branch.Value;
|
||||
// 1番目の兄弟の情報を使う
|
||||
// 枝分かれ。特別処理
|
||||
var firstSibling = Info[branch.FirstSiblingIndex];
|
||||
var firstPosition = NextPositions[branch.FirstSiblingIndex];
|
||||
var local_d = particle.InitLocalPosition - firstSibling.InitLocalPosition;
|
||||
NextPositions[particleIndex + 1] = firstPosition + Data[particle.ParentIndex].Rotation * local_d;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 位置を長さで拘束
|
||||
NextPositions[particleIndex + 1] = NextPositions[particleIndex] +
|
||||
(NextPositions[particleIndex + 1] - NextPositions[particleIndex]).normalized
|
||||
* Info[particleIndex + 1].InitLocalPosition.magnitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,21 +44,20 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
public struct WeftConstraintJob : IJobParallelFor
|
||||
{
|
||||
public float Hookean;
|
||||
FrameInfo Frame;
|
||||
[ReadOnly] public NativeArray<(SpringConstraint, ClothRect)> ClothRects;
|
||||
[ReadOnly] public NativeArray<(int, SpringConstraint, ClothRect)> ClothRects;
|
||||
[ReadOnly] public NativeArray<Vector3> CurrentPositions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<Vector3> Force;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<Vector3> ImpulsiveForces;
|
||||
|
||||
public void Execute(int rectIndex)
|
||||
{
|
||||
var (spring, rect) = ClothRects[rectIndex];
|
||||
var (clothGridIndex, spring, rect) = ClothRects[rectIndex];
|
||||
var p0 = CurrentPositions[spring._p0];
|
||||
var p1 = CurrentPositions[spring._p1];
|
||||
var d = Vector3.Distance(p0, p1);
|
||||
var f = (d - spring._rest) * Hookean;
|
||||
var dx = (p1 - p0).normalized * f / Frame.SqDeltaTime;
|
||||
Force[spring._p0] += dx;
|
||||
Force[spring._p1] -= dx;
|
||||
var dx = (p1 - p0).normalized * f;
|
||||
ImpulsiveForces[spring._p0] += dx;
|
||||
ImpulsiveForces[spring._p1] -= dx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +74,7 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
public void Execute(int warpIndex)
|
||||
{
|
||||
var warp = Warps[warpIndex];
|
||||
for (int particleIndex = warp.StartIndex; particleIndex < warp.EndIndex - 1; ++particleIndex)
|
||||
for (int particleIndex = warp.PrticleRange.Start; particleIndex < warp.PrticleRange.End - 1; ++particleIndex)
|
||||
{
|
||||
//回転を適用
|
||||
var p = Info[particleIndex];
|
||||
|
||||
@@ -1,66 +1,52 @@
|
||||
using System;
|
||||
using UniGLTF.SpringBoneJobs.Blittables;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Jobs;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
public struct TransformInfo
|
||||
/// <summary>
|
||||
/// UniGLTF.SpringBoneJobs.Blittables.BlittableJointMutable と同じ。
|
||||
/// Range が違う。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ParticleSettings
|
||||
{
|
||||
public TransformType TransformType;
|
||||
public int ParentIndex;
|
||||
public Quaternion InitLocalRotation;
|
||||
public Vector3 InitLocalPosition;
|
||||
public BlittableJointMutable Settings;
|
||||
}
|
||||
[Range(0, 1)]
|
||||
public float Stiffness;
|
||||
[Range(0, 1)]
|
||||
public float Deceleration;
|
||||
public Vector3 Gravity;
|
||||
public float Radius;
|
||||
|
||||
public struct TransformData
|
||||
{
|
||||
public Matrix4x4 ToWorld;
|
||||
public Vector3 Position => ToWorld.GetPosition();
|
||||
public Quaternion Rotation => ToWorld.rotation;
|
||||
public Matrix4x4 ToLocal;
|
||||
|
||||
public TransformData(TransformAccess t)
|
||||
public static readonly ParticleSettings Default = new ParticleSettings
|
||||
{
|
||||
ToWorld = t.localToWorldMatrix;
|
||||
ToLocal = t.worldToLocalMatrix;
|
||||
Stiffness = 0.08f,
|
||||
Gravity = new Vector3(0, -1.0f, 0),
|
||||
Deceleration = 0.4f,
|
||||
Radius = 0.02f,
|
||||
};
|
||||
|
||||
public void FromBlittableJointMutable(BlittableJointMutable src)
|
||||
{
|
||||
Stiffness = src.stiffnessForce;
|
||||
Gravity = src.gravityDir * src.gravityPower;
|
||||
Deceleration = src.dragForce;
|
||||
Radius = src.radius;
|
||||
}
|
||||
public TransformData(Transform t)
|
||||
|
||||
public BlittableJointMutable ToBlittableJointMutable()
|
||||
{
|
||||
ToWorld = t.localToWorldMatrix;
|
||||
ToLocal = t.worldToLocalMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
// [Input]
|
||||
public struct InputTransformJob : IJobParallelForTransform
|
||||
{
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[WriteOnly] public NativeArray<TransformData> InputData;
|
||||
[WriteOnly] public NativeArray<Vector3> CurrentPositions;
|
||||
|
||||
[WriteOnly] public NativeArray<int> CollisionCount;
|
||||
[WriteOnly] public NativeArray<Vector3> CollisionDelta;
|
||||
[WriteOnly] public NativeArray<Vector3> Forces;
|
||||
|
||||
public void Execute(int particleIndex, TransformAccess transform)
|
||||
{
|
||||
InputData[particleIndex] = new TransformData(transform);
|
||||
|
||||
var particle = Info[particleIndex];
|
||||
if (particle.TransformType.PositionInput())
|
||||
return new BlittableJointMutable
|
||||
{
|
||||
// only warp root position update
|
||||
CurrentPositions[particleIndex] = transform.position;
|
||||
}
|
||||
|
||||
// clear cloth
|
||||
CollisionCount[particleIndex] = 0;
|
||||
CollisionDelta[particleIndex] = Vector3.zero;
|
||||
Forces[particleIndex] = Vector3.zero;
|
||||
stiffnessForce = Stiffness,
|
||||
gravityPower = 1.0f,
|
||||
gravityDir = Gravity,
|
||||
dragForce = Deceleration,
|
||||
radius = Radius,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +55,7 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
public FrameInfo Frame;
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<TransformData> CurrentTransforms;
|
||||
[ReadOnly] public NativeArray<Vector3> Forces;
|
||||
[ReadOnly] public NativeArray<Vector3> ImpulsiveForces;
|
||||
[ReadOnly] public NativeArray<Vector3> CurrentPositions;
|
||||
[ReadOnly] public NativeArray<Vector3> PrevPositions;
|
||||
[WriteOnly] public NativeArray<Vector3> NextPositions;
|
||||
@@ -80,18 +66,19 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
var particle = Info[particleIndex];
|
||||
if (particle.TransformType.Movable())
|
||||
{
|
||||
var parentIndex = particle.ParentIndex;
|
||||
// var parentPosition = CurrentPositions[parentIndex];
|
||||
var parent = Info[parentIndex];
|
||||
var parent = Info[particle.ParentIndex];
|
||||
var parentParentRotation = CurrentTransforms[parent.ParentIndex].Rotation;
|
||||
|
||||
var external = (particle.Settings.gravityDir * particle.Settings.gravityPower + Frame.Force) * Frame.DeltaTime;
|
||||
var local_rest = parentParentRotation * parent.InitLocalRotation * particle.InitLocalPosition;
|
||||
var world_rest = CurrentPositions[particle.ParentIndex] + local_rest;
|
||||
var resilience_force = world_rest - CurrentPositions[particleIndex];
|
||||
var velocity = (CurrentPositions[particleIndex] - PrevPositions[particleIndex]) * (1.0f - particle.Settings.Deceleration);
|
||||
|
||||
var newPosition = CurrentPositions[particleIndex]
|
||||
+ (CurrentPositions[particleIndex] - PrevPositions[particleIndex]) * (1.0f - particle.Settings.dragForce)
|
||||
+ parentParentRotation * parent.InitLocalRotation * particle.InitLocalPosition *
|
||||
particle.Settings.stiffnessForce * Frame.DeltaTime // 親の回転による子ボーンの移動目標
|
||||
+ external
|
||||
+ velocity
|
||||
+ ImpulsiveForces[particleIndex]
|
||||
+ resilience_force * particle.Settings.Stiffness
|
||||
+ (particle.Settings.Gravity + Frame.Force) * Frame.SqDeltaTime
|
||||
;
|
||||
|
||||
NextPositions[particleIndex] = newPosition;
|
||||
@@ -105,19 +92,4 @@ namespace UniVRM10.ClothWarp.Jobs
|
||||
NextRotations[particleIndex] = CurrentTransforms[particleIndex].Rotation;
|
||||
}
|
||||
}
|
||||
|
||||
// [Output]
|
||||
public struct OutputTransformJob : IJobParallelForTransform
|
||||
{
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<Quaternion> NextRotations;
|
||||
public void Execute(int particleIndex, TransformAccess transform)
|
||||
{
|
||||
var info = Info[particleIndex];
|
||||
if (info.TransformType.Writable())
|
||||
{
|
||||
transform.rotation = NextRotations[particleIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using SphereTriangle;
|
||||
using UniGLTF.SpringBoneJobs.Blittables;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
public struct RectCollisionJob : IJobParallelFor
|
||||
{
|
||||
// cloth
|
||||
[ReadOnly] public NativeArray<(int, SpringConstraint, ClothRect)> ClothRects;
|
||||
[WriteOnly] public NativeArray<(Vector3, Vector3, Vector3, Vector3)> ClothRectResults;
|
||||
|
||||
// collider
|
||||
[ReadOnly] public NativeArray<BlittableCollider> Colliders;
|
||||
[ReadOnly] public NativeArray<Matrix4x4> CurrentColliders;
|
||||
|
||||
// particle
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<Vector3> NextPositions;
|
||||
|
||||
// collider group
|
||||
[ReadOnly] public NativeArray<ClothInfo> Cloths;
|
||||
[ReadOnly] public NativeArray<int> ColliderGroupRef;
|
||||
[ReadOnly] public NativeArray<ArrayRange> ColliderGroup;
|
||||
[ReadOnly] public NativeArray<int> ColliderRef;
|
||||
|
||||
public void Execute(int rectIndex)
|
||||
{
|
||||
var (clothGridIndex, spring, rect) = ClothRects[rectIndex];
|
||||
|
||||
var a = NextPositions[rect._a];
|
||||
var b = NextPositions[rect._b];
|
||||
var c = NextPositions[rect._c];
|
||||
var d = NextPositions[rect._d];
|
||||
var aabb = GetBoundsFrom4(a, b, c, d);
|
||||
|
||||
// d x-x c
|
||||
// |/
|
||||
// a x
|
||||
var _triangle1 = new Triangle(c, d, a);
|
||||
// x c
|
||||
// /|
|
||||
// a x-x b
|
||||
var _triangle0 = new Triangle(a, b, c);
|
||||
|
||||
var cloth = Cloths[clothGridIndex];
|
||||
|
||||
for (int groupRefIndex = cloth.ColliderGroupRefRange.Start; groupRefIndex < cloth.ColliderGroupRefRange.End; ++groupRefIndex)
|
||||
{
|
||||
var groupIndex = ColliderGroupRef[groupRefIndex];
|
||||
|
||||
var group = ColliderGroup[groupIndex];
|
||||
for (int colliderRefIndex = group.Start; colliderRefIndex < group.End; ++colliderRefIndex)
|
||||
{
|
||||
var colliderIndex = ColliderRef[colliderRefIndex];
|
||||
var collider = Colliders[colliderIndex];
|
||||
var collider_matrix = CurrentColliders[collider.transformIndex];
|
||||
if (!aabb.Intersects(GetBounds(collider, collider_matrix)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 面の片側だけにヒットさせる
|
||||
// 行き過ぎて戻るときに素通りする
|
||||
// var p = _triangle0.Plane.ClosestPointOnPlane(col_pos);
|
||||
// var dot = Vector3.Dot(_triangle0.Plane.normal, col_pos - p);
|
||||
// if (_initialColliderNormalSide[collider] * dot < 0)
|
||||
// {
|
||||
// // 片側
|
||||
// continue;
|
||||
// }
|
||||
|
||||
var abc = TryCollide(collider, collider_matrix, _triangle0, out var l0);
|
||||
var cda = TryCollide(collider, collider_matrix, _triangle1, out var l1);
|
||||
if (!Info[rect._c].TransformType.Movable())
|
||||
{
|
||||
// cloth の上端。cd が固定
|
||||
if (abc)
|
||||
{
|
||||
a += l0.GetDelta(collider.radius);
|
||||
b += l0.GetDelta(collider.radius);
|
||||
}
|
||||
else if (cda)
|
||||
{
|
||||
a += l1.GetDelta(collider.radius);
|
||||
b += l1.GetDelta(collider.radius);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abc && cda)
|
||||
{
|
||||
a += l0.GetDelta(collider.radius);
|
||||
b += l0.GetDelta(collider.radius);
|
||||
c += l1.GetDelta(collider.radius);
|
||||
d += l1.GetDelta(collider.radius);
|
||||
}
|
||||
else if (abc)
|
||||
{
|
||||
a += l0.GetDelta(collider.radius);
|
||||
b += l0.GetDelta(collider.radius);
|
||||
c += l0.GetDelta(collider.radius);
|
||||
}
|
||||
else if (cda)
|
||||
{
|
||||
c += l1.GetDelta(collider.radius);
|
||||
d += l1.GetDelta(collider.radius);
|
||||
a += l1.GetDelta(collider.radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClothRectResults[rectIndex] = (a, b, c, d);
|
||||
}
|
||||
|
||||
static bool TryCollide(BlittableCollider collider, in Matrix4x4 colliderMatrix, in Triangle t, out LineSegment l)
|
||||
{
|
||||
var col_pos = colliderMatrix.MultiplyPoint(collider.offset);
|
||||
if (collider.colliderType == BlittableColliderType.Capsule)
|
||||
{
|
||||
// capsule
|
||||
var tail_pos = colliderMatrix.MultiplyPoint(collider.tailOrNormal);
|
||||
var result = TriangleCapsuleCollisionSolver.Collide(t, new LineSegment(col_pos, tail_pos), collider.radius);
|
||||
var type = result.TryGetClosest(out l);
|
||||
return type.HasValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// sphere
|
||||
return TryCollideSphere(t, col_pos, collider.radius, out l);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="triangle"></param>
|
||||
/// <param name="collider"></param>
|
||||
/// <param name="radius"></param>
|
||||
/// <returns>collider => 衝突点 への線分を返す</returns>
|
||||
static bool TryCollideSphere(in Triangle triangle, in Vector3 collider, float radius, out LineSegment l)
|
||||
{
|
||||
var p = triangle.Plane.ClosestPointOnPlane(collider);
|
||||
var distance = Vector3.Distance(p, collider);
|
||||
if (distance > radius)
|
||||
{
|
||||
l = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (triangle.IsSameSide(p))
|
||||
{
|
||||
l = new LineSegment(collider, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
var (closestPoint, d) = triangle.GetClosest(collider);
|
||||
if (d > radius)
|
||||
{
|
||||
l = default;
|
||||
return false;
|
||||
}
|
||||
l = new LineSegment(collider, closestPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Bounds GetBoundsFrom4(in Vector3 a, in Vector3 b, in Vector3 c, in Vector3 d)
|
||||
{
|
||||
var aabb = new Bounds(a, Vector3.zero);
|
||||
aabb.Encapsulate(b);
|
||||
aabb.Encapsulate(c);
|
||||
aabb.Encapsulate(d);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
public static Bounds GetBounds(BlittableCollider collider, Matrix4x4 m)
|
||||
{
|
||||
switch (collider.colliderType)
|
||||
{
|
||||
case BlittableColliderType.Capsule:
|
||||
{
|
||||
var h = m.MultiplyPoint(collider.offset);
|
||||
var t = m.MultiplyPoint(collider.tailOrNormal);
|
||||
var d = h - t;
|
||||
var aabb = new Bounds((h + t) * 0.5f, new Vector3(Mathf.Abs(d.x), Mathf.Abs(d.y), Mathf.Abs(d.z)));
|
||||
aabb.Expand(collider.radius * 2);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
case BlittableColliderType.Sphere:
|
||||
return new Bounds(m.MultiplyPoint(collider.offset), new Vector3(collider.radius, collider.radius, collider.radius));
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct RectCollisionReduceJob : IJob
|
||||
{
|
||||
[ReadOnly] public NativeArray<(int, SpringConstraint, ClothRect)> ClothRects;
|
||||
[ReadOnly] public NativeArray<(Vector3, Vector3, Vector3, Vector3)> ClothRectResults;
|
||||
[ReadOnly] public NativeArray<Vector3> NextPositions;
|
||||
|
||||
public NativeArray<int> RectCollisionCount;
|
||||
public NativeArray<Vector3> RectCollisionDelta;
|
||||
public void Execute()
|
||||
{
|
||||
for (int rectIndex = 0; rectIndex < ClothRects.Length; ++rectIndex)
|
||||
{
|
||||
var (clothGridIndex, spring, rect) = ClothRects[rectIndex];
|
||||
var (a, b, c, d) = ClothRectResults[rectIndex];
|
||||
|
||||
if (a != NextPositions[rect._a])
|
||||
{
|
||||
RectCollisionDelta[rect._a] += a - NextPositions[rect._a];
|
||||
RectCollisionCount[rect._a] += 1;
|
||||
}
|
||||
if (b != NextPositions[rect._b])
|
||||
{
|
||||
RectCollisionDelta[rect._b] += b - NextPositions[rect._b];
|
||||
RectCollisionCount[rect._b] += 1;
|
||||
}
|
||||
if (c != NextPositions[rect._c])
|
||||
{
|
||||
RectCollisionDelta[rect._c] += c - NextPositions[rect._c];
|
||||
RectCollisionCount[rect._c] += 1;
|
||||
}
|
||||
if (d != NextPositions[rect._d])
|
||||
{
|
||||
RectCollisionDelta[rect._d] += d - NextPositions[rect._d];
|
||||
RectCollisionCount[rect._d] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 127d372d227187449960f33a08791017
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Jobs;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
public struct BranchInfo
|
||||
{
|
||||
public int FirstSiblingIndex;
|
||||
}
|
||||
|
||||
public struct TransformInfo
|
||||
{
|
||||
public TransformType TransformType;
|
||||
public int ParentIndex;
|
||||
public Quaternion InitLocalRotation;
|
||||
public Vector3 InitLocalPosition;
|
||||
public ParticleSettings Settings;
|
||||
public int WarpIndex;
|
||||
public BranchInfo? Branch;
|
||||
}
|
||||
|
||||
public struct TransformData
|
||||
{
|
||||
public Matrix4x4 ToWorld;
|
||||
public Vector3 Position => ToWorld.GetPosition();
|
||||
public Quaternion Rotation => ToWorld.rotation;
|
||||
public Matrix4x4 ToLocal;
|
||||
|
||||
public TransformData(TransformAccess t)
|
||||
{
|
||||
ToWorld = t.localToWorldMatrix;
|
||||
ToLocal = t.worldToLocalMatrix;
|
||||
}
|
||||
public TransformData(Transform t)
|
||||
{
|
||||
ToWorld = t.localToWorldMatrix;
|
||||
ToLocal = t.worldToLocalMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
// [Input]
|
||||
public struct InputTransformJob : IJobParallelForTransform
|
||||
{
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[WriteOnly] public NativeArray<TransformData> InputData;
|
||||
[WriteOnly] public NativeArray<Vector3> CurrentPositions;
|
||||
|
||||
[WriteOnly] public NativeArray<int> CollisionCount;
|
||||
[WriteOnly] public NativeArray<Vector3> CollisionDelta;
|
||||
[WriteOnly] public NativeArray<Vector3> ImpulsiveForces;
|
||||
|
||||
public void Execute(int particleIndex, TransformAccess transform)
|
||||
{
|
||||
InputData[particleIndex] = new TransformData(transform);
|
||||
|
||||
var particle = Info[particleIndex];
|
||||
if (particle.TransformType.PositionInput())
|
||||
{
|
||||
// only warp root position update
|
||||
CurrentPositions[particleIndex] = transform.position;
|
||||
}
|
||||
|
||||
// clear cloth
|
||||
CollisionCount[particleIndex] = 0;
|
||||
CollisionDelta[particleIndex] = Vector3.zero;
|
||||
ImpulsiveForces[particleIndex] = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
// [Output]
|
||||
public struct OutputTransformJob : IJobParallelForTransform
|
||||
{
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<Quaternion> NextRotations;
|
||||
public void Execute(int particleIndex, TransformAccess transform)
|
||||
{
|
||||
var info = Info[particleIndex];
|
||||
if (info.TransformType.Writable())
|
||||
{
|
||||
transform.rotation = NextRotations[particleIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f3379ab5e1202d43a95e72ece6bda3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +1,14 @@
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
public struct ArrayRange
|
||||
{
|
||||
public int Start;
|
||||
public int End;
|
||||
}
|
||||
|
||||
public struct WarpInfo
|
||||
{
|
||||
public int StartIndex;
|
||||
public int EndIndex;
|
||||
public ArrayRange PrticleRange;
|
||||
public ArrayRange ColliderGroupRefRange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using SphereTriangle;
|
||||
using UniGLTF.SpringBoneJobs.Blittables;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniVRM10.ClothWarp.Jobs
|
||||
{
|
||||
public struct WarpCollisionJob : IJobParallelFor
|
||||
{
|
||||
// collider
|
||||
[ReadOnly] public NativeArray<BlittableCollider> Colliders;
|
||||
[ReadOnly] public NativeArray<Matrix4x4> CurrentColliders;
|
||||
|
||||
// particle
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<Vector3> NextPositions;
|
||||
[ReadOnly] public NativeArray<bool> ClothUsedParticles;
|
||||
[WriteOnly] public NativeArray<Vector3> StrandCollision;
|
||||
|
||||
// collider group
|
||||
[ReadOnly] public NativeArray<WarpInfo> Warps;
|
||||
[ReadOnly] public NativeArray<int> ColliderGroupRef;
|
||||
[ReadOnly] public NativeArray<ArrayRange> ColliderGroup;
|
||||
[ReadOnly] public NativeArray<int> ColliderRef;
|
||||
|
||||
public void Execute(int particleIndex)
|
||||
{
|
||||
if (
|
||||
// cloth でない
|
||||
!ClothUsedParticles[particleIndex]
|
||||
// 枝のjointでない
|
||||
&& !Info[particleIndex].Branch.HasValue
|
||||
)
|
||||
{
|
||||
var info = Info[particleIndex];
|
||||
var pos = NextPositions[particleIndex];
|
||||
var warp = Warps[info.WarpIndex];
|
||||
|
||||
for (int groupRefIndex = warp.ColliderGroupRefRange.Start; groupRefIndex < warp.ColliderGroupRefRange.End; ++groupRefIndex)
|
||||
{
|
||||
var groupIndex = ColliderGroupRef[groupRefIndex];
|
||||
var group = ColliderGroup[groupIndex];
|
||||
for (int colliderRefIndex = group.Start; colliderRefIndex < group.End; ++colliderRefIndex)
|
||||
{
|
||||
var colliderIndex = ColliderRef[colliderRefIndex];
|
||||
var c = Colliders[colliderIndex];
|
||||
var m = CurrentColliders[c.transformIndex];
|
||||
|
||||
if (c.colliderType == BlittableColliderType.Capsule)
|
||||
{
|
||||
if (SphereSphereCollision.TryCollideCapsuleAndSphere(m.MultiplyPoint(c.offset), m.MultiplyPoint(c.tailOrNormal), c.radius,
|
||||
pos, info.Settings.Radius, out var l))
|
||||
{
|
||||
pos += l.GetDelta(c.radius);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SphereSphereCollision.TryCollideSphereAndSphere(m.MultiplyPoint(c.offset), c.radius,
|
||||
pos, info.Settings.Radius, out var l))
|
||||
{
|
||||
pos += l.GetDelta(c.radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StrandCollision[particleIndex] = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80e53200fa3841748ad1d1416efdd53b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -23,7 +23,7 @@ namespace UniVRM10.ClothWarp
|
||||
foreach (var particle in warp.Particles)
|
||||
{
|
||||
var child_index = _MakeAParticle(joint, env, particle.Transform,
|
||||
particle.Settings.radius, 1);
|
||||
particle.Settings.Radius, 1);
|
||||
var child = _particles[child_index];
|
||||
strand.Particles.Add(child);
|
||||
joint.Children.Add(child);
|
||||
|
||||
@@ -230,19 +230,19 @@ namespace SphereTriangle
|
||||
return true;
|
||||
}
|
||||
|
||||
// public void DrawGizmos()
|
||||
// {
|
||||
// var r = Vector3.Distance(_triangle0.b, _triangle0.c) * 0.1f;
|
||||
// _DrawGizmos(_triangle0, _s0, _trinagle0Collision, r);
|
||||
// _DrawGizmos(_triangle1, _s1, _triangle1Collision, r);
|
||||
// public void DrawGizmos()
|
||||
// {
|
||||
// var r = Vector3.Distance(_triangle0.b, _triangle0.c) * 0.1f;
|
||||
// _DrawGizmos(_triangle0, _s0, _trinagle0Collision, r);
|
||||
// _DrawGizmos(_triangle1, _s1, _triangle1Collision, r);
|
||||
|
||||
// #if AABB_DEBUG
|
||||
// Gizmos.matrix = Matrix4x4.identity;
|
||||
// Gizmos.color = Color.cyan;
|
||||
// var aabb = GetBoundsFrom4(_triangle0.a, _triangle0.b, _triangle1.a, _triangle1.b);
|
||||
// Gizmos.DrawWireCube(aabb.center, aabb.size);
|
||||
// #endif
|
||||
// }
|
||||
// #if AABB_DEBUG
|
||||
// Gizmos.matrix = Matrix4x4.identity;
|
||||
// Gizmos.color = Color.cyan;
|
||||
// var aabb = GetBoundsFrom4(_triangle0.a, _triangle0.b, _triangle1.a, _triangle1.b);
|
||||
// Gizmos.DrawWireCube(aabb.center, aabb.size);
|
||||
// #endif
|
||||
// }
|
||||
|
||||
// void _DrawGizmos(in Triangle t, TriangleCapsuleCollisionSolver solver, float collision, float radius)
|
||||
// {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace SphereTriangle
|
||||
{
|
||||
public static class SphereSphereCollision
|
||||
{
|
||||
/// <summary>
|
||||
/// collide sphere a and sphere b.
|
||||
/// move sphere b to resolved if collide.
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="ra"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <param name="ba"></param>
|
||||
/// <param name="resolved"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryCollideSphereAndSphere(
|
||||
in Vector3 from, float ra,
|
||||
in Vector3 to, float rb,
|
||||
out LineSegment resolved
|
||||
)
|
||||
{
|
||||
var d = Vector3.Distance(from, to);
|
||||
if (d > (ra + rb))
|
||||
{
|
||||
resolved = default;
|
||||
return false;
|
||||
}
|
||||
Vector3 normal = (to - from).normalized;
|
||||
resolved = new(from, from + normal * (d - rb));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// collide capsule and sphere b.
|
||||
/// move sphere b to resolved if collide.
|
||||
/// </summary>
|
||||
/// <param name="capsuleHead"></param>
|
||||
/// <param name="capsuleTail"></param>
|
||||
/// <param name="capsuleRadius"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="rb"></param>
|
||||
public static bool TryCollideCapsuleAndSphere(
|
||||
in Vector3 capsuleHead,
|
||||
in Vector3 capsuleTail,
|
||||
float capsuleRadius,
|
||||
in Vector3 b,
|
||||
float rb,
|
||||
out LineSegment resolved
|
||||
)
|
||||
{
|
||||
var P = (capsuleTail - capsuleHead);
|
||||
var Q = b - capsuleHead;
|
||||
var dot = Vector3.Dot(P.normalized, Q);
|
||||
if (dot <= 0)
|
||||
{
|
||||
// head側半球の球判定
|
||||
return TryCollideSphereAndSphere(capsuleHead, capsuleRadius, b, rb, out resolved);
|
||||
}
|
||||
|
||||
var t = dot / P.magnitude;
|
||||
if (t >= 1.0f)
|
||||
{
|
||||
// tail側半球の球判定
|
||||
return TryCollideSphereAndSphere(capsuleTail, capsuleRadius, b, rb, out resolved);
|
||||
}
|
||||
|
||||
// head-tail上の m_transform.position との最近点
|
||||
var p = capsuleHead + P * t;
|
||||
return TryCollideSphereAndSphere(p, capsuleRadius, b, rb, out resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 990d1e921d604e348ab7fd04216b01d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user