Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blend Shape Importing #247

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ private void DrawSimpleInspector(UsdAsset usdAsset)
usdAsset.m_importCameras = EditorGUILayout.Toggle("Import Cameras", usdAsset.m_importCameras);
usdAsset.m_importMeshes = EditorGUILayout.Toggle("Import Meshes", usdAsset.m_importMeshes);
usdAsset.m_importSkinning = EditorGUILayout.Toggle("Import Skinning", usdAsset.m_importSkinning);
usdAsset.m_importBlendShapes = EditorGUILayout.Toggle("Import BlendShapes", usdAsset.m_importBlendShapes);
usdAsset.m_importTransforms = EditorGUILayout.Toggle("Import Transforms", usdAsset.m_importTransforms);
if (EditorGUI.EndChangeCheck())
EditorUtility.SetDirty(usdAsset);
Expand Down
Binary file modified package/com.unity.formats.usd/Runtime/Plugins/USD.NET.dll
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ [HideInInspector] [Tooltip("When enabled, set the GPU Instancing flag on all mat
public bool m_importCameras = true;
public bool m_importMeshes = true;
public bool m_importSkinning = true;
public bool m_importBlendShapes = true;
public bool m_importTransforms = true;
public bool m_importSceneInstances = true;
public bool m_importPointInstances = true;
Expand Down Expand Up @@ -264,6 +265,7 @@ public void OptionsToState(SceneImportOptions options)
m_importCameras = options.importCameras;
m_importMeshes = options.importMeshes;
m_importSkinning = options.importSkinning;
m_importBlendShapes = options.importBlendShapes;
m_importHierarchy = options.importHierarchy;
m_importTransforms = options.importTransforms;
m_importSceneInstances = options.importSceneInstances;
Expand Down Expand Up @@ -317,6 +319,7 @@ public void StateToOptions(ref SceneImportOptions options)
options.importCameras = m_importCameras;
options.importMeshes = m_importMeshes;
options.importSkinning = m_importSkinning;
options.importBlendShapes = m_importBlendShapes;
options.importHierarchy = m_importHierarchy;
options.importTransforms = m_importTransforms;
options.importSceneInstances = m_importSceneInstances;
Expand Down Expand Up @@ -751,6 +754,7 @@ public static void PrepOptionsForTimeChange(ref SceneImportOptions options)

options.meshOptions.generateLightmapUVs = false;
options.importSkinWeights = false;
options.importBlendShapeTargets = false;

// Note that tangent and Normals must be updated when the mesh deforms.
options.importHierarchy = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public class SceneImportOptions
public bool importMeshes = true;
public bool importSkinning = true;
public bool importSkinWeights = true;
public bool importBlendShapeTargets = true;
public bool importBlendShapes = true;
public bool importTransforms = true;
public bool importSceneInstances = true;
public bool importPointInstances = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ public static IEnumerator BuildScene(Scene scene,
//
// SkinnedMesh bone bindings.
//
if (importOptions.importSkinning)
if (importOptions.importSkinning || importOptions.importBlendShapes)
{
Profiler.BeginSample("USD: Build Skeletons");
var skeletonSamples = new Dictionary<pxr.SdfPath, SkeletonSample>();
Expand Down Expand Up @@ -989,6 +989,7 @@ public static IEnumerator BuildScene(Scene scene,
Profiler.BeginSample("Build Bind Transforms");
var skelPath = skelBinding.GetSkeleton().GetPath();
SkeletonSample skelSample = null;

if (!skeletonSamples.TryGetValue(skelPath, out skelSample))
{
skelSample = new SkeletonSample();
Expand Down Expand Up @@ -1026,43 +1027,87 @@ public static IEnumerator BuildScene(Scene scene,

Profiler.EndSample();

if (importOptions.importSkinWeights)
if (importOptions.importSkinWeights || importOptions.importBlendShapeTargets)
{
//
// Apply skinning weights to each skinned mesh.
//
Profiler.BeginSample("Apply Skin Weights");
Profiler.BeginSample("Apply Skin Weights and Blend Shapes");
foreach (var skinningQuery in skelBinding.GetSkinningTargetsAsVector())
{

pxr.SdfPath meshPath = skinningQuery.GetPrim().GetPath();
try
{
var goMesh = primMap[meshPath];

Profiler.BeginSample("Build Skinned Mesh");
SkeletonImporter.BuildSkinnedMesh(
meshPath,
skelPath,
skelSample,
skinningQuery,
goMesh,
primMap,
importOptions);
Profiler.EndSample();
var goMesh = primMap[meshPath];

// In terms of performance, this is almost free.
// TODO: Check if this is correct or should be something specific (not always the first child).
goMesh.GetComponent<SkinnedMeshRenderer>().rootBone =
primMap[skelPath].transform.GetChild(0);

if (importOptions.importSkinWeights && skinningQuery.HasJointInfluences())
{
try
{

Profiler.BeginSample("Build Skinned Mesh");
SkeletonImporter.BuildSkinnedMesh(
meshPath,
skelPath,
skelSample,
skinningQuery,
goMesh,
primMap,
importOptions);
Profiler.EndSample();

// In terms of performance, this is almost free.
SkinnedMeshRenderer smr = goMesh.GetComponent<SkinnedMeshRenderer>();
smr.sharedMesh.ClearBlendShapes();
// TODO: Check if this is correct or should be something specific (not always the first child).
smr.rootBone = primMap[skelPath].transform.GetChild(0);
}
catch (System.Exception ex)
{
Debug.LogException(new ImportException("Error skinning mesh: " + meshPath,
ex));
}
}
catch (System.Exception ex)

if (importOptions.importBlendShapeTargets && skinningQuery.HasBlendShapes())
{
Debug.LogException(new ImportException("Error skinning mesh: " + meshPath, ex));
try
{
Profiler.BeginSample("Add Blend Shapes");
BlendShapeImporter.BuildBlendShapeTargets(
meshPath,
goMesh,
scene,
skinningQuery,
importOptions);
Profiler.EndSample();
}
catch (System.Exception ex)
{
Debug.LogException(new ImportException($"Error adding blend shapes: {meshPath}", ex));
}
}
}


Profiler.EndSample();
}

Profiler.BeginSample("Apply Blend Shape Weights");
if (importOptions.importBlendShapes)
{
foreach (var skinningQuery in skelBinding.GetSkinningTargetsAsVector())
{
if (skinningQuery.HasBlendShapes())
{
var go = primMap[skinningQuery.GetPrim().GetPath()];
Profiler.BeginSample("Build Blend Shape Weights");
BlendShapeImporter.BuildBlendShapeWeights(go, scene, skinningQuery);
Profiler.EndSample();
}
}
}
Profiler.EndSample();
}
}
catch (System.Exception ex)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Net;
using pxr;
using UnityEngine;
using UnityEngine.Profiling;
using USD.NET;
using USD.NET.Unity;

namespace Unity.Formats.USD
{
public static class BlendShapeImporter
{
public static void BuildBlendShapeTargets(
string meshPath,
GameObject go,
Scene scene,
UsdSkelSkinningQuery skinningQuery,
SceneImportOptions options
)
{
if (!skinningQuery.HasBlendShapes())
{
return;
}

var smr = go.GetComponent<SkinnedMeshRenderer>();
if (!smr)
{
throw new Exception(
$"Error importing {meshPath} SkinnedMeshRenderer not present on GameObject"
);
}

var mesh = smr.sharedMesh;
mesh.ClearBlendShapes();

Dictionary<string, BlendShapeSample> blendShapeSamples = new Dictionary<string, BlendShapeSample>();
var skelBindingApi = new UsdSkelBindingAPI(skinningQuery.GetPrim());
var blendShapeQuery = new UsdSkelBlendShapeQuery(skelBindingApi);

for (uint i = 0; i < blendShapeQuery.GetNumBlendShapes(); ++i)
{
BlendShapeSample blendShapeSample = new BlendShapeSample();
var blendShape = blendShapeQuery.GetBlendShape(i).GetPrim();
scene.Read(blendShape.GetPath(), blendShapeSample);
blendShapeSamples[blendShape.GetName()] = blendShapeSample;
}

bool changeHandedness = options.changeHandedness == BasisTransformation.SlowAndSafe ||
options.changeHandedness == BasisTransformation.SlowAndSafeAsFBX;

VtTokenArray blendShapeOrder = new VtTokenArray();
skinningQuery.GetBlendShapeOrder(blendShapeOrder);

Profiler.BeginSample("Add Blend Shape Offsets");
for (int i = 0; i < blendShapeQuery.GetNumBlendShapes(); ++i)
{
var blendShapeName = blendShapeOrder[i].ToString();
var blendShapeIndices = blendShapeSamples[blendShapeName].pointIndices;
var blendShapeOffsets = blendShapeSamples[blendShapeName].offsets;
var blendShapeNormalOffsets = blendShapeSamples[blendShapeName].normalOffsets;

if (blendShapeIndices != null && blendShapeIndices?.Length != mesh.vertexCount)
{
Vector3[] offsets = new Vector3[mesh.vertexCount];
Vector3[] normalOffsets = new Vector3[mesh.vertexCount];
for (int j = 0; j < blendShapeIndices?.Length; ++j)
{
int blendIndex = blendShapeIndices[j];
offsets[blendIndex] = blendShapeOffsets[j];
normalOffsets[blendIndex] = blendShapeNormalOffsets[j];
if (changeHandedness)
{
offsets[blendIndex] = UnityTypeConverter.ChangeBasis(offsets[blendIndex]);
normalOffsets[blendIndex] = UnityTypeConverter.ChangeBasis(normalOffsets[blendIndex]);
}
}
mesh.AddBlendShapeFrame(blendShapeName, 100f,
offsets, normalOffsets, null);
}
else
{
if (changeHandedness)
{
Vector3[] offsets = new Vector3[mesh.vertexCount];
Vector3[] normalOffsets = new Vector3[mesh.vertexCount];
for (int j = 0; j < mesh.vertexCount; ++j)
{
offsets[j] = UnityTypeConverter.ChangeBasis(blendShapeOffsets[j]);
normalOffsets[j] = UnityTypeConverter.ChangeBasis(blendShapeNormalOffsets[j]);
}
mesh.AddBlendShapeFrame(blendShapeName, 100f,
offsets, normalOffsets, null);
}
else
{
mesh.AddBlendShapeFrame(blendShapeName, 100f,
blendShapeOffsets, blendShapeNormalOffsets, null);
}
}
}
Profiler.EndSample();
}

public static void BuildBlendShapeWeights(
GameObject go,
Scene scene,
UsdSkelSkinningQuery skinningQuery
)
{
Profiler.BeginSample("Get Skeleton");
var skelBindingApi = new UsdSkelBindingAPI(skinningQuery.GetPrim());
var skeletonTargets = skelBindingApi.GetSkeletonRel().GetForwardedTargets();
Profiler.EndSample();
if (skeletonTargets.Count == 0)
{
return;
}
Profiler.BeginSample("Get Animation Target");
skelBindingApi = new UsdSkelBindingAPI(scene.GetPrimAtPath(skeletonTargets[0]));
var animTargets = skelBindingApi.GetAnimationSourceRel().GetForwardedTargets();
Profiler.EndSample();
if (animTargets.Count == 0)
{
return;
}

Profiler.BeginSample("Get SkelAnim");
var skelAnimTarget = scene.GetPrimAtPath(animTargets[0]);
var skelAnimation = new UsdSkelAnimation(skelAnimTarget);
var skelAnimSample = new SkelAnimationSample();
Profiler.EndSample();

Profiler.BeginSample("Read Animation Sample");
scene.Read(skelAnimation.GetPath(), skelAnimSample);
Profiler.EndSample();

Profiler.BeginSample("Get Skinned Mesh");
var smr = go.GetComponent<SkinnedMeshRenderer>();
if (!smr)
{
throw new Exception(
$"Error settingWeights on {skinningQuery.GetPrim().GetPath()} SkinnedMeshRenderer not present on GameObject"
);
}
Profiler.EndSample();

Profiler.BeginSample("Set Blend Shape Weights");
for (int i = 0; i < skelAnimSample.blendShapeWeights.Length; ++i)
{
smr.SetBlendShapeWeight(i, skelAnimSample.blendShapeWeights[i] * 100f);
}
Profiler.EndSample();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,16 @@ public class SkelAnimationSample : SampleBase

[UsdVariability(Variability.Uniform)] public string[] blendShapes;

[UsdVariability(Variability.Uniform)] public float[] blendShapeWeights;
public float[] blendShapeWeights;
}

[System.Serializable]
[UsdSchema("BlendShape")]
public class BlendShapeSample : SampleBase
{
public Vector3[] offsets;
public uint[] pointIndices;
[UsdVariability(Variability.Uniform)] public Vector3[] offsets;
[UsdVariability(Variability.Uniform)] public Vector3[] normalOffsets;
[UsdVariability(Variability.Uniform)] public int[] pointIndices;
}

public class SkeletonIo
Expand Down
1 change: 1 addition & 0 deletions src/Swig/pxr/usd/usdSkel/usdSkel.i
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
%include "usdSkelSkeleton.i"
%include "usdSkelSkeletonQuery.i"
%include "usdSkelSkinningQuery.i"
%include "usdSkelBlendShapeQuery.i"
%include "usdSkelTopology.i"
%include "usdSkelUtils.i"

Expand Down
10 changes: 10 additions & 0 deletions src/Swig/pxr/usd/usdSkel/usdSkelBlendShapeQuery.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
%module UsdSkelSkinningQuery
%{
#include "pxr/usd/usdSkel/blendShapeQuery.h"
#include "pxr/base/vt/array.h"
%}

// TODO: Need type AnimMapperRefPtr.
%ignore UsdSkelBlendShapeQuery::GetMapper;

%include "pxr/usd/usdSkel/blendShapeQuery.h"
Loading