GameObject LoadGltf(List <string> warnings) { // This is intended to be identical to using GltfModelBuilder, except synchronous. GameObject go; bool fromPoly = (m_Location.GetLocationType() == Location.Type.PolyAssetId); string localPath = m_Location.AbsolutePath; string assetLocation = Path.GetDirectoryName(localPath); // Synchronous, so don't use slow image loading var loader = new TiltBrushUriLoader(localPath, assetLocation, loadImages: false); try { ImportGltf.GltfImportResult result = ImportGltf.Import( localPath, loader, new ImportMaterialCollector(assetLocation, uniqueSeed: localPath), fromPoly ? kPolyGltfImportOptions : kGltfImportOptions); go = result.root; m_ImportMaterialCollector = (ImportMaterialCollector)result.materialCollector; } catch (Exception ex) { m_LoadError = new LoadError("Invalid data", ex.Message); go = null; m_AllowExport = false; Debug.LogException(ex); } m_AllowExport = (go != null); return(go); }
public override void OnImportAsset(AssetImportContext ctx) { IUriLoader loader = new BufferedStreamLoader( ctx.assetPath, Path.GetDirectoryName(ctx.assetPath)); ImportGltf.GltfImportResult result = ImportGltf.Import( ctx.assetPath, loader, null, kOptions); // The "identifier" param passed here is supposed to be: // - Unique to this asset // - Deterministic (if possible) foreach (var obj in result.textures) { if (!AssetDatabase.Contains(obj)) { ctx.AddObjectToAsset("Texture/" + obj.name, obj); } } foreach (var obj in result.materials) { ctx.AddObjectToAsset("Material/" + obj.name, obj); } foreach (var obj in result.meshes) { ctx.AddObjectToAsset("Mesh/" + obj.name, obj); } string objectName = Path.GetFileNameWithoutExtension(ctx.assetPath); result.root.name = objectName; ctx.AddObjectToAsset("ROOT", result.root); ctx.SetMainObject(result.root); }
protected override GameObject DoUnityThreadWork(IDisposable state__, out IEnumerable <Null> meshEnumerable, out ImportMaterialCollector importMaterialCollector) { meshEnumerable = null; importMaterialCollector = null; GameObject rootObject = null; using (IDisposable state_ = state__) { var state = state_ as ImportGltf.ImportState; if (state != null) { string assetLocation = Path.GetDirectoryName(m_localPath); // EndImport doesn't try to use the loadImages functionality of UriLoader anyway. // It knows it's on the main thread, so chooses to use Unity's fast loading. var loader = new TiltBrushUriLoader(m_localPath, assetLocation, loadImages: false); ImportGltf.GltfImportResult result = ImportGltf.EndImport( state, loader, new ImportMaterialCollector(assetLocation, uniqueSeed: m_localPath), out meshEnumerable); if (result != null) { rootObject = result.root; importMaterialCollector = (ImportMaterialCollector)result.materialCollector; } } } IsValid = (rootObject != null); return(rootObject); }
// Save like so: // PtAsset (references GameObject) // Meshes, Textures, Materials // GameObject // References sub-asset meshes in PtAsset // // Does not currently handle replacement static void SaveAsSeparateWithMeshesInAsset( ImportGltf.GltfImportResult result, string assetPath, string prefabPath) { Directory.CreateDirectory(Path.GetDirectoryName(assetPath)); Directory.CreateDirectory(Path.GetDirectoryName(prefabPath)); GameObject prefabToReplace = null; { PtAsset assetToReplace = AssetDatabase.LoadAssetAtPath <PtAsset>(assetPath); if (assetToReplace != null) { if (assetToReplace.assetPrefab == null) { Debug.LogErrorFormat("Couldn't find prefab for asset {0}.", assetToReplace); return; } prefabToReplace = assetToReplace.assetPrefab; } } PtAsset ptAsset = ScriptableObject.CreateInstance <PtAsset>(); ptAsset.title = "bogus title"; // Must make ptAsset a real asset before adding objects to it AssetDatabase.CreateAsset(ptAsset, assetPath); // Should make meshes real assets before saving them into a prefab AddResultsToAsset(result, ptAsset); AssetDatabase.ImportAsset(assetPath); // Create prefab after meshes are safely ensconced as assets result.root.AddComponent <PtAssetObject>().asset = ptAsset; GameObject newPrefab = null; if (prefabToReplace != null) { // Replace the existing prefab with our new object, without breaking prefab connections. // There's nothing but prefab in the asset so we don't need to worry about clearing // anything else out. newPrefab = PrefabUtility.ReplacePrefab( result.root, prefabToReplace, ReplacePrefabOptions.ReplaceNameBased); } else { if (File.Exists(prefabPath)) { // Could probably handle this like the case above; replace it. Debug.LogErrorFormat("Unexpected: overwriting a prefab {0}", prefabPath); } else { newPrefab = PrefabUtility.CreatePrefab(prefabPath, result.root); } } ptAsset.assetPrefab = newPrefab; EditorUtility.SetDirty(ptAsset); // Maybe not needed? AssetDatabase.Refresh(); }
/* * public override void OnImportAsset(AssetImportContext ctx) { * IUriLoader loader = new BufferedStreamLoader( * ctx.assetPath, Path.GetDirectoryName(ctx.assetPath)); * * ImportGltf.GltfImportResult result = ImportGltf.Import( * ctx.assetPath, loader, null, kOptions); * * // The "identifier" param passed here is supposed to be: * // - Unique to this asset * // - Deterministic (if possible) * foreach (var obj in result.textures) { * if (! AssetDatabase.Contains(obj)) { * ctx.AddObjectToAsset("Texture/" + obj.name, obj); * } * } * foreach (var obj in result.materials) { * ctx.AddObjectToAsset("Material/" + obj.name, obj); * } * foreach (var obj in result.meshes) { * ctx.AddObjectToAsset("Mesh/" + obj.name, obj); * } * string objectName = Path.GetFileNameWithoutExtension(ctx.assetPath); * result.root.name = objectName; * ctx.AddObjectToAsset("ROOT", result.root); * ctx.SetMainObject(result.root); * } */ public static GameObject ImportTiltBrushAsset(string path) { IUriLoader loader = new BufferedStreamLoader( path, Path.GetDirectoryName(path));//Directory.GetParent(path).ToString()); ImportGltf.GltfImportResult result = ImportGltf.Import( path, loader, null, kOptions); return(result.root); }
public static void TestSaveAsSinglePrefab() { IUriLoader binLoader = new BufferedStreamLoader(Path.GetDirectoryName(Path.Combine(RepoRoot, kMoto))); ImportGltf.GltfImportResult result = null; using (TextReader reader = new StreamReader(Path.Combine(RepoRoot, kMoto))) { result = ImportGltf.Import(GltfSchemaVersion.GLTF1, reader, binLoader, PolyImportOptions.Default()); } string prefabPath = "Assets/Poly/TestData/single_p.prefab"; SaveAsSinglePrefab(result, prefabPath); GameObject.DestroyImmediate(result.root); }
public static void TestSaveAsSeparateWithMeshesInAsset() { IUriLoader binLoader = new BufferedStreamLoader(Path.GetDirectoryName(Path.Combine(RepoRoot, kAllBrush10))); ImportGltf.GltfImportResult result = null; using (TextReader reader = new StreamReader(Path.Combine(RepoRoot, kAllBrush10))) { result = ImportGltf.Import(GltfSchemaVersion.GLTF1, reader, binLoader, PolyImportOptions.Default()); } string assetPath = "Assets/Poly/TestData/separate_a.asset"; string prefabPath = "Assets/Poly/TestData/separate_p.prefab"; SaveAsSeparateWithMeshesInAsset(result, assetPath, prefabPath); GameObject.DestroyImmediate(result.root); }
static void AddResultsToAsset(ImportGltf.GltfImportResult result, UnityEngine.Object asset) { foreach (var texture in result.textures) { AssetDatabase.AddObjectToAsset(texture, asset); } foreach (var mesh in result.meshes) { AssetDatabase.AddObjectToAsset(mesh, asset); } foreach (var material in result.materials) { AssetDatabase.AddObjectToAsset(material, asset); } }
public void Update() { // We process at most one import result per frame, to avoid doing too much work // in the main thread. ImportOperation operation; lock (finishedOperationsLock) { if (finishedOperations.Count == 0) { return; } operation = finishedOperations.Dequeue(); } if (!operation.status.ok) { // Import failed. operation.callback(operation.status, root: null, meshCreator: null); return; } try { IEnumerable meshCreator; ImportGltf.GltfImportResult result = ImportGltf.EndImport(operation.importState, operation.loader, out meshCreator); if (!operation.options.clientThrottledMainThread) { // If we're not in throttled mode, create all the meshes immediately by exhausting // the meshCreator enumeration. Otherwise, it's the caller's responsibility to // do this. foreach (var unused in meshCreator) /* empty */ } { meshCreator = null; } // Success. operation.callback(PolyStatus.Success(), result.root, meshCreator); } catch (Exception ex) { // Import failed. Debug.LogException(ex); operation.callback(PolyStatus.Error("Failed to convert import to Unity objects.", ex), root: null, meshCreator: null); } }
// Save like so: // GameObject // Meshes, Textures, Materials // Sub-objects reference sibling meshes // PtAsset references the top-level prefab // // When replacing an existing prefab: // - scene objects that reference the prefab get properly updated // - Unless explicitly destroyed, old meshes stick around in the prefab and are // not replaced (so the prefab has many meshes with duplicate names) // - Scene objects that reference mesh sub-assets keep referencing the same mesh // (if they are left around) or get dangling mesh references (if they are destroyed) // There is no known way to replace the existing meshes by name or otherwise, // unless we implement it manually (eg by mutating the mesh sub-assets) // static void SaveAsSinglePrefab( ImportGltf.GltfImportResult result, string prefabPath) { Directory.CreateDirectory(Path.GetDirectoryName(prefabPath)); PtAsset ptAsset = ScriptableObject.CreateInstance <PtAsset>(); ptAsset.name = Path.GetFileName(prefabPath).Replace(".prefab", ""); GameObject oldPrefab = AssetDatabase.LoadAssetAtPath <GameObject>(prefabPath); GameObject prefab = null; if (oldPrefab == null) { // Chicken and egg problem: the Meshes aren't assets yet, so refs to them will dangle prefab = PrefabUtility.CreatePrefab(prefabPath, result.root); AddResultsToAsset(result, prefab); // This fixes up the dangling refs prefab = PrefabUtility.ReplacePrefab(result.root, prefab); } else { // ReplacePrefab only removes the GameObjects from the asset. // Clear out all non-prefab junk (ie, meshes), because otherwise it piles up. // The main difference between LoadAllAssetRepresentations and LoadAllAssets // is that the former returns MonoBehaviours and the latter does not. foreach (var obj in AssetDatabase.LoadAllAssetRepresentationsAtPath(prefabPath)) { if (!(obj is GameObject)) { Object.DestroyImmediate(obj, allowDestroyingAssets: true); } } AddResultsToAsset(result, oldPrefab); prefab = PrefabUtility.ReplacePrefab( result.root, oldPrefab, ReplacePrefabOptions.ReplaceNameBased); } AssetDatabase.AddObjectToAsset(ptAsset, prefab); ptAsset.assetPrefab = prefab; EditorUtility.SetDirty(ptAsset); AssetDatabase.ImportAsset(prefabPath); }
/// <summary> /// Executes the given import request, producing a PtAsset and a prefab. /// </summary> /// <param name="request">The request to perform.</param> private static void ExecuteImportRequest(ImportRequest request) { PtDebug.LogFormat("Executing import request: {0}", request); string gltfFullPath = PtUtils.ToAbsolutePath(request.gltfLocalPath); string assetLocalPath = request.ptAssetLocalPath; string assetFullPath = PtUtils.ToAbsolutePath(assetLocalPath); PtAsset assetToReplace = AssetDatabase.LoadAssetAtPath <PtAsset>(assetLocalPath); GameObject prefabToReplace = null; if (assetToReplace != null) { if (assetToReplace.assetPrefab == null) { Debug.LogErrorFormat("Couldn't find prefab for asset {0}.", assetToReplace); PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Prefab not found"); return; } prefabToReplace = assetToReplace.assetPrefab; } // Determine if file is glTF2 or glTF1. bool isGltf2 = Path.GetExtension(request.gltfLocalPath) == ".gltf2"; // First, import the GLTF and build a GameObject from it. EditorUtility.DisplayProgressBar(PROGRESS_BAR_TITLE, PROGRESS_BAR_TEXT, 0.5f); // Use a SanitizedPath stream loader because any format file we have downloaded and saved to disk we // have replaced the original relative path string with the MD5 string hash. This custom stream loader // will always convert uris passed to it to this hash value, and read them from there. IUriLoader binLoader = new HashedPathBufferedStreamLoader(Path.GetDirectoryName(gltfFullPath)); ImportGltf.GltfImportResult result = null; using (TextReader reader = new StreamReader(gltfFullPath)) { result = ImportGltf.Import(isGltf2 ? GltfSchemaVersion.GLTF2 : GltfSchemaVersion.GLTF1, reader, binLoader, request.options.baseOptions); } EditorUtility.ClearProgressBar(); string baseName = PtUtils.GetPtAssetBaseName(request.polyAsset); result.root.name = baseName; // Create the asset (delete it first if it exists). if (File.Exists(assetFullPath)) { AssetDatabase.DeleteAsset(assetLocalPath); // If we are replacing an existing asset, we should rename the replacement to the new name, // since the name reflects the identity of the asset. So if the user is importing the asset // dog_a381b3g to replace what was previously cat_v81938.asset, the replacement file should // be named dog_a381b3g.asset, not cat_v81938.asset. assetLocalPath = PtUtils.GetDefaultPtAssetPath(request.polyAsset); assetFullPath = PtUtils.ToAbsolutePath(assetLocalPath); } Directory.CreateDirectory(Path.GetDirectoryName(assetFullPath)); // Create the new PtAsset and fill it in. AssetDatabase.CreateAsset(ScriptableObject.CreateInstance <PtAsset>(), assetLocalPath); PtAsset newAsset = AssetDatabase.LoadAssetAtPath <PtAsset>(assetLocalPath); newAsset.name = baseName; newAsset.title = request.polyAsset.displayName ?? ""; newAsset.author = request.polyAsset.authorName ?? ""; newAsset.license = request.polyAsset.license; newAsset.url = request.polyAsset.Url; // Ensure the imported object has a PtAssetObject component which references the PtAsset. result.root.AddComponent <PtAssetObject>().asset = newAsset; // Add all the meshes to the PtAsset. SaveMeshes(result.meshes, newAsset); // If the asset has materials, save those to the PtAsset. if (result.materials != null) { SaveMaterials(result.materials, newAsset); } // If the asset has textures, save those to the PtAsset. if (result.textures != null) { SaveTextures(result.textures, newAsset); } // Reimport is required to ensure custom asset displays correctly. AssetDatabase.ImportAsset(assetLocalPath); GameObject newPrefab; if (prefabToReplace) { // Replace the existing prefab with our new object, without breaking prefab connections. newPrefab = PrefabUtility.ReplacePrefab(result.root, prefabToReplace, ReplacePrefabOptions.ReplaceNameBased); AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(newPrefab), baseName); } else { // Create a new prefab. // Prefab path is the same as the asset path but with the extension changed to '.prefab'. string prefabLocalPath = Regex.Replace(assetLocalPath, "\\.asset$", ".prefab"); if (!prefabLocalPath.EndsWith(".prefab")) { Debug.LogErrorFormat("Error: failed to compute prefab path for {0}", assetLocalPath); PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Prefab path error"); return; } newPrefab = PrefabUtility.CreatePrefab(prefabLocalPath, result.root); } // Now ensure the asset points to the prefab. newAsset.assetPrefab = newPrefab; if (newAsset.assetPrefab == null) { Debug.LogErrorFormat("Could not get asset prefab reference for asset {0}", newAsset); PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Prefab ref error"); } GameObject.DestroyImmediate(result.root); AssetDatabase.Refresh(); if (request.options.alsoInstantiate) { PrefabUtility.InstantiatePrefab(newPrefab); } PtDebug.LogFormat("GLTF import complete: {0}", request); PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_SUCCESSFUL, isGltf2 ? "GLTF2" : "GLTF1"); // If this is a third-party asset, we need to update the attributions file. AttributionFileGenerator.Generate(/* showUi */ false); EditorWindow.GetWindow <AssetBrowserWindow>().HandleAssetImported(request.polyAsset.name); // Select the prefab in the editor so the user knows where it is. AssetDatabase.Refresh(); Selection.activeObject = newPrefab; EditorGUIUtility.PingObject(newPrefab); }