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 }