示例#1
0
        /// Returns true on success.
        /// Returns false and shows a user-visible error on failure.
        static public bool InitializeDirectoryWithUserError(
            string directoryName,
            string failureMessage = null)
        {
            string err = null;

            try {
                if (Directory.Exists(directoryName))
                {
                    return(true);
                }
                Directory.CreateDirectory(directoryName);
            } catch (System.UnauthorizedAccessException e) {
                err = e.Message;
            } catch (IOException e) {
                err = e.Message;
            }

            if (failureMessage == null)
            {
                failureMessage = "Failed to create directory";
            }
            if (err != null)
            {
                OutputWindowScript.Error(failureMessage, err);
                return(false);
            }
            return(true);
        }
示例#2
0
        /// Main entry point
        public static bool Export(string outputFile, string format, string fbxVersion = null)
        {
            using (var G = new FbxExportGlobals(outputFile)) {
                int fmt = G.m_manager.GetIOPluginRegistry().FindWriterIDByDescription(format);
                if (!G.m_exporter.Initialize(outputFile, fmt, G.m_ioSettings))
                {
                    OutputWindowScript.Error("FBX export failed", "Could not initialize exporter");
                    return(false);
                }
                if (!String.IsNullOrEmpty(fbxVersion))
                {
                    G.m_exporter.SetFileExportVersion(new FbxString(fbxVersion));
                }

                G.m_scene = FbxScene.Create(G.m_manager, "scene");
                if (G.m_scene == null)
                {
                    OutputWindowScript.Error("FBX export failed", "Could not initialize scene");
                    return(false);
                }

                String version = string.Format("{0}.{1}", App.Config.m_VersionNumber,
                                               App.Config.m_BuildStamp);
                FbxDocumentInfo info = FbxDocumentInfo.Create(G.m_manager, "DocInfo");
                info.Original_ApplicationVendor.Set(new FbxString(App.kDisplayVendorName));
                info.Original_ApplicationName.Set(new FbxString(App.kAppDisplayName));
                info.Original_ApplicationVersion.Set(new FbxString(version));
                info.LastSaved_ApplicationVendor.Set(new FbxString(App.kDisplayVendorName));
                info.LastSaved_ApplicationName.Set(new FbxString(App.kAppDisplayName));
                info.LastSaved_ApplicationVersion.Set(new FbxString(version));
                // The toolkit's FBX parser is too simple to be able to read anything but
                // the UserData/Properties70 node, so add the extra info as a custom property
                var stringType = info.Original_ApplicationVersion.GetPropertyDataType();
                var prop       = FbxProperty.Create(info.Original, stringType, "RequiredToolkitVersion");
                prop.SetString(FbxUtils.kRequiredToolkitVersion);

                G.m_scene.SetDocumentInfo(info);
                G.m_scene.GetGlobalSettings().SetSystemUnit(FbxSystemUnit.m);

                try {
                    WriteObjectsAndConnections2(G);
                    G.m_exporter.Export(G.m_scene);
                } catch (InvalidOperationException e) {
                    OutputWindowScript.Error("FBX export failed", e.Message);
                    return(false);
                } catch (IOException e) {
                    OutputWindowScript.Error("FBX export failed", e.Message);
                    return(false);
                }
                return(true);
            }
        }
示例#3
0
 private void DisplayWarnings(List <string> warnings)
 {
     if (warnings.Count > 0)
     {
         TiltBrush.ControllerConsoleScript.m_Instance.AddNewLine(
             "Loading " + Path.GetFileName(m_Location.AbsolutePath), true);
         foreach (string warning in warnings)
         {
             TiltBrush.ControllerConsoleScript.m_Instance.AddNewLine(
                 OutputWindowScript.GetShorterFileName(warning.Replace("/", @"\")), false);
         }
     }
 }
示例#4
0
        // Emits user-visible error on failure
        protected IEnumerator SpawnModelCoroutine(string reason)
        {
            if (m_Model == null)
            {
                // Same as calling Model.RequestModelPreload -> RequestModelLoadInternal, except
                // this won't ignore the request if the load-into-memory previously failed.
                App.PolyAssetCatalog.RequestModelLoad(m_PacAsset.AssetId, reason);
                m_LoadingOverlay.SetActive(true);
            }
            // It is possible from this section forward that the user may have moved on to a different page
            // on the Poly panel, which is why we use a local copy of 'model' rather than m_Model.
            string assetId = m_PacAsset.AssetId;
            Model  model;

            // A model in the catalog will become non-null once the gltf has been downloaded or is in the
            // cache.
            while ((model = App.PolyAssetCatalog.GetModel(assetId)) == null)
            {
                yield return(null);
            }
            // We only want to disable the loading overlay if the button is still referring to the same
            // asset.
            if (assetId == m_PacAsset.AssetId)
            {
                m_LoadingOverlay.SetActive(false);
            }

            // A model becomes valid once the gltf has been successfully read into a Unity mesh.
            if (!model.m_Valid)
            {
                // The model might be in the "loaded with error" state, but it seems harmless to try again.
                // If the user keeps clicking, we'll keep trying.
                yield return(model.LoadFullyCoroutine(reason));

                Debug.Assert(model.m_Valid || model.Error != null);
            }

            if (!model.m_Valid)
            {
                // TODO: Is there a reason not to do this unconditionally?
                if (assetId == m_PacAsset.AssetId)
                {
                    RefreshModelButton();
                }
                OutputWindowScript.Error($"Couldn't load model: {model.Error?.message}", model.Error?.detail);
            }
            else
            {
                SpawnValidModel(model);
            }
        }
示例#5
0
 static public bool CheckDiskSpaceWithError(string path, string error = null)
 {
     if (error == null)
     {
         error = "Out of disk space!";
     }
     if (!FileUtils.HasFreeSpace(path))
     {
         OutputWindowScript.ReportFileSaved(error, null,
                                            OutputWindowScript.InfoCardSpawnPos.Brush);
         return(false);
     }
     return(true);
 }
        void Awake()
        {
            m_Instance = this;

            m_BasePosition = m_BaseOffset;

            m_Lines = new OutputLine[m_NumLines];
            for (int i = 0; i < m_NumLines; ++i)
            {
                m_Lines[i]                  = new OutputLine();
                m_Lines[i].m_Line           = (GameObject)Instantiate(m_TextLine, m_BasePosition, Quaternion.identity);
                m_Lines[i].m_Renderer       = m_Lines[i].m_Line.GetComponent <Renderer>();
                m_Lines[i].m_TextMesh       = m_Lines[i].m_Line.GetComponent <TextMeshPro>();
                m_Lines[i].m_Color          = m_TextColor;
                m_Lines[i].m_TextMesh.color = m_TextColor;
                m_Lines[i].m_Line.transform.SetParent(transform);
                m_Lines[i].m_Line.transform.localPosition += new Vector3(m_LineOffset, 0, 0);
                m_Lines[i].m_Line.SetActive(false);
            }

            m_LineQueue = new Queue <QueuedLine>(m_NumLines * 4);
        }
示例#7
0
        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);
        }
示例#8
0
        /// The directory must already have been created
        public bool WriteToDisk()
        {
            // First, iterate over everything that needs to be written to disk and verify it's valid.
            // If any invalid elements are found, it's likely due to a download failure and we will abort
            // the entire process.
            if (m_RootElement.assetBytes == null)
            {
                return(false);
            }
            ulong requiredDiskSpace = (ulong)m_RootElement.assetBytes.Length;

            for (int j = 0; j < m_ResourceElements.Count; ++j)
            {
                if (m_ResourceElements[j].assetBytes == null)
                {
                    // Download failed on one of the elements.
                    return(false);
                }
                else
                {
                    requiredDiskSpace += (ulong)m_ResourceElements[j].assetBytes.Length;
                }
            }

            // Next, check to see if we have enough disk space to write all the files.
            string assetDir = App.PolyAssetCatalog.GetCacheDirectoryForAsset(m_AssetId);

            if (!FileUtils.HasFreeSpace(assetDir, requiredDiskSpace / (1024 * 1024)))
            {
                OutputWindowScript.Error(String.Format("Out of disk space! {0} {1}",
                                                       requiredDiskSpace, m_AssetId));
                return(false);
            }

            //
            // Next, begin writing to disk, remembering each file written to make the operation atomic.
            //
            var written = new List <string>();

            if (!Directory.Exists(assetDir))
            {
                UnityEngine.Debug.LogErrorFormat("Caller did not create directory for me: {0}", assetDir);
            }
            string rootFilePath = Path.Combine(assetDir, GetPolySanitizedFilePath(m_RootElement.filePath));

            if (!FileUtils.InitializeDirectory(Path.GetDirectoryName(rootFilePath)) ||
                !FileUtils.WriteBytesIgnoreExceptions(m_RootElement.assetBytes, rootFilePath))
            {
                return(false);
            }
            written.Add(rootFilePath);

            // Write all resources to disk
            for (int j = 0; j < m_ResourceElements.Count; ++j)
            {
                string filePath = Path.Combine(assetDir, GetPolySanitizedFilePath(m_ResourceElements[j].filePath));
                if (!FileUtils.InitializeDirectory(Path.GetDirectoryName(filePath)) ||
                    !FileUtils.WriteBytesIgnoreExceptions(m_ResourceElements[j].assetBytes, filePath))
                {
                    RemoveFiles(written);
                    return(false);
                }
                written.Add(filePath);
            }
            return(true);
        }
示例#9
0
        private ExportResults ExportHelper(
            SceneStatePayload payload,
            string outputFile,
            bool binary,
            bool doExtras,
            int gltfVersion,
            bool allowHttpUri)
        {
            // TODO: Ownership of this temp directory is sloppy.
            // Payload and export share the same dir and we assume that the exporter:
            // 1. will not write files whose names conflict with payload's
            // 2. will clean up the entire directory when done
            // This works, as long as the payload isn't used for more than one export (it currently isn't)
            using (var exporter = new GlTF_ScriptableExporter(payload.temporaryDirectory, gltfVersion))
            {
                exporter.AllowHttpUri = allowHttpUri;
                try
                {
                    m_exporter        = exporter;
                    exporter.G.binary = binary;

                    exporter.BeginExport(outputFile);
                    exporter.SetMetadata(payload.generator, copyright: null);
                    if (doExtras)
                    {
                        SetExtras(exporter, payload);
                    }

                    if (payload.env.skyCubemap != null)
                    {
                        // Add the skybox texture to the export.
                        string texturePath     = ExportUtils.GetTexturePath(payload.env.skyCubemap);
                        string textureFilename = Path.GetFileName(texturePath);
                        exporter.G.extras["TB_EnvironmentSkybox"] =
                            ExportFileReference.CreateLocal(texturePath, textureFilename);
                    }

                    WriteObjectsAndConnections(exporter, payload);

                    string[] exportedFiles = exporter.EndExport();
                    return(new ExportResults
                    {
                        success = true,
                        exportedFiles = exportedFiles,
                        numTris = exporter.NumTris
                    });
                }
                catch (InvalidOperationException e)
                {
                    OutputWindowScript.Error("glTF export failed", e.Message);
                    // TODO: anti-pattern. Let the exception bubble up so caller can log it properly
                    // Actually, InvalidOperationException is now somewhat expected in experimental, since
                    // the gltf exporter does not check IExportableMaterial.SupportsDetailedMaterialInfo.
                    // But we still want the logging for standalone builds.
                    Debug.LogException(e);
                    return(new ExportResults {
                        success = false
                    });
                }
                catch (IOException e)
                {
                    OutputWindowScript.Error("glTF export failed", e.Message);
                    return(new ExportResults {
                        success = false
                    });
                }
                finally
                {
                    payload.Destroy();
                    // The lifetime of ExportGlTF, GlTF_ScriptableExporter, and GlTF_Globals instances
                    // is identical. This is solely to be pedantic.
                    m_exporter = null;
                }
            }
        }
示例#10
0
        void LoadModel()
        {
            // Clean up existing model
            if (m_ModelInstance != null)
            {
                GameObject.Destroy(m_ModelInstance.gameObject);
            }

            // Early out if we don't have a model to clone.
            // This can happen if model loading is deferred.
            if (m_Model == null || m_Model.m_ModelParent == null)
            {
                return;
            }

            m_ModelInstance = Instantiate(m_Model.m_ModelParent);
            m_ModelInstance.gameObject.SetActive(true);
            m_ModelInstance.parent = this.transform;

            Coords.AsLocal[m_ModelInstance] = TrTransform.identity;
            float maxExtent = 2 * Mathf.Max(m_Model.m_MeshBounds.extents.x,
                                            Mathf.Max(m_Model.m_MeshBounds.extents.y, m_Model.m_MeshBounds.extents.z));
            float size;

            if (maxExtent == 0.0f)
            {
                // If we created a widget with a model that doesn't have geo, we won't have calculated a
                // bounds worth much.  In that case, give us a default size.
                size = 1.0f;
            }
            else
            {
                size = kInitialSizeMeters_RS * App.METERS_TO_UNITS / maxExtent;
            }

            m_InitSize_CS = size / Coords.CanvasPose.scale;

            // Models are created in the main canvas.  Cache model layer in case it's overridden later.
            HierarchyUtils.RecursivelySetLayer(transform, App.Scene.MainCanvas.gameObject.layer);
            m_BackupLayer = m_ModelInstance.gameObject.layer;

            // Set a new batchId on this model so it can be picked up in GPU intersections.
            m_BatchId = GpuIntersector.GetNextBatchId();
            HierarchyUtils.RecursivelySetMaterialBatchID(m_ModelInstance, m_BatchId);
            WidgetManager.m_Instance.AddWidgetToBatchMap(this, m_BatchId);

            Vector3 ratios = GetBoundsRatios(m_Model.m_MeshBounds);

            m_ContainerBloat.x = Mathf.Max(0, m_MinContainerRatio - ratios.x);
            m_ContainerBloat.y = Mathf.Max(0, m_MinContainerRatio - ratios.y);
            m_ContainerBloat.z = Mathf.Max(0, m_MinContainerRatio - ratios.z);
            m_ContainerBloat  /= m_MinContainerRatio;               // Normalize for the min ratio.
            m_ContainerBloat  *= m_MaxBloat / App.Scene.Pose.scale; // Apply bloat to appropriate axes.

            m_BoxCollider.size = m_Model.m_MeshBounds.size + m_ContainerBloat;
            m_BoxCollider.transform.localPosition = m_Model.m_MeshBounds.center;

            InitSnapGhost(m_Model.m_ModelParent, m_ModelInstance);

            // Remove previous model vertex recording.
            WidgetManager.m_Instance.AdjustModelVertCount(-m_NumVertsTrackedByWidgetManager);
            m_NumVertsTrackedByWidgetManager = 0;

            m_ObjModelScript = GetComponentInChildren <ObjModelScript>();
            m_ObjModelScript.Init();
            if (m_ObjModelScript.NumMeshes == 0)
            {
                OutputWindowScript.Error("No usable geometry in model");
            }
            else
            {
                m_NumVertsTrackedByWidgetManager = m_ObjModelScript.GetNumVertsInMeshes();
                WidgetManager.m_Instance.AdjustModelVertCount(m_NumVertsTrackedByWidgetManager);
            }

            if (m_Model.IsCached())
            {
                m_Model.RefreshCache();
            }
        }
示例#11
0
        public IEnumerator ShareVideo(string filename)
        {
            m_UploadsInProgress++;

            if (!App.GoogleIdentity.LoggedIn)
            {
                // Need user to login
                yield return(App.GoogleIdentity.ReauthorizeAsync().AsIeNull());
            }
            FileInfo fileInfo = new FileInfo(filename);
            long     filesize = fileInfo.Length;

            if (filesize == 0)
            {
                OutputWindowScript.Error("Video processing failed");
                m_UploadsInProgress--;
                yield break;
            }

            string uploadUrl = null;

            using (UnityWebRequest www = InitResumableUpload(filename, filesize)) {
                yield return(App.GoogleIdentity.Authenticate(www).AsIeNull());

                yield return(www.SendWebRequest());

                if (www.isNetworkError)
                {
                    OutputWindowScript.Error("Network error");
                    m_UploadsInProgress--;
                    yield break;
                }

                if (www.responseCode == 200)
                {
                    uploadUrl = www.GetResponseHeader("Location");
                }
                else if (www.responseCode == 401)
                {
                    OutputWindowScript.Error("Authorization failed");
                    m_UploadsInProgress--;
                    yield break;
                }
                else if (www.responseCode == 403)
                {
                    OutputWindowScript.Error(
                        "Upload forbidden.\nYou may need to create a YouTube channel first.");
                    m_UploadsInProgress--;
                    yield break;
                }
                else
                {
                    OutputWindowScript.Error(String.Format("Error pushing to YouTube: {0}", www.responseCode));
                    m_UploadsInProgress--;
                    yield break;
                }
            }

            if (uploadUrl == null)
            {
                OutputWindowScript.Error(
                    "Upload failed.\nYou may need to create a YouTube channel first.");
                m_UploadsInProgress--;
                yield break;
            }

            m_UploadStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
            byte[] buffer = new byte[filesize];
            m_UploadStream.Read(buffer, 0, (int)filesize);
            using (UnityWebRequest www = UnityWebRequest.Put(uploadUrl, buffer)) {
                www.SetRequestHeader("Content-Type", "video/*");
                yield return(App.GoogleIdentity.Authenticate(www).AsIeNull());

                yield return(www.SendWebRequest());

                m_UploadStream.Close();
                m_UploadStream = null;

                if (www.isNetworkError)
                {
                    OutputWindowScript.Error("Network error");
                    m_UploadsInProgress--;
                    yield break;
                }
                else if (www.responseCode >= 400)
                {
                    // Some kind of error - extract the error message
                    while (!www.downloadHandler.isDone)
                    {
                        yield return(null);
                    }
                    JObject json    = JObject.Parse(www.downloadHandler.text);
                    string  message = json["message"].ToString();
                    OutputWindowScript.Error(String.Format("YouTube error: {0}", message));
                    m_UploadsInProgress--;
                    yield break;
                }
            }

            App.OpenURL("http://www.youtube.com/my_videos");
            OutputWindowScript.m_Instance.CreateInfoCardAtController(InputManager.ControllerName.Brush,
                                                                     m_UploadedMessage);
            m_UploadsInProgress--;
        }