在WorldWindow定制控件是从Control类派生出来的,需要自己操纵GDI+绘制所需要的界面效果,这种自定义控件比较耗费精力,需要比较深厚的GDI+和DirectX 3D开发功底。(区别于用户控件UserControl,也叫组合控件,大多是对.NET工具箱提供的默认基础控件进行的组合和轻量级的改变,实现起来比较简单)。为了明确定制控件Control和用户控件UserControl之间的体系关系,此处给出了Control类继承层次结构(源自MSDN)以及WorldWindow类的类图:在WorldWindow中成员较多,此处仅就比较重要的成员作详细分析:

     

  需要特别说明的是:在WorldWindow中星球球体的渲染和经纬网格的渲染时分别绘制的,星球由类World表示,经纬网格由类LatLongGrid表示。

WorldWindow定制控件的主要字段、方法和属性有:

private Device m_Device3d;//定制窗口绑定的绘制设备

private PresentParameters m_presentParams;//Direct3D绘制呈现参数

private DrawArgs drawArgs; //绘制参数

private World m_World; //定制窗口中需要绘制的星球

private Cache m_Cache;// //定制窗口中需要绘制的星球的缓冲区

private Thread m_WorkerThread;//后台工作线程

private bool showDiagnosticInfo; //是否显示诊断信息

private string _caption = "";//标题

private long lastFpsUpdateTime; //最后一次Fps的更新时间

private int frameCounter; //帧数

private float fps;//每秒渲染的帧速率

private string saveScreenShotFilePath; //保存截屏文件路径

private ImageFileFormat saveScreenShotImageFileFormat = ImageFileFormat.Bmp; //保存截屏文件格式

private bool m_WorkerThreadRunning;//判断工作线程是否正在运行

private LayerManagerButton layerManagerButton; //图层管理器按钮

private MenuBar _menuBar = new MenuBar(MenuAnchor.Top, 90);

private bool m_isRenderDisabled; // True when WW isn't active - CPU saver

private bool isMouseDragging; //是否正在用鼠标拖拽场景

private Point mouseDownStartPosition = Point.Empty; //鼠标按下时的起始位置

private bool renderWireFrame; //是否以线框模式渲染

private System.Timers.Timer m_FpsTimer = new System.Timers.Timer(250);

public void Goto( WorldWind.Net.WorldWindUri uri )转到URI连接。

public void GotoLatLon(double latitude, double longitude, double heading, double altitude, double perpendicularViewRange, double tilt)移动到指定的经纬度位置,内部实际是通过调用this.drawArgs.WorldCamera.SetPosition(latitude, longitude, heading, altitude, tilt);函数实现具体功能。

public void SaveScreenshot(string filePath)对当前场景截屏保存为图片文件。

public void OnApplicationIdle(object sender, EventArgs e)星球的消息循环,在WorldWind.cs文件的主函数static void Main(string[] args)中被调用。其内部调用了WorldWindow类的Render()函数。

private static bool IsAppStillIdle判断程序是否空闲,如果控件返回真。

protected override void OnPaint(PaintEventArgs e),任何继承自Control类的定制控件均需要重载Control类的函数,当窗口需要重新绘制时被调用。

private Device m_Device3d;//绘制场景所需要的DirectX 3D设备对象

private DrawArgs drawArgs;//场景绘制时指定的绘制参数

private World m_World;// WorldWindow定制控件中表示星球球体的对象。星球球体的渲染和经纬网格的渲染时分别绘制的,星球由类World表示,经纬网格由类LatLongGrid表示

private Cache m_Cache;//用于缓存高程数据和纹理数据的本地缓存对象

private Thread m_WorkerThread; //后台工作线程,使用多线程的方式实现高程数据和纹理数据的远程下载,并及时更新应用程序的图形界面(GUI)

public World CurrentWorld;//公开的属性CurrentWorld的Set属性除了完成将星球对象的引用保存在m_World成员中外,还实例化了运动相机MomentumCamera和星球球体的经纬网格LatLongGrid,并且将经纬网格对象添加到星球对象的可渲染对象列表中

public void Render();//该方法首先判断星球对象m_World是否为空、后台工作线程WorkerThread是否为空,并依次作相应的处理;其次,在开始绘制之前调用绘制参数对象drawArgs的相机对象WorldCamera所保留的Update ()方法this.drawArgs.WorldCamera.Update(m_Device3d)更新DirectX 3D设备对象m_Device3d,然后开启绘制标识,设置绘制模式、调用星球对象m_World的渲染函数m_World.Render(this.drawArgs)完成星球主体的渲染、绘制十字光标his.DrawCrossHairs();、统计绘制帧速率、渲染各个自定义的窗体挂件m_RootWidget.Render(drawArgs)和m_NewRootWidget.Render(drawArgs)、实时响应用户的截屏操作SaveScreenShot()、在渲染窗口的右上角绘制位置信息RenderPositionInfo()、完成菜单条的渲染_menuBar.Render(drawArgs)、线型图形的渲染m_FpsGraph.Render(drawArgs)、输出星球对象m_World的屏幕消息字符串

重载鼠标滑轮滚动消息protected override void OnMouseWheel(MouseEventArgs e)

重载键盘按键按下消息protected override void OnKeyDown(KeyEventArgs e)

重载键盘按键弹起消息protected override void OnKeyUp(KeyEventArgs e)

重载键盘按键消息protected override void OnKeyPress(KeyPressEventArgs e)

重载预处理消息方法,在消息循环过程中在消息被丢弃之前事先处理键盘和输入信息public override bool PreProcessMessage(ref Message msg)

紧接着,定义两个比较重要的方法体public bool HandleKeyDown(KeyEventArgs e)和public bool HandleKeyUp(KeyEventArgs e)分别用来处理按键按下和弹起时的消息,其中按键按下的处理方法HandleKeyDown里面基本上包含了WorldWind的全部按键操作控制实现代码

重载了鼠标按下、弹起、双击、移动和离开等五个消息的处理方法:

protected override void OnMouseDown(MouseEventArgs e)

protected override void OnMouseDoubleClick(MouseEventArgs e)

protected override void OnMouseUp(MouseEventArgs e)

protected override void OnMouseMove(MouseEventArgs e)

protected override void OnMouseLeave(EventArgs e)

其中,鼠标按下的消息处理方法中,先记录下当前按下鼠标的位置,然后先让各个自定义的窗体挂件m_RootWidget.OnMouseDown(e)和m_NewRootWidget.OnMouseDown(e)处理,再让菜单条this._menuBar.OnMouseDown(e)作出处理,以便对用户的操作作出实时响应,最后标识用户是左键按下还是右键按下。

鼠标按下的消息处理方法比较重要,里面除了实现先让自定义的窗体挂件和菜单条处理鼠标按下的消息外,还实简单地现了通过光线追踪(Ray Tracing)技术进行拾取检测等操作

this.drawArgs.WorldCamera.PickingRayIntersection(DrawArgs.LastMousePosition.X,DrawArgs.LastMousePosition.Y,out targetLatitude,out targetLongitude),最后在调用方法his.drawArgs.WorldCamera.PointGoto(targetLatitude, targetLongitude)使星球飞行到用户通过右键单击所想要去的地方去漫游(左键双击表示放大,右键双击表示放大)。

接下来的定义了方法private void InitializeGraphics()和private void OnDeviceReset(object sender, EventArgs e),分别初始化使用DirectX 3D用来渲染图形时的设备状态和当DirectX 3D设备对象m_Device3d需要重置时调用的方法,用户切换渲染窗口和调整渲染窗口都会使设备对象失效,当再次使用时需要重置。不熟悉这一部分内容的读者可以查阅DirectX 3D三维图形绘制的基础知识,此处不再赘述。

最后一个重要的方法是private void WorkerThreadFunc(),即前面提到的后台工作线程的绑定方法体。其内部调用了查询性能计数器方法和星球对象的更新函数:

PerformanceTimer.QueryPerformanceCounter(ref startTicks);

m_World.Update(this.drawArgs);

其中,后者使用多线程的方式实现高程数据和纹理数据的本地调用或远程下载,完成用户所请求的区域的地形数据的绘制和纹理映射,并及时更新应用程序的图形界面(GUI)。详细过程请参照m_World.Update(this.drawArgs)内部实现。

WorldWind源码剖析系列:窗口定制控件类WorldWindow的更多相关文章

  1. WorldWind源码剖析系列:星球表面渲染类WorldSurfaceRenderer

    星球表面渲染类WorldSurfaceRenderer描述如何渲染星球类(如地球)表面影像纹理.该类的类图如下. 星球类World包含的主要的字段.属性和方法如下: public const int ...

  2. WorldWind源码剖析系列:插件列表视图类PluginListView和插件列表视图项类PluginListItem

    WorldWind中的插件类是个庞大的类,可以说从软件设计层面上统筹可扩展的插件体系的设计思想是WorldWind中的精华,值得学习和借鉴.插件体系中的所用到的类可以分为两大类,一类是插件类Plugi ...

  3. WorldWind源码剖析系列:经纬度格网类LatLongGrid

    经纬度格网类LatLongGrid继承自可渲染对象类RenderableObject,是WorldWind中用来在星球外表绘制经纬度格网的封装类.其类图如下所示. 绘制经纬网格的主体函数为Render ...

  4. WorldWind源码剖析系列:可渲染对象类RenderableObject

    RenderableObject是WorldWind中所有需要渲染的对象的父类,继承了接口IRenderable和Icomparable.其派生类体系如下所示.RenderableObject的成员如 ...

  5. WorldWind源码剖析系列:视景体类Frustum

    PluginSDK中的视景体类Frustum是三维计算机图形学中的概念,主要用来描述透视投影的过程.三维计算机图形学中关于三维物体的渲染,Direct3D和OpenGL都是先通过对现实世界中的场景先进 ...

  6. WorldWind源码剖析系列:配置载入器类ConfigurationLoader

    配置载入器类ConfigurationLoader主要从指定的路径中加载保存星球相关参数的xml文件,从中读取数据来构造星球对象及其所关联的可渲染子对象列表并返回.该类的类图如下所示. 该类所包含的主 ...

  7. WorldWind源码剖析系列:地形访问器类TerrainAccessor

    地形访问器类TerrainAccessor 地形访问器类TerrainAccessor提供了对地形(高程)Terrain (elevation)访问的各种接口interface,是NltTerrain ...

  8. WorldWind源码剖析系列:二维点类Point2d和三维点类Point3d

    PluginSDK中的点主要有二维和三维两种类型,分别用来描述平面和立体点.其类图如下. 这两个类比较简单.其字段成员主要用来描述点对象在各坐标轴上的分量. 属性Length用来返回二维和三维点的距离 ...

  9. WorldWind源码剖析系列:星球类World

    星球类World代表通用的星球类,因为可能需要绘制除地球之外的其它星球,如月球.火星等.该类的类图如下. 需要说明的是,在WorldWind中星球球体的渲染和经纬网格的渲染时分别绘制的.经纬网格的渲染 ...

随机推荐

  1. 4.Factory Pattern(工厂模式)

    工厂模式(Factory Pattern)定义: 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到子类. 针对实现编程,但是当我们每次使用new时候,不正是在针对 ...

  2. 【16】命令模式(Command Pattern)

    一.前言 最近项目中发现,对于设计模式的了解是必不可少的,当然对于设计模式的应用那更是重要,可以说是否懂得应用设计模式在项目中是衡量一个程序员的技术水平,因为对于一个功能的实现,高级工程师和初级工程师 ...

  3. 火狐hr标签的兼容性问题

    想在盒子里加一条白色横线 加了以下代码 页面效果如下<hr style="width:80%;height:1px;border:none;border-top:1px solid # ...

  4. web界面 之 登录 (初稿)

    web登录一般注意以下几点: 1)拥有元素:用户名,密码(密码字符显示与隐藏功能),登录按钮,忘记密码链接(忘记用户名链接),注册链接 2)用户体验: •页面显示,光标默认停在第一个输入框 利用htm ...

  5. 基于纤程(Fiber)实现C++异步编程库(一):原理及示例

    纤程(Fiber)和协程(coroutine)是差不多的概念,也叫做用户级线程或者轻线程之类的.Windows系统提供了一组API用户创建和使用纤程,本文中的库就是基于这组API实现的,所以无法跨平台 ...

  6. ionic插件安装与卸载

    使用下面的命令查询.安装.卸载插件: $ ionic plugin list //列出所有已安装插件 $ ionic plugin remove 插件名 //先根据上面的list列出插件,然后根据插件 ...

  7. 为notifyIcon.icon属性赋图片

    System.Drawing.Bitmap bp; bp = new Bitmap(Application.StartupPath + @"\image\" + MeiYeShuo ...

  8. ejb-jar.xml

    所有bean类(无论是会话bean还是实体bean)必须实现的最基本的接口是javax.ejb.EnterpriseBean接口. 所有的会话bean必须实现javax.ejb.SessionBean ...

  9. Linux 进程管理之四大名捕

    本文来自网络,著作权归原创作者所有,不做任何商业用途. 一.四大名捕 四大名捕,最初出现于温瑞安创作的武侠小说,是朝廷中正义力量诸葛小花的四大徒弟,四人各怀绝技,分别是轻功暗器高手“无情”.内功卓越的 ...

  10. python 进程池pool

    进程池子 当你成千上万的业务需要创建成千上万的进程时,我们可以提前定义一个进程池 from multiprocessing import Pool p = Pool(10) #进程池创建方式,类似空任 ...