在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. Oracle总结之plsql编程(基础九)

    原创作品,转自请注明出处:https://www.cnblogs.com/sunshine5683/p/10344302.html 接着上次总结,继续今天的总结,今天主要总结plsql中控制语句,如条 ...

  2. JavaScript中七种数据类型·中·一

    Standing on Shoulders of Giants; 说到JavaScript里的类型很容易就让人想起 42和"42",分别是string型和number型,但是他们可 ...

  3. js-redux学习笔记

    1.action creator 就是函数,负责构建一个 action (是的,action creator 这个名字已经很明显了)并返回它. var actionCreator = function ...

  4. Oracle常用方法备份

    1.UNION ALL 和UNION.INTERSECT.MINUS 连接所有的查询结果.去掉重复的结果.操作返回查询结果中公共的行.在进行两个表格或者两个查询结果的时候,返回在第一个表格/查询结果中 ...

  5. Finereport和Finebi的区别

    1.问题描述 大家现在可能都知道,目前帆软旗下特色产品主要是FineReport和FineBI,但是部分用户可能会有个疑问:FineReport和FineBI到底有什么区别? 2.产品介绍FineRe ...

  6. GDAL打开HDF格式时遇到的中文路径问题(未解决)

    众所周知,中文环境下(VS2010 C++工程编码为多字节编码),在使用1.8.0版本以后的GDAL打开中文路径下的影像文件(如GeoTiff文件)时, 需对中文文件路径做特殊处理,有2种方法:(我使 ...

  7. window下安装RabbitMQ

    RabbitMQ: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.消 ...

  8. webpack+sass+vue 入门教程(二)

    六.新建webpack配置文件 webpack.config.js 文件整体框架内容如下,后续会详细说明每个配置项的配置 webpack.config.js直接放在项目demo目录下 module.e ...

  9. hashlib 简单的登录例子

    hashlib例子: =============================================== import hashlib md5 = hashlib.md5() md5.up ...

  10. 一致性Hash(Consistent Hashing)原理剖析

    引入 在业务开发中,我们常把数据持久化到数据库中.如果需要读取这些数据,除了直接从数据库中读取外,为了减轻数据库的访问压力以及提高访问速度,我们更多地引入缓存来对数据进行存取.读取数据的过程一般为: ...