// Pass: // parameterName - // used for the FbxTexture name. Can be something arbitrary since as far as I know, // the texture node's name is unused by importers. private static void AddTextureToMaterial( FbxExportGlobals G, ExportFileReference fileRef, FbxSurfaceLambert fbxMaterial, string parameterName) { Debug.Assert(File.Exists(fileRef.m_originalLocation)); var destPath = Path.Combine(G.m_outputDir, fileRef.m_uri); if (!File.Exists(destPath)) { if (!FileUtils.InitializeDirectoryWithUserError(Path.GetDirectoryName(destPath))) { return; } File.Copy(fileRef.m_originalLocation, destPath); } // It's kind of weird that the parameter name is used for the texture node's name, // but as far as I can tell nobody cares about that name, so whatever. FbxFileTexture fbxTexture = FbxFileTexture.Create(G.m_scene, parameterName); fbxTexture.SetFileName(destPath); fbxTexture.SetTextureUse(FbxTexture.ETextureUse.eStandard); fbxTexture.SetMappingType(FbxTexture.EMappingType.eUV); fbxTexture.SetMaterialUse(FbxFileTexture.EMaterialUse.eModelMaterial); fbxTexture.UVSet.Set(new FbxString("uv0")); // It's also weird that we only ever assign to the Diffuse slot. // Shouldn't we be looking at the parameter name and assigning to Diffuse, Normal, etc // based on what we see? // TODO: check fbxMaterial.Diffuse.ConnectSrcObject(fbxTexture); fbxMaterial.TransparentColor.ConnectSrcObject(fbxTexture); }
public void Init() { string cacheDir = Path.Combine(Application.persistentDataPath, "assetCache"); m_CacheDir = cacheDir.Replace("\\", "/"); // Use a different directory from m_CacheDir to avoid having to make ValidGltfCache() // smart enough to allow directories with only a thumbnail and no asset data. m_ThumbnailCacheDir = Path.Combine(Application.persistentDataPath, "assetThumbnail") .Replace("\\", "/"); m_ActiveRequests = new List<AssetGetter>(); m_RequestLoadQueue = new List<ModelLoadRequest>(); m_LoadQueue = new List<ModelLoadRequest>(); FileUtils.InitializeDirectoryWithUserError(m_CacheDir, "Failed to create asset cache"); // Create and populate model map. m_ModelsByAssetId = new Dictionary<string, Model>(); try { foreach (string folderPath in EnumerateCacheDirectories()) { string folderName = Path.GetFileName(folderPath); string gltfFile = ValidGltfCache(folderPath, folderName); if (gltfFile != null) { string path = Path.Combine(folderPath, folderName); path = Path.Combine(path, gltfFile); m_ModelsByAssetId[folderName] = new Model(Model.Location.PolyAsset(folderName, path)); } else { Debug.LogWarningFormat("Deleting invalid cache folder {0}", folderName); Directory.Delete(folderPath, true); } } } catch (DirectoryNotFoundException e) { Debug.LogException(e); } catch (UnauthorizedAccessException e) { Debug.LogException(e); } m_AssetSetByType = new Dictionary<PolySetType, AssetSet> { { PolySetType.User, new AssetSet() }, { PolySetType.Liked, new AssetSet() }, { PolySetType.Featured, new AssetSet { m_RefreshRequested = true } } }; App.Instance.AppExit += () => { var models = EnumerateCacheDirectories() .OrderBy(d => Directory.GetLastAccessTimeUtc(d)).ToArray(); for (int excess = models.Count() - kAssetDiskCacheSize; excess > 0; excess--) { Directory.Delete(models[excess - 1], true); } }; }
// Returns a writable name for the export file, creating any directories as necessary; // or null on failure. private static string MakeExportPath(string parent, string basename, string ext) { string child = FileUtils.GenerateNonexistentFilename(parent, basename: ext, extension: ""); if (!FileUtils.InitializeDirectoryWithUserError( child, "Failed to create export directory for " + ext)) { return(null); } return(Path.Combine(child, string.Format("{0}.{1}", basename, ext))); }
private static string GetExportBaseName() { var current = SaveLoadScript.m_Instance.SceneFile; string basename = (current.Valid) ? Path.GetFileNameWithoutExtension(current.FullPath).Replace(" ", "_") : "Untitled"; string directoryName = FileUtils.GenerateNonexistentFilename( App.UserExportPath(), basename, ""); if (!FileUtils.InitializeDirectoryWithUserError(directoryName, "Failed to export")) { throw new InvalidOperationException("Directory full (?!)"); } return(Path.Combine(directoryName, basename)); }
private static void CreateTestDirectory() { if (sm_TestDirectory == null) { sm_TestDirectory = Path.Combine(Path.GetTempPath(), "TiltBrushUnitTests"); } if (Directory.Exists(sm_TestDirectory)) { Directory.Delete(sm_TestDirectory, true); } string failureMessage = string.Format("Can't create unit test directory: {0}", sm_TestDirectory); bool dirCreated = FileUtils.InitializeDirectoryWithUserError(sm_TestDirectory, failureMessage); if (!dirCreated) { throw new VrAssetServiceException(failureMessage); } }
private static void ExportBrushStrokesFbx() { var current = SaveLoadScript.m_Instance.SceneFile; string basename = (current.Valid) ? Path.GetFileNameWithoutExtension(current.FullPath).Replace(" ", "_") : "Untitled"; string directoryName = FileUtils.GenerateNonexistentFilename( App.UserExportPath(), basename, ""); if (!FileUtils.InitializeDirectoryWithUserError(directoryName, "Failed to export")) { return; } string fbxName = Path.Combine(directoryName, basename + ".fbx"); ExportFbx.Export(fbxName, ExportFbx.kFbxAscii); }
public TemporaryUploadDirectory() { #if UNITY_EDITOR if (App.Config && App.Config.m_DebugUpload) { // Delay deleting the directory until the next upload string dirName = Path.Combine(Application.temporaryCachePath, "Upload"); if (Directory.Exists(dirName)) { try { Directory.Delete(dirName, true); } catch (Exception e) { Debug.LogException(e); } } } #endif Value = FileUtils.GenerateNonexistentFilename( Application.temporaryCachePath, "Upload", ""); string failureMessage = $"Can't create upload directory: {Value}"; bool dirCreated = FileUtils.InitializeDirectoryWithUserError(Value, failureMessage); if (!dirCreated) { throw new VrAssetServiceException(failureMessage); } }
static public bool StartVideoCapture(string filePath, VideoRecorder recorder, UsdPathSerializer usdPathSerializer, bool offlineRender = false) { // Only one video at a time. if (m_ActiveVideoRecording != null) { return(false); } // Don't start recording unless there is enough space left. if (!FileUtils.InitializeDirectoryWithUserError( Path.GetDirectoryName(filePath), "Failed to start video capture")) { return(false); } // Vertical video is disabled. recorder.IsPortrait = false; // Start the capture first, which may fail, so do this before toggling any state. // While the state below is important for the actual frame capture, starting the capture process // does not require it. int sampleRate = 0; if (AudioCaptureManager.m_Instance.IsCapturingAudio) { sampleRate = AudioCaptureManager.m_Instance.SampleRate; } if (!recorder.StartCapture(filePath, sampleRate, AudioCaptureManager.m_Instance.IsCapturingAudio, offlineRender, offlineRender ? App.UserConfig.Video.OfflineFPS : App.UserConfig.Video.FPS)) { OutputWindowScript.ReportFileSaved("Failed to start capture!", null, OutputWindowScript.InfoCardSpawnPos.Brush); return(false); } m_ActiveVideoRecording = recorder; // Perform any necessary VR camera rendering optimizations to reduce CPU & GPU workload // Debug reduce quality for capture. // XXX This should just be ADAPTIVE RENDERING if (m_DebugVideoCaptureQualityLevel != -1) { m_PreCaptureQualityLevel = QualityControls.m_Instance.QualityLevel; QualityControls.m_Instance.QualityLevel = m_DebugVideoCaptureQualityLevel; } App.VrSdk.SetHmdScalingFactor(m_VideoCaptureResolutionScale); // Setup SSAA RenderWrapper wrapper = recorder.gameObject.GetComponent <RenderWrapper>(); m_PreCaptureSuperSampling = wrapper.SuperSampling; wrapper.SuperSampling = m_SuperSampling; #if USD_SUPPORTED // Read from the Usd serializer if we're recording offline. Write to it otherwise. m_UsdPathSerializer = usdPathSerializer; if (!offlineRender) { m_UsdPath = SaveLoadScript.m_Instance.SceneFile.Valid ? Path.ChangeExtension(filePath, "usda") : null; m_RecordingStopwatch = new System.Diagnostics.Stopwatch(); m_RecordingStopwatch.Start(); if (!m_UsdPathSerializer.StartRecording(m_UsdPath)) { UnityEngine.Object.Destroy(m_UsdPathSerializer); m_UsdPathSerializer = null; } } else { recorder.SetCaptureFramerate(Mathf.RoundToInt(App.UserConfig.Video.OfflineFPS)); m_UsdPath = null; if (m_UsdPathSerializer.Load(App.Config.m_VideoPathToRender)) { m_UsdPathSerializer.StartPlayback(); } else { UnityEngine.Object.Destroy(m_UsdPathSerializer); m_UsdPathSerializer = null; } } #endif return(true); }
public static string ExportScene() { var current = SaveLoadScript.m_Instance.SceneFile; string safeHumanName = FileUtils.SanitizeFilename(current.HumanName); string basename = FileUtils.SanitizeFilename( (current.Valid && (safeHumanName != "")) ? safeHumanName : "Untitled"); string parent = FileUtils.GenerateNonexistentFilename(App.UserExportPath(), basename, ""); if (!FileUtils.InitializeDirectoryWithUserError( parent, "Failed to create export directory")) { return(""); } // Set up progress bar. var progress = new Progress(); if (App.PlatformConfig.EnableExportJson) { progress.SetWork("json"); } #if FBX_SUPPORTED if (App.PlatformConfig.EnableExportFbx) { progress.SetWork("fbx"); } #endif #if USD_SUPPORTED if (App.PlatformConfig.EnableExportUsd) { progress.SetWork("usd"); } #endif #if (UNITY_EDITOR || EXPERIMENTAL_ENABLED) if (Config.IsExperimental) { progress.SetWork("wrl"); progress.SetWork("stl"); #if FBX_SUPPORTED progress.SetWork("obj"); #endif } #endif if (App.PlatformConfig.EnableExportGlb) { progress.SetWork("glb"); } string filename; if (App.PlatformConfig.EnableExportJson && (filename = MakeExportPath(parent, basename, "json")) != null) { using (var unused = new AutoTimer("raw export")) { OverlayManager.m_Instance.UpdateProgress(0.1f); ExportRaw.Export(filename); } } progress.CompleteWork("json"); #if FBX_SUPPORTED if (App.PlatformConfig.EnableExportFbx && (filename = MakeExportPath(parent, basename, "fbx")) != null) { using (var unused = new AutoTimer("fbx export")) { OverlayManager.m_Instance.UpdateProgress(0.3f); ExportFbx.Export(filename, App.UserConfig.Export.ExportBinaryFbx ? ExportFbx.kFbxBinary : ExportFbx.kFbxAscii, App.UserConfig.Export.ExportFbxVersion); OverlayManager.m_Instance.UpdateProgress(0.5f); } } progress.CompleteWork("fbx"); #endif #if USD_SUPPORTED if (App.PlatformConfig.EnableExportUsd && (filename = MakeExportPath(parent, basename, "usd")) != null) { using (var unused = new AutoTimer("usd export")) { ExportUsd.ExportPayload(filename); } } progress.CompleteWork("usd"); #endif #if (UNITY_EDITOR || EXPERIMENTAL_ENABLED) if (Config.IsExperimental && (filename = MakeExportPath(parent, basename, "wrl")) != null) { ExportVrml.Export(filename); progress.CompleteWork("wrl"); } if (Config.IsExperimental && (filename = MakeExportPath(parent, basename, "stl")) != null) { ExportStl.Export(filename); progress.CompleteWork("stl"); } #if FBX_SUPPORTED if (Config.IsExperimental && App.PlatformConfig.EnableExportFbx && (filename = MakeExportPath(parent, basename, "obj")) != null) { // This has never been tested with the new fbx export style and may not work ExportFbx.Export(filename, ExportFbx.kObj); progress.CompleteWork("obj"); } #endif #endif var results = new ExportGlTF.ExportResults(); if (App.PlatformConfig.EnableExportGlb) { string extension = App.Config.m_EnableGlbVersion2 ? "glb" : "glb1"; int gltfVersion = App.Config.m_EnableGlbVersion2 ? 2 : 1; filename = MakeExportPath(parent, basename, extension); if (filename != null) { using (var unused = new AutoTimer("glb export")) { OverlayManager.m_Instance.UpdateProgress(0.7f); var exporter = new ExportGlTF(); // TBT doesn't need (or want) brush textures in the output because it replaces all // the materials, so it's fine to keep those http:. However, Sketchfab doesn't support // http textures so if uploaded, this glb will have missing textures. results = exporter.ExportBrushStrokes( filename, AxisConvention.kGltf2, binary: true, doExtras: false, includeLocalMediaContent: true, gltfVersion: gltfVersion); progress.CompleteWork("glb"); } } } OutputWindowScript.m_Instance.CreateInfoCardAtController( InputManager.ControllerName.Brush, basename + " exported! Your download will begin in 5 seconds."); ControllerConsoleScript.m_Instance.AddNewLine("Located in " + App.UserExportPath()); string readmeFilename = Path.Combine(App.UserExportPath(), kExportReadmeName); if (!File.Exists(readmeFilename) && !Directory.Exists(readmeFilename)) { File.WriteAllText(readmeFilename, kExportReadmeBody); } return(results.exportedFiles[0]); }
internal static FbxSurfaceMaterial CreateFbxMaterial( FbxExportGlobals G, string meshNamespace, IExportableMaterial exportableMaterial, HashSet <string> createdMaterialNames) { string materialName; if (exportableMaterial is BrushDescriptor) { // Toolkit uses this guid (in "N" format) to look up a BrushDescriptor. // See Toolkit's ModelImportSettings.GetDescriptorForStroke materialName = $"{exportableMaterial.UniqueName:N}_{meshNamespace}_{exportableMaterial.DurableName}"; } else if (exportableMaterial is DynamicExportableMaterial dem) { // Comes from {fbx,obj,gltf,...} import from {Poly, Media Library} // This is a customized version of a BrushDescriptor -- almost certainly // Pbr{Blend,Opaque}{Double,Single}Sided with maybe an added texture and // some of its params customized. // TBT will merge the material created by Unity for this FbxMaterial with // the premade material it has for the parent guid. materialName = $"{dem.Parent.m_Guid:N}_{meshNamespace}_{dem.DurableName}"; } else { Debug.LogWarning($"Unknown class {exportableMaterial.GetType().Name}"); materialName = $"{meshNamespace}_{exportableMaterial.DurableName}"; } // If only ExportFbx were a non-static class we could merge it with FbxExportGlobals materialName = ExportUtils.CreateUniqueName(materialName, createdMaterialNames); FbxSurfaceLambert material = FbxSurfaceLambert.Create(G.m_scene, materialName); material.Ambient.Set(new FbxDouble3(0, 0, 0)); material.Diffuse.Set(new FbxDouble3(1.0, 1.0, 1.0)); if (exportableMaterial.EmissiveFactor > 0) { material.EmissiveFactor.Set(exportableMaterial.EmissiveFactor); material.Emissive.Set(new FbxDouble3(1.0, 1.0, 1.0)); } if (exportableMaterial.BlendMode != ExportableMaterialBlendMode.None) { var blendMode = FbxProperty.Create(material, Globals.FbxStringDT, "BlendMode"); switch (exportableMaterial.BlendMode) { case ExportableMaterialBlendMode.AlphaMask: blendMode.SetString(new FbxString("AlphaMask")); material.TransparencyFactor.Set(0.2); break; case ExportableMaterialBlendMode.AdditiveBlend: blendMode.SetString(new FbxString("AdditiveBlend")); break; } } // Export the texture if (exportableMaterial.HasExportTexture()) { // This is not perfectly unique, but it is good enough for fbx export // better would be to use <durable>_<guid> but that's ugly, and nobody uses // the textures anyway, so... let's leave well enough alone for now. string albedoTextureName = exportableMaterial.DurableName; var fullTextureDir = Path.Combine(G.m_outputDir, kRelativeTextureDir); if (!Directory.Exists(fullTextureDir)) { if (!FileUtils.InitializeDirectoryWithUserError(fullTextureDir)) { throw new IOException("Cannot write textures"); } } string src = exportableMaterial.GetExportTextureFilename(); var textureFileName = albedoTextureName + ".png"; var textureFilePath = Path.Combine(fullTextureDir, textureFileName); FileInfo srcInfo = new FileInfo(src); if (srcInfo.Exists && !new FileInfo(textureFilePath).Exists) { srcInfo.CopyTo(textureFilePath); } FbxFileTexture texture = FbxFileTexture.Create(G.m_scene, albedoTextureName + "_texture"); texture.SetFileName(textureFilePath); texture.SetTextureUse(FbxTexture.ETextureUse.eStandard); texture.SetMappingType(FbxTexture.EMappingType.eUV); texture.SetMaterialUse(FbxFileTexture.EMaterialUse.eModelMaterial); texture.UVSet.Set(new FbxString("uv0")); material.Diffuse.ConnectSrcObject(texture); material.TransparentColor.ConnectSrcObject(texture); } else { foreach (var kvp in exportableMaterial.TextureUris) { string parameterName = kvp.Key; string textureUri = kvp.Value; if (ExportFileReference.IsHttp(textureUri)) { // fbx can't deal with http references to textures continue; } ExportFileReference fileRef = ExportFileReference.GetOrCreateSafeLocal( G.m_disambiguationContext, textureUri, exportableMaterial.UriBase, $"{meshNamespace}_{Path.GetFileName(textureUri)}"); AddTextureToMaterial(G, fileRef, material, parameterName); } } return(material); }
private static void ExportEnvironments() { #if !GAMEOBJ_EXPORT_TO_GLTF Debug.LogError("Enable the define and fix up the code"); #else // Save the original RenderSettings Environment.RenderSettingsLite originalRenderSettings = Environment.GetRenderSettings(); // Clear out the existing environments directory to do a clean export string projectPath = Path.GetDirectoryName(Application.dataPath); string environmentExportPath = Path.Combine(projectPath, ExportUtils.kProjectRelativeEnvironmentExportRoot); try { Directory.Delete(environmentExportPath, recursive: true); } catch (DirectoryNotFoundException) { // It's okay if this directory doesn't exist yet as it will be created later. } // Clear out the existing textures directory to do a clean export string textureExportPath = Path.Combine(projectPath, ExportUtils.kProjectRelativeTextureExportRoot); try { Directory.Delete(textureExportPath, recursive: true); } catch (DirectoryNotFoundException) { // It's okay if this directory doesn't exist yet as it will be created later. } if (!FileUtils.InitializeDirectoryWithUserError( textureExportPath, "Failed to export, can't create texture export directory")) { return; } // Get the environment TiltBrushManifest manifest = AssetDatabase.LoadAssetAtPath <TiltBrushManifest>("Assets/Manifest.asset"); foreach (Environment env in manifest.Environments) { // Copy over the RenderSettings Environment.SetRenderSettings(env.m_RenderSettings); // Set up the environment string envGuid = env.m_Guid.ToString("D"); Debug.LogFormat("Exporting environment: {0}", env.m_RenderSettings.m_EnvironmentPrefab); GameObject envPrefab = Resources.Load <GameObject>(env.m_RenderSettings.m_EnvironmentPrefab); GameObject envGameObject = UObject.Instantiate(envPrefab); envGameObject.name = envGuid; // Hide game objects that don't get exported to Poly. foreach (Transform child in envGameObject.transform) { if (SceneSettings.ExcludeFromPolyExport(child)) { child.gameObject.SetActive(false); } } // Set up the environment export directory string directoryName = Path.Combine(environmentExportPath, envGuid); if (!FileUtils.InitializeDirectoryWithUserError( directoryName, "Failed to export, can't create environment export directory")) { return; } string basename = FileUtils.SanitizeFilename(envGameObject.name); string gltfName = Path.Combine(directoryName, basename + ".gltf"); var exporter = new ExportGlTF(); exporter.ExportGameObject(envGameObject, gltfName, env); // DestroyImmediate is required because editor mode never runs object garbage collection. UObject.DestroyImmediate(envGameObject); } // Restore the original RenderSettings Environment.SetRenderSettings(originalRenderSettings); #endif }