[WorldWind学习]11.TerrainViewer插件和双线程

 TerrainViewer的入口Main()函数:

View Code
 1  static void Main(string[] args)
 2  {
 3             using (Viewer viewer = new Viewer())
 4               {
 5                  viewer.ProcessArgs(args);        // Read command line args
 6                  viewer.InitializeDevice();        // Direct 3D device setup
 7                 viewer.InitializeKeyboard();    // Keyboard setup
 8                  viewer.InitializeMapList();        // Create Map menu
 9                  viewer.InitializeVerticalFactorMenu(); // Create vertical factor menu
10                 viewer.InitializeTexturesMenu();// Create Textures menu
11                  viewer.InitializeSidesMenu();    // Create Sides menu
12                  viewer.InitializeSkyMenu();        // Create Sky menu
13                  viewer.MapMenuSelectMap(0);        // Load first map
14                  viewer.Show();
15                  Application.Run(viewer);        // Run
16              }
17 }

初始化设备:InitializeDevice()

鼠标交互的基本原理:

根据地图位置(参数dist)设置相机,之后相机位置保持不变。滚轮操作和键盘操作更改dist,修改相机位置。

如下为处理键盘操作的部分代码。

 1 // Change Distance
 2 if (keys[Key.NumPadPlus] && !shift && !ctrl)    
 3       {
 4     dist -= dist * 0.02f;
 5     redraw = true;
 6       }
 7 if (keys[Key.NumPadMinus] && !shift && !ctrl)
 8       {
 9     dist += dist * 0.02f;
10     redraw = true;
11       }

查看重载的OnPaint函数,在OnPaint中调用了CameraViewSetup()方法。

1 // Camera setup
2   private void CameraViewSetup()
3   {
4       float aspectRatio =  (float)device.Viewport.Width / device.Viewport.Height;
5       device.Transform.Projection = Matrix.PerspectiveFovLH(fov, aspectRatio, mapWidth == 0 ? 15f : (float)(mapWidth / 10), mapWidth == 0 ? 5000f : (float)(mapWidth * 3));
6       device.Transform.View = Matrix.LookAtLH(new Vector3(0,0,dist), new Vector3(0,0,0), new Vector3(1,0,0));
7   }

鼠标左键点击拖动,移动地图,更改了参数dx和dy。

鼠标右键点击拖动,旋转地图,更改了参数angle和angle2

 1 protected override void OnMouseMove(MouseEventArgs e)
 2         {
 3             if(mouseDownStartPosition == Point.Empty)
 4                 return;
 5             // Mouse drag
 6             bool isMouseLeftButtonDown = ((int)e.Button & (int)MouseButtons.Left) != 0;
 7             bool isMouseRightButtonDown = ((int)e.Button & (int)MouseButtons.Right) != 0;
 8             double dxMouse = this.mouseDownStartPosition.X - e.X;
 9             double dyMouse = this.mouseDownStartPosition.Y - e.Y;
10             if(isMouseLeftButtonDown && !isMouseRightButtonDown) 
11             {
12                 // Move map
13                 double moveFactor = dist * 0.001f;
14                 dx = mouseDownStartDx - (float)(Math.Sin(angle) * dxMouse * moveFactor) + (float)(Math.Cos(angle) * dyMouse * moveFactor);
15                 dy = mouseDownStartDy - (float)(Math.Cos(angle) * dxMouse * moveFactor) - (float)(Math.Sin(angle) * dyMouse * moveFactor);
16                 redraw = true;
17             }
18             if(isMouseRightButtonDown && !isMouseLeftButtonDown) 
19             {
20                 // Rotate map
21                 double spinFactor = 0.003f;
22                 angle = mouseDownStartAngle + (float)(dxMouse * spinFactor * rightClickFactor);
23                 angle2 = mouseDownStartAngle2 + (float)(dyMouse * spinFactor * rightClickFactor);
24                 redraw = true;
25             }
26             if(isMouseRightButtonDown && isMouseLeftButtonDown) 
27             {
28                 // Rotate light
29                 double spinFactor = 0.003f;
30                 lightHeading = mouseDownLightAngle + (float)(dxMouse * spinFactor);
31                 lightElevation = mouseDownLightAngle2 + (float)(dyMouse * spinFactor);
32                 redraw = true;
33             }
34         }

在OnPaint函数中调用了如下代码。

1 // Translation and Orientation angle / angle2
2 if(angle2 < 0) angle2 = 0;
3 if(angle2 > Math.PI / 2) angle2 = (float)(Math.PI / 2);
4 device.Transform.World = Matrix.Translation(dx, dy, dz);
5 device.Transform.World *= Matrix.RotationZ(angle);
6 device.Transform.World *= Matrix.RotationY(angle2);

改变世界坐标矩阵,实现平移和旋转。

这个插件和WW鼠标操作的机制很像,感觉游戏或者三维就是不断地修改参数(变量为某一限定域所有),然后重新渲染场景。

不过游戏是以第一人称观察(第三人称相机)(相机在动),物体位置不动,所以有身临其境的感觉;而这个插件的基本模式则是改变模型,是物体在动(相机不动)。还是有些区别的。

当然这个插件和ArcScene也可以改变相机,不过不是基本的。

此插件通过不断触发OnPaint事件来更改场景,WW通过Idle()来更改场景?

WW是双线程的?这个再理解一下。

MainApplication方法调用worldWindow.Render();方法,即主线程负责渲染。

WorldWindow的Render()方法启动了一个名为WorldWindow.WorkerThreadFunc线程。

  1 public void Render()
  2         {
  3             long startTicks = 0;
  4             PerformanceTimer.QueryPerformanceCounter(ref startTicks);
  5 
  6             try
  7             {
  8                 this.drawArgs.BeginRender();
  9 
 10                 // Render the sky according to view - example, close to earth, render sky blue, render space as black
 11                 System.Drawing.Color backgroundColor = System.Drawing.Color.Black;
 12 
 13                 /*if(drawArgs.WorldCamera != null && 
 14                     drawArgs.WorldCamera.Altitude < 1000000f &&
 15                     m_World != null &&
 16                     m_World.Name.IndexOf("Earth") >= 0)
 17                 {
 18                     float percent = 1 - (float)(drawArgs.WorldCamera.Altitude / 1000000);
 19                     if(percent > 1.0f)
 20                         percent = 1.0f;
 21                     else if(percent < 0.0f)
 22                         percent = 0.0f;
 23 
 24                     backgroundColor = System.Drawing.Color.FromArgb(
 25                         (int)(World.Settings.SkyColor.R*percent),
 26                         (int)(World.Settings.SkyColor.G*percent),
 27                         (int)(World.Settings.SkyColor.B*percent));
 28                 }*/
 29 
 30                 m_Device3d.Clear(ClearFlags.Target | ClearFlags.ZBuffer, backgroundColor, 1.0f, 0);
 31 
 32                 if (m_World == null)
 33                 {
 34                     m_Device3d.BeginScene();
 35                     m_Device3d.EndScene();
 36                     m_Device3d.Present();
 37                     Thread.Sleep(25);
 38                     return;
 39                 }
 40 
 41                 if (m_WorkerThread == null)
 42                 {
 43                     m_WorkerThreadRunning = true;
 44                     m_WorkerThread = new Thread(new ThreadStart(WorkerThreadFunc));
 45                     m_WorkerThread.Name = "WorldWindow.WorkerThreadFunc";
 46                     m_WorkerThread.IsBackground = true;
 47                     if (World.Settings.UseBelowNormalPriorityUpdateThread)
 48                     {
 49                         m_WorkerThread.Priority = ThreadPriority.BelowNormal;
 50                     }
 51                     else
 52                     {
 53                         m_WorkerThread.Priority = ThreadPriority.Normal;
 54                     }
 55                     // BelowNormal makes rendering smooth, but on slower machines updates become slow or stops
 56                     // TODO: Implement dynamic FPS limiter (or different solution)
 57                     m_WorkerThread.Start();//启动后台线程
 58                 }
 59 
 60                 this.drawArgs.WorldCamera.Update(m_Device3d);//更新相机
 61 
 62                 m_Device3d.BeginScene();
 63 
 64                 // Set fill mode
 65                 if (renderWireFrame)
 66                     m_Device3d.RenderState.FillMode = FillMode.WireFrame;
 67                 else
 68                     m_Device3d.RenderState.FillMode = FillMode.Solid;
 69 
 70                 drawArgs.RenderWireFrame = renderWireFrame;
 71 
 72                 // Render the current planet
 73                 m_World.Render(this.drawArgs);//渲染地球
 74 
 75                 if (World.Settings.ShowCrosshairs)
 76                     this.DrawCrossHairs();
 77 
 78                 frameCounter++;
 79                 if (frameCounter == 30)
 80                 {
 81                     fps = frameCounter / (float)(DrawArgs.CurrentFrameStartTicks - lastFpsUpdateTime) * PerformanceTimer.TicksPerSecond;
 82                     frameCounter = 0;
 83                     lastFpsUpdateTime = DrawArgs.CurrentFrameStartTicks;
 84                 }
 85 
 86                 m_RootWidget.Render(drawArgs);//渲染部件
 87                 m_NewRootWidget.Render(drawArgs);
 88 
 89                 if (saveScreenShotFilePath != null)
 90                     SaveScreenShot();
 91 
 92                 drawArgs.device.RenderState.ZBufferEnable = false;
 93 
 94                 // 3D rendering complete, switch to 2D for UI rendering
 95 
 96                 // Restore normal fill mode
 97                 if (renderWireFrame)
 98                     m_Device3d.RenderState.FillMode = FillMode.Solid;
 99 
100                 // Disable fog for UI
101                 m_Device3d.RenderState.FogEnable = false;
102 
103                 /*
104                                 if(World.Settings.ShowDownloadIndicator)
105                                 {
106                                     if(m_downloadIndicator == null)
107                                         m_downloadIndicator = new DownloadIndicator();
108                                     m_downloadIndicator.Render(drawArgs);
109                                 }
110                 */
111                 RenderPositionInfo();
112 
113                 _menuBar.Render(drawArgs);
114                 m_FpsGraph.Render(drawArgs);
115                 
116                 if (m_World.OnScreenMessages != null)
117                 {
118                     try
119                     {
120                         foreach (OnScreenMessage dm in m_World.OnScreenMessages)
121                         {
122                             int xPos = (int)Math.Round(dm.X * this.Width);
123                             int yPos = (int)Math.Round(dm.Y * this.Height);
124                             Rectangle posRect =
125                                 new Rectangle(xPos, yPos, this.Width, this.Height);
126                             this.drawArgs.defaultDrawingFont.DrawText(null,
127                                 dm.Message, posRect,
128                                 DrawTextFormat.NoClip | DrawTextFormat.WordBreak,
129                                 Color.White);
130                         }
131                     }
132                     catch (Exception)
133                     {
134                         // Don't let a script error cancel the frame.
135                     }
136                 }
137 
138                 m_Device3d.EndScene();
139             }
140             catch (Exception ex)
141             {
142                 Log.Write(ex);
143             }
144             finally
145             {
146                 
147                 if(World.Settings.ShowFpsGraph)
148                 {
149                     long endTicks = 0;
150                     PerformanceTimer.QueryPerformanceCounter(ref endTicks);
151                     float elapsedMilliSeconds = 1000.0f / (1000.0f*(float)(endTicks - startTicks)/PerformanceTimer.TicksPerSecond);
152                     m_FrameTimes.Add(elapsedMilliSeconds);
153                 }
154                 this.drawArgs.EndRender();
155             }
156             drawArgs.UpdateMouseCursor(this);
157         }

启动后台线程后调用异步方法WorkerThreadFunc()

 1 /// <summary>
 2         /// Background worker thread loop (updates UI)
 3         /// </summary>
 4         private void WorkerThreadFunc()
 5         {
 6             const int refreshIntervalMs = 150; // Max 6 updates per seconds
 7             while(m_WorkerThreadRunning)
 8             {
 9                 try
10                 {
11                     if(World.Settings.UseBelowNormalPriorityUpdateThread && m_WorkerThread.Priority == System.Threading.ThreadPriority.Normal)
12                     {
13                         m_WorkerThread.Priority = System.Threading.ThreadPriority.BelowNormal;
14                     }
15                     else if(!World.Settings.UseBelowNormalPriorityUpdateThread && m_WorkerThread.Priority == System.Threading.ThreadPriority.BelowNormal)
16                     {
17                         m_WorkerThread.Priority = System.Threading.ThreadPriority.Normal;
18                     }
19 
20                     long startTicks = 0;
21                     PerformanceTimer.QueryPerformanceCounter(ref startTicks);
22 
23                     m_World.Update(this.drawArgs);//更新世界对象
24 
25                     long endTicks = 0;
26                     PerformanceTimer.QueryPerformanceCounter(ref endTicks);
27                     float elapsedMilliSeconds = 1000*(float)(endTicks - startTicks)/PerformanceTimer.TicksPerSecond;
28                     float remaining = refreshIntervalMs - elapsedMilliSeconds;
29                     if(remaining > 0)
30                         Thread.Sleep((int)remaining);
31                 }
32                 catch(Exception caught)
33                 {
34                     Log.Write(caught);
35                 }
36             }
37         }

World的Update方法:

Update
 1  public override void Update(DrawArgs drawArgs)
 2         {
 3             if (!this.isInitialized)
 4             {
 5                 this.Initialize(drawArgs);
 6             }
 7 
 8             if (this.RenderableObjects != null)
 9             {
10                 this.RenderableObjects.Update(drawArgs);
11             }
12             if (this.m_WorldSurfaceRenderer != null)
13             {
14                 this.m_WorldSurfaceRenderer.Update(drawArgs);
15             }
16 
17             if (this.m_projectedVectorRenderer != null)
18             {
19                 this.m_projectedVectorRenderer.Update(drawArgs);
20             }
21 
22             if (this.TerrainAccessor != null)
23             {
24                 if (drawArgs.WorldCamera.Altitude < 300000)
25                 {
26                     if (System.DateTime.Now - this.lastElevationUpdate > TimeSpan.FromMilliseconds(500))
27                     {
28                         drawArgs.WorldCamera.TerrainElevation = (short)this.TerrainAccessor.GetElevationAt(drawArgs.WorldCamera.Latitude.Degrees, drawArgs.WorldCamera.Longitude.Degrees, 100.0 / drawArgs.WorldCamera.ViewRange.Degrees);
29                         this.lastElevationUpdate = System.DateTime.Now;
30                     }
31                 }
32                 else
33                     drawArgs.WorldCamera.TerrainElevation = 0;
34             }
35             else
36             {
37                 drawArgs.WorldCamera.TerrainElevation = 0;
38             }
39 
40             if (World.Settings.EnableAtmosphericScattering && m_outerSphere != null)
41                 m_outerSphere.Update(drawArgs);
42         }
文章未经说明均属原创,学习笔记可能有大段的引用,一般会注明参考文献。 欢迎大家留言交流,转载请注明出处。
原文地址:https://www.cnblogs.com/yhlx125/p/3015197.html