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 }