From 66790404344f241bbc37cd6b18b6e7f262ae7a0d Mon Sep 17 00:00:00 2001
From: 0b5vr <0b5vr@0b5vr.com>
Date: Fri, 26 Dec 2025 17:37:55 +0900
Subject: [PATCH 1/3] perf: Align the implementation of fromToQuaternion with
the pseudocode in the spec
Since `from` is always `(0, 1, 0)`, we can simplify the logic to reduce computational overhead
When dot is approximately -1, we use `(1, 0, 0; 0)` as the spec specifies
See: https://github.com/0b5vr/vrm-specification/blob/75fbd48a7cb1d7250fa955838af6140e9c84844c/specification/VRMC_springBone_limit-1.0/README.ja.md#rotation-1
---
.../SpringBoneJobs/Anglelimit/Anglelimit.cs | 35 +++++++++----------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs b/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs
index 178ab3d23..145a37469 100644
--- a/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs
+++ b/Packages/UniGLTF/Runtime/SpringBoneJobs/Anglelimit/Anglelimit.cs
@@ -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.normalize(new quaternion(to.z, 0f, -to.x, dot1));
}
-
}
}
\ No newline at end of file
From 1f2570a41c232d0631a48db6f6c363cf5e2c3c1c Mon Sep 17 00:00:00 2001
From: tdw46
Date: Mon, 29 Dec 2025 22:28:31 -0500
Subject: [PATCH 2/3] =?UTF-8?q?Fix:=20blendshape=20normals=20import=20(Uni?=
=?UTF-8?q?GLTF/UniVRM)=20=20=20-=20Properly=20decode=20morph=20target=20V?=
=?UTF-8?q?EC3=20accessors=20(normalized=20BYTE/SHORT,=20etc.)=20for=20POS?=
=?UTF-8?q?ITION/NORMAL/TANGENT=20=20=20-=20Preserve=20imported=20morph=20?=
=?UTF-8?q?normal=20deltas=20(remove=20heuristic=20recompute)=20=20=20-=20?=
=?UTF-8?q?Add=20intermediate=20frames=20for=20normal-only=20targets=20to?=
=?UTF-8?q?=20stabilize=20Unity=E2=80=99s=20normal=20interpolation=20=20?=
=?UTF-8?q?=20-=20Copy=20all=20blendshape=20frames=20when=20duplicating=20?=
=?UTF-8?q?meshes=20so=20extra=20frames=20are=20retained?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Runtime/MeshUtility/MeshExtensions.cs | 24 +--
.../Runtime/UniGLTF/IO/MeshIO/MeshData.cs | 146 +++++++++++++++++-
.../Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs | 90 +++++++++--
Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs | 25 +++
.../UniGLTF/Runtime/UniGLTF/IO/SByte3.cs.meta | 2 +
Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs | 25 +++
.../UniGLTF/Runtime/UniGLTF/IO/Short3.cs.meta | 2 +
7 files changed, 285 insertions(+), 29 deletions(-)
create mode 100644 Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs
create mode 100644 Packages/UniGLTF/Runtime/UniGLTF/IO/SByte3.cs.meta
create mode 100644 Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs
create mode 100644 Packages/UniGLTF/Runtime/UniGLTF/IO/Short3.cs.meta
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/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..e276c7847 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;
@@ -10,6 +9,36 @@ namespace UniGLTF
internal static class MeshUploader
{
private const float FrameWeight = 100.0f;
+ private const float EpsilonSqr = 1e-16f;
+
+ private static bool HasAnyNonZero(Vector3[] delta)
+ {
+ if (delta == null) return false;
+ for (int i = 0; i < delta.Length; i++)
+ {
+ if (delta[i].sqrMagnitude > EpsilonSqr) return true;
+ }
+ return false;
+ }
+
+ private static Vector3[] CalcDeltaNormalsForWeight(
+ Vector3[] baseNormals,
+ Vector3[] deltaNormalsAt100,
+ float weight01)
+ {
+ var delta = new Vector3[baseNormals.Length];
+ for (int i = 0; i < baseNormals.Length; i++)
+ {
+ var n = baseNormals[i] + deltaNormalsAt100[i] * weight01;
+ var sqr = n.sqrMagnitude;
+ if (sqr > float.Epsilon)
+ {
+ n *= 1.0f / Mathf.Sqrt(sqr);
+ }
+ delta[i] = n - baseNormals[i];
+ }
+ return delta;
+ }
///
/// 頂点情報をMeshに対して送る
@@ -41,28 +70,59 @@ namespace UniGLTF
}
}
- private static async Task BuildBlendShapeAsync(IAwaitCaller awaitCaller, Mesh mesh, BlendShape blendShape,
- Vector3[] emptyVertices)
+ private static async Task BuildBlendShapeAsync(
+ IAwaitCaller awaitCaller,
+ Mesh mesh,
+ BlendShape blendShape,
+ Vector3[] emptyVertices,
+ Vector3[] baseNormals)
{
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;
+
+ // Unity blendshape normal interpolation can look slightly off when vertex deltas are all-zero
+ // (normal-only targets). Add a few intermediate frames with renormalized normals to keep the
+ // interpolation closer to the intended (unit-length) normals across weights.
+ if (hasNormals && !HasAnyNonZero(positions) && HasAnyNonZero(normals))
+ {
+ foreach (var frameWeight in new[] { 25.0f, 50.0f, 75.0f })
+ {
+ var deltaNormals = CalcDeltaNormalsForWeight(baseNormals, normals, frameWeight / 100.0f);
+ mesh.AddBlendShapeFrame(blendShape.Name, frameWeight,
+ emptyVertices,
+ deltaNormals,
+ null
+ );
+ }
+
+ mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight,
+ emptyVertices,
+ normals,
+ null
+ );
+
+ Profiler.EndSample();
+ return;
+ }
+
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 +136,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
);
}
@@ -129,10 +189,16 @@ namespace UniGLTF
if (data.BlendShapes.Count > 0)
{
+ var baseNormals = mesh.normals;
var emptyVertices = new Vector3[mesh.vertexCount];
foreach (var blendShape in data.BlendShapes)
{
- await BuildBlendShapeAsync(awaitCaller, mesh, blendShape, emptyVertices);
+ await BuildBlendShapeAsync(
+ awaitCaller,
+ mesh,
+ blendShape,
+ emptyVertices,
+ baseNormals);
}
}
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
From 371ceeff86506bb886e1f526c8db9c1f907b7079 Mon Sep 17 00:00:00 2001
From: tdw46
Date: Tue, 30 Dec 2025 11:04:27 -0500
Subject: [PATCH 3/3] Remove interpolation steps for blendshape normals as they
were not necessary.
---
.../Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs | 62 +------------------
1 file changed, 2 insertions(+), 60 deletions(-)
diff --git a/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs b/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs
index e276c7847..1a7aab51c 100644
--- a/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs
+++ b/Packages/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs
@@ -9,36 +9,6 @@ namespace UniGLTF
internal static class MeshUploader
{
private const float FrameWeight = 100.0f;
- private const float EpsilonSqr = 1e-16f;
-
- private static bool HasAnyNonZero(Vector3[] delta)
- {
- if (delta == null) return false;
- for (int i = 0; i < delta.Length; i++)
- {
- if (delta[i].sqrMagnitude > EpsilonSqr) return true;
- }
- return false;
- }
-
- private static Vector3[] CalcDeltaNormalsForWeight(
- Vector3[] baseNormals,
- Vector3[] deltaNormalsAt100,
- float weight01)
- {
- var delta = new Vector3[baseNormals.Length];
- for (int i = 0; i < baseNormals.Length; i++)
- {
- var n = baseNormals[i] + deltaNormalsAt100[i] * weight01;
- var sqr = n.sqrMagnitude;
- if (sqr > float.Epsilon)
- {
- n *= 1.0f / Mathf.Sqrt(sqr);
- }
- delta[i] = n - baseNormals[i];
- }
- return delta;
- }
///
/// 頂点情報をMeshに対して送る
@@ -74,8 +44,7 @@ namespace UniGLTF
IAwaitCaller awaitCaller,
Mesh mesh,
BlendShape blendShape,
- Vector3[] emptyVertices,
- Vector3[] baseNormals)
+ Vector3[] emptyVertices)
{
Vector3[] positions = null;
Vector3[] normals = null;
@@ -89,31 +58,6 @@ namespace UniGLTF
var hasPositions = positions.Length == mesh.vertexCount;
var hasNormals = normals.Length == mesh.vertexCount;
- // Unity blendshape normal interpolation can look slightly off when vertex deltas are all-zero
- // (normal-only targets). Add a few intermediate frames with renormalized normals to keep the
- // interpolation closer to the intended (unit-length) normals across weights.
- if (hasNormals && !HasAnyNonZero(positions) && HasAnyNonZero(normals))
- {
- foreach (var frameWeight in new[] { 25.0f, 50.0f, 75.0f })
- {
- var deltaNormals = CalcDeltaNormalsForWeight(baseNormals, normals, frameWeight / 100.0f);
- mesh.AddBlendShapeFrame(blendShape.Name, frameWeight,
- emptyVertices,
- deltaNormals,
- null
- );
- }
-
- mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight,
- emptyVertices,
- normals,
- null
- );
-
- Profiler.EndSample();
- return;
- }
-
if (positions.Length > 0)
{
if (hasPositions)
@@ -189,7 +133,6 @@ namespace UniGLTF
if (data.BlendShapes.Count > 0)
{
- var baseNormals = mesh.normals;
var emptyVertices = new Vector3[mesh.vertexCount];
foreach (var blendShape in data.BlendShapes)
{
@@ -197,8 +140,7 @@ namespace UniGLTF
awaitCaller,
mesh,
blendShape,
- emptyVertices,
- baseNormals);
+ emptyVertices);
}
}