diff --git a/.github/workflows/create-unitypackage.yml b/.github/workflows/create-unitypackage.yml index cec71c23c..030024170 100644 --- a/.github/workflows/create-unitypackage.yml +++ b/.github/workflows/create-unitypackage.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 60 steps: - id: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive lfs: true @@ -82,7 +82,7 @@ jobs: - name: Upload test results if: ${{ always() }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: run-edit-mode-tests.xml path: ${{ env.UNITY_PROJECT_PATH }}/run-edit-mode-tests.xml @@ -100,7 +100,7 @@ jobs: echo "Success to create UnityPackage." - name: Upload UnityPackage - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: unitypackage path: ${{ env.UNITY_PROJECT_PATH }}/*.unitypackage diff --git a/Packages/UniGLTF/Editor/UniGLTF.Editor.asmdef b/Packages/UniGLTF/Editor/UniGLTF.Editor.asmdef index 46cc4b400..b3736347e 100644 --- a/Packages/UniGLTF/Editor/UniGLTF.Editor.asmdef +++ b/Packages/UniGLTF/Editor/UniGLTF.Editor.asmdef @@ -37,7 +37,17 @@ "name": "com.atteneder.gltfast", "expression": "", "define": "UNIGLTF_DISABLE_DEFAULT_GLTF_IMPORTER" - } + }, + { + "name": "com.unity.cloud.gltfast", + "expression": "", + "define": "UNIGLTF_DISABLE_DEFAULT_GLB_IMPORTER" + }, + { + "name": "com.unity.cloud.gltfast", + "expression": "", + "define": "UNIGLTF_DISABLE_DEFAULT_GLTF_IMPORTER" + } ], "noEngineReferences": false } \ No newline at end of file diff --git a/Packages/UniGLTF/Editor/UniGLTF/ScriptedImporter/TextureExtractor.cs b/Packages/UniGLTF/Editor/UniGLTF/ScriptedImporter/TextureExtractor.cs index 05fb2110a..cac1a48c8 100644 --- a/Packages/UniGLTF/Editor/UniGLTF/ScriptedImporter/TextureExtractor.cs +++ b/Packages/UniGLTF/Editor/UniGLTF/ScriptedImporter/TextureExtractor.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using UnityEngine; -using UnityEditor; using System.Linq; +using Unity.Profiling; +using UnityEditor; +using UnityEngine; namespace UniGLTF { @@ -20,6 +21,9 @@ namespace UniGLTF private readonly IReadOnlyDictionary m_subAssets; UnityPath m_textureDirectory; + private static ProfilerMarker s_MarkerStartExtractTextures = new ProfilerMarker("Start Extract Textures"); + private static ProfilerMarker s_MarkerDelayedExtractTextures = new ProfilerMarker("Delayed Extract Textures"); + public TextureExtractor(GltfData data, UnityPath textureDirectory, IReadOnlyDictionary subAssets) { m_data = data; @@ -77,14 +81,33 @@ namespace UniGLTF Action addRemap, Action> onCompleted = null) { + s_MarkerStartExtractTextures.Begin(); + var extractor = new TextureExtractor(data, textureDirectory, subAssets); - foreach (var param in textureDescriptorGenerator.Get().GetEnumerable()) + try { - extractor.Extract(param.SubAssetKey, param); + AssetDatabase.StartAssetEditing(); + + foreach (var param in textureDescriptorGenerator.Get().GetEnumerable()) + { + extractor.Extract(param.SubAssetKey, param); + } } + catch (Exception e) + { + Debug.LogException(e); + } + finally + { + AssetDatabase.StopAssetEditing(); + } + + s_MarkerStartExtractTextures.End(); EditorApplication.delayCall += () => { + s_MarkerDelayedExtractTextures.Begin(); + // Wait for the texture assets to be imported foreach (var (key, targetPath) in extractor.Textures) @@ -97,6 +120,8 @@ namespace UniGLTF } } + s_MarkerDelayedExtractTextures.End(); + if (onCompleted != null) { onCompleted(extractor.Textures.Values); diff --git a/Packages/UniGLTF/Runtime/MeshUtility/MeshExtensions.cs b/Packages/UniGLTF/Runtime/MeshUtility/MeshExtensions.cs index 90029be67..e1921a6eb 100644 --- a/Packages/UniGLTF/Runtime/MeshUtility/MeshExtensions.cs +++ b/Packages/UniGLTF/Runtime/MeshUtility/MeshExtensions.cs @@ -52,8 +52,8 @@ namespace UniGLTF.MeshUtility if (copyBlendShape) { - var vertices = src.vertices; - var normals = src.normals; + var deltaVertices = new Vector3[src.vertexCount]; + var deltaNormals = new Vector3[src.vertexCount]; Vector3[] tangents = null; if (Symbols.VRM_NORMALIZE_BLENDSHAPE_TANGENT) { @@ -62,14 +62,18 @@ namespace UniGLTF.MeshUtility for (int i = 0; i < src.blendShapeCount; ++i) { - src.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); - dst.AddBlendShapeFrame( - src.GetBlendShapeName(i), - src.GetBlendShapeFrameWeight(i, 0), - vertices, - normals, - tangents - ); + var frameCount = src.GetBlendShapeFrameCount(i); + for (int f = 0; f < frameCount; ++f) + { + src.GetBlendShapeFrameVertices(i, f, deltaVertices, deltaNormals, tangents); + dst.AddBlendShapeFrame( + src.GetBlendShapeName(i), + src.GetBlendShapeFrameWeight(i, f), + deltaVertices, + deltaNormals, + tangents + ); + } } } diff --git a/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs b/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs index 178ab3d23..52f916f6c 100644 --- a/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs +++ b/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs @@ -18,7 +18,7 @@ namespace UniGLTF.SpringBoneJobs case AnglelimitTypes.Cone: { var angleSpaceToWorld = anglelimitSpaceToWorld(logic, joint, parentRotation); - var tailDir = math.mul(math.inverse(angleSpaceToWorld), math.normalize(nextTail - head)); + var tailDir = math.mul(math.inverse(angleSpaceToWorld), math.normalizesafe(nextTail - head)); tailDir = AnglelimitCone.Apply(tailDir, joint.anglelimit1); return head + math.mul(angleSpaceToWorld, tailDir) * logic.length; } @@ -26,7 +26,7 @@ namespace UniGLTF.SpringBoneJobs case AnglelimitTypes.Hinge: { var angleSpaceToWorld = anglelimitSpaceToWorld(logic, joint, parentRotation); - var tailDir = math.mul(math.inverse(angleSpaceToWorld), math.normalize(nextTail - head)); + var tailDir = math.mul(math.inverse(angleSpaceToWorld), math.normalizesafe(nextTail - head)); tailDir = AnglelimitHinge.Apply(tailDir, joint.anglelimit1); return head + math.mul(angleSpaceToWorld, tailDir) * logic.length; } @@ -35,7 +35,7 @@ namespace UniGLTF.SpringBoneJobs case AnglelimitTypes.Spherical: { var angleSpaceToWorld = anglelimitSpaceToWorld(logic, joint, parentRotation); - var tailDir = math.mul(math.inverse(angleSpaceToWorld), math.normalize(nextTail - head)); + var tailDir = math.mul(math.inverse(angleSpaceToWorld), math.normalizesafe(nextTail - head)); tailDir = AnglelimitSpherical.Apply(tailDir, joint.anglelimit1, joint.anglelimit2); return head + math.mul(angleSpaceToWorld, tailDir) * logic.length; } @@ -52,7 +52,7 @@ namespace UniGLTF.SpringBoneJobs in quaternion parentRotation) { // Y+方向からjointのheadからtailに向かうベクトルへの最小回転 - var axisRotation = fromToQuaternion(new float3(0, 1, 0), logic.boneAxis); + var axisRotation = getAxisRotation(logic.boneAxis); // limitのローカル空間をワールド空間に写像する回転 return @@ -63,29 +63,28 @@ namespace UniGLTF.SpringBoneJobs ; } - // https://discussions.unity.com/t/unity-mathematics-equivalent-to-quaternion-fromtorotation/237459 - public static quaternion fromToQuaternion(in float3 from, in float3 to) + /// + /// Y軸正方向から `to` への回転を表すクォータニオンを計算して返す。 + /// `to` は正規化されていると仮定する。 + /// + /// See: https://github.com/0b5vr/vrm-specification/blob/75fbd48a7cb1d7250fa955838af6140e9c84844c/specification/VRMC_springBone_limit-1.0/README.ja.md#rotation-1 + /// + /// TODO: Replace with the appropriate link to the specification later + /// + public static quaternion getAxisRotation(in float3 to) { - var fromNorm = math.normalize(from); - var toNorm = math.normalize(to); - var dot = math.dot(fromNorm, toNorm); + // dot(from, to) + 1 + var dot1 = to.y + 1f; - // Handle the case where from and to are parallel but opposite - if (math.abs(dot + 1f) < 1e-6f) // dot is approximately -1 + // Handle the case where from and to are parallel and opposite + if (dot1 < 1e-8f) // dot is approximately -1 { - // Find a perpendicular axis - var perpAxis = math.abs(fromNorm.x) > math.abs(fromNorm.z) - ? new float3(-fromNorm.y, fromNorm.x, 0f) - : new float3(0f, -fromNorm.z, fromNorm.y); - return quaternion.AxisAngle(math.normalize(perpAxis), math.PI); + return new quaternion(1f, 0f, 0f, 0f); } // General case - return quaternion.AxisAngle( - angle: math.acos(math.clamp(dot, -1f, 1f)), - axis: math.normalize(math.cross(fromNorm, toNorm)) - ); + // quaternion(cross(from, to); dot(from, to) + 1).normalized + return math.normalizesafe(new quaternion(to.z, 0f, -to.x, dot1)); } - } } \ No newline at end of file diff --git a/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/AnglelimitHinge.cs b/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/AnglelimitHinge.cs index 571984689..45f291929 100644 --- a/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/AnglelimitHinge.cs +++ b/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/AnglelimitHinge.cs @@ -12,7 +12,7 @@ namespace UniGLTF.SpringBoneJobs // x要素を0にし、正規化する float3 tailDir = src; tailDir.x = 0.0f; - tailDir = math.normalize(tailDir); + tailDir = math.normalizesafe(tailDir); // tailDirのy要素をjointに設定されたangleの余弦と比較する var cosAngle = math.cos(limitAngle); diff --git a/Packages/UniGLTF/Runtime/SpringBoneJobs/Blittables/BlittableTransform.cs b/Packages/UniGLTF/Runtime/SpringBoneJobs/Blittables/BlittableTransform.cs index 16bb8e855..5b4c6680a 100644 --- a/Packages/UniGLTF/Runtime/SpringBoneJobs/Blittables/BlittableTransform.cs +++ b/Packages/UniGLTF/Runtime/SpringBoneJobs/Blittables/BlittableTransform.cs @@ -140,7 +140,7 @@ namespace UniGLTF.SpringBoneJobs.Blittables if(parent.HasValue) { - newLocalRotation = math.normalize(math.mul(math.inverse(parent.Value.rotation), newRotation)); + newLocalRotation = math.normalizesafe(math.mul(math.inverse(parent.Value.rotation), newRotation)); newLocalToWorldMatrix = math.mul(parent.Value.localToWorldMatrix, float4x4.TRS(localPosition, newLocalRotation, localScale)); } else diff --git a/Packages/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs b/Packages/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs index b507f60d7..5af4ef132 100644 --- a/Packages/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs +++ b/Packages/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs @@ -125,7 +125,7 @@ namespace UniGLTF.SpringBoneJobs.InputPorts parentTransformIndex: Array.IndexOf(Transforms, joint.Transform.parent), tailTransformIndex: Array.IndexOf(Transforms, tailJoint.Transform), localRotation: joint.DefaultLocalRotation, - boneAxis: math.normalize(localChildPosition), + boneAxis: math.normalizesafe(localChildPosition), length: math.length(localChildPosition)); } } diff --git a/Packages/UniGLTF/Runtime/SpringBoneJobs/SpringBoneCollision.cs b/Packages/UniGLTF/Runtime/SpringBoneJobs/SpringBoneCollision.cs index a8f5fc3b4..8898f3804 100644 --- a/Packages/UniGLTF/Runtime/SpringBoneJobs/SpringBoneCollision.cs +++ b/Packages/UniGLTF/Runtime/SpringBoneJobs/SpringBoneCollision.cs @@ -47,10 +47,10 @@ namespace UniGLTF.SpringBoneJobs if (math.lengthsq(nextTail - worldPosition) <= (r * r)) { // ヒット。Colliderの半径方向に押し出す - var normal = math.normalize(nextTail - worldPosition); + var normal = math.normalizesafe(nextTail - worldPosition); var posFromCollider = worldPosition + normal * r; // 長さをboneLengthに強制 - newNextTail = headTransform.position + math.normalize(posFromCollider - headTransform.position) * logic.length; + newNextTail = headTransform.position + math.normalizesafe(posFromCollider - headTransform.position) * logic.length; return true; } else @@ -76,7 +76,7 @@ namespace UniGLTF.SpringBoneJobs // head側半球の球判定 return TryResolveSphereCollision(joint, collider, worldPosition, headTransform, maxColliderScale, logic, nextTail, out newNextTail); } - var P = math.normalize(direction); + var P = math.normalizesafe(direction); var Q = headTransform.position - worldPosition; var dot = math.dot(P, Q); if (dot <= 0) @@ -110,7 +110,7 @@ namespace UniGLTF.SpringBoneJobs in float3 nextTail, out float3 newNextTail) { var transformedOffset = MathHelper.MultiplyPoint(colliderTransform.localToWorldMatrix, collider.offset); - var transformedNormal = math.normalize(MathHelper.MultiplyVector(colliderTransform.localToWorldMatrix, collider.tailOrNormal)); + var transformedNormal = math.normalizesafe(MathHelper.MultiplyVector(colliderTransform.localToWorldMatrix, collider.tailOrNormal)); var delta = nextTail - transformedOffset; // ジョイントとコライダーの距離。負の値は衝突していることを示す @@ -145,7 +145,7 @@ namespace UniGLTF.SpringBoneJobs // ジョイントとコライダーの距離の方向。衝突している場合、この方向にジョイントを押し出す if (distance < 0) { - var direction = -1 * math.normalize(delta); + var direction = -1 * math.normalizesafe(delta); newNextTail = nextTail - direction * distance; return true; } @@ -192,7 +192,7 @@ namespace UniGLTF.SpringBoneJobs // ジョイントとコライダーの距離の方向。衝突している場合、この方向にジョイントを押し出す if (distance < 0) { - var direction = -1 * math.normalize(delta); + var direction = -1 * math.normalizesafe(delta); newNextTail = nextTail - direction * distance; return true; } diff --git a/Packages/UniGLTF/Runtime/SpringBoneJobs/UpdateFastSpringBoneJob.cs b/Packages/UniGLTF/Runtime/SpringBoneJobs/UpdateFastSpringBoneJob.cs index 2cec59241..9357877f5 100644 --- a/Packages/UniGLTF/Runtime/SpringBoneJobs/UpdateFastSpringBoneJob.cs +++ b/Packages/UniGLTF/Runtime/SpringBoneJobs/UpdateFastSpringBoneJob.cs @@ -92,7 +92,7 @@ namespace UniGLTF.SpringBoneJobs + external * scalingFactor; // 外力による移動量 // 長さをboneLengthに強制 - nextTail = headTransform.position + math.normalize(nextTail - headTransform.position) * logic.length; + nextTail = headTransform.position + math.normalizesafe(nextTail - headTransform.position) * logic.length; nextTail = Anglelimit.Apply(logic, joint, parentRotation, head: headTransform.position, nextTail: nextTail); diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/MaterialIO/RenderPipelineUtility.cs b/Packages/UniGLTF/Runtime/UniGLTF/IO/MaterialIO/RenderPipelineUtility.cs index 76ecc0547..d072bec45 100644 --- a/Packages/UniGLTF/Runtime/UniGLTF/IO/MaterialIO/RenderPipelineUtility.cs +++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/MaterialIO/RenderPipelineUtility.cs @@ -1,4 +1,4 @@ -using UnityEngine.Rendering; +using UnityEngine.Rendering; namespace UniGLTF { @@ -6,19 +6,19 @@ namespace UniGLTF { public static RenderPipelineTypes GetRenderPipelineType() { - RenderPipeline currentPipeline = RenderPipelineManager.currentPipeline; + RenderPipelineAsset currentRenderPipelineAsset = GraphicsSettings.currentRenderPipeline; - if (currentPipeline == null) + if (currentRenderPipelineAsset == null) { return RenderPipelineTypes.BuiltinRenderPipeline; } - if (currentPipeline.GetType().Name.Contains("HDRenderPipeline")) + if (currentRenderPipelineAsset.GetType().Name.Contains("HDRenderPipeline")) { return RenderPipelineTypes.HighDefinitionRenderPipeline; } - if (currentPipeline.GetType().Name.Contains("UniversalRenderPipeline")) + if (currentRenderPipelineAsset.GetType().Name.Contains("UniversalRenderPipeline")) { return RenderPipelineTypes.UniversalRenderPipeline; } @@ -26,4 +26,4 @@ namespace UniGLTF return RenderPipelineTypes.Unknown; } } -} \ No newline at end of file +} diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs b/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs index 9cb09fd4e..ccc8aa6cf 100644 --- a/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs +++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs @@ -283,6 +283,138 @@ namespace UniGLTF } } + private static NativeArray GetMorphTargetVec3(GltfData data, int accessorIndex, string attribute) + { + if (accessorIndex < 0) return data.NativeArrayManager.CreateNativeArray(0); + + var accessor = data.GLTF.accessors[accessorIndex]; + if (accessor.type != "VEC3") + { + throw new ArgumentException($"unknown {attribute} type: {accessor.componentType}:{accessor.type}"); + } + + static float NormalizeSByte(sbyte v) + { + // glTF normalized signed integer maps min to -1.0 exactly. + return Mathf.Max(v / 127.0f, -1.0f); + } + + static float NormalizeShort(short v) + { + // glTF normalized signed integer maps min to -1.0 exactly. + return Mathf.Max(v / 32767.0f, -1.0f); + } + + switch (accessor.componentType) + { + case glComponentType.FLOAT: + return data.GetArrayFromAccessor(accessorIndex); + + case glComponentType.BYTE: + { + var src = data.GetArrayFromAccessor(accessorIndex); + var dst = data.NativeArrayManager.CreateNativeArray(src.Length); + if (accessor.normalized) + { + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3( + NormalizeSByte(v.x), + NormalizeSByte(v.y), + NormalizeSByte(v.z)); + } + } + else + { + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3(v.x, v.y, v.z); + } + } + return dst; + } + + case glComponentType.UNSIGNED_BYTE: + { + var src = data.GetArrayFromAccessor(accessorIndex); + var dst = data.NativeArrayManager.CreateNativeArray(src.Length); + if (accessor.normalized) + { + const float factor = 1.0f / 255.0f; + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3(v.x * factor, v.y * factor, v.z * factor); + } + } + else + { + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3(v.x, v.y, v.z); + } + } + return dst; + } + + case glComponentType.SHORT: + { + var src = data.GetArrayFromAccessor(accessorIndex); + var dst = data.NativeArrayManager.CreateNativeArray(src.Length); + if (accessor.normalized) + { + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3( + NormalizeShort(v.x), + NormalizeShort(v.y), + NormalizeShort(v.z)); + } + } + else + { + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3(v.x, v.y, v.z); + } + } + return dst; + } + + case glComponentType.UNSIGNED_SHORT: + { + var src = data.GetArrayFromAccessor(accessorIndex); + var dst = data.NativeArrayManager.CreateNativeArray(src.Length); + if (accessor.normalized) + { + const float factor = 1.0f / 65535.0f; + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3(v.x * factor, v.y * factor, v.z * factor); + } + } + else + { + for (int i = 0; i < src.Length; ++i) + { + var v = src[i]; + dst[i] = new Vector3(v.x, v.y, v.z); + } + } + return dst; + } + + default: + throw new NotImplementedException($"unknown {attribute} type: {accessor.componentType}:{accessor.type}"); + } + } + /// /// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど /// glTF 的にはありうる。 @@ -438,7 +570,7 @@ namespace UniGLTF var blendShape = GetOrCreateBlendShape(i); if (primTarget.POSITION != -1) { - var array = data.GetArrayFromAccessor(primTarget.POSITION); + var array = GetMorphTargetVec3(data, primTarget.POSITION, "POSITION"); if (array.Length != positions.Length) { throw new Exception("different length"); @@ -449,7 +581,7 @@ namespace UniGLTF if (primTarget.NORMAL != -1) { - var array = data.GetArrayFromAccessor(primTarget.NORMAL); + var array = GetMorphTargetVec3(data, primTarget.NORMAL, "NORMAL"); if (array.Length != positions.Length) { throw new Exception("different length"); @@ -460,7 +592,7 @@ namespace UniGLTF if (primTarget.TANGENT != -1) { - var array = data.GetArrayFromAccessor(primTarget.TANGENT); + var array = GetMorphTargetVec3(data, primTarget.TANGENT, "TANGENT"); if (array.Length != positions.Length) { throw new Exception("different length"); @@ -579,7 +711,7 @@ namespace UniGLTF if (hasPosition) { - var morphPositions = data.GetArrayFromAccessor(primTarget.POSITION); + var morphPositions = GetMorphTargetVec3(data, primTarget.POSITION, "POSITION"); blendShape.Positions.Capacity = morphPositions.Length; for (var j = 0; j < positions.Length; ++j) { @@ -589,7 +721,7 @@ namespace UniGLTF if (hasNormal) { - var morphNormals = data.GetArrayFromAccessor(primTarget.NORMAL); + var morphNormals = GetMorphTargetVec3(data, primTarget.NORMAL, "NORMAL"); blendShape.Normals.Capacity = morphNormals.Length; for (var j = 0; j < positions.Length; ++j) { @@ -600,7 +732,7 @@ namespace UniGLTF if (hasTangent) { - var morphTangents = data.GetArrayFromAccessor(primTarget.TANGENT); + var morphTangents = GetMorphTargetVec3(data, primTarget.TANGENT, "TANGENT"); blendShape.Tangents.Capacity = morphTangents.Length; for (var j = 0; j < positions.Length; ++j) { @@ -639,4 +771,4 @@ namespace UniGLTF } } } -} \ No newline at end of file +} diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs b/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs index 99adf2c88..1a7aab51c 100644 --- a/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs +++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Profiling; @@ -41,28 +40,33 @@ namespace UniGLTF } } - private static async Task BuildBlendShapeAsync(IAwaitCaller awaitCaller, Mesh mesh, BlendShape blendShape, + private static async Task BuildBlendShapeAsync( + IAwaitCaller awaitCaller, + Mesh mesh, + BlendShape blendShape, Vector3[] emptyVertices) { Vector3[] positions = null; Vector3[] normals = null; await awaitCaller.Run(() => { - positions = blendShape.Positions.ToArray(); - if (blendShape.Normals != null) - { - normals = blendShape.Normals.ToArray(); - } + positions = blendShape.Positions != null ? blendShape.Positions.ToArray() : Array.Empty(); + normals = blendShape.Normals != null ? blendShape.Normals.ToArray() : Array.Empty(); }); Profiler.BeginSample("MeshUploader.BuildBlendShapeAsync"); + var hasPositions = positions.Length == mesh.vertexCount; + var hasNormals = normals.Length == mesh.vertexCount; + if (positions.Length > 0) { - if (positions.Length == mesh.vertexCount) + if (hasPositions) { + var deltaNormals = hasNormals ? normals : null; + mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight, positions, - normals.Length == mesh.vertexCount && normals.Length == positions.Length ? normals : null, + deltaNormals, null ); } @@ -76,7 +80,7 @@ namespace UniGLTF // add empty blend shape for keep blend shape index mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight, emptyVertices, - null, + normals.Length == mesh.vertexCount ? normals : null, null ); } @@ -132,7 +136,11 @@ namespace UniGLTF var emptyVertices = new Vector3[mesh.vertexCount]; foreach (var blendShape in data.BlendShapes) { - await BuildBlendShapeAsync(awaitCaller, mesh, blendShape, emptyVertices); + await BuildBlendShapeAsync( + awaitCaller, + mesh, + blendShape, + emptyVertices); } } diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs b/Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs new file mode 100644 index 000000000..4dfcd1c75 --- /dev/null +++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.InteropServices; + +namespace UniGLTF +{ + [Serializable, StructLayout(LayoutKind.Sequential, Pack = 1)] + public readonly struct SByte3 : IEquatable + { + public readonly sbyte x; + public readonly sbyte y; + public readonly sbyte z; + + public SByte3(sbyte _x, sbyte _y, sbyte _z) + { + x = _x; + y = _y; + z = _z; + } + + public bool Equals(SByte3 other) + { + return x == other.x && y == other.y && z == other.z; + } + } +} diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs.meta b/Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs.meta new file mode 100644 index 000000000..cc7a12d6e --- /dev/null +++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 93c772ed657e23c448e9b036d9c9071c diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs b/Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs new file mode 100644 index 000000000..264b39fc6 --- /dev/null +++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.InteropServices; + +namespace UniGLTF +{ + [Serializable, StructLayout(LayoutKind.Sequential, Pack = 1)] + public readonly struct Short3 : IEquatable + { + public readonly short x; + public readonly short y; + public readonly short z; + + public Short3(short _x, short _y, short _z) + { + x = _x; + y = _y; + z = _z; + } + + public bool Equals(Short3 other) + { + return x == other.x && y == other.y && z == other.z; + } + } +} diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs.meta b/Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs.meta new file mode 100644 index 000000000..c3e51f6dd --- /dev/null +++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f74b316e688844c428c7b45dcb38ee54 diff --git a/Packages/UniGLTF/Runtime/UniGLTF/PackageVersion.cs b/Packages/UniGLTF/Runtime/UniGLTF/PackageVersion.cs index 44645ee4b..a3c5813ed 100644 --- a/Packages/UniGLTF/Runtime/UniGLTF/PackageVersion.cs +++ b/Packages/UniGLTF/Runtime/UniGLTF/PackageVersion.cs @@ -5,7 +5,7 @@ namespace UniGLTF { public const int MAJOR = 0; public const int MINOR = 131; - public const int PATCH = 0; - public const string VERSION = "0.131.0"; + public const int PATCH = 1; + public const string VERSION = "0.131.1"; } } diff --git a/Packages/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs b/Packages/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs index 6f4374687..b4c6417eb 100644 --- a/Packages/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs +++ b/Packages/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs @@ -255,9 +255,10 @@ namespace UniGLTF } } - static Dictionary> PoseMap = new(); + static readonly Dictionary> PoseMap = new(); + public static IReadOnlyDictionary SafeGetInitialPose( - Transform root, bool useCache = true) + Transform root, bool useCache = false) { if (useCache && PoseMap.TryGetValue(root, out var pose)) { @@ -278,7 +279,10 @@ namespace UniGLTF } // add cache - PoseMap.Add(root, pose); + if (useCache) + { + PoseMap.Add(root, pose); + } return pose; } diff --git a/Packages/UniGLTF/Runtime/UniGLTF/UniGLTFVersion.cs b/Packages/UniGLTF/Runtime/UniGLTF/UniGLTFVersion.cs index 27595cc47..6f0a7ff0c 100644 --- a/Packages/UniGLTF/Runtime/UniGLTF/UniGLTFVersion.cs +++ b/Packages/UniGLTF/Runtime/UniGLTF/UniGLTFVersion.cs @@ -5,7 +5,7 @@ namespace UniGLTF { public const int MAJOR = 2; public const int MINOR = 67; - public const int PATCH = 0; - public const string VERSION = "2.67.0"; + public const int PATCH = 1; + public const string VERSION = "2.67.1"; } } diff --git a/Packages/UniGLTF/Runtime/UniHumanoid/HumanoidLoader.cs b/Packages/UniGLTF/Runtime/UniHumanoid/HumanoidLoader.cs index 61f07d70b..77c68c458 100644 --- a/Packages/UniGLTF/Runtime/UniHumanoid/HumanoidLoader.cs +++ b/Packages/UniGLTF/Runtime/UniHumanoid/HumanoidLoader.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using UniGLTF; using UniGLTF.Utils; using UnityEngine; @@ -51,11 +53,19 @@ namespace UniHumanoid x => x); /// - /// Avatar を保持する既存の Animatorヒエラルキーの Transform を変更したのちに、 - /// HumanBone のマッピングを流用して、新たな Avatar を作り直す。 - /// 古い Avatar は破棄する。 + /// Recreates an animiator's humanoid avatar. + /// The old Avatar is discarded. /// public static void RebuildHumanAvatar(Animator animator) + { + var task = RebuildHumanAvatarAsync(animator, new ImmediateCaller()); + if (!task.IsCompleted) + { + throw new Exception("task not completed"); + } + } + + public static async Task RebuildHumanAvatarAsync(Animator animator, IAwaitCaller awaitCaller) { if (animator == null) { @@ -73,16 +83,22 @@ namespace UniHumanoid newAvatar.name = "re-created"; // var newAvatar = LoadHumanoidAvatarFromAnimator(animator); - // Animator.avatar を代入したときに副作用でTransformが変更されるのを回避するために削除します。 + // 1. Delete this to avoid changing Transform as a side effect when assigning Animator.avatar. if (Application.isPlaying) { GameObject.Destroy(animator); + + // https://github.com/vrm-c/UniVRM/pull/2764 + // Require IAwaitCaller that has NextFrame capability. RuntimeOnlyAwaitCaller etc. not ImmediateCaller. + // Else, the following AddComponent call will fail. + await awaitCaller.NextFrame(); } else { GameObject.DestroyImmediate(animator); } - // 新たに AddComponent する + + // 2. Attach a new one target.AddComponent().avatar = newAvatar; } } diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller.meta b/Packages/UniGLTF/Runtime/Utils/AwaitCaller.meta similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller.meta rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller.meta diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/IAwaitCaller.cs b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/IAwaitCaller.cs similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/IAwaitCaller.cs rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/IAwaitCaller.cs diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/IAwaitCaller.cs.meta b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/IAwaitCaller.cs.meta similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/IAwaitCaller.cs.meta rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/IAwaitCaller.cs.meta diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/ImmediateCaller.cs b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/ImmediateCaller.cs similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/ImmediateCaller.cs rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/ImmediateCaller.cs diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/ImmediateCaller.cs.meta b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/ImmediateCaller.cs.meta similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/ImmediateCaller.cs.meta rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/ImmediateCaller.cs.meta diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/NextFrameTaskScheduler.cs b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/NextFrameTaskScheduler.cs similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/NextFrameTaskScheduler.cs rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/NextFrameTaskScheduler.cs diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/NextFrameTaskScheduler.cs.meta b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/NextFrameTaskScheduler.cs.meta similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/NextFrameTaskScheduler.cs.meta rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/NextFrameTaskScheduler.cs.meta diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyAwaitCaller.cs b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyAwaitCaller.cs similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyAwaitCaller.cs rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyAwaitCaller.cs diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyAwaitCaller.cs.meta b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyAwaitCaller.cs.meta similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyAwaitCaller.cs.meta rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyAwaitCaller.cs.meta diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs.meta b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs.meta similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs.meta rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/RuntimeOnlyNoThreadAwaitCaller.cs.meta diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/TinyManagedTaskScheduler.cs b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/TinyManagedTaskScheduler.cs similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/TinyManagedTaskScheduler.cs rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/TinyManagedTaskScheduler.cs diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/TinyManagedTaskScheduler.cs.meta b/Packages/UniGLTF/Runtime/Utils/AwaitCaller/TinyManagedTaskScheduler.cs.meta similarity index 100% rename from Packages/UniGLTF/Runtime/UniGLTF/IO/AwaitCaller/TinyManagedTaskScheduler.cs.meta rename to Packages/UniGLTF/Runtime/Utils/AwaitCaller/TinyManagedTaskScheduler.cs.meta diff --git a/Packages/UniGLTF/Runtime/Utils/MathHelper.cs b/Packages/UniGLTF/Runtime/Utils/MathHelper.cs index 86cb3646b..70ee62b9b 100644 --- a/Packages/UniGLTF/Runtime/Utils/MathHelper.cs +++ b/Packages/UniGLTF/Runtime/Utils/MathHelper.cs @@ -27,33 +27,36 @@ namespace UniGLTF.Runtime.Utils [MethodImpl(MethodImplOptions.AggressiveInlining)] public static quaternion FromToRotation(in float3 fromVector, in float3 toVector) { - if (math.lengthsq(fromVector) == 0 || math.lengthsq(toVector) == 0) + const float epsilon = 1e-6f; + const float epsilonSq = epsilon * epsilon; + + if (math.lengthsq(fromVector) < epsilonSq || math.lengthsq(toVector) < epsilonSq) { return quaternion.identity; } - float3 from = math.normalize(fromVector); - float3 to = math.normalize(toVector); + float3 from = math.normalizesafe(fromVector); + float3 to = math.normalizesafe(toVector); - var dot = math.dot(from, to); - switch(dot) + var dot = math.clamp(math.dot(from, to), -1.0f, 1.0f); + switch (dot) { - case >= 1.0f: - return quaternion.identity; - case <= -1.0f: + case > 1.0f - epsilon: + return quaternion.identity; + case < -1.0f + epsilon: { var axis = math.cross(from, new float3(1, 0, 0)); - if (math.lengthsq(axis) < 0.0001f) + if (math.lengthsq(axis) < epsilonSq) { axis = math.cross(from, new float3(0, 1, 0)); } - return quaternion.AxisAngle(math.normalize(axis), math.PI); + return quaternion.AxisAngle(math.normalizesafe(axis), math.PI); } default: { var angle = math.acos(dot); var axis = math.cross(from, to); - return quaternion.AxisAngle(math.normalize(axis), angle); + return quaternion.AxisAngle(math.normalizesafe(axis), angle); } } } diff --git a/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs b/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs index 5c1395b64..cf1cc3b8f 100644 --- a/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs +++ b/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs @@ -45,5 +45,66 @@ namespace UniGLTF var expected = Quaternion.FromToRotation(_vector1, _vector2); Assert.That(MathHelper.Approximately(result, expected), Is.True); } + + [Test] + public void FromToRotationMatchesUnityForStandardCasesTest() + { + AssertFromToRotationMatchesUnity(new float3(1, 0, 0), new float3(0, 1, 0)); + AssertFromToRotationMatchesUnity(new float3(0, 1, 0), new float3(0, 0, 1)); + AssertFromToRotationMatchesUnity(new float3(1, 2, 3), new float3(4, 5, 6)); + AssertFromToRotationMatchesUnity(new float3(-2, 0.5f, 3), new float3(1, -4, 0.25f)); + } + + [Test] + public void FromToRotationSameDirectionTest() + { + AssertFromToRotation(new float3(1, 0, 0), new float3(2, 0, 0)); + AssertFromToRotation(new float3(1, 2, 3), new float3(2, 4, 6)); + } + + [Test] + public void FromToRotationOppositeDirectionTest() + { + AssertFromToRotation(new float3(1, 0, 0), new float3(-1, 0, 0)); + AssertFromToRotation(new float3(0, 1, 0), new float3(0, -1, 0)); + AssertFromToRotation(new float3(0, 0, 1), new float3(0, 0, -1)); + AssertFromToRotation(new float3(1, 2, 3), new float3(-1, -2, -3)); + } + + [Test] + public void FromToRotationNearlyOppositeDirectionTest() + { + AssertFromToRotation(new float3(1, 0, 0), math.normalize(new float3(-1, 0.0001f, 0))); + AssertFromToRotation(new float3(1, 2, 3), math.normalize(new float3(-1.0001f, -2, -3))); + } + + [Test] + public void FromToRotationZeroVectorTest() + { + Assert.That(MathHelper.Approximately(MathHelper.FromToRotation(float3.zero, new float3(0, 1, 0)), quaternion.identity), Is.True); + Assert.That(MathHelper.Approximately(MathHelper.FromToRotation(new float3(1, 0, 0), float3.zero), quaternion.identity), Is.True); + } + + [Test] + public void FromToRotationTinyVectorTest() + { + var result = MathHelper.FromToRotation(new float3(1e-12f, 0, 0), new float3(0, 1, 0)); + Assert.That(MathHelper.Approximately(result, quaternion.identity), Is.True); + } + + private static void AssertFromToRotation(float3 from, float3 to) + { + var result = MathHelper.FromToRotation(from, to); + var rotated = math.mul(result, math.normalize(from)); + var dot = math.dot(math.normalize(rotated), math.normalize(to)); + Assert.That(dot, Is.GreaterThan(0.9999f)); + } + + private static void AssertFromToRotationMatchesUnity(float3 from, float3 to) + { + var result = MathHelper.FromToRotation(from, to); + var expected = Quaternion.FromToRotation(from, to); + Assert.That(MathHelper.Approximately(result, expected), Is.True); + } } } diff --git a/Packages/UniGLTF/package.json b/Packages/UniGLTF/package.json index d7c324bb2..3689c218a 100644 --- a/Packages/UniGLTF/package.json +++ b/Packages/UniGLTF/package.json @@ -1,6 +1,6 @@ { "name": "com.vrmc.gltf", - "version": "0.131.0", + "version": "0.131.1", "displayName": "UniGLTF", "description": "GLTF importer and exporter", "unity": "2021.3", diff --git a/Packages/VRM/Editor/Format/VRMEditorImporterContext.cs b/Packages/VRM/Editor/Format/VRMEditorImporterContext.cs index 7bfc9826d..4f4cda57e 100644 --- a/Packages/VRM/Editor/Format/VRMEditorImporterContext.cs +++ b/Packages/VRM/Editor/Format/VRMEditorImporterContext.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using UniGLTF; +using Unity.Profiling; using UnityEditor; using UnityEngine; @@ -12,6 +13,8 @@ namespace VRM UnityPath m_prefabPath; List m_paths = new List(); + private static ProfilerMarker s_MarkerConvertAndExtractImages = new ProfilerMarker("Convert and Extract Images"); + public ITextureDescriptorGenerator TextureDescriptorGenerator => m_context.TextureDescriptorGenerator; public VRMEditorImporterContext(VRMImporterContext context, UnityPath prefabPath) @@ -76,22 +79,27 @@ namespace VRM /// public void ConvertAndExtractImages(Action> onTextureReloaded) { + s_MarkerConvertAndExtractImages.Begin(); + // // convert images(metallic roughness, occlusion map) // var task = m_context.LoadMaterialsAsync(new ImmediateCaller()); if (!task.IsCompleted) { + s_MarkerConvertAndExtractImages.End(); throw new Exception(); } if (task.IsFaulted) { if (task.Exception is AggregateException ae && ae.InnerExceptions.Count == 1) { + s_MarkerConvertAndExtractImages.End(); throw ae.InnerException; } else { + s_MarkerConvertAndExtractImages.End(); throw task.Exception; } } @@ -100,6 +108,7 @@ namespace VRM var task2 = m_context.ReadMetaAsync(new ImmediateCaller()); if (!task2.IsCompleted || task2.IsCanceled || task2.IsFaulted) { + s_MarkerConvertAndExtractImages.End(); throw new Exception(); } @@ -110,6 +119,8 @@ namespace VRM var vrmTextures = new BuiltInVrmMaterialDescriptorGenerator(m_context.VRM); var dirName = $"{m_prefabPath.FileNameWithoutExtension}.Textures"; TextureExtractor.ExtractTextures(m_context.Data, m_prefabPath.Parent.Child(dirName), m_context.TextureDescriptorGenerator, subAssets, (_x, _y) => { }, onTextureReloaded); + + s_MarkerConvertAndExtractImages.End(); } void SaveAsAsset(SubAssetKey _, UnityEngine.Object o) diff --git a/Packages/VRM/Editor/Format/vrmAssetPostprocessor.cs b/Packages/VRM/Editor/Format/vrmAssetPostprocessor.cs index 1ebaaee95..282ccc374 100644 --- a/Packages/VRM/Editor/Format/vrmAssetPostprocessor.cs +++ b/Packages/VRM/Editor/Format/vrmAssetPostprocessor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using UniGLTF; +using Unity.Profiling; using UnityEditor; using UnityEngine; @@ -10,6 +11,8 @@ namespace VRM { public class vrmAssetPostprocessor : AssetPostprocessor { + private static ProfilerMarker s_MarkerCreatePrefab = new ProfilerMarker("Create Prefab"); + #if !VRM_STOP_ASSETPOSTPROCESSOR static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { @@ -63,6 +66,9 @@ namespace VRM return; } + System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + /// /// これは EditorApplication.delayCall により呼び出される。 /// @@ -73,24 +79,46 @@ namespace VRM /// Action> onCompleted = texturePaths => { + s_MarkerCreatePrefab.Begin(); + var map = texturePaths .Select(x => x.LoadAsset()) .ToDictionary(x => new SubAssetKey(x), x => x as UnityEngine.Object); - var settings = new ImporterContextSettings(); - // 確実に Dispose するために敢えて再パースしている - using (var data = new GlbFileParser(vrmPath).Parse()) - using (var context = new VRMImporterContext(new VRMData(data), externalObjectMap: map, settings: settings)) + try { - var editor = new VRMEditorImporterContext(context, prefabPath); - foreach (var textureInfo in context.TextureDescriptorGenerator.Get().GetEnumerable()) + AssetDatabase.StartAssetEditing(); + + var settings = new ImporterContextSettings(); + + // 確実に Dispose するために敢えて再パースしている + using (var data = new GlbFileParser(vrmPath).Parse()) + using (var context = new VRMImporterContext(new VRMData(data), externalObjectMap: map, settings: settings)) { - TextureImporterConfigurator.Configure(textureInfo, context.TextureFactory.ExternalTextures); + var editor = new VRMEditorImporterContext(context, prefabPath); + foreach (var textureInfo in context.TextureDescriptorGenerator.Get().GetEnumerable()) + { + TextureImporterConfigurator.Configure(textureInfo, context.TextureFactory.ExternalTextures); + } + var loaded = context.Load(); + editor.SaveAsAsset(loaded); } - var loaded = context.Load(); - editor.SaveAsAsset(loaded); + + } + catch (Exception e) + { + Debug.LogException(e); + } + finally + { + AssetDatabase.StopAssetEditing(); } + s_MarkerCreatePrefab.End(); + + sw.Stop(); + + Debug.Log($"Import complete [importMs={sw.ElapsedMilliseconds}]"); }; using (var data = new GlbFileParser(vrmPath).Parse()) diff --git a/Packages/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs b/Packages/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs index 77269756d..ec0b8bf09 100644 --- a/Packages/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs +++ b/Packages/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using UniGLTF; using UniGLTF.MeshUtility; using UniGLTF.Utils; @@ -54,6 +55,15 @@ namespace VRM /// 強制的にT-Pose化するか /// BlendShape の現状をbakeするか public static void Execute(GameObject go, bool forceTPose, bool useCurrentBlendShapeWeight) + { + var task = ExecuteAsync(go, forceTPose, useCurrentBlendShapeWeight, new ImmediateCaller()); + if (!task.IsCompleted) + { + throw new Exception("task not completed"); + } + } + + public static async Task ExecuteAsync(GameObject go, bool forceTPose, bool useCurrentBlendShapeWeight, IAwaitCaller awaitCaller) { if (forceTPose) { @@ -83,7 +93,7 @@ namespace VRM // 回転とスケールが除去された新しいヒエラルキーからAvatarを作る if (go.TryGetComponent(out var animator)) { - HumanoidLoader.RebuildHumanAvatar(animator); + await HumanoidLoader.RebuildHumanAvatarAsync(animator, awaitCaller); } } diff --git a/Packages/VRM/Tests/VRM.Tests.asmdef b/Packages/VRM/Tests/VRM.Tests.asmdef index 5d30901d9..4b755ecfd 100644 --- a/Packages/VRM/Tests/VRM.Tests.asmdef +++ b/Packages/VRM/Tests/VRM.Tests.asmdef @@ -7,9 +7,9 @@ "GUID:8d76e605759c3f64a957d63ef96ada7c", "GUID:5f875fdc81c40184c8333b9d63c6ddd5", "GUID:f9fe54bb3090be448aa10ac92648a614", - "GUID:da3e51d19d51a544fa14d43fee843098", "GUID:27619889b8ba8c24980f49ee34dbb44a", - "GUID:0acc523941302664db1f4e527237feb3" + "GUID:0acc523941302664db1f4e527237feb3", + "GUID:1cd941934d098654fa21a13f28346412" ], "includePlatforms": [ "Editor" diff --git a/Packages/VRM/package.json b/Packages/VRM/package.json index 8ada7d563..81a14cb9c 100644 --- a/Packages/VRM/package.json +++ b/Packages/VRM/package.json @@ -1,6 +1,6 @@ { "name": "com.vrmc.univrm", - "version": "0.131.0", + "version": "0.131.1", "displayName": "VRM", "description": "VRM importer", "unity": "2021.3", @@ -14,7 +14,7 @@ "name": "VRM Consortium" }, "dependencies": { - "com.vrmc.gltf": "0.131.0", + "com.vrmc.gltf": "0.131.1", "com.unity.ugui": "1.0.0" }, "samples": [ diff --git a/Packages/VRM10/Editor/Components/Expression/ExpressionEditorHelper.cs b/Packages/VRM10/Editor/Components/Expression/ExpressionEditorHelper.cs index 4791a460a..bc23218ea 100644 --- a/Packages/VRM10/Editor/Components/Expression/ExpressionEditorHelper.cs +++ b/Packages/VRM10/Editor/Components/Expression/ExpressionEditorHelper.cs @@ -99,6 +99,21 @@ namespace UniVRM10 } } + public static bool Vec3Prop(Rect rect, SerializedProperty prop) + { + var oldValue = prop.vector3Value; + var newValue = EditorGUI.Vector3Field(rect, prop.displayName, oldValue); + if (newValue != oldValue) + { + prop.vector3Value = newValue; + return true; + } + else + { + return false; + } + } + static Rect AdvanceRect(ref float x, float y, float w, float h) { var rect = new Rect(x, y, w, h); diff --git a/Packages/VRM10/Editor/Components/Expression/PreviewFaceRenderer.cs b/Packages/VRM10/Editor/Components/Expression/PreviewFaceRenderer.cs index 4020fcf06..db49a99c0 100644 --- a/Packages/VRM10/Editor/Components/Expression/PreviewFaceRenderer.cs +++ b/Packages/VRM10/Editor/Components/Expression/PreviewFaceRenderer.cs @@ -110,6 +110,10 @@ namespace UniVRM10 int subMeshCount = item.Mesh.subMeshCount; for (int i = 0; i < subMeshCount; i++) { + if (item.SkinnedMeshRenderer != null) + { + item.SkinnedMeshRenderer.BakeMesh(item.Mesh); + } m_previewUtility.DrawMesh(item.Mesh, item.Position, item.Rotation, item.Materials[i], i); diff --git a/Packages/VRM10/Editor/Components/Expression/ReorderableNodeTransformBindingList.cs b/Packages/VRM10/Editor/Components/Expression/ReorderableNodeTransformBindingList.cs new file mode 100644 index 000000000..55dcf27f3 --- /dev/null +++ b/Packages/VRM10/Editor/Components/Expression/ReorderableNodeTransformBindingList.cs @@ -0,0 +1,133 @@ +using System; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + + +namespace UniVRM10 +{ + public class ReorderableNodeTransformBindingList + { + ReorderableList m_ValuesList; + SerializedProperty m_valuesProp; + bool m_changed; + + public ReorderableNodeTransformBindingList(SerializedObject serializedObject, PreviewSceneManager previewSceneManager, int height) + { + m_valuesProp = serializedObject.FindProperty(nameof(VRM10Expression.NodeTransformBindings)); + m_ValuesList = new ReorderableList(serializedObject, m_valuesProp); + m_ValuesList.elementHeight = height * 4; + m_ValuesList.drawElementCallback = + (rect, index, isActive, isFocused) => + { + var element = m_valuesProp.GetArrayElementAtIndex(index); + rect.height -= 4; + rect.y += 2; + if (DrawNodeTransformBinding(rect, element, previewSceneManager, height)) + { + m_changed = true; + } + }; + } + + /// + /// NodeTransform List のElement描画 + /// + static bool DrawNodeTransformBinding(Rect position, SerializedProperty property, + PreviewSceneManager scene, int height) + { + bool changed = false; + if (scene != null) + { + var y = position.y; + var rect = new Rect(position.x, y, position.width, height); + int pathIndex; + if (ExpressionEditorHelper.StringPopup(rect, property.FindPropertyRelative(nameof(NodeTransformBinding.RelativePath)), scene.NodeTransformPathList, out pathIndex)) + { + changed = true; + } + + // T` + y += height; + rect = new Rect(position.x, y, position.width, height); + if (ExpressionEditorHelper.Vec3Prop(rect, property.FindPropertyRelative(nameof(NodeTransformBinding.OffsetTranslation)))) + { + changed = true; + } + + // R + y += height; + rect = new Rect(position.x, y, position.width, height); + EditorGUI.BeginChangeCheck(); + EditorGUI.PropertyField(rect, property.FindPropertyRelative(nameof(NodeTransformBinding.OffsetRotation))); + if (EditorGUI.EndChangeCheck()) + { + changed = true; + } + + // S + y += height; + rect = new Rect(position.x, y, position.width, height); + if (ExpressionEditorHelper.Vec3Prop(rect, property.FindPropertyRelative(nameof(NodeTransformBinding.TargetScale)))) + { + changed = true; + } + } + return changed; + } + + public void SetValues(NodeTransformBinding[] bindings) + { + m_valuesProp.ClearArray(); + m_valuesProp.arraySize = bindings.Length; + for (int i = 0; i < bindings.Length; ++i) + { + var item = m_valuesProp.GetArrayElementAtIndex(i); + + var endProperty = item.GetEndProperty(); + while (item.NextVisible(true)) + { + if (SerializedProperty.EqualContents(item, endProperty)) + { + break; + } + + switch (item.name) + { + case nameof(NodeTransformBinding.RelativePath): + item.stringValue = bindings[i].RelativePath; + break; + + case nameof(NodeTransformBinding.OffsetTranslation): + item.vector3Value = bindings[i].OffsetTranslation; + break; + + case nameof(NodeTransformBinding.OffsetRotation): + item.quaternionValue = bindings[i].OffsetRotation; + break; + + case nameof(NodeTransformBinding.TargetScale): + item.vector3Value = bindings[i].TargetScale; + break; + + default: + throw new Exception(); + } + } + } + + } + + public bool Draw(string label) + { + m_changed = false; + m_ValuesList.DoLayoutList(); + if (GUILayout.Button($"Clear {label}")) + { + m_changed = true; + m_valuesProp.arraySize = 0; + } + return m_changed; + } + } +} diff --git a/Packages/VRM10/Editor/Components/Expression/ReorderableNodeTransformBindingList.cs.meta b/Packages/VRM10/Editor/Components/Expression/ReorderableNodeTransformBindingList.cs.meta new file mode 100644 index 000000000..3c72f298b --- /dev/null +++ b/Packages/VRM10/Editor/Components/Expression/ReorderableNodeTransformBindingList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 186e6dd105c86904497f3ac0dfd61212 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/VRM10/Editor/Components/Expression/SerializedExpressionEditor.cs b/Packages/VRM10/Editor/Components/Expression/SerializedExpressionEditor.cs index 782d355a3..768cc15f0 100644 --- a/Packages/VRM10/Editor/Components/Expression/SerializedExpressionEditor.cs +++ b/Packages/VRM10/Editor/Components/Expression/SerializedExpressionEditor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using UnityEditor; @@ -28,21 +29,24 @@ namespace UniVRM10 ReorderableMorphTargetBindingList m_morphTargetBindings; ReorderableMaterialColorBindingList m_materialColorBindings; ReorderableMaterialUVBindingList m_materialUVBindings; - + ReorderableNodeTransformBindingList m_nodeTransformBindings; #region Editor values bool m_changed; - static int s_Mode; static bool s_MorphTargetFoldout = true; static bool s_OptionFoldout; static bool s_ListFoldout; - static string[] MODES = new[]{ - "MorphTarget", - "Material Color", - "Texture Transform" - }; + enum ListMode + { + MorphTarget, + MaterialColor, + TextureTransform, + NodeTransform, + } + static ListMode s_Mode; + static string[] MODES = ((ListMode[])Enum.GetValues(typeof(ListMode))).Select(x => x.ToString()).ToArray(); PreviewMeshItem[] m_items; #endregion @@ -71,6 +75,7 @@ namespace UniVRM10 m_morphTargetBindings = new ReorderableMorphTargetBindingList(serializedObject, previewSceneManager, 20); m_materialColorBindings = new ReorderableMaterialColorBindingList(serializedObject, previewSceneManager?.MaterialNames, 20); m_materialUVBindings = new ReorderableMaterialUVBindingList(serializedObject, previewSceneManager?.MaterialNames, 20); + m_nodeTransformBindings = new ReorderableNodeTransformBindingList(serializedObject, previewSceneManager, 20); m_items = previewSceneManager.EnumRenderItems .Where(x => x.SkinnedMeshRenderer != null) @@ -109,33 +114,39 @@ namespace UniVRM10 if (s_ListFoldout) { EditorGUI.indentLevel++; - s_Mode = GUILayout.Toolbar(s_Mode, MODES); + s_Mode = (ListMode)GUILayout.Toolbar((int)s_Mode, MODES); switch (s_Mode) { - case 0: - // MorphTarget + case ListMode.MorphTarget: { - if (m_morphTargetBindings.Draw("MorphTarget")) + if (m_morphTargetBindings.Draw(s_Mode.ToString())) { m_changed = true; } } break; - case 1: - // Material + case ListMode.MaterialColor: { - if (m_materialColorBindings.Draw("MaterialColor")) + if (m_materialColorBindings.Draw(s_Mode.ToString())) { m_changed = true; } } break; - case 2: - // TextureTransform + case ListMode.TextureTransform: { - if (m_materialUVBindings.Draw("TextureTransform")) + if (m_materialUVBindings.Draw(s_Mode.ToString())) + { + m_changed = true; + } + } + break; + + case ListMode.NodeTransform: + { + if (m_nodeTransformBindings.Draw(s_Mode.ToString())) { m_changed = true; } diff --git a/Packages/VRM10/Editor/Vrm10SerializerGenerator.cs b/Packages/VRM10/Editor/Vrm10SerializerGenerator.cs index ec5b753d9..cd3b24015 100644 --- a/Packages/VRM10/Editor/Vrm10SerializerGenerator.cs +++ b/Packages/VRM10/Editor/Vrm10SerializerGenerator.cs @@ -36,6 +36,7 @@ namespace UniVRM10 private const string Vrm10SpecDir = "vrm-specification/specification"; private const string Vrm10FormatGeneratedDir = "Packages/VRM10/Runtime/Format"; + private const string UniGltfFormatGeneratedDir = "Packages/UniGLTF/Runtime/UniGLTF/Format"; public static void Run(bool debug) { @@ -48,7 +49,7 @@ namespace UniVRM10 // VRMC_hdr_emissiveMultiplier new GenerateInfo( $"{Vrm10SpecDir}/VRMC_materials_hdr_emissiveMultiplier-1.0/schema/VRMC_materials_hdr_emissiveMultiplier.json", - "Assets/UniGLTF/Runtime/UniGLTF/Format/ExtensionsAndExtras/EmissiveMultiplier" + $"{UniGltfFormatGeneratedDir}/ExtensionsAndExtras/EmissiveMultiplier" ), // VRMC_vrm @@ -94,6 +95,14 @@ namespace UniVRM10 $"{Vrm10SpecDir}/VRMC_springBone_limit-1.0/schema/VRMC_springBone_limit.schema.json", $"{Vrm10FormatGeneratedDir}/SpringBoneLimit" ), + + // VRMC_vrm_expressions_node_transform-1.0 + // (experimental) + // https://github.com/ousttrue/vrm-specification/tree/VRMC_vrm_expression_joint + new GenerateInfo( + $"{Vrm10SpecDir}/VRMC_vrm_expressions_node_transform-1.0/schema/VRMC_vrm_expressions_node_transform.schema.json", + $"{Vrm10FormatGeneratedDir}/ExpressionsNodeTransform" + ), }; foreach (var arg in args) diff --git a/Packages/VRM10/Runtime/Components/Expression/ExpressionMerger.cs b/Packages/VRM10/Runtime/Components/Expression/ExpressionMerger.cs index dd54cb70d..de9a6a55f 100644 --- a/Packages/VRM10/Runtime/Components/Expression/ExpressionMerger.cs +++ b/Packages/VRM10/Runtime/Components/Expression/ExpressionMerger.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using UniGLTF.Utils; using UnityEngine; namespace UniVRM10 @@ -23,9 +24,13 @@ namespace UniVRM10 MorphTargetBindingMerger m_morphTargetBindingMerger; MaterialValueBindingMerger m_materialValueBindingMerger; + NodeTransformBindingMerger m_boneTransformBindingMerger; - public ExpressionMerger(VRM10ObjectExpression expressions, Transform root, bool isPrefabInstance) + public ExpressionMerger(VRM10ObjectExpression expressions, + Transform root, + bool isPrefabInstance, + IReadOnlyDictionary initPose) { m_clipMap = expressions.Clips.ToDictionary( x => expressions.CreateKey(x.Clip), @@ -35,6 +40,7 @@ namespace UniVRM10 m_valueMap = new Dictionary(ExpressionKey.Comparer); m_morphTargetBindingMerger = new MorphTargetBindingMerger(m_clipMap, root); m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root, isPrefabInstance); + m_boneTransformBindingMerger = new NodeTransformBindingMerger(m_clipMap, root, initPose); } /// @@ -50,6 +56,7 @@ namespace UniVRM10 m_morphTargetBindingMerger.Apply(); m_materialValueBindingMerger.Apply(); + m_boneTransformBindingMerger.Apply(); } private void AccumulateValue(ExpressionKey key, float value) @@ -69,6 +76,7 @@ namespace UniVRM10 m_morphTargetBindingMerger.AccumulateValue(key, value); m_materialValueBindingMerger.AccumulateValue(clip, value); + m_boneTransformBindingMerger.AccumulateValue(key, value); } public void Dispose() diff --git a/Packages/VRM10/Runtime/Components/Expression/NodeTransformBinding.cs b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBinding.cs new file mode 100644 index 000000000..a6ecff11f --- /dev/null +++ b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBinding.cs @@ -0,0 +1,56 @@ +using System; +using UniGLTF.Utils; +using UnityEngine; + +namespace UniVRM10 +{ + [Serializable] + public struct NodeTransformBinding + { + public string RelativePath; + + /// + /// t = init_t + offset_t * weight + /// disable if offset_t = (0, 0, 0) + /// + public Vector3 OffsetTranslation; + + /// + /// r = slerp(init_t, init_t * offset r, weight) + /// disable if rotation_t = (0, 0, 0, 1) + /// + public Quaternion OffsetRotation; + + /// + /// s = lerp(init_s, blend_s, weight) + /// disalbe if blend_s = init_s. maybe(1, 1, 1) + /// + public Vector3 TargetScale; + + /// + /// + /// + /// + /// + /// 0 to 1.0 + public NodeTransformBinding(string path, in Vector3 t, in Quaternion r, in Vector3 s) + { + RelativePath = path; + OffsetTranslation = t; + OffsetRotation = r; + TargetScale = s; + } + + public void Apply(Transform node, in TransformState init, float weight) + { + node.SetLocalPositionAndRotation( + init.LocalPosition + this.OffsetTranslation * weight, + Quaternion.Slerp(init.LocalRotation, init.LocalRotation * this.OffsetRotation, weight) + ); + if (this.TargetScale != Vector3.zero) + { + node.localScale = Vector3.Lerp(init.LocalScale, this.TargetScale, weight); + } + } + } +} \ No newline at end of file diff --git a/Packages/VRM10/Runtime/Components/Expression/NodeTransformBinding.cs.meta b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBinding.cs.meta new file mode 100644 index 000000000..bb06db17f --- /dev/null +++ b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBinding.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd660f7a77373234c85064f97150eef6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/VRM10/Runtime/Components/Expression/NodeTransformBindingMerger.cs b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBindingMerger.cs new file mode 100644 index 000000000..c972a8437 --- /dev/null +++ b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBindingMerger.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using UniGLTF.Utils; +using UnityEngine; + + +namespace UniVRM10 +{ + internal sealed class NodeTransformBindingMerger + { + IReadOnlyDictionary _clipMap; + Transform _root; + IReadOnlyDictionary _initPose; + Dictionary _weightMap = new(); + + public NodeTransformBindingMerger( + IReadOnlyDictionary clipMap, + Transform root, + IReadOnlyDictionary initPose) + { + _clipMap = clipMap; + _root = root; + _initPose = initPose; + } + + public void AccumulateValue(ExpressionKey key, float value) + { + _weightMap[key] = value; + } + + public void Apply() + { + foreach (var (k, weight) in _weightMap) + { + if (_clipMap.TryGetValue(k, out var clip)) + { + foreach (var b in clip.NodeTransformBindings) + { + var node = _root.GetFromPath(b.RelativePath); + if (node != null) + { + if (_initPose.TryGetValue(node, out var init)) + { + b.Apply(node, init, weight); + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Packages/VRM10/Runtime/Components/Expression/NodeTransformBindingMerger.cs.meta b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBindingMerger.cs.meta new file mode 100644 index 000000000..1e48344b2 --- /dev/null +++ b/Packages/VRM10/Runtime/Components/Expression/NodeTransformBindingMerger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fe2bf5e71647b0f4d93ca6c22bb9fb7f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs b/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs index 2765bd254..5d7a6a28b 100644 --- a/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs +++ b/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs @@ -123,7 +123,9 @@ namespace UniVRM10 { return new PreviewMeshItem(t.RelativePathFrom(root), t, skinnedMeshRenderer.sharedMaterials) { - Mesh = skinnedMeshRenderer.sharedMesh, + SkinnedMeshRenderer = skinnedMeshRenderer, + Mesh = new Mesh(), // for bake + BlendShapeNames = new string[]{}, }; } } diff --git a/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs b/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs index c93504344..30bdbe5a2 100644 --- a/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs +++ b/Packages/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs @@ -3,6 +3,7 @@ using System.Linq; using UnityEngine; using System; using UniGLTF; +using UniGLTF.Utils; namespace UniVRM10 @@ -16,6 +17,8 @@ namespace UniVRM10 public bool hasError; + private IReadOnlyDictionary m_defaultTransformStates; + #if UNITY_EDITOR public static PreviewSceneManager GetOrCreate(GameObject prefab) { @@ -85,7 +88,7 @@ namespace UniVRM10 private void Initialize(GameObject prefab) { hasError = false; - + Prefab = prefab; var materialNames = new List(); @@ -104,14 +107,14 @@ namespace UniVRM10 { dst = new Material(src); map.Add(src, dst); - + if (!PreviewMaterialUtil.TryCreateForPreview(dst, out var previewMaterialItem)) { hasError = true; // Return cloned material for preview return dst; } - + m_materialMap.Add(src.name, previewMaterialItem); materialNames.Add(src.name); @@ -119,6 +122,10 @@ namespace UniVRM10 return dst; }; + // initPose for nodeTransform + m_defaultTransformStates = transform.GetComponentsInChildren() + .ToDictionary(tf => tf, tf => new TransformState(tf)); + m_meshes = transform.Traverse() .Select(x => PreviewMeshItem.Create(x, transform, getOrCreateMaterial)) .Where(x => x != null) @@ -136,8 +143,13 @@ namespace UniVRM10 .Where(x => x.SkinnedMeshRenderer != null) .Select(x => x.Path) .ToArray(); + m_nodeTransformPathList = transform.GetComponentsInChildren() + .Select(x => x.RelativePathFrom(transform)) + .Where(x => x != null) + .ToArray() + ; - if(TryGetComponent(out var animator)) + if (TryGetComponent(out var animator)) { var head = animator.GetBoneTransform(HumanBodyBones.Head); if (head != null) @@ -184,6 +196,12 @@ namespace UniVRM10 get { return m_skinnedMeshRendererPathList; } } + string[] m_nodeTransformPathList; + public string[] NodeTransformPathList + { + get { return m_nodeTransformPathList; } + } + public string[] GetBlendShapeNames(int blendShapeMeshIndex) { if (blendShapeMeshIndex >= 0 && blendShapeMeshIndex < m_blendShapeMeshes.Length) @@ -228,19 +246,28 @@ namespace UniVRM10 return; } + // + // bake NodeTransform + // + foreach (var nodeTransform in bake.NodeTransformBindings) + { + var node = transform.GetFromPath(nodeTransform.RelativePath); + if (m_defaultTransformStates.TryGetValue(node, out var init)) + { + nodeTransform.Apply(node, init, weight); + } + } + // // Bake Expression // m_bounds = default(Bounds); if (m_meshes != null) { - if (bake != null) + foreach (var x in m_meshes) { - foreach (var x in m_meshes) - { - x.Bake(bake.MorphTargetBindings, weight); - m_bounds.Expand(x.Mesh.bounds.size); - } + x.Bake(bake.MorphTargetBindings, weight); + m_bounds.Expand(x.Mesh.bounds.size); } } diff --git a/Packages/VRM10/Runtime/Components/Expression/VRM10Expression.cs b/Packages/VRM10/Runtime/Components/Expression/VRM10Expression.cs index b75473e24..d0fd471d3 100644 --- a/Packages/VRM10/Runtime/Components/Expression/VRM10Expression.cs +++ b/Packages/VRM10/Runtime/Components/Expression/VRM10Expression.cs @@ -46,5 +46,11 @@ namespace UniVRM10 /// [SerializeField] public UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType OverrideMouth; + + /// + /// from UniVRM-132.0. experimental + /// + [SerializeField] + public NodeTransformBinding[] NodeTransformBindings = new NodeTransformBinding[] { }; } } diff --git a/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs b/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs index e702496b1..e22956304 100644 --- a/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs +++ b/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs @@ -77,7 +77,7 @@ namespace UniVRM10 } Constraints = instance.GetComponentsInChildren(); LookAt = new Vrm10RuntimeLookAt(instance, instance.Humanoid, ControlRig); - Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable, isPrefabInstance); + Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable, isPrefabInstance, initPose); SpringBone = springBoneRuntime; } diff --git a/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs b/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs index 0a38b0481..3c13e449f 100644 --- a/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs +++ b/Packages/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using UniGLTF.Utils; +using UnityEngine; namespace UniVRM10 { @@ -23,9 +25,13 @@ namespace UniVRM10 public float LookAtOverrideRate { get; private set; } public float MouthOverrideRate { get; private set; } - internal Vrm10RuntimeExpression(Vrm10Instance target, ILookAtEyeDirectionApplicable eyeDirectionApplicable, bool isPrefabInstance) + internal Vrm10RuntimeExpression(Vrm10Instance target, + ILookAtEyeDirectionApplicable eyeDirectionApplicable, + bool isPrefabInstance, + IReadOnlyDictionary initPose + ) { - _merger = new ExpressionMerger(target.Vrm.Expression, target.transform, isPrefabInstance); + _merger = new ExpressionMerger(target.Vrm.Expression, target.transform, isPrefabInstance, initPose); _keys = target.Vrm.Expression.Clips .Select(x => target.Vrm.Expression.CreateKey(x.Clip)) .ToList(); diff --git a/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform.meta b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform.meta new file mode 100644 index 000000000..e63834052 --- /dev/null +++ b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d80842db26fe76645bf195c10b9c6bf7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Deserializer.g.cs b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Deserializer.g.cs new file mode 100644 index 000000000..dfa8a3469 --- /dev/null +++ b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Deserializer.g.cs @@ -0,0 +1,145 @@ +// This file is generated from JsonSchema. Don't modify this source code. +using UniJSON; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UniGLTF.Extensions.VRMC_vrm_expressions_node_transform { + +public static class GltfDeserializer +{ + public static readonly Utf8String ExtensionNameUtf8 = Utf8String.From(VRMC_vrm_expressions_node_transform.ExtensionName); + +public static bool TryGet(UniGLTF.glTFExtension src, out VRMC_vrm_expressions_node_transform extension) +{ + if(src is UniGLTF.glTFExtensionImport extensions) + { + foreach(var kv in extensions.ObjectItems()) + { + if(kv.Key.GetUtf8String() == ExtensionNameUtf8) + { + extension = Deserialize(kv.Value); + return true; + } + } + } + + extension = default; + return false; +} + + +public static VRMC_vrm_expressions_node_transform Deserialize(JsonNode parsed) +{ + var value = new VRMC_vrm_expressions_node_transform(); + + foreach(var kv in parsed.ObjectItems()) + { + var key = kv.Key.GetString(); + + if(key=="extensions"){ + value.Extensions = new glTFExtensionImport(kv.Value); + continue; + } + + if(key=="extras"){ + value.Extras = new glTFExtensionImport(kv.Value); + continue; + } + + if(key=="nodeTransformBinds"){ + value.NodeTransformBinds = Deserialize_NodeTransformBinds(kv.Value); + continue; + } + + } + return value; +} + +public static List Deserialize_NodeTransformBinds(JsonNode parsed) +{ + var value = new List(); + foreach(var x in parsed.ArrayItems()) + { + value.Add(Deserialize_NodeTransformBinds_ITEM(x)); + } + return value; +} + +public static NodeTransformBind Deserialize_NodeTransformBinds_ITEM(JsonNode parsed) +{ + var value = new NodeTransformBind(); + + foreach(var kv in parsed.ObjectItems()) + { + var key = kv.Key.GetString(); + + if(key=="extensions"){ + value.Extensions = new glTFExtensionImport(kv.Value); + continue; + } + + if(key=="extras"){ + value.Extras = new glTFExtensionImport(kv.Value); + continue; + } + + if(key=="node"){ + value.Node = kv.Value.GetInt32(); + continue; + } + + if(key=="rotation"){ + value.Rotation = __nodeTransformBinds_ITEM_Deserialize_Rotation(kv.Value); + continue; + } + + if(key=="scale"){ + value.Scale = __nodeTransformBinds_ITEM_Deserialize_Scale(kv.Value); + continue; + } + + if(key=="translation"){ + value.Translation = __nodeTransformBinds_ITEM_Deserialize_Translation(kv.Value); + continue; + } + + } + return value; +} + +public static float[] __nodeTransformBinds_ITEM_Deserialize_Rotation(JsonNode parsed) +{ + var value = new float[parsed.GetArrayCount()]; + int i=0; + foreach(var x in parsed.ArrayItems()) + { + value[i++] = x.GetSingle(); + } + return value; +} + +public static float[] __nodeTransformBinds_ITEM_Deserialize_Scale(JsonNode parsed) +{ + var value = new float[parsed.GetArrayCount()]; + int i=0; + foreach(var x in parsed.ArrayItems()) + { + value[i++] = x.GetSingle(); + } + return value; +} + +public static float[] __nodeTransformBinds_ITEM_Deserialize_Translation(JsonNode parsed) +{ + var value = new float[parsed.GetArrayCount()]; + int i=0; + foreach(var x in parsed.ArrayItems()) + { + value[i++] = x.GetSingle(); + } + return value; +} + +} // GltfDeserializer +} // UniGLTF diff --git a/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Deserializer.g.cs.meta b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Deserializer.g.cs.meta new file mode 100644 index 000000000..4d946ab7e --- /dev/null +++ b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Deserializer.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a0544899292fe54e961d98e2f7d755c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Format.g.cs b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Format.g.cs new file mode 100644 index 000000000..48421d4b1 --- /dev/null +++ b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Format.g.cs @@ -0,0 +1,43 @@ +// This file is generated from JsonSchema. Don't modify this source code. +using System; +using System.Collections.Generic; + + +namespace UniGLTF.Extensions.VRMC_vrm_expressions_node_transform +{ + + public class NodeTransformBind + { + // Dictionary object with extension-specific objects. + public object Extensions; + + // Application-specific data. + public object Extras; + + // The node index. + public int? Node; + + // The node's unit quaternion rotation in the order (x, y, z, w), where w is the scalar. + public float[] Rotation; + + // The node's non-uniform scale, given as the scaling factors along the x, y, and z axes. + public float[] Scale; + + // The node's translation along the x, y, and z axes. + public float[] Translation; + } + + public class VRMC_vrm_expressions_node_transform + { + public const string ExtensionName = "VRMC_vrm_expressions_node_transform"; + + // Dictionary object with extension-specific objects. + public object Extensions; + + // Application-specific data. + public object Extras; + + // Specify a node transform + public List NodeTransformBinds; + } +} diff --git a/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Format.g.cs.meta b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Format.g.cs.meta new file mode 100644 index 000000000..311817f13 --- /dev/null +++ b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Format.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f6d0369358084249b383bea969b16a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Serializer.g.cs b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Serializer.g.cs new file mode 100644 index 000000000..a3f38a515 --- /dev/null +++ b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Serializer.g.cs @@ -0,0 +1,141 @@ +// This file is generated from JsonSchema. Don't modify this source code. +using System; +using System.Collections.Generic; +using System.Linq; +using UniJSON; + +namespace UniGLTF.Extensions.VRMC_vrm_expressions_node_transform { + + static public class GltfSerializer + { + + public static void SerializeTo(ref UniGLTF.glTFExtension dst, VRMC_vrm_expressions_node_transform extension) + { + if (dst is glTFExtensionImport) + { + throw new NotImplementedException(); + } + + if (!(dst is glTFExtensionExport extensions)) + { + extensions = new glTFExtensionExport(); + dst = extensions; + } + + var f = new JsonFormatter(); + Serialize(f, extension); + extensions.Add(VRMC_vrm_expressions_node_transform.ExtensionName, f.GetStoreBytes()); + } + + +public static void Serialize(JsonFormatter f, VRMC_vrm_expressions_node_transform value) +{ + f.BeginMap(); + + + if(value.Extensions!=null){ + f.Key("extensions"); + (value.Extensions as glTFExtension).Serialize(f); + } + + if(value.Extras!=null){ + f.Key("extras"); + (value.Extras as glTFExtension).Serialize(f); + } + + if(value.NodeTransformBinds!=null&&value.NodeTransformBinds.Count()>=1){ + f.Key("nodeTransformBinds"); + Serialize_NodeTransformBinds(f, value.NodeTransformBinds); + } + + f.EndMap(); +} + +public static void Serialize_NodeTransformBinds(JsonFormatter f, List value) +{ + f.BeginList(); + + foreach(var item in value) + { + Serialize_NodeTransformBinds_ITEM(f, item); + + } + f.EndList(); +} + +public static void Serialize_NodeTransformBinds_ITEM(JsonFormatter f, NodeTransformBind value) +{ + f.BeginMap(); + + + if(value.Extensions!=null){ + f.Key("extensions"); + (value.Extensions as glTFExtension).Serialize(f); + } + + if(value.Extras!=null){ + f.Key("extras"); + (value.Extras as glTFExtension).Serialize(f); + } + + if(value.Node.HasValue){ + f.Key("node"); + f.Value(value.Node.GetValueOrDefault()); + } + + if(value.Rotation!=null&&value.Rotation.Count()>=4){ + f.Key("rotation"); + __nodeTransformBinds_ITEM_Serialize_Rotation(f, value.Rotation); + } + + if(value.Scale!=null&&value.Scale.Count()>=3){ + f.Key("scale"); + __nodeTransformBinds_ITEM_Serialize_Scale(f, value.Scale); + } + + if(value.Translation!=null&&value.Translation.Count()>=3){ + f.Key("translation"); + __nodeTransformBinds_ITEM_Serialize_Translation(f, value.Translation); + } + + f.EndMap(); +} + +public static void __nodeTransformBinds_ITEM_Serialize_Rotation(JsonFormatter f, float[] value) +{ + f.BeginList(); + + foreach(var item in value) + { + f.Value(item); + + } + f.EndList(); +} + +public static void __nodeTransformBinds_ITEM_Serialize_Scale(JsonFormatter f, float[] value) +{ + f.BeginList(); + + foreach(var item in value) + { + f.Value(item); + + } + f.EndList(); +} + +public static void __nodeTransformBinds_ITEM_Serialize_Translation(JsonFormatter f, float[] value) +{ + f.BeginList(); + + foreach(var item in value) + { + f.Value(item); + + } + f.EndList(); +} + + } // class +} // namespace diff --git a/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Serializer.g.cs.meta b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Serializer.g.cs.meta new file mode 100644 index 000000000..773237b4d --- /dev/null +++ b/Packages/VRM10/Runtime/Format/ExpressionsNodeTransform/Serializer.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16a49328d069d9f47840c104597af4a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs b/Packages/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs index f820abb77..655df84f1 100644 --- a/Packages/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs +++ b/Packages/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs @@ -44,10 +44,10 @@ namespace UniGLTF.Extensions.VRMC_springBone_limit // Application-specific data. public object Extras; - // The phi angle of the spherical limit in radians. If the phi angle is set to π or greater, the angle will be interpreted as π by the implementation. + // The pitch angle of the spherical limit in radians. If the pitch angle is set to π or greater, the angle will be interpreted as π by the implementation. public float? Pitch; - // The theta angle of the spherical limit in radians. If the theta angle is set to π/2 or greater, the angle will be interpreted as π/2 by the implementation. + // The yaw angle of the spherical limit in radians. If the yaw angle is set to π/2 or greater, the angle will be interpreted as π/2 by the implementation. public float? Yaw; // The rotation from the default orientation of the spherical limit. The rotation is represented as a quaternion (x, y, z, w), where w is the scalar. diff --git a/Packages/VRM10/Runtime/IO/ExpressionExtensions.cs b/Packages/VRM10/Runtime/IO/ExpressionExtensions.cs index 847a922d2..8120c52d1 100644 --- a/Packages/VRM10/Runtime/IO/ExpressionExtensions.cs +++ b/Packages/VRM10/Runtime/IO/ExpressionExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using UniGLTF; using UniGLTF.Extensions.VRMC_vrm; +using UniGLTF.Extensions.VRMC_vrm_expressions_node_transform; using UnityEngine; namespace UniVRM10 @@ -60,7 +61,15 @@ namespace UniVRM10 var binding = default(UniVRM10.MaterialUVBinding?); if (material != null) { - var (scale, offset) = UniGLTF.TextureTransform.VerticalFlipScaleOffset(new Vector2(bind.Scale[0], bind.Scale[1]), new Vector2(bind.Offset[0], bind.Offset[1])); + // Default values: scale [1, 1], offset [0, 0] + Vector2 scaleVec = bind.Scale != null && bind.Scale.Length >= 2 + ? new Vector2(bind.Scale[0], bind.Scale[1]) + : new Vector2(1.0f, 1.0f); + Vector2 offsetVec = bind.Offset != null && bind.Offset.Length >= 2 + ? new Vector2(bind.Offset[0], bind.Offset[1]) + : new Vector2(0.0f, 0.0f); + + var (scale, offset) = UniGLTF.TextureTransform.VerticalFlipScaleOffset(scaleVec, offsetVec); try { @@ -78,5 +87,29 @@ namespace UniVRM10 } return binding; } + + public static NodeTransformBinding? Build10(this NodeTransformBind bind, GameObject root, Vrm10Importer importer) + { + if (bind.Node.TryGetValidIndex(importer.Nodes.Count, out var nodeIndex)) + { + var node = importer.Nodes[nodeIndex]; + var relativePath = node.RelativePathFrom(root.transform); + + var t = bind.Translation != null && bind.Translation.Length >= 3 + ? new Vector3(bind.Translation[0], bind.Translation[1], bind.Translation[2]) + : Vector3.zero; + var r = bind.Rotation != null && bind.Rotation.Length >= 4 + ? new Quaternion(bind.Rotation[0], bind.Rotation[1], bind.Rotation[2], bind.Rotation[3]) + : Quaternion.identity; + var s = bind.Scale != null && bind.Scale.Length >= 3 + ? new Vector3(bind.Scale[0], bind.Scale[1], bind.Scale[2]) + : node.transform.localScale; + return new NodeTransformBinding(relativePath, t, r, s); + } + else + { + return default; + } + } } } diff --git a/Packages/VRM10/Runtime/IO/Material/BuiltInRP/Import/Materials/BuiltInVrm10MToonMaterialImporter.cs b/Packages/VRM10/Runtime/IO/Material/BuiltInRP/Import/Materials/BuiltInVrm10MToonMaterialImporter.cs index 6fba92303..1a4f62fec 100644 --- a/Packages/VRM10/Runtime/IO/Material/BuiltInRP/Import/Materials/BuiltInVrm10MToonMaterialImporter.cs +++ b/Packages/VRM10/Runtime/IO/Material/BuiltInRP/Import/Materials/BuiltInVrm10MToonMaterialImporter.cs @@ -44,7 +44,7 @@ namespace UniVRM10 // use material.name, because material name may renamed in GltfParser. matDesc = new MaterialDescriptor( m.name, - Shader.Find(MToon10Meta.UnityShaderName), + Shader, null, Vrm10MToonTextureImporter.EnumerateAllTextures(data, m, mtoon).ToDictionary(tuple => tuple.key, tuple => tuple.Item2.Item2), TryGetAllFloats(m, mtoon).ToDictionary(tuple => tuple.key, tuple => tuple.value), diff --git a/Packages/VRM10/Runtime/IO/Material/URP/Import/Materials/UrpVrm10MToonMaterialImporter.cs b/Packages/VRM10/Runtime/IO/Material/URP/Import/Materials/UrpVrm10MToonMaterialImporter.cs index ada10ec84..24eb37478 100644 --- a/Packages/VRM10/Runtime/IO/Material/URP/Import/Materials/UrpVrm10MToonMaterialImporter.cs +++ b/Packages/VRM10/Runtime/IO/Material/URP/Import/Materials/UrpVrm10MToonMaterialImporter.cs @@ -39,7 +39,7 @@ namespace UniVRM10 // use material.name, because material name may renamed in GltfParser. matDesc = new MaterialDescriptor( m.name, - Shader.Find(MToon10Meta.UnityUrpShaderName), + Shader, null, Vrm10MToonTextureImporter.EnumerateAllTextures(data, m, mtoon).ToDictionary(tuple => tuple.key, tuple => tuple.Item2.Item2), BuiltInVrm10MToonMaterialImporter.TryGetAllFloats(m, mtoon).ToDictionary(tuple => tuple.key, tuple => tuple.value), diff --git a/Packages/VRM10/Runtime/IO/Vrm10Exporter.cs b/Packages/VRM10/Runtime/IO/Vrm10Exporter.cs index a0bf43867..fc6724f5d 100644 --- a/Packages/VRM10/Runtime/IO/Vrm10Exporter.cs +++ b/Packages/VRM10/Runtime/IO/Vrm10Exporter.cs @@ -131,16 +131,16 @@ namespace UniVRM10 } /// - /// revere X + /// reverse X /// /// /// - static float[] ReverseX(Vector3 v) + static float[] ReverseXToFloat3(Vector3 v) { return new float[] { -v.x, v.y, v.z }; } - static float[] ReverseX(Quaternion q) + static float[] ReverseXToFloat4(Quaternion q) { q = UniGLTF.Axes.X.Create().InvertQuaternion(q); return new float[] { q.x, q.y, q.z, q.w }; @@ -330,7 +330,7 @@ namespace UniVRM10 shape.Sphere = new UniGLTF.Extensions.VRMC_springBone.ColliderShapeSphere { Radius = z.Radius, - Offset = ReverseX(z.Offset), + Offset = ReverseXToFloat3(z.Offset), }; break; } @@ -340,8 +340,8 @@ namespace UniVRM10 shape.Capsule = new UniGLTF.Extensions.VRMC_springBone.ColliderShapeCapsule { Radius = z.Radius, - Offset = ReverseX(z.Offset), - Tail = ReverseX(z.Tail), + Offset = ReverseXToFloat3(z.Offset), + Tail = ReverseXToFloat3(z.Tail), }; break; } @@ -352,7 +352,7 @@ namespace UniVRM10 shape.Sphere = new UniGLTF.Extensions.VRMC_springBone.ColliderShapeSphere { Radius = 1000.0f, - Offset = ReverseX(z.Offset - z.TailOrNormal.normalized * DISTANCE), + Offset = ReverseXToFloat3(z.Offset - z.TailOrNormal.normalized * DISTANCE), }; break; } @@ -382,7 +382,7 @@ namespace UniVRM10 shape.Sphere = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeSphere { Radius = z.Radius, - Offset = ReverseX(z.Offset), + Offset = ReverseXToFloat3(z.Offset), }; break; } @@ -392,8 +392,8 @@ namespace UniVRM10 shape.Capsule = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeCapsule { Radius = z.Radius, - Offset = ReverseX(z.Offset), - Tail = ReverseX(z.Tail), + Offset = ReverseXToFloat3(z.Offset), + Tail = ReverseXToFloat3(z.Tail), }; break; } @@ -403,7 +403,7 @@ namespace UniVRM10 shape.Sphere = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeSphere { Radius = z.Radius, - Offset = ReverseX(z.Offset), + Offset = ReverseXToFloat3(z.Offset), Inside = true, }; break; @@ -414,8 +414,8 @@ namespace UniVRM10 shape.Capsule = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeCapsule { Radius = z.Radius, - Offset = ReverseX(z.Offset), - Tail = ReverseX(z.Tail), + Offset = ReverseXToFloat3(z.Offset), + Tail = ReverseXToFloat3(z.Tail), Inside = true, }; break; @@ -425,8 +425,8 @@ namespace UniVRM10 { shape.Plane = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapePlane { - Offset = ReverseX(z.Offset), - Normal = ReverseX(z.Normal), + Offset = ReverseXToFloat3(z.Offset), + Normal = ReverseXToFloat3(z.Normal), }; break; } @@ -447,7 +447,7 @@ namespace UniVRM10 HitRadius = y.m_jointRadius, DragForce = y.m_dragForce, Stiffness = y.m_stiffnessForce, - GravityDir = ReverseX(y.m_gravityDir), + GravityDir = ReverseXToFloat3(y.m_gravityDir), GravityPower = y.m_gravityPower, }; @@ -461,7 +461,7 @@ namespace UniVRM10 { Cone = new UniGLTF.Extensions.VRMC_springBone_limit.ConeLimit { - Rotation = ReverseX(y.m_limitSpaceOffset), + Rotation = ReverseXToFloat4(y.m_limitSpaceOffset), Angle = y.m_pitch, } } @@ -479,7 +479,7 @@ namespace UniVRM10 { Hinge = new UniGLTF.Extensions.VRMC_springBone_limit.HingeLimit { - Rotation = ReverseX(y.m_limitSpaceOffset), + Rotation = ReverseXToFloat4(y.m_limitSpaceOffset), Angle = y.m_pitch, } } @@ -497,7 +497,7 @@ namespace UniVRM10 { Spherical = new UniGLTF.Extensions.VRMC_springBone_limit.SphericalLimit { - Rotation = ReverseX(y.m_limitSpaceOffset), + Rotation = ReverseXToFloat4(y.m_limitSpaceOffset), Pitch = y.m_pitch, Yaw = y.m_yaw, } @@ -779,6 +779,19 @@ namespace UniVRM10 }; } + static UniGLTF.Extensions.VRMC_vrm_expressions_node_transform.NodeTransformBind ExportNodeTransformBinding(NodeTransformBinding binding, Func getIndex) + { + var translation = ReverseXToFloat3(binding.OffsetTranslation); + var rotation = ReverseXToFloat4(binding.OffsetRotation); + return new UniGLTF.Extensions.VRMC_vrm_expressions_node_transform.NodeTransformBind + { + Node = getIndex(binding.RelativePath), + Translation = translation, + Rotation = rotation, + Scale = new float[] { binding.TargetScale.x, binding.TargetScale.y, binding.TargetScale.z }, + }; + } + static UniGLTF.Extensions.VRMC_vrm.Expression ExportExpression(VRM10Expression e, Vrm10Instance vrmController, Model model, ModelExporter converter) { if (e == null) @@ -786,7 +799,7 @@ namespace UniVRM10 return null; } - Func getIndexFromRelativePath = relativePath => + Func getRendererNodeIndexFromRelativePath = relativePath => { var rendererNode = vrmController.transform.GetFromPath(relativePath); var renderer = rendererNode.GetComponent(); @@ -831,7 +844,7 @@ namespace UniVRM10 { try { - var binding = ExportMorphTargetBinding(b, getIndexFromRelativePath); + var binding = ExportMorphTargetBinding(b, getRendererNodeIndexFromRelativePath); if (binding.Node < 0) { // node もしくは renderer が存在しない @@ -867,6 +880,43 @@ namespace UniVRM10 UniGLTFLogger.Warning($"{ex}"); } } + + if (e.NodeTransformBindings != null && e.NodeTransformBindings.Length > 0) + { + Func getNodeIndexFromRelativePath = relativePath => + { + var n = vrmController.transform.GetFromPath(relativePath); + var node = converter.Nodes[n.gameObject]; + return model.Nodes.IndexOf(node); + }; + + var nodeTransform = new UniGLTF.Extensions.VRMC_vrm_expressions_node_transform.VRMC_vrm_expressions_node_transform + { + NodeTransformBinds = new(), + }; + foreach (var b in e.NodeTransformBindings) + { + try + { + var binding = ExportNodeTransformBinding(b, getNodeIndexFromRelativePath); + if (binding.Node < 0) + { + // node もしくは renderer が存在しない + continue; + } + + nodeTransform.NodeTransformBinds.Add(binding); + } + catch (Exception ex) + { + UniGLTFLogger.Warning($"{ex}"); + } + } + glTFExtension extensions = default; + UniGLTF.Extensions.VRMC_vrm_expressions_node_transform.GltfSerializer.SerializeTo(ref extensions, nodeTransform); + vrmExpression.Extensions = extensions; + } + return vrmExpression; } diff --git a/Packages/VRM10/Runtime/IO/Vrm10Importer.cs b/Packages/VRM10/Runtime/IO/Vrm10Importer.cs index e0541a57d..557b51b7d 100644 --- a/Packages/VRM10/Runtime/IO/Vrm10Importer.cs +++ b/Packages/VRM10/Runtime/IO/Vrm10Importer.cs @@ -286,6 +286,25 @@ namespace UniVRM10 clip.MaterialUVBindings = new MaterialUVBinding[] { }; } + if (UniGLTF.Extensions.VRMC_vrm_expressions_node_transform.GltfDeserializer.TryGet( + expression.Extensions as glTFExtension, + out UniGLTF.Extensions.VRMC_vrm_expressions_node_transform.VRMC_vrm_expressions_node_transform nodeTransform)) + { + if (nodeTransform.NodeTransformBinds != null) + { + clip.NodeTransformBindings = nodeTransform.NodeTransformBinds? + .Select(x => x.Build10(Root, this)) + .Where(x => x.HasValue) + .Select(x => x.Value) + .ToArray(); + } + else + { + clip.NodeTransformBindings = new NodeTransformBinding[] { }; + } + + } + m_expressions.Add((preset, clip)); } return clip; diff --git a/Packages/VRM10/Tests/VRM10.Tests.asmdef b/Packages/VRM10/Tests/VRM10.Tests.asmdef index afe244664..2a71a7b8b 100644 --- a/Packages/VRM10/Tests/VRM10.Tests.asmdef +++ b/Packages/VRM10/Tests/VRM10.Tests.asmdef @@ -7,9 +7,9 @@ "GUID:8d76e605759c3f64a957d63ef96ada7c", "GUID:5f875fdc81c40184c8333b9d63c6ddd5", "GUID:f9fe54bb3090be448aa10ac92648a614", - "GUID:da3e51d19d51a544fa14d43fee843098", "GUID:27619889b8ba8c24980f49ee34dbb44a", - "GUID:0acc523941302664db1f4e527237feb3" + "GUID:0acc523941302664db1f4e527237feb3", + "GUID:1cd941934d098654fa21a13f28346412" ], "includePlatforms": [ "Editor" diff --git a/Packages/VRM10/package.json b/Packages/VRM10/package.json index c400650fa..544b77a7a 100644 --- a/Packages/VRM10/package.json +++ b/Packages/VRM10/package.json @@ -1,6 +1,6 @@ { "name": "com.vrmc.vrm", - "version": "0.131.0", + "version": "0.131.1", "displayName": "VRM-1.0", "description": "VRM-1.0 importer", "unity": "2021.3", @@ -15,7 +15,7 @@ }, "dependencies": { "com.unity.timeline": "1.7.6", - "com.vrmc.gltf": "0.131.0" + "com.vrmc.gltf": "0.131.1" }, "samples": [ { diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index c3101490b..545d38a9e 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -163,7 +163,7 @@ "depth": 0, "source": "embedded", "dependencies": { - "com.vrmc.gltf": "0.131.0", + "com.vrmc.gltf": "0.131.1", "com.unity.ugui": "1.0.0" } }, @@ -173,7 +173,7 @@ "source": "embedded", "dependencies": { "com.unity.timeline": "1.7.6", - "com.vrmc.gltf": "0.131.0" + "com.vrmc.gltf": "0.131.1" } }, "com.unity.modules.ai": {