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         }