+| | | 1 | | // Copyright 2018 Jeremy Cowles. All rights reserved. |
+| | | 2 | | // |
+| | | 3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
+| | | 4 | | // you may not use this file except in compliance with the License. |
+| | | 5 | | // You may obtain a copy of the License at |
+| | | 6 | | // |
+| | | 7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
+| | | 8 | | // |
+| | | 9 | | // Unless required by applicable law or agreed to in writing, software |
+| | | 10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
+| | | 11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| | | 12 | | // See the License for the specific language governing permissions and |
+| | | 13 | | // limitations under the License. |
+| | | 14 | | |
+| | | 15 | | using System.Collections; |
+| | | 16 | | using System.Collections.Generic; |
+| | | 17 | | using UnityEngine; |
+| | | 18 | | using UnityEngine.Profiling; |
+| | | 19 | | using USD.NET; |
+| | | 20 | | using USD.NET.Unity; |
+| | | 21 | | using Unity.Jobs; |
+| | | 22 | | #if UNITY_EDITOR |
+| | | 23 | | using UnityEditor; |
+| | | 24 | | using System.IO; |
+| | | 25 | | |
+| | | 26 | | #endif |
+| | | 27 | | |
+| | | 28 | | namespace Unity.Formats.USD |
+| | | 29 | | { |
+| | | 30 | | /// <summary> |
+| | | 31 | | /// An interface for delegating import behavior to a third party. |
+| | | 32 | | /// </summary> |
+| | | 33 | | public interface IImporter |
+| | | 34 | | { |
+| | | 35 | | void BeginReading(Scene scene, PrimMap primMap, SceneImportOptions importOptions); |
+| | | 36 | | |
+| | | 37 | | IEnumerator Import(Scene scene, |
+| | | 38 | | PrimMap primMap, |
+| | | 39 | | SceneImportOptions importOptions); |
+| | | 40 | | } |
+| | | 41 | | |
+| | | 42 | | /// <summary> |
+| | | 43 | | /// Root entry point for importing an entire USD scene. |
+| | | 44 | | /// </summary> |
+| | | 45 | | public static class SceneImporter |
+| | | 46 | | { |
+| | | 47 | | public class ImportException : System.Exception |
+| | | 48 | | { |
+| | 0 | 49 | | public ImportException() : base() |
+| | 0 | 50 | | { |
+| | 0 | 51 | | } |
+| | | 52 | | |
+| | 0 | 53 | | public ImportException(string message) : base(message) |
+| | 0 | 54 | | { |
+| | 0 | 55 | | } |
+| | | 56 | | |
+| | | 57 | | public ImportException(string message, System.Exception innerException) |
+| | 0 | 58 | | : base(message, innerException) |
+| | 0 | 59 | | { |
+| | 0 | 60 | | } |
+| | | 61 | | } |
+| | | 62 | | |
+| | | 63 | | /// <summary> |
+| | | 64 | | /// The active mesh importer to be used when ImportUsd is called. |
+| | | 65 | | /// </summary> |
+| | | 66 | | public static IImporter ActiveMeshImporter; |
+| | | 67 | | |
+| | | 68 | | static SceneImporter() |
+| | 2 | 69 | | { |
+| | 2 | 70 | | SceneImporter.ActiveMeshImporter = new MeshImportStrategy( |
+| | | 71 | | MeshImporter.BuildMesh, |
+| | | 72 | | MeshImporter.BuildSkinnedMesh); |
+| | 2 | 73 | | } |
+| | | 74 | | |
+| | | 75 | | #if UNITY_EDITOR |
+| | | 76 | | /// <summary> |
+| | | 77 | | /// Custom importer. This works almost exactly as the ScriptedImporter, but does not require |
+| | | 78 | | /// the new API. |
+| | | 79 | | /// </summary> |
+| | | 80 | | public static void SavePrefab(GameObject rootObject, |
+| | | 81 | | string prefabPath, |
+| | | 82 | | string playableClipName, |
+| | | 83 | | SceneImportOptions importOptions) |
+| | 4 | 84 | | { |
+| | 4 | 85 | | Directory.CreateDirectory(Path.GetDirectoryName(prefabPath)); |
+| | | 86 | | |
+| | 4 | 87 | | GameObject oldPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath); |
+| | 4 | 88 | | GameObject prefab = null; |
+| | | 89 | | |
+| | 4 | 90 | | if (oldPrefab == null) |
+| | 4 | 91 | | { |
+| | | 92 | | // Create the prefab. At this point, the meshes do not yet exist and will be |
+| | | 93 | | // dangling references |
+| | 4 | 94 | | prefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); |
+| | | 95 | | HashSet<Mesh> meshes; |
+| | | 96 | | HashSet<Material> materials; |
+| | 4 | 97 | | AddObjectsToAsset(rootObject, prefab, importOptions, out meshes, out materials); |
+| | | 98 | | |
+| | 16 | 99 | | foreach (var mesh in meshes) |
+| | 2 | 100 | | { |
+| | 2 | 101 | | AssetDatabase.AddObjectToAsset(mesh, prefab); |
+| | 2 | 102 | | } |
+| | | 103 | | |
+| | 40 | 104 | | foreach (var mat in materials) |
+| | 14 | 105 | | { |
+| | 14 | 106 | | AssetDatabase.AddObjectToAsset(mat, prefab); |
+| | 14 | 107 | | } |
+| | | 108 | | |
+| | | 109 | | // Fix the dangling references. |
+| | 4 | 110 | | prefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); |
+| | 4 | 111 | | var playable = ScriptableObject.CreateInstance<UsdPlayableAsset>(); |
+| | | 112 | | |
+| | 4 | 113 | | playable.SourceUsdAsset.defaultValue = prefab.GetComponent<UsdAsset>(); |
+| | 4 | 114 | | playable.name = playableClipName; |
+| | 4 | 115 | | AssetDatabase.AddObjectToAsset(playable, prefab); |
+| | 4 | 116 | | prefab = PrefabUtility.SavePrefabAsset(prefab); |
+| | 4 | 117 | | } |
+| | | 118 | | else |
+| | 0 | 119 | | { |
+| | | 120 | | HashSet<Mesh> meshes; |
+| | | 121 | | HashSet<Material> materials; |
+| | | 122 | | |
+| | 0 | 123 | | oldPrefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); |
+| | 0 | 124 | | AddObjectsToAsset(rootObject, oldPrefab, importOptions, out meshes, out materials); |
+| | | 125 | | |
+| | | 126 | | // ReplacePrefab only removes the GameObjects from the asset. |
+| | | 127 | | // Clear out all non-prefab junk (ie, meshes), because otherwise it piles up. |
+| | | 128 | | // The main difference between LoadAllAssetRepresentations and LoadAllAssets |
+| | | 129 | | // is that the former returns MonoBehaviours and the latter does not. |
+| | 0 | 130 | | foreach (var obj in AssetDatabase.LoadAllAssetRepresentationsAtPath(prefabPath)) |
+| | 0 | 131 | | { |
+| | 0 | 132 | | if (obj is GameObject) |
+| | 0 | 133 | | { |
+| | 0 | 134 | | continue; |
+| | | 135 | | } |
+| | | 136 | | |
+| | 0 | 137 | | if (obj is Mesh && meshes.Contains((Mesh)obj)) |
+| | 0 | 138 | | { |
+| | 0 | 139 | | meshes.Remove((Mesh)obj); |
+| | 0 | 140 | | continue; |
+| | | 141 | | } |
+| | | 142 | | |
+| | 0 | 143 | | if (obj is Material && materials.Contains((Material)obj)) |
+| | 0 | 144 | | { |
+| | 0 | 145 | | materials.Remove((Material)obj); |
+| | 0 | 146 | | continue; |
+| | | 147 | | } |
+| | | 148 | | |
+| | 0 | 149 | | Object.DestroyImmediate(obj, allowDestroyingAssets: true); |
+| | 0 | 150 | | } |
+| | | 151 | | |
+| | 0 | 152 | | foreach (var mesh in meshes) |
+| | 0 | 153 | | { |
+| | 0 | 154 | | AssetDatabase.AddObjectToAsset(mesh, oldPrefab); |
+| | 0 | 155 | | } |
+| | | 156 | | |
+| | 0 | 157 | | foreach (var mat in materials) |
+| | 0 | 158 | | { |
+| | 0 | 159 | | AssetDatabase.AddObjectToAsset(mat, oldPrefab); |
+| | 0 | 160 | | } |
+| | | 161 | | |
+| | 0 | 162 | | prefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); |
+| | | 163 | | |
+| | 0 | 164 | | var playable = ScriptableObject.CreateInstance<UsdPlayableAsset>(); |
+| | 0 | 165 | | playable.SourceUsdAsset.defaultValue = prefab.GetComponent<UsdAsset>(); |
+| | 0 | 166 | | playable.name = playableClipName; |
+| | 0 | 167 | | AssetDatabase.AddObjectToAsset(playable, prefab); |
+| | 0 | 168 | | PrefabUtility.SavePrefabAsset(prefab); |
+| | 0 | 169 | | } |
+| | | 170 | | |
+| | 4 | 171 | | AssetDatabase.ImportAsset(prefabPath, ImportAssetOptions.ForceUpdate); |
+| | 4 | 172 | | AssetDatabase.SaveAssets(); |
+| | 4 | 173 | | } |
+| | | 174 | | |
+| | | 175 | | static void AddObjectsToAsset(GameObject rootObject, |
+| | | 176 | | Object asset, |
+| | | 177 | | SceneImportOptions importOptions, |
+| | | 178 | | out HashSet<Mesh> usedMeshes, |
+| | | 179 | | out HashSet<Material> usedMaterials) |
+| | 4 | 180 | | { |
+| | 4 | 181 | | var meshes = new HashSet<Mesh>(); |
+| | 4 | 182 | | var materials = new HashSet<Material>(); |
+| | | 183 | | |
+| | 4 | 184 | | materials.Add(importOptions.materialMap.DisplayColorMaterial); |
+| | 4 | 185 | | materials.Add(importOptions.materialMap.MetallicWorkflowMaterial); |
+| | 4 | 186 | | materials.Add(importOptions.materialMap.SpecularWorkflowMaterial); |
+| | | 187 | | |
+| | 4 | 188 | | var tempMat = importOptions.materialMap.DisplayColorMaterial; |
+| | 4 | 189 | | if (tempMat != null && AssetDatabase.GetAssetPath(tempMat) == "") |
+| | 4 | 190 | | { |
+| | 4 | 191 | | materials.Add(tempMat); |
+| | 4 | 192 | | } |
+| | | 193 | | |
+| | 4 | 194 | | tempMat = importOptions.materialMap.MetallicWorkflowMaterial; |
+| | 4 | 195 | | if (tempMat != null && AssetDatabase.GetAssetPath(tempMat) == "") |
+| | 4 | 196 | | { |
+| | 4 | 197 | | materials.Add(tempMat); |
+| | 4 | 198 | | } |
+| | | 199 | | |
+| | 4 | 200 | | tempMat = importOptions.materialMap.SpecularWorkflowMaterial; |
+| | 4 | 201 | | if (tempMat != null && AssetDatabase.GetAssetPath(tempMat) == "") |
+| | 4 | 202 | | { |
+| | 4 | 203 | | materials.Add(tempMat); |
+| | 4 | 204 | | } |
+| | | 205 | | |
+| | 16 | 206 | | foreach (var mf in rootObject.GetComponentsInChildren<MeshFilter>()) |
+| | 2 | 207 | | { |
+| | 2 | 208 | | if (!mf) |
+| | 0 | 209 | | { |
+| | 0 | 210 | | continue; |
+| | | 211 | | } |
+| | | 212 | | |
+| | 2 | 213 | | if (mf.sharedMesh != null && meshes.Add(mf.sharedMesh)) |
+| | 2 | 214 | | { |
+| | 2 | 215 | | mf.sharedMesh.name = mf.name; |
+| | 2 | 216 | | } |
+| | 2 | 217 | | } |
+| | | 218 | | |
+| | 16 | 219 | | foreach (var mf in rootObject.GetComponentsInChildren<MeshRenderer>()) |
+| | 2 | 220 | | { |
+| | 2 | 221 | | if (!mf) |
+| | 0 | 222 | | { |
+| | 0 | 223 | | continue; |
+| | | 224 | | } |
+| | | 225 | | |
+| | 10 | 226 | | foreach (var mat in mf.sharedMaterials) |
+| | 2 | 227 | | { |
+| | 2 | 228 | | if (mat != null && !materials.Add(mat)) |
+| | 0 | 229 | | { |
+| | 0 | 230 | | mat.name = mf.name; |
+| | 0 | 231 | | continue; |
+| | | 232 | | } |
+| | 2 | 233 | | } |
+| | 2 | 234 | | } |
+| | | 235 | | |
+| | 12 | 236 | | foreach (var mf in rootObject.GetComponentsInChildren<SkinnedMeshRenderer>()) |
+| | 0 | 237 | | { |
+| | 0 | 238 | | if (!mf) |
+| | 0 | 239 | | { |
+| | 0 | 240 | | continue; |
+| | | 241 | | } |
+| | | 242 | | |
+| | 0 | 243 | | if (mf.sharedMesh != null && meshes.Add(mf.sharedMesh)) |
+| | 0 | 244 | | { |
+| | 0 | 245 | | mf.sharedMesh.name = mf.name; |
+| | 0 | 246 | | } |
+| | | 247 | | |
+| | 0 | 248 | | foreach (var mat in mf.sharedMaterials) |
+| | 0 | 249 | | { |
+| | 0 | 250 | | if (mat != null && !materials.Add(mat)) |
+| | 0 | 251 | | { |
+| | 0 | 252 | | mat.name = mf.name; |
+| | 0 | 253 | | continue; |
+| | | 254 | | } |
+| | 0 | 255 | | } |
+| | 0 | 256 | | } |
+| | | 257 | | |
+| | 4 | 258 | | usedMeshes = meshes; |
+| | 4 | 259 | | usedMaterials = materials; |
+| | 4 | 260 | | } |
+| | | 261 | | |
+| | | 262 | | #endif |
+| | | 263 | | |
+| | | 264 | | public static void ImportUsd(GameObject goRoot, |
+| | | 265 | | Scene scene, |
+| | | 266 | | PrimMap primMap, |
+| | | 267 | | SceneImportOptions importOptions) |
+| | 70 | 268 | | { |
+| | 70 | 269 | | ImportUsd(goRoot, scene, primMap, false, importOptions); |
+| | 70 | 270 | | } |
+| | | 271 | | |
+| | | 272 | | public static void ImportUsd(GameObject goRoot, |
+| | | 273 | | Scene scene, |
+| | | 274 | | PrimMap primMap, |
+| | | 275 | | bool composingSubtree, |
+| | | 276 | | SceneImportOptions importOptions) |
+| | 73 | 277 | | { |
+| | 73 | 278 | | if (scene == null) |
+| | 0 | 279 | | { |
+| | 0 | 280 | | throw new ImportException("Null USD Scene"); |
+| | | 281 | | } |
+| | | 282 | | |
+| | | 283 | | // The matrix to convert USD (right-handed) to Unity (left-handed) is different for the legacy FBX importer |
+| | | 284 | | // and incorrectly swaps the X-axis rather than the Z-axis. This changes the basisChange matrix to match the |
+| | | 285 | | // user options. <see cref="Unity.Formats.USD.SceneImportOptions.BasisTransformation"/> for additional detai |
+| | | 286 | | // Note that in those specific cases, the inverse matrix are identical to the original one, in general, |
+| | | 287 | | // UnityTypeConverter.inverseBasisChange should be equal to UnityTypeConverter.basisChange.inverse. |
+| | 73 | 288 | | if (importOptions.changeHandedness == BasisTransformation.SlowAndSafeAsFBX) |
+| | 8 | 289 | | { |
+| | | 290 | | // To be consistent with FBX basis change, ensure it's the X axis that is inverted. |
+| | 8 | 291 | | Vector3 matrixDiagonal = new Vector3(-1, 1, 1); |
+| | 8 | 292 | | UnityTypeConverter.basisChange = Matrix4x4.Scale(matrixDiagonal); |
+| | 8 | 293 | | UnityTypeConverter.inverseBasisChange = Matrix4x4.Scale(matrixDiagonal); |
+| | 8 | 294 | | } |
+| | | 295 | | else |
+| | 65 | 296 | | { |
+| | | 297 | | // Ensure it's the Z axis that is inverted. |
+| | 65 | 298 | | Vector3 matrixDiagonal = new Vector3(1, 1, -1); |
+| | 65 | 299 | | UnityTypeConverter.basisChange = Matrix4x4.Scale(matrixDiagonal); |
+| | 65 | 300 | | UnityTypeConverter.inverseBasisChange = Matrix4x4.Scale(matrixDiagonal); |
+| | 65 | 301 | | } |
+| | | 302 | | |
+| | 73 | 303 | | scene.SetInterpolation(importOptions.interpolate |
+| | | 304 | | ? Scene.InterpolationMode.Linear |
+| | | 305 | | : Scene.InterpolationMode.Held); |
+| | | 306 | | |
+| | 73 | 307 | | SceneImporter.BuildScene(scene, |
+| | | 308 | | goRoot, |
+| | | 309 | | importOptions, |
+| | | 310 | | primMap, |
+| | | 311 | | composingSubtree); |
+| | 73 | 312 | | } |
+| | | 313 | | |
+| | | 314 | | /// <summary> |
+| | | 315 | | /// Rebuilds the USD scene as Unity GameObjects, maintaining a mapping from USD to Unity. |
+| | | 316 | | /// </summary> |
+| | | 317 | | public static PrimMap BuildScene(Scene scene, |
+| | | 318 | | GameObject root, |
+| | | 319 | | SceneImportOptions importOptions, |
+| | | 320 | | PrimMap primMap, |
+| | | 321 | | bool composingSubtree) |
+| | 73 | 322 | | { |
+| | | 323 | | try |
+| | 73 | 324 | | { |
+| | 73 | 325 | | Profiler.BeginSample("USD: Build Scene"); |
+| | 73 | 326 | | var builder = BuildScene(scene, |
+| | | 327 | | root, |
+| | | 328 | | importOptions, |
+| | | 329 | | primMap, |
+| | | 330 | | 0, |
+| | | 331 | | composingSubtree); |
+| | 224 | 332 | | while (builder.MoveNext()) |
+| | 151 | 333 | | { |
+| | 151 | 334 | | } |
+| | | 335 | | |
+| | 73 | 336 | | return primMap; |
+| | | 337 | | } |
+| | | 338 | | finally |
+| | 73 | 339 | | { |
+| | 73 | 340 | | Profiler.EndSample(); |
+| | 73 | 341 | | } |
+| | 73 | 342 | | } |
+| | | 343 | | |
+| | | 344 | | private static void RemoveComponent<T>(GameObject go) |
+| | 438 | 345 | | { |
+| | 438 | 346 | | var c = go.GetComponent<T>() as Component; |
+| | 438 | 347 | | if (c == null) |
+| | 413 | 348 | | { |
+| | 413 | 349 | | return; |
+| | | 350 | | } |
+| | | 351 | | #if UNITY_EDITOR |
+| | 25 | 352 | | Component.DestroyImmediate(c); |
+| | | 353 | | #else |
+| | | 354 | | Component.Destroy(c); |
+| | | 355 | | #endif |
+| | 438 | 356 | | } |
+| | | 357 | | |
+| | | 358 | | /// <summary> |
+| | | 359 | | /// Rebuilds the USD scene as Unity GameObjects, with a limited budget per update. |
+| | | 360 | | /// </summary> |
+| | | 361 | | public static IEnumerator BuildScene(Scene scene, |
+| | | 362 | | GameObject root, |
+| | | 363 | | SceneImportOptions importOptions, |
+| | | 364 | | PrimMap primMap, |
+| | | 365 | | float targetFrameMilliseconds, |
+| | | 366 | | bool composingSubtree) |
+| | 73 | 367 | | { |
+| | 73 | 368 | | var timer = new System.Diagnostics.Stopwatch(); |
+| | 73 | 369 | | var usdPrimRoot = new pxr.SdfPath(importOptions.usdRootPath); |
+| | | 370 | | |
+| | | 371 | | // Setting an arbitrary fudge factor of 20% is very non-scientific, however it's better than |
+| | | 372 | | // nothing. The correct way to hit a deadline is to predict how long each iteration actually |
+| | | 373 | | // takes and then return early if the estimated time is over budget. |
+| | 73 | 374 | | float targetTime = targetFrameMilliseconds * .8f; |
+| | | 375 | | |
+| | 73 | 376 | | timer.Start(); |
+| | | 377 | | |
+| | | 378 | | // Reconstruct the USD hierarchy as Unity GameObjects. |
+| | | 379 | | // A PrimMap is returned for tracking the USD <-> Unity mapping. |
+| | 73 | 380 | | Profiler.BeginSample("USD: Build Hierarchy"); |
+| | 73 | 381 | | if (importOptions.importHierarchy || importOptions.forceRebuild) |
+| | 73 | 382 | | { |
+| | | 383 | | // When a USD file is fully RE-imported, all exsiting USD data must be removed. The old |
+| | | 384 | | // assumption was that the root would never have much more than the UsdAsset component |
+| | | 385 | | // itself, however it's now clear that the root may also have meaningful USD data added |
+| | | 386 | | // too. |
+| | | 387 | | // |
+| | | 388 | | // TODO(jcowles): This feels like a workaround. What we really want here is an "undo" |
+| | | 389 | | // process for changes made to the root GameObject. For example, to clean up non-USD |
+| | | 390 | | // components which may have been added (e.g. what if a mesh is imported to the root? |
+| | | 391 | | // currently the MeshRenderer etc will remain after re-import). |
+| | 73 | 392 | | RemoveComponent<UsdAssemblyRoot>(root); |
+| | 73 | 393 | | RemoveComponent<UsdVariantSet>(root); |
+| | 73 | 394 | | RemoveComponent<UsdModelRoot>(root); |
+| | 73 | 395 | | RemoveComponent<UsdLayerStack>(root); |
+| | 73 | 396 | | RemoveComponent<UsdPayload>(root); |
+| | 73 | 397 | | RemoveComponent<UsdPrimSource>(root); |
+| | | 398 | | |
+| | 73 | 399 | | primMap.Clear(); |
+| | 73 | 400 | | HierarchyBuilder.BuildGameObjects(scene, |
+| | | 401 | | root, |
+| | | 402 | | usdPrimRoot, |
+| | | 403 | | scene.Find(usdPrimRoot.ToString(), "UsdSchemaBase"), |
+| | | 404 | | primMap, |
+| | | 405 | | importOptions); |
+| | 73 | 406 | | } |
+| | | 407 | | |
+| | 73 | 408 | | Profiler.EndSample(); |
+| | | 409 | | |
+| | 73 | 410 | | if (ShouldYield(targetTime, timer)) |
+| | 49 | 411 | | { |
+| | 49 | 412 | | yield return null; |
+| | 49 | 413 | | ResetTimer(timer); |
+| | 49 | 414 | | } |
+| | | 415 | | |
+| | 73 | 416 | | Profiler.BeginSample("USD: Post Process Hierarchy"); |
+| | 219 | 417 | | foreach (var processor in root.GetComponents<IImportPostProcessHierarchy>()) |
+| | 0 | 418 | | { |
+| | | 419 | | try |
+| | 0 | 420 | | { |
+| | 0 | 421 | | processor.PostProcessHierarchy(primMap, importOptions); |
+| | 0 | 422 | | } |
+| | 0 | 423 | | catch (System.Exception ex) |
+| | 0 | 424 | | { |
+| | 0 | 425 | | Debug.LogException(ex); |
+| | 0 | 426 | | } |
+| | 0 | 427 | | } |
+| | | 428 | | |
+| | 73 | 429 | | Profiler.EndSample(); |
+| | | 430 | | |
+| | 73 | 431 | | if (ShouldYield(targetTime, timer)) |
+| | 1 | 432 | | { |
+| | 1 | 433 | | yield return null; |
+| | 1 | 434 | | ResetTimer(timer); |
+| | 1 | 435 | | } |
+| | | 436 | | |
+| | | 437 | | // |
+| | | 438 | | // Pre-process UsdSkelRoots. |
+| | | 439 | | // |
+| | | 440 | | |
+| | 73 | 441 | | var skelRoots = new List<pxr.UsdSkelRoot>(); |
+| | 73 | 442 | | if (importOptions.importSkinning) |
+| | 73 | 443 | | { |
+| | 73 | 444 | | Profiler.BeginSample("USD: Process UsdSkelRoots"); |
+| | 225 | 445 | | foreach (var path in primMap.SkelRoots) |
+| | 3 | 446 | | { |
+| | | 447 | | try |
+| | 3 | 448 | | { |
+| | 3 | 449 | | var skelRootPrim = scene.GetPrimAtPath(path); |
+| | 3 | 450 | | if (!skelRootPrim) |
+| | 0 | 451 | | { |
+| | 0 | 452 | | Debug.LogWarning("SkelRoot prim not found: " + path); |
+| | 0 | 453 | | continue; |
+| | | 454 | | } |
+| | | 455 | | |
+| | 3 | 456 | | var skelRoot = new pxr.UsdSkelRoot(skelRootPrim); |
+| | 3 | 457 | | if (!skelRoot) |
+| | 0 | 458 | | { |
+| | 0 | 459 | | Debug.LogWarning("SkelRoot prim not SkelRoot type: " + path); |
+| | 0 | 460 | | continue; |
+| | | 461 | | } |
+| | | 462 | | |
+| | 3 | 463 | | skelRoots.Add(skelRoot); |
+| | 3 | 464 | | GameObject go = primMap[path]; |
+| | 3 | 465 | | ImporterBase.GetOrAddComponent<Animator>(go, true); |
+| | 3 | 466 | | } |
+| | 0 | 467 | | catch (System.Exception ex) |
+| | 0 | 468 | | { |
+| | 0 | 469 | | Debug.LogException( |
+| | | 470 | | new ImportException("Error pre-processing SkelRoot <" + path + ">", ex)); |
+| | 0 | 471 | | } |
+| | | 472 | | |
+| | 3 | 473 | | if (ShouldYield(targetTime, timer)) |
+| | 0 | 474 | | { |
+| | 0 | 475 | | yield return null; |
+| | 0 | 476 | | ResetTimer(timer); |
+| | 0 | 477 | | } |
+| | 3 | 478 | | } |
+| | | 479 | | |
+| | 73 | 480 | | Profiler.EndSample(); |
+| | 73 | 481 | | } |
+| | | 482 | | |
+| | | 483 | | // |
+| | | 484 | | // Import known prim types. |
+| | | 485 | | // |
+| | | 486 | | |
+| | | 487 | | // Materials. |
+| | 73 | 488 | | Profiler.BeginSample("USD: Build Materials"); |
+| | 73 | 489 | | if (importOptions.ShouldBindMaterials) |
+| | 1 | 490 | | { |
+| | 5 | 491 | | foreach (var pathAndSample in scene.ReadAll<MaterialSample>(primMap.Materials)) |
+| | 1 | 492 | | { |
+| | | 493 | | try |
+| | 1 | 494 | | { |
+| | 1 | 495 | | var mat = MaterialImporter.BuildMaterial(scene, |
+| | | 496 | | pathAndSample.path, |
+| | | 497 | | pathAndSample.sample, |
+| | | 498 | | importOptions); |
+| | 1 | 499 | | if (mat != null) |
+| | 1 | 500 | | { |
+| | 1 | 501 | | importOptions.materialMap[pathAndSample.path] = mat; |
+| | 1 | 502 | | } |
+| | 1 | 503 | | } |
+| | 0 | 504 | | catch (System.Exception ex) |
+| | 0 | 505 | | { |
+| | 0 | 506 | | Debug.LogException( |
+| | | 507 | | new ImportException("Error processing material <" + pathAndSample.path + ">", ex)); |
+| | 0 | 508 | | } |
+| | | 509 | | |
+| | 1 | 510 | | if (ShouldYield(targetTime, timer)) |
+| | 1 | 511 | | { |
+| | 1 | 512 | | yield return null; |
+| | 1 | 513 | | ResetTimer(timer); |
+| | 1 | 514 | | } |
+| | 1 | 515 | | } |
+| | 1 | 516 | | } |
+| | | 517 | | |
+| | 73 | 518 | | Profiler.EndSample(); |
+| | | 519 | | |
+| | | 520 | | // |
+| | | 521 | | // Start threads. |
+| | | 522 | | // |
+| | | 523 | | ReadAllJob<SanitizedXformSample> readXforms; |
+| | 73 | 524 | | if (importOptions.importTransforms) |
+| | 73 | 525 | | { |
+| | 73 | 526 | | readXforms = new ReadAllJob<SanitizedXformSample>(scene, primMap.Xforms, importOptions); |
+| | 73 | 527 | | readXforms.Schedule(primMap.Xforms.Length, 4); |
+| | 73 | 528 | | } |
+| | | 529 | | |
+| | 73 | 530 | | if (importOptions.importMeshes) |
+| | 73 | 531 | | { |
+| | 73 | 532 | | ActiveMeshImporter.BeginReading(scene, primMap, importOptions); |
+| | 73 | 533 | | } |
+| | | 534 | | |
+| | 73 | 535 | | JobHandle.ScheduleBatchedJobs(); |
+| | | 536 | | |
+| | | 537 | | |
+| | | 538 | | // Xforms. |
+| | | 539 | | // |
+| | | 540 | | // Note that we are specifically filtering on XformSample, not Xformable, this way only |
+| | | 541 | | // Xforms are processed to avoid doing that work redundantly. |
+| | 73 | 542 | | if (importOptions.importTransforms) |
+| | 73 | 543 | | { |
+| | 73 | 544 | | Profiler.BeginSample("USD: Build Xforms"); |
+| | 521 | 545 | | foreach (var pathAndSample in readXforms) |
+| | 151 | 546 | | { |
+| | | 547 | | try |
+| | 151 | 548 | | { |
+| | 151 | 549 | | if (pathAndSample.path == usdPrimRoot) |
+| | 39 | 550 | | { |
+| | | 551 | | // Never read the xform from the USD root, that will be authored in Unity. |
+| | 39 | 552 | | continue; |
+| | | 553 | | } |
+| | | 554 | | |
+| | 112 | 555 | | GameObject go = primMap[pathAndSample.path]; |
+| | 112 | 556 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions); |
+| | 112 | 557 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); |
+| | 112 | 558 | | } |
+| | 0 | 559 | | catch (System.Exception ex) |
+| | 0 | 560 | | { |
+| | 0 | 561 | | Debug.LogException( |
+| | | 562 | | new ImportException("Error processing xform <" + pathAndSample.path + ">", ex)); |
+| | 0 | 563 | | } |
+| | | 564 | | |
+| | 112 | 565 | | if (ShouldYield(targetTime, timer)) |
+| | 9 | 566 | | { |
+| | 9 | 567 | | yield return null; |
+| | 9 | 568 | | ResetTimer(timer); |
+| | 9 | 569 | | } |
+| | 112 | 570 | | } |
+| | | 571 | | |
+| | 225 | 572 | | foreach (var pathAndSample in scene.ReadAll<XformSample>(primMap.SkelRoots)) |
+| | 3 | 573 | | { |
+| | | 574 | | try |
+| | 3 | 575 | | { |
+| | 3 | 576 | | if (pathAndSample.path == usdPrimRoot) |
+| | 3 | 577 | | { |
+| | | 578 | | // Never read the xform from the USD root, that will be authored in Unity. |
+| | 3 | 579 | | continue; |
+| | | 580 | | } |
+| | | 581 | | |
+| | 0 | 582 | | GameObject go = primMap[pathAndSample.path]; |
+| | 0 | 583 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions); |
+| | 0 | 584 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); |
+| | 0 | 585 | | } |
+| | 0 | 586 | | catch (System.Exception ex) |
+| | 0 | 587 | | { |
+| | 0 | 588 | | Debug.LogException( |
+| | | 589 | | new ImportException("Error processing xform <" + pathAndSample.path + ">", ex)); |
+| | 0 | 590 | | } |
+| | | 591 | | |
+| | 0 | 592 | | if (ShouldYield(targetTime, timer)) |
+| | 0 | 593 | | { |
+| | 0 | 594 | | yield return null; |
+| | 0 | 595 | | ResetTimer(timer); |
+| | 0 | 596 | | } |
+| | 0 | 597 | | } |
+| | | 598 | | |
+| | 73 | 599 | | if (importOptions.importSkinning) |
+| | 73 | 600 | | { |
+| | 225 | 601 | | foreach (var pathAndSample in scene.ReadAll<XformSample>(primMap.Skeletons)) |
+| | 3 | 602 | | { |
+| | | 603 | | try |
+| | 3 | 604 | | { |
+| | 3 | 605 | | if (pathAndSample.path == usdPrimRoot) |
+| | 0 | 606 | | { |
+| | | 607 | | // Never read the xform from the USD root, that will be authored in Unity. |
+| | 0 | 608 | | continue; |
+| | | 609 | | } |
+| | | 610 | | |
+| | 3 | 611 | | GameObject go = primMap[pathAndSample.path]; |
+| | 3 | 612 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), |
+| | | 613 | | importOptions); |
+| | 3 | 614 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, |
+| | | 615 | | scene); |
+| | 3 | 616 | | } |
+| | 0 | 617 | | catch (System.Exception ex) |
+| | 0 | 618 | | { |
+| | 0 | 619 | | Debug.LogException( |
+| | | 620 | | new ImportException("Error processing xform <" + pathAndSample.path + ">", ex)); |
+| | 0 | 621 | | } |
+| | | 622 | | |
+| | 3 | 623 | | if (ShouldYield(targetTime, timer)) |
+| | 1 | 624 | | { |
+| | 1 | 625 | | yield return null; |
+| | 1 | 626 | | ResetTimer(timer); |
+| | 1 | 627 | | } |
+| | 3 | 628 | | } |
+| | 73 | 629 | | } |
+| | | 630 | | |
+| | 73 | 631 | | Profiler.EndSample(); |
+| | 73 | 632 | | } |
+| | | 633 | | |
+| | | 634 | | // Meshes. |
+| | 73 | 635 | | if (importOptions.importMeshes) |
+| | 73 | 636 | | { |
+| | 73 | 637 | | Profiler.BeginSample("USD: Build Meshes"); |
+| | 73 | 638 | | IEnumerator it = ActiveMeshImporter.Import(scene, primMap, importOptions); |
+| | | 639 | | |
+| | 194 | 640 | | while (it.MoveNext()) |
+| | 121 | 641 | | { |
+| | 121 | 642 | | if (ShouldYield(targetTime, timer)) |
+| | 49 | 643 | | { |
+| | 49 | 644 | | yield return null; |
+| | 49 | 645 | | ResetTimer(timer); |
+| | 49 | 646 | | } |
+| | 121 | 647 | | } |
+| | | 648 | | |
+| | 73 | 649 | | Profiler.EndSample(); |
+| | | 650 | | |
+| | | 651 | | // Cubes. |
+| | 73 | 652 | | Profiler.BeginSample("USD: Build Cubes"); |
+| | 223 | 653 | | foreach (var pathAndSample in scene.ReadAll<CubeSample>(primMap.Cubes)) |
+| | 2 | 654 | | { |
+| | | 655 | | try |
+| | 2 | 656 | | { |
+| | 2 | 657 | | GameObject go = primMap[pathAndSample.path]; |
+| | 2 | 658 | | pxr.UsdPrim prim = scene.GetPrimAtPath(pathAndSample.path); |
+| | | 659 | | |
+| | 2 | 660 | | NativeImporter.ImportObject(scene, go, prim, importOptions); |
+| | 2 | 661 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); |
+| | 2 | 662 | | bool skinnedMesh = IsSkinnedMesh(prim, primMap, importOptions); |
+| | 2 | 663 | | CubeImporter.BuildCube(pathAndSample.sample, go, importOptions, skinnedMesh); |
+| | 2 | 664 | | } |
+| | 0 | 665 | | catch (System.Exception ex) |
+| | 0 | 666 | | { |
+| | 0 | 667 | | Debug.LogException( |
+| | | 668 | | new ImportException("Error processing cube <" + pathAndSample.path + ">", ex)); |
+| | 0 | 669 | | } |
+| | | 670 | | |
+| | 2 | 671 | | if (ShouldYield(targetTime, timer)) |
+| | 2 | 672 | | { |
+| | 2 | 673 | | yield return null; |
+| | 2 | 674 | | ResetTimer(timer); |
+| | 2 | 675 | | } |
+| | 2 | 676 | | } |
+| | | 677 | | |
+| | 73 | 678 | | Profiler.EndSample(); |
+| | | 679 | | |
+| | | 680 | | // Spheres. |
+| | 73 | 681 | | Profiler.BeginSample("USD: Build Spheres"); |
+| | 243 | 682 | | foreach (var pathAndSample in scene.ReadAll<SphereSample>(primMap.Spheres)) |
+| | 12 | 683 | | { |
+| | | 684 | | try |
+| | 12 | 685 | | { |
+| | 12 | 686 | | GameObject go = primMap[pathAndSample.path]; |
+| | 12 | 687 | | pxr.UsdPrim prim = scene.GetPrimAtPath(pathAndSample.path); |
+| | | 688 | | |
+| | 12 | 689 | | NativeImporter.ImportObject(scene, go, prim, importOptions); |
+| | 12 | 690 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); |
+| | 12 | 691 | | bool skinnedMesh = IsSkinnedMesh(prim, primMap, importOptions); |
+| | 12 | 692 | | SphereImporter.BuildSphere(pathAndSample.sample, go, importOptions, skinnedMesh); |
+| | 12 | 693 | | } |
+| | 0 | 694 | | catch (System.Exception ex) |
+| | 0 | 695 | | { |
+| | 0 | 696 | | Debug.LogException( |
+| | | 697 | | new ImportException("Error processing sphere <" + pathAndSample.path + ">", ex)); |
+| | 0 | 698 | | } |
+| | | 699 | | |
+| | 12 | 700 | | if (ShouldYield(targetTime, timer)) |
+| | 11 | 701 | | { |
+| | 11 | 702 | | yield return null; |
+| | 11 | 703 | | ResetTimer(timer); |
+| | 11 | 704 | | } |
+| | 12 | 705 | | } |
+| | | 706 | | |
+| | 73 | 707 | | Profiler.EndSample(); |
+| | 73 | 708 | | } |
+| | | 709 | | |
+| | | 710 | | // Cameras. |
+| | 73 | 711 | | if (importOptions.importCameras) |
+| | 73 | 712 | | { |
+| | 73 | 713 | | Profiler.BeginSample("USD: Cameras"); |
+| | 247 | 714 | | foreach (var pathAndSample in scene.ReadAll<SanitizedCameraSample>(primMap.Cameras)) |
+| | 14 | 715 | | { |
+| | | 716 | | try |
+| | 14 | 717 | | { |
+| | 14 | 718 | | GameObject go = primMap[pathAndSample.path]; |
+| | 14 | 719 | | pathAndSample.sample.Sanitize(scene, importOptions); |
+| | 14 | 720 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions); |
+| | 14 | 721 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); |
+| | | 722 | | |
+| | | 723 | | // In order to match FBX importer buggy behavior, the camera xform need an extra rotation. |
+| | | 724 | | // FBX importer is fixed in 2020 though with an option to do an axis bake on import. |
+| | | 725 | | // If axis bake is used, no need to use the SlowAndSafeAsFBX mode. |
+| | 14 | 726 | | if (importOptions.changeHandedness == BasisTransformation.SlowAndSafeAsFBX) |
+| | 8 | 727 | | { |
+| | 8 | 728 | | go.transform.localRotation *= Quaternion.Euler(180.0f, 0.0f, 180.0f); |
+| | 8 | 729 | | } |
+| | | 730 | | |
+| | | 731 | | // The camera has many value-type parameters that need to be handled correctly when not |
+| | | 732 | | // not animated. For now, only the camera transform will animate, until this is fixed. |
+| | 14 | 733 | | if (scene.AccessMask == null || scene.IsPopulatingAccessMask) |
+| | 14 | 734 | | { |
+| | 14 | 735 | | CameraImporter.BuildCamera(pathAndSample.sample, go, importOptions); |
+| | 14 | 736 | | } |
+| | 14 | 737 | | } |
+| | 0 | 738 | | catch (System.Exception ex) |
+| | 0 | 739 | | { |
+| | 0 | 740 | | Debug.LogException( |
+| | | 741 | | new ImportException("Error processing camera <" + pathAndSample.path + ">", ex)); |
+| | 0 | 742 | | } |
+| | | 743 | | |
+| | 14 | 744 | | if (ShouldYield(targetTime, timer)) |
+| | 6 | 745 | | { |
+| | 6 | 746 | | yield return null; |
+| | 6 | 747 | | ResetTimer(timer); |
+| | 6 | 748 | | } |
+| | 14 | 749 | | } |
+| | | 750 | | |
+| | 73 | 751 | | Profiler.EndSample(); |
+| | 73 | 752 | | } |
+| | | 753 | | |
+| | | 754 | | // Build out masters for instancing. |
+| | 73 | 755 | | Profiler.BeginSample("USD: Build Instances"); |
+| | 225 | 756 | | foreach (var masterRootPath in primMap.GetMasterRootPaths()) |
+| | 3 | 757 | | { |
+| | | 758 | | try |
+| | 3 | 759 | | { |
+| | 3 | 760 | | Transform masterRootXf = primMap[masterRootPath].transform; |
+| | | 761 | | |
+| | | 762 | | // Transforms |
+| | 3 | 763 | | if (importOptions.importTransforms) |
+| | 3 | 764 | | { |
+| | 3 | 765 | | Profiler.BeginSample("USD: Build Xforms"); |
+| | 9 | 766 | | foreach (var pathAndSample in scene.ReadAll<SanitizedXformSample>(masterRootPath)) |
+| | 0 | 767 | | { |
+| | | 768 | | try |
+| | 0 | 769 | | { |
+| | 0 | 770 | | GameObject go = primMap[pathAndSample.path]; |
+| | 0 | 771 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), |
+| | | 772 | | importOptions); |
+| | 0 | 773 | | pathAndSample.sample.Sanitize(scene, importOptions); |
+| | 0 | 774 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, |
+| | | 775 | | scene); |
+| | 0 | 776 | | } |
+| | 0 | 777 | | catch (System.Exception ex) |
+| | 0 | 778 | | { |
+| | 0 | 779 | | Debug.LogException( |
+| | | 780 | | new ImportException("Error processing xform <" + pathAndSample.path + ">", ex)); |
+| | 0 | 781 | | } |
+| | 0 | 782 | | } |
+| | | 783 | | |
+| | 9 | 784 | | foreach (var pathAndSample in scene.ReadAll<SanitizedXformSample>(primMap.Skeletons)) |
+| | 0 | 785 | | { |
+| | | 786 | | try |
+| | 0 | 787 | | { |
+| | 0 | 788 | | GameObject go = primMap[pathAndSample.path]; |
+| | 0 | 789 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), |
+| | | 790 | | importOptions); |
+| | 0 | 791 | | pathAndSample.sample.Sanitize(scene, importOptions); |
+| | 0 | 792 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, |
+| | | 793 | | scene); |
+| | 0 | 794 | | } |
+| | 0 | 795 | | catch (System.Exception ex) |
+| | 0 | 796 | | { |
+| | 0 | 797 | | Debug.LogException( |
+| | | 798 | | new ImportException("Error processing xform <" + pathAndSample.path + ">", ex)); |
+| | 0 | 799 | | } |
+| | 0 | 800 | | } |
+| | | 801 | | |
+| | 3 | 802 | | Profiler.EndSample(); |
+| | 3 | 803 | | } |
+| | | 804 | | |
+| | | 805 | | // Meshes. |
+| | 3 | 806 | | if (importOptions.importMeshes) |
+| | 3 | 807 | | { |
+| | 3 | 808 | | Profiler.BeginSample("USD: Build Meshes"); |
+| | 15 | 809 | | foreach (var pathAndSample in scene.ReadAll<SanitizedMeshSample>(masterRootPath)) |
+| | 3 | 810 | | { |
+| | | 811 | | try |
+| | 3 | 812 | | { |
+| | 3 | 813 | | GameObject go = primMap[pathAndSample.path]; |
+| | 3 | 814 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), |
+| | | 815 | | importOptions); |
+| | 3 | 816 | | pathAndSample.sample.Sanitize(scene, importOptions); |
+| | 3 | 817 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, |
+| | | 818 | | scene); |
+| | 3 | 819 | | var subsets = MeshImporter.ReadGeomSubsets(scene, pathAndSample.path); |
+| | 3 | 820 | | bool isDynamic = scene.AccessMask != null |
+| | | 821 | | ? scene.AccessMask.Included.ContainsKey(pathAndSample.path) |
+| | | 822 | | : false; |
+| | 3 | 823 | | MeshImporter.BuildMesh(pathAndSample.path, pathAndSample.sample, subsets, go, |
+| | | 824 | | importOptions, isDynamic); |
+| | 3 | 825 | | } |
+| | 0 | 826 | | catch (System.Exception ex) |
+| | 0 | 827 | | { |
+| | 0 | 828 | | Debug.LogException( |
+| | | 829 | | new ImportException("Error processing mesh <" + pathAndSample.path + ">", ex)); |
+| | 0 | 830 | | } |
+| | 3 | 831 | | } |
+| | | 832 | | |
+| | 3 | 833 | | Profiler.EndSample(); |
+| | | 834 | | |
+| | | 835 | | // Cubes. |
+| | 3 | 836 | | Profiler.BeginSample("USD: Build Cubes"); |
+| | 9 | 837 | | foreach (var pathAndSample in scene.ReadAll<CubeSample>(masterRootPath)) |
+| | 0 | 838 | | { |
+| | | 839 | | try |
+| | 0 | 840 | | { |
+| | 0 | 841 | | GameObject go = primMap[pathAndSample.path]; |
+| | 0 | 842 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), |
+| | | 843 | | importOptions); |
+| | 0 | 844 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, |
+| | | 845 | | scene); |
+| | 0 | 846 | | CubeImporter.BuildCube(pathAndSample.sample, go, importOptions); |
+| | 0 | 847 | | } |
+| | 0 | 848 | | catch (System.Exception ex) |
+| | 0 | 849 | | { |
+| | 0 | 850 | | Debug.LogException( |
+| | | 851 | | new ImportException("Error processing cube <" + pathAndSample.path + ">", ex)); |
+| | 0 | 852 | | } |
+| | 0 | 853 | | } |
+| | | 854 | | |
+| | 3 | 855 | | Profiler.EndSample(); |
+| | | 856 | | |
+| | | 857 | | // Spheres. |
+| | 3 | 858 | | Profiler.BeginSample("USD: Build Spheres"); |
+| | 3 | 859 | | var sphereSamples = scene.ReadAll<SphereSample>(masterRootPath); |
+| | 9 | 860 | | foreach (var pathAndSample in sphereSamples) |
+| | 0 | 861 | | { |
+| | | 862 | | try |
+| | 0 | 863 | | { |
+| | 0 | 864 | | GameObject go = primMap[pathAndSample.path]; |
+| | 0 | 865 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), |
+| | | 866 | | importOptions); |
+| | 0 | 867 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, |
+| | | 868 | | scene); |
+| | 0 | 869 | | SphereImporter.BuildSphere(pathAndSample.sample, go, importOptions); |
+| | 0 | 870 | | } |
+| | 0 | 871 | | catch (System.Exception ex) |
+| | 0 | 872 | | { |
+| | 0 | 873 | | Debug.LogException( |
+| | | 874 | | new ImportException("Error processing sphere <" + pathAndSample.path + ">", ex)); |
+| | 0 | 875 | | } |
+| | 0 | 876 | | } |
+| | | 877 | | |
+| | 3 | 878 | | Profiler.EndSample(); |
+| | 3 | 879 | | } |
+| | | 880 | | |
+| | | 881 | | // Cameras. |
+| | 3 | 882 | | if (importOptions.importCameras) |
+| | 3 | 883 | | { |
+| | 3 | 884 | | Profiler.BeginSample("USD: Build Cameras"); |
+| | 9 | 885 | | foreach (var pathAndSample in scene.ReadAll<SanitizedCameraSample>(masterRootPath)) |
+| | 0 | 886 | | { |
+| | | 887 | | try |
+| | 0 | 888 | | { |
+| | 0 | 889 | | GameObject go = primMap[pathAndSample.path]; |
+| | 0 | 890 | | pathAndSample.sample.Sanitize(scene, importOptions); |
+| | 0 | 891 | | NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), |
+| | | 892 | | importOptions); |
+| | 0 | 893 | | XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, |
+| | | 894 | | scene); |
+| | 0 | 895 | | CameraImporter.BuildCamera(pathAndSample.sample, go, importOptions); |
+| | 0 | 896 | | } |
+| | 0 | 897 | | catch (System.Exception ex) |
+| | 0 | 898 | | { |
+| | 0 | 899 | | Debug.LogException( |
+| | | 900 | | new ImportException("Error processing camera <" + pathAndSample.path + ">", ex)); |
+| | 0 | 901 | | } |
+| | 0 | 902 | | } |
+| | | 903 | | |
+| | 3 | 904 | | Profiler.EndSample(); |
+| | 3 | 905 | | } |
+| | 3 | 906 | | } |
+| | 0 | 907 | | catch (System.Exception ex) |
+| | 0 | 908 | | { |
+| | 0 | 909 | | Debug.LogException( |
+| | | 910 | | new ImportException("Error processing master <" + masterRootPath + ">", ex)); |
+| | 0 | 911 | | } |
+| | | 912 | | |
+| | 3 | 913 | | if (ShouldYield(targetTime, timer)) |
+| | 3 | 914 | | { |
+| | 3 | 915 | | yield return null; |
+| | 3 | 916 | | ResetTimer(timer); |
+| | 3 | 917 | | } |
+| | 3 | 918 | | } // Instances. |
+| | | 919 | | |
+| | 73 | 920 | | Profiler.EndSample(); |
+| | | 921 | | |
+| | | 922 | | // |
+| | | 923 | | // Post-process dependencies: materials and bones. |
+| | | 924 | | // |
+| | | 925 | | |
+| | 73 | 926 | | Profiler.BeginSample("USD: Process Material Bindings"); |
+| | | 927 | | try |
+| | 73 | 928 | | { |
+| | | 929 | | // TODO: Currently ProcessMaterialBindings runs too long and will go over budget for any |
+| | | 930 | | // large scene. However, pulling the loop into this code feels wrong in terms of |
+| | | 931 | | // responsibilities. |
+| | | 932 | | |
+| | | 933 | | // Process all material bindings in a single vectorized request. |
+| | 73 | 934 | | MaterialImporter.ProcessMaterialBindings(scene, importOptions); |
+| | 73 | 935 | | } |
+| | 0 | 936 | | catch (System.Exception ex) |
+| | 0 | 937 | | { |
+| | 0 | 938 | | Debug.LogException(new ImportException("Failed in ProcessMaterialBindings", ex)); |
+| | 0 | 939 | | } |
+| | | 940 | | |
+| | 73 | 941 | | Profiler.EndSample(); |
+| | | 942 | | |
+| | 73 | 943 | | if (ShouldYield(targetTime, timer)) |
+| | 10 | 944 | | { |
+| | 10 | 945 | | yield return null; |
+| | 10 | 946 | | ResetTimer(timer); |
+| | 10 | 947 | | } |
+| | | 948 | | |
+| | | 949 | | // |
+| | | 950 | | // SkinnedMesh bone bindings. |
+| | | 951 | | // |
+| | 73 | 952 | | if (importOptions.importSkinning) |
+| | 73 | 953 | | { |
+| | 73 | 954 | | Profiler.BeginSample("USD: Build Skeletons"); |
+| | 73 | 955 | | var skeletonSamples = new Dictionary<pxr.SdfPath, SkeletonSample>(); |
+| | 225 | 956 | | foreach (var skelRoot in skelRoots) |
+| | 3 | 957 | | { |
+| | | 958 | | try |
+| | 3 | 959 | | { |
+| | 3 | 960 | | var bindings = new pxr.UsdSkelBindingVector(); |
+| | 3 | 961 | | if (!primMap.SkelBindings.TryGetValue(skelRoot.GetPath(), out bindings)) |
+| | 0 | 962 | | { |
+| | 0 | 963 | | Debug.LogWarning("No bindings found skelRoot: " + skelRoot.GetPath()); |
+| | 0 | 964 | | } |
+| | | 965 | | |
+| | 3 | 966 | | if (bindings.Count == 0) |
+| | 0 | 967 | | { |
+| | 0 | 968 | | Debug.LogWarning("No bindings found skelRoot: " + skelRoot.GetPath()); |
+| | 0 | 969 | | } |
+| | | 970 | | |
+| | 15 | 971 | | foreach (var skelBinding in bindings) |
+| | 3 | 972 | | { |
+| | | 973 | | // The SkelRoot will likely have a skeleton binding, but it's inherited, so the bound |
+| | | 974 | | // skeleton isn't actually known until it's queried from the binding. Still, we would |
+| | | 975 | | // like not to reprocess skeletons redundantly, so skeletons are cached into a |
+| | | 976 | | // dictionary. |
+| | | 977 | | |
+| | 3 | 978 | | Profiler.BeginSample("Build Bind Transforms"); |
+| | 3 | 979 | | var skelPath = skelBinding.GetSkeleton().GetPath(); |
+| | 3 | 980 | | SkeletonSample skelSample = null; |
+| | 3 | 981 | | if (!skeletonSamples.TryGetValue(skelPath, out skelSample)) |
+| | 3 | 982 | | { |
+| | 3 | 983 | | skelSample = new SkeletonSample(); |
+| | | 984 | | |
+| | 3 | 985 | | Profiler.BeginSample("Read Skeleton"); |
+| | 3 | 986 | | scene.Read(skelPath, skelSample); |
+| | 3 | 987 | | Profiler.EndSample(); |
+| | | 988 | | |
+| | 3 | 989 | | skeletonSamples.Add(skelPath, skelSample); |
+| | | 990 | | |
+| | | 991 | | // The bind pose is bone's inverse transformation matrix. This is done once here, so eac |
+| | | 992 | | // skinned mesh doesn't need to do it redundantly. |
+| | 3 | 993 | | SkeletonImporter.BuildBindTransforms(skelPath, skelSample, importOptions); |
+| | | 994 | | |
+| | | 995 | | // Validate the binding transforms. |
+| | 3 | 996 | | var bindXforms = new pxr.VtMatrix4dArray(); |
+| | 3 | 997 | | var prim = scene.GetPrimAtPath(skelPath); |
+| | 3 | 998 | | var skel = new pxr.UsdSkelSkeleton(prim); |
+| | | 999 | | |
+| | 3 | 1000 | | Profiler.BeginSample("Get SkelQuery"); |
+| | 3 | 1001 | | pxr.UsdSkelSkeletonQuery skelQuery = primMap.SkelCache.GetSkelQuery(skel); |
+| | 3 | 1002 | | Profiler.EndSample(); |
+| | | 1003 | | |
+| | 3 | 1004 | | Profiler.BeginSample("Get JointWorldBind Transforms"); |
+| | 3 | 1005 | | if (!skelQuery.GetJointWorldBindTransforms(bindXforms)) |
+| | 0 | 1006 | | { |
+| | 0 | 1007 | | throw new ImportException("Failed to compute binding transforms for <" + skelPath + |
+| | | 1008 | | ">"); |
+| | | 1009 | | } |
+| | | 1010 | | |
+| | 3 | 1011 | | Profiler.EndSample(); |
+| | | 1012 | | |
+| | 3 | 1013 | | SkeletonImporter.BuildDebugBindTransforms(skelSample, primMap[skelPath], importOptions); |
+| | 3 | 1014 | | } |
+| | | 1015 | | |
+| | 3 | 1016 | | Profiler.EndSample(); |
+| | | 1017 | | |
+| | 3 | 1018 | | if (importOptions.importSkinWeights) |
+| | 3 | 1019 | | { |
+| | | 1020 | | // |
+| | | 1021 | | // Apply skinning weights to each skinned mesh. |
+| | | 1022 | | // |
+| | 3 | 1023 | | Profiler.BeginSample("Apply Skin Weights"); |
+| | 27 | 1024 | | foreach (var skinningQuery in skelBinding.GetSkinningTargetsAsVector()) |
+| | 9 | 1025 | | { |
+| | 9 | 1026 | | pxr.SdfPath meshPath = skinningQuery.GetPrim().GetPath(); |
+| | | 1027 | | try |
+| | 9 | 1028 | | { |
+| | 9 | 1029 | | var goMesh = primMap[meshPath]; |
+| | | 1030 | | |
+| | 9 | 1031 | | Profiler.BeginSample("Build Skinned Mesh"); |
+| | 9 | 1032 | | SkeletonImporter.BuildSkinnedMesh( |
+| | | 1033 | | meshPath, |
+| | | 1034 | | skelPath, |
+| | | 1035 | | skelSample, |
+| | | 1036 | | skinningQuery, |
+| | | 1037 | | goMesh, |
+| | | 1038 | | primMap, |
+| | | 1039 | | importOptions); |
+| | 9 | 1040 | | Profiler.EndSample(); |
+| | | 1041 | | |
+| | | 1042 | | // In terms of performance, this is almost free. |
+| | | 1043 | | // TODO: Check if this is correct or should be something specific (not always th |
+| | 9 | 1044 | | goMesh.GetComponent<SkinnedMeshRenderer>().rootBone = |
+| | | 1045 | | primMap[skelPath].transform.GetChild(0); |
+| | 9 | 1046 | | } |
+| | 0 | 1047 | | catch (System.Exception ex) |
+| | 0 | 1048 | | { |
+| | 0 | 1049 | | Debug.LogException(new ImportException("Error skinning mesh: " + meshPath, ex)); |
+| | 0 | 1050 | | } |
+| | 9 | 1051 | | } |
+| | | 1052 | | |
+| | 3 | 1053 | | Profiler.EndSample(); |
+| | 3 | 1054 | | } |
+| | 3 | 1055 | | } |
+| | 3 | 1056 | | } |
+| | 0 | 1057 | | catch (System.Exception ex) |
+| | 0 | 1058 | | { |
+| | 0 | 1059 | | Debug.LogException( |
+| | | 1060 | | new ImportException("Error processing SkelRoot <" + skelRoot.GetPath() + ">", ex)); |
+| | 0 | 1061 | | } |
+| | 3 | 1062 | | } // foreach SkelRoot |
+| | | 1063 | | |
+| | 73 | 1064 | | Profiler.EndSample(); |
+| | | 1065 | | |
+| | 73 | 1066 | | if (ShouldYield(targetTime, timer)) |
+| | 2 | 1067 | | { |
+| | 2 | 1068 | | yield return null; |
+| | 2 | 1069 | | ResetTimer(timer); |
+| | 2 | 1070 | | } |
+| | | 1071 | | |
+| | | 1072 | | // |
+| | | 1073 | | // Bone transforms. |
+| | | 1074 | | // |
+| | 73 | 1075 | | Profiler.BeginSample("USD: Pose Bones"); |
+| | 225 | 1076 | | foreach (var pathAndSample in skeletonSamples) |
+| | 3 | 1077 | | { |
+| | 3 | 1078 | | var skelPath = pathAndSample.Key; |
+| | | 1079 | | |
+| | | 1080 | | try |
+| | 3 | 1081 | | { |
+| | 3 | 1082 | | var prim = scene.GetPrimAtPath(skelPath); |
+| | 3 | 1083 | | var skel = new pxr.UsdSkelSkeleton(prim); |
+| | | 1084 | | |
+| | 3 | 1085 | | pxr.UsdSkelSkeletonQuery skelQuery = primMap.SkelCache.GetSkelQuery(skel); |
+| | 3 | 1086 | | var joints = skelQuery.GetJointOrder(); |
+| | 3 | 1087 | | var restXforms = new pxr.VtMatrix4dArray(); |
+| | 3 | 1088 | | var time = scene.Time.HasValue ? scene.Time.Value : pxr.UsdTimeCode.Default(); |
+| | | 1089 | | |
+| | 3 | 1090 | | Profiler.BeginSample("Compute Joint Local Transforms"); |
+| | 3 | 1091 | | if (!skelQuery.ComputeJointLocalTransforms(restXforms, time, atRest: false)) |
+| | 0 | 1092 | | { |
+| | 0 | 1093 | | throw new ImportException("Failed to compute bind transforms for <" + skelPath + ">"); |
+| | | 1094 | | } |
+| | | 1095 | | |
+| | 3 | 1096 | | Profiler.EndSample(); |
+| | | 1097 | | |
+| | 3 | 1098 | | Profiler.BeginSample("Build Bones"); |
+| | 30 | 1099 | | for (int i = 0; i < joints.size(); i++) |
+| | 12 | 1100 | | { |
+| | 12 | 1101 | | var jointPath = scene.GetSdfPath(joints[i]); |
+| | 12 | 1102 | | if (joints[i] == "/") |
+| | 0 | 1103 | | { |
+| | 0 | 1104 | | jointPath = skelPath; |
+| | 0 | 1105 | | } |
+| | 12 | 1106 | | else if (jointPath.IsAbsolutePath()) |
+| | 0 | 1107 | | { |
+| | 0 | 1108 | | Debug.LogException( |
+| | | 1109 | | new System.Exception("Unexpected absolute joint path: " + jointPath)); |
+| | 0 | 1110 | | jointPath = new pxr.SdfPath(joints[i].ToString().TrimStart('/')); |
+| | 0 | 1111 | | jointPath = skelPath.AppendPath(jointPath); |
+| | 0 | 1112 | | } |
+| | | 1113 | | else |
+| | 12 | 1114 | | { |
+| | 12 | 1115 | | jointPath = skelPath.AppendPath(jointPath); |
+| | 12 | 1116 | | } |
+| | | 1117 | | |
+| | 12 | 1118 | | var goBone = primMap[jointPath]; |
+| | | 1119 | | |
+| | 12 | 1120 | | Profiler.BeginSample("Convert Matrix"); |
+| | 12 | 1121 | | var restXform = UnityTypeConverter.FromMatrix(restXforms[i]); |
+| | 12 | 1122 | | Profiler.EndSample(); |
+| | | 1123 | | |
+| | 12 | 1124 | | Profiler.BeginSample("Build Bone"); |
+| | 12 | 1125 | | SkeletonImporter.BuildSkeletonBone(skelPath, goBone, restXform, joints, importOptions); |
+| | 12 | 1126 | | Profiler.EndSample(); |
+| | 12 | 1127 | | } |
+| | | 1128 | | |
+| | 3 | 1129 | | Profiler.EndSample(); |
+| | 3 | 1130 | | } |
+| | 0 | 1131 | | catch (System.Exception ex) |
+| | 0 | 1132 | | { |
+| | 0 | 1133 | | Debug.LogException( |
+| | | 1134 | | new ImportException("Error processing SkelRoot <" + skelPath + ">", ex)); |
+| | 0 | 1135 | | } |
+| | | 1136 | | |
+| | 3 | 1137 | | if (ShouldYield(targetTime, timer)) |
+| | 1 | 1138 | | { |
+| | 1 | 1139 | | yield return null; |
+| | 1 | 1140 | | ResetTimer(timer); |
+| | 1 | 1141 | | } |
+| | 3 | 1142 | | } |
+| | | 1143 | | |
+| | 73 | 1144 | | Profiler.EndSample(); |
+| | 73 | 1145 | | } |
+| | | 1146 | | |
+| | | 1147 | | // |
+| | | 1148 | | // Apply instancing. |
+| | | 1149 | | // |
+| | 73 | 1150 | | if (importOptions.importSceneInstances) |
+| | 73 | 1151 | | { |
+| | 73 | 1152 | | Profiler.BeginSample("USD: Build Scene-Instances"); |
+| | | 1153 | | try |
+| | 73 | 1154 | | { |
+| | | 1155 | | // Build scene instances. |
+| | 73 | 1156 | | InstanceImporter.BuildSceneInstances(primMap, importOptions); |
+| | 73 | 1157 | | } |
+| | 0 | 1158 | | catch (System.Exception ex) |
+| | 0 | 1159 | | { |
+| | 0 | 1160 | | Debug.LogException(new ImportException("Failed in BuildSceneInstances", ex)); |
+| | 0 | 1161 | | } |
+| | | 1162 | | |
+| | 73 | 1163 | | Profiler.EndSample(); |
+| | 73 | 1164 | | } |
+| | | 1165 | | |
+| | 73 | 1166 | | if (ShouldYield(targetTime, timer)) |
+| | 4 | 1167 | | { |
+| | 4 | 1168 | | yield return null; |
+| | 4 | 1169 | | ResetTimer(timer); |
+| | 4 | 1170 | | } |
+| | | 1171 | | |
+| | | 1172 | | // Build point instances. |
+| | 73 | 1173 | | if (importOptions.importPointInstances) |
+| | 73 | 1174 | | { |
+| | 73 | 1175 | | Profiler.BeginSample("USD: Build Point-Instances"); |
+| | | 1176 | | // TODO: right now all point instancer data is read, but we only need prototypes and indices. |
+| | 223 | 1177 | | foreach (var pathAndSample in scene.ReadAll<PointInstancerSample>()) |
+| | 2 | 1178 | | { |
+| | | 1179 | | try |
+| | 2 | 1180 | | { |
+| | 2 | 1181 | | GameObject instancerGo = primMap[pathAndSample.path]; |
+| | | 1182 | | |
+| | | 1183 | | // Now build the point instances. |
+| | 2 | 1184 | | InstanceImporter.BuildPointInstances(scene, |
+| | | 1185 | | primMap, |
+| | | 1186 | | pathAndSample.path, |
+| | | 1187 | | pathAndSample.sample, |
+| | | 1188 | | instancerGo, |
+| | | 1189 | | importOptions); |
+| | 2 | 1190 | | } |
+| | 0 | 1191 | | catch (System.Exception ex) |
+| | 0 | 1192 | | { |
+| | 0 | 1193 | | Debug.LogError("Error processing point instancer <" + pathAndSample.path + ">: " + ex.Message); |
+| | 0 | 1194 | | } |
+| | | 1195 | | |
+| | 2 | 1196 | | if (ShouldYield(targetTime, timer)) |
+| | 2 | 1197 | | { |
+| | 2 | 1198 | | yield return null; |
+| | 2 | 1199 | | ResetTimer(timer); |
+| | 2 | 1200 | | } |
+| | 2 | 1201 | | } |
+| | | 1202 | | |
+| | 73 | 1203 | | Profiler.EndSample(); |
+| | 73 | 1204 | | } |
+| | | 1205 | | |
+| | | 1206 | | // |
+| | | 1207 | | // Apply root transform corrections. |
+| | | 1208 | | // |
+| | 73 | 1209 | | Profiler.BeginSample("USD: Build Root Transforms"); |
+| | 73 | 1210 | | if (!composingSubtree) |
+| | 70 | 1211 | | { |
+| | 70 | 1212 | | if (!root) |
+| | 0 | 1213 | | { |
+| | | 1214 | | // There is no single root, |
+| | | 1215 | | // Apply root transform corrections to all imported root prims. |
+| | 0 | 1216 | | foreach (KeyValuePair<pxr.SdfPath, GameObject> kvp in primMap) |
+| | 0 | 1217 | | { |
+| | 0 | 1218 | | if (kvp.Key.IsRootPrimPath() && kvp.Value != null) |
+| | 0 | 1219 | | { |
+| | | 1220 | | // The root object at which the USD scene will be reconstructed. |
+| | | 1221 | | // It may need a Z-up to Y-up conversion and a right- to left-handed change of basis. |
+| | 0 | 1222 | | XformImporter.BuildSceneRoot(scene, kvp.Value.transform, importOptions); |
+| | 0 | 1223 | | } |
+| | 0 | 1224 | | } |
+| | 0 | 1225 | | } |
+| | | 1226 | | else |
+| | 70 | 1227 | | { |
+| | | 1228 | | // There is only one root, apply a single transform correction. |
+| | 70 | 1229 | | XformImporter.BuildSceneRoot(scene, root.transform, importOptions); |
+| | 70 | 1230 | | } |
+| | 70 | 1231 | | } |
+| | | 1232 | | |
+| | 73 | 1233 | | Profiler.EndSample(); |
+| | | 1234 | | |
+| | 73 | 1235 | | Profiler.BeginSample("USD: Post Process Components"); |
+| | 219 | 1236 | | foreach (var processor in root.GetComponents<IImportPostProcessComponents>()) |
+| | 0 | 1237 | | { |
+| | | 1238 | | try |
+| | 0 | 1239 | | { |
+| | 0 | 1240 | | processor.PostProcessComponents(primMap, importOptions); |
+| | 0 | 1241 | | } |
+| | 0 | 1242 | | catch (System.Exception ex) |
+| | 0 | 1243 | | { |
+| | 0 | 1244 | | Debug.LogException(ex); |
+| | 0 | 1245 | | } |
+| | 0 | 1246 | | } |
+| | | 1247 | | |
+| | 73 | 1248 | | Profiler.EndSample(); |
+| | 73 | 1249 | | } |
+| | | 1250 | | |
+| | | 1251 | | private static bool ShouldYield(float targetTime, System.Diagnostics.Stopwatch timer) |
+| | 641 | 1252 | | { |
+| | 641 | 1253 | | return timer.ElapsedMilliseconds > targetTime; |
+| | 641 | 1254 | | } |
+| | | 1255 | | |
+| | | 1256 | | private static void ResetTimer(System.Diagnostics.Stopwatch timer) |
+| | 151 | 1257 | | { |
+| | 151 | 1258 | | timer.Stop(); |
+| | 151 | 1259 | | timer.Reset(); |
+| | 151 | 1260 | | timer.Start(); |
+| | 151 | 1261 | | } |
+| | | 1262 | | |
+| | | 1263 | | private static bool IsSkinnedMesh(pxr.UsdPrim prim, PrimMap primMap, SceneImportOptions importOptions) |
+| | 14 | 1264 | | { |
+| | 14 | 1265 | | bool skinnedMesh = false; |
+| | | 1266 | | |
+| | 14 | 1267 | | if (importOptions.importSkinning && primMap.SkelCache != null) |
+| | 14 | 1268 | | { |
+| | 14 | 1269 | | pxr.UsdSkelSkinningQuery skinningQuery = primMap.SkelCache.GetSkinningQuery(prim); |
+| | 14 | 1270 | | primMap.SkinningQueries[prim.GetPath()] = skinningQuery; |
+| | 14 | 1271 | | skinnedMesh = skinningQuery.IsValid(); |
+| | 14 | 1272 | | } |
+| | | 1273 | | |
+| | 14 | 1274 | | return skinnedMesh; |
+| | 14 | 1275 | | } |
+| | | 1276 | | } |
+| | | 1277 | | } |
+
+