WorldWind中显示图像的是一个继承自Control的WorldWindow控件,代码:html
public class WorldWindow : Control, IGlobeapp
初始化代码:less
public WorldWindow() {ide this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);oop // The m_Device3d can't be created unless the control is at least 1 x 1 pixels in sizethis this.Size = new Size(1,1); spa try线程 {3d if(!IsInDesignMode()) this.InitializeGraphics();orm ...... } catch (InvalidCallException caught) { throw new InvalidCallException( "Unable to locate a compatible graphics adapter.", caught ); } catch (NotAvailableException caught) { throw new NotAvailableException( "Unable to locate a compatible graphics adapter.", caught ); } } |
初始化代码中主要的是:
(1) 设置窗体的样式:
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
(2) 初始化DirectX,具体的关于DirectX的初始化参见《WorldWind-Direct3D的初始化》 :
if(!IsInDesignMode()) this.InitializeGraphics();
WorldWindow控件的 OnPaint() 方法:
protected override void OnPaint(PaintEventArgs e) { try { if(m_Device3d==null) { e.Graphics.Clear(SystemColors.Control); return; } Render(); m_Device3d.Present(); } catch(DeviceLostException) { try { AttemptRecovery(); Render(); m_Device3d.Present(); } catch(DirectXException){ } } } |
从代码中能够看出,主要的渲染操做都存在于 Render() 方法中。
Render()方法:
public void Render() { try { this.drawArgs.BeginRender();
System.Drawing.Color backgroundColor = System.Drawing.Color.Black; m_Device3d.Clear(ClearFlags.Target | ClearFlags.ZBuffer, backgroundColor, 1.0f, 0); if (m_World == null) { m_Device3d.BeginScene(); m_Device3d.EndScene(); m_Device3d.Present(); Thread.Sleep(25); return; } // 用于对图像进行逻辑处理的线程 if (m_WorkerThread == null) { m_WorkerThreadRunning = true; m_WorkerThread = new Thread(new ThreadStart(WorkerThreadFunc)); m_WorkerThread.Name = "WorldWindow.WorkerThreadFunc"; m_WorkerThread.IsBackground = true; if (World.Settings.UseBelowNormalPriorityUpdateThread) { m_WorkerThread.Priority = ThreadPriority.BelowNormal; } else { m_WorkerThread.Priority = ThreadPriority.Normal; } m_WorkerThread.Start(); } m_Device3d.BeginScene();
// 渲染当前星球,这是主要的渲染过程 m_World.Render(this.drawArgs); // 渲染位置信息 RenderPositionInfo(); // 渲染菜单栏 _menuBar.Render(drawArgs); m_FpsGraph.Render(drawArgs); // 渲染屏幕消息 if (m_World.OnScreenMessages != null) { try { foreach (OnScreenMessage dm in m_World.OnScreenMessages) { int xPos = (int)Math.Round(dm.X * this.Width); int yPos = (int)Math.Round(dm.Y * this.Height); Rectangle posRect = new Rectangle(xPos, yPos, this.Width, this.Height); this.drawArgs.defaultDrawingFont.DrawText(null, dm.Message, posRect, DrawTextFormat.NoClip | DrawTextFormat.WordBreak, Color.White); } } catch (Exception){} } m_Device3d.EndScene(); } catch (Exception ex){ Log.Write(ex);} finally{ this.drawArgs.EndRender();} drawArgs.UpdateMouseCursor(this); } |
图像逻辑处理线程方法:
private void WorkerThreadFunc() { const int refreshIntervalMs = 150; // 更新频率:Max 6 updates per second while(m_WorkerThreadRunning) { try { long startTicks = 0; PerformanceTimer.QueryPerformanceCounter(ref startTicks);
m_World.Update(this.drawArgs);
long endTicks = 0; PerformanceTimer.QueryPerformanceCounter(ref endTicks);
float elapsedMilliSeconds = 1000*(float)(endTicks - startTicks)/PerformanceTimer.TicksPerSecond; float remaining = refreshIntervalMs - elapsedMilliSeconds; if(remaining > 0) Thread.Sleep((int)remaining); } catch(Exception caught){ Log.Write(caught);} } } |
WorldWind渲染循环:
/// <summary> /// The world render loop. Borrowed from FlightGear and Tom Miller's blog /// </summary> public void OnApplicationIdle(object sender, EventArgs e) { // Sleep will always overshoot by a bit so under-sleep by 2ms in the hopes of never oversleeping. const float SleepOverHeadSeconds = 2e-3f; // Overhead associated with displaying the frame const float PresentOverheadSeconds = 0;//3e-4f; try { if (Parent.Focused && !Focused) Focus(); while (IsAppStillIdle) { Render(); if (World.Settings.ThrottleFpsHz > 0) { // optionally throttle the frame rate (to get consistent frame rates or reduce CPU usage. float frameSeconds = 1.0f / World.Settings.ThrottleFpsHz - PresentOverheadSeconds; // Sleep for remaining period of time until next render float sleepSeconds = frameSeconds - SleepOverHeadSeconds - DrawArgs.SecondsSinceLastFrame; if(sleepSeconds > 0) { // Don't sleep too long. We don't know the accuracy of Thread.Sleep Thread.Sleep((int) (1000 * sleepSeconds)); // Burn off what little time still remains at 100% CPU load while(DrawArgs.SecondsSinceLastFrame < frameSeconds) { // Patience } } } drawArgs.Present(); } } catch(DeviceLostException){ AttemptRecovery();} catch(Exception caught){ Log.Write(caught);} } |
IsAppStillIdle属性
/// <summary> /// Determine whether any window messages is queued. /// </summary> private static bool IsAppStillIdle { get { NativeMethods.Message msg; return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); } } |
WorldWind的渲染循环是在 Application Idle 时进行的:
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException); MainApplication app = new MainApplication(); Application.Idle += new EventHandler(app.WorldWindow.OnApplicationIdle); Application.Run(app); |