xBIM 實戰03 使用WPF技術實現IFC模型的載入與瀏覽

  • 2019 年 10 月 4 日
  • 筆記

  WPF應用程式在底層使用 DirectX ,無論設計複雜的3D圖形(這是 DirectX 的特長所在)還是繪製簡單的按鈕與文本,所有繪圖工作都是通過 DirectX 管線完成的。在硬體加速方面也帶來了好處,DirectX 在渲染圖形時會將儘可能多的工作遞交給圖形處理單元(GPU)去處理,GPU是顯示卡的專用處理器。

因為 DirectX 能理解可由顯示卡直接渲染的高層元素,如紋理和漸變,所以 DirectX 效率更高。而 GDI/GDI+不理解這些高層元素,因此必須將他們轉換成逐像素指令,而通過現代顯示卡渲染這些指令更慢。

  WPF憑藉著出色的3D渲染能力,使其成為在客戶端載入渲染3D模型不二的選擇。在3D模型查看器中載入BIM文件(.ifc格式),顯示效果如下圖所示:

主要業務邏輯如下:

 1         /// <summary>   2         ///  載入模型文件   3         /// </summary>   4         /// <param name="modelFileName"></param>   5         public void LoadAnyModel(string modelFileName)   6         {   7             var fInfo = new FileInfo(modelFileName);   8             if (!fInfo.Exists)   9                 return;  10  11             if (fInfo.FullName.ToLower() == GetOpenedModelFileName())  12                 return;  13  14             // 沒有撤回功能;如果在這一點之後失敗,那麼應該關閉當前文件。  15             CloseAndDeleteTemporaryFiles();  16             SetOpenedModelFileName(modelFileName.ToLower());  17             ProgressStatusBar.Visibility = Visibility.Visible;  18             SetWorkerForFileLoad();  19  20             var ext = fInfo.Extension.ToLower();  21             switch (ext)  22             {  23                 case ".ifc":    // Ifc 文件  24                 case ".ifcxml": // IfcXml 文件  25                 case ".ifczip": // zip 文件,包含  xbim 或者 ifc 文件  26                 case ".zip":    // zip 文件,包含  xbim 或者 ifc 文件  27                 case ".xbimf":  28                 case ".xbim":  29                     _loadFileBackgroundWorker.RunWorkerAsync(modelFileName);  30                     break;  31                 default:  32                     Logger.LogWarning("Extension '{extension}' has not been recognised.", ext);  33                     break;  34             }  35         }

其中調用的主要方法如下:

 1         /// <summary>   2         ///  整理所有打開的文件並關閉所有打開的模型   3         /// </summary>   4         private void CloseAndDeleteTemporaryFiles()   5         {   6             try   7             {   8                 if (_loadFileBackgroundWorker != null && _loadFileBackgroundWorker.IsBusy)   9                 {  10                     _loadFileBackgroundWorker.CancelAsync(); //通知執行緒取消操作  11                 }  12  13                 SetOpenedModelFileName(null);  14                 if (Model != null)  15                 {  16                     Model.Dispose();  17                     ModelProvider.ObjectInstance = null;  18                     ModelProvider.Refresh();  19                 }  20  21                 if (!(DrawingControl.DefaultLayerStyler is SurfaceLayerStyler))  22                 {  23                     SetDefaultModeStyler(null, null);  24                 }  25             }  26             finally  27             {  28                 if (!(_loadFileBackgroundWorker != null && _loadFileBackgroundWorker.IsBusy && _loadFileBackgroundWorker.CancellationPending)) //它仍然在運行,但已經取消了  29                 {  30                     if (!string.IsNullOrWhiteSpace(_temporaryXbimFileName) && File.Exists(_temporaryXbimFileName))  31                     {  32                         File.Delete(_temporaryXbimFileName);  33                     }  34                     _temporaryXbimFileName = null;  35                 }  36                 else  37                 {  38                     //它將在工作執行緒中清除  39                 }  40             }  41         }
 1         private void SetOpenedModelFileName(string ifcFilename)   2         {   3             _openedModelFileName = ifcFilename;   4             // 嘗試通過用於多執行緒的委託更新窗口標題   5             Dispatcher.BeginInvoke(new Action(delegate   6             {   7                 Title = string.IsNullOrEmpty(ifcFilename)   8                     ? "Xbim Xplorer"   9                     : "Xbim Xplorer - [" + ifcFilename + "]";  10             }));  11         }
 1         private void SetWorkerForFileLoad()   2         {   3             _loadFileBackgroundWorker = new BackgroundWorker   4             {   5                 WorkerReportsProgress = true,   6                 WorkerSupportsCancellation = true   7             };   8             _loadFileBackgroundWorker.ProgressChanged += OnProgressChanged;   9             _loadFileBackgroundWorker.DoWork += OpenAcceptableExtension;  10             _loadFileBackgroundWorker.RunWorkerCompleted += FileLoadCompleted;  11         }
 1        private void OnProgressChanged(object s, ProgressChangedEventArgs args)   2         {   3             if (args.ProgressPercentage < 0 || args.ProgressPercentage > 100)   4                 return;   5   6             Application.Current.Dispatcher.BeginInvoke(   7                 DispatcherPriority.Send,   8                 new Action(() =>   9                 {  10                     ProgressBar.Value = args.ProgressPercentage;  11                     StatusMsg.Text = (string)args.UserState;  12                 }));  13  14         }
 1        private void OpenAcceptableExtension(object s, DoWorkEventArgs args)   2         {   3             var worker = s as BackgroundWorker;   4             var selectedFilename = args.Argument as string;   5   6             try   7             {   8                 if (worker == null)   9                     throw new Exception("Background thread could not be accessed");  10                 _temporaryXbimFileName = Path.GetTempFileName();  11                 SetOpenedModelFileName(selectedFilename);  12                 var model = IfcStore.Open(selectedFilename, null, null, worker.ReportProgress, FileAccessMode);  13                 if (_meshModel)  14                 {  15                     // 匹配直接模型  16                     if (model.GeometryStore.IsEmpty)  17                     {  18                         try  19                         {  20                             var context = new Xbim3DModelContext(model);  21  22                             if (!_multiThreading)  23                                 context.MaxThreads = 1;  24 #if FastExtrusion  25                             context.UseSimplifiedFastExtruder = _simpleFastExtrusion;  26 #endif  27                             SetDeflection(model);  28                             // 升級到新的幾何圖形表示,使用默認的三維模型  29                             context.CreateContext(worker.ReportProgress, App.ContextWcsAdjustment);  30                         }  31                         catch (Exception geomEx)  32                         {  33                             var sb = new StringBuilder();  34                             sb.AppendLine($"Error creating geometry context of '{selectedFilename}' {geomEx.StackTrace}.");  35                             var newException = new Exception(sb.ToString(), geomEx);  36                             Logger.LogError(0, newException, "Error creating geometry context of {filename}", selectedFilename);  37                         }  38                     }  39  40                     // 匹配引用  41                     foreach (var modelReference in model.ReferencedModels)  42                     {  43                         // 根據需要創建聯合幾何體上下文  44                         Debug.WriteLine(modelReference.Name);  45                         if (modelReference.Model == null)  46                             continue;  47                         if (!modelReference.Model.GeometryStore.IsEmpty)  48                             continue;  49                         var context = new Xbim3DModelContext(modelReference.Model);  50                         if (!_multiThreading)  51                             context.MaxThreads = 1;  52 #if FastExtrusion  53                         context.UseSimplifiedFastExtruder = _simpleFastExtrusion;  54 #endif  55                         SetDeflection(modelReference.Model);  56                         // 升級到新的幾何圖形表示,使用默認的三維模型  57                         context.CreateContext(worker.ReportProgress, App.ContextWcsAdjustment);  58                     }  59                     if (worker.CancellationPending)  60                     // 如果已請求取消,則不要打開結果文件  61                     {  62                         try  63                         {  64                             model.Close();  65                             if (File.Exists(_temporaryXbimFileName))  66                             {  67                                 File.Delete(_temporaryXbimFileName);  68                             }  69  70                             _temporaryXbimFileName = null;  71                             SetOpenedModelFileName(null);  72                         }  73                         catch (Exception ex)  74                         {  75                             Logger.LogError(0, ex, "Failed to cancel open of model {filename}", selectedFilename);  76                         }  77                         return;  78                     }  79                 }  80                 else  81                 {  82                     Logger.LogWarning("Settings prevent mesh creation.");  83                 }  84                 args.Result = model;  85             }  86             catch (Exception ex)  87             {  88                 var sb = new StringBuilder();  89                 sb.AppendLine($"Error opening '{selectedFilename}' {ex.StackTrace}.");  90                 var newException = new Exception(sb.ToString(), ex);  91                 Logger.LogError(0, ex, "Error opening {filename}", selectedFilename);  92                 args.Result = newException;  93             }  94         }
 1        private void FileLoadCompleted(object s, RunWorkerCompletedEventArgs args)   2         {   3             if (args.Result is IfcStore)   4             {   5                 // 這將觸發將模型載入到視圖中的事件   6                 ModelProvider.ObjectInstance = args.Result;   7                 ModelProvider.Refresh();   8                 ProgressBar.Value = 0;   9                 StatusMsg.Text = "Ready";  10                 AddRecentFile();  11             }  12             else  13             {  14                 var errMsg = args.Result as string;  15                 if (!string.IsNullOrEmpty(errMsg))  16                 {  17                     MessageBox.Show(this, errMsg, "Error Opening File", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.None, MessageBoxOptions.None);  18                 }  19  20                 var exception = args.Result as Exception;  21                 if (exception != null)  22                 {  23                     var sb = new StringBuilder();  24  25                     var indent = "";  26                     while (exception != null)  27                     {  28                         sb.AppendFormat("{0}{1}n", indent, exception.Message);  29                         exception = exception.InnerException;  30                         indent += "t";  31                     }  32                     MessageBox.Show(this, sb.ToString(), "Error Opening Ifc File", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.None, MessageBoxOptions.None);  33                 }  34                 ProgressBar.Value = 0;  35                 StatusMsg.Text = "Error/Ready";  36                 SetOpenedModelFileName("");  37             }  38             FireLoadingComplete(s, args);  39         }
 1         /// <summary>   2         ///  模型文件載入完成事件   3         /// </summary>   4         public event LoadingCompleteEventHandler LoadingComplete;   5   6         private void FireLoadingComplete(object s, RunWorkerCompletedEventArgs args)   7         {   8             if (LoadingComplete != null)   9             {  10                 LoadingComplete(s, args);  11             }  12         }