WorldWind源码剖析系列:星球球体的加载与渲染
WorldWind源码剖析系列:星球球体的加载与渲染
WorldWind中主函数Main()的分析
在文件WorldWind.cs中主函数Main()阐明了WorldWind的初始化运行机制(如图1所示 ):
图1 WorldWind的初始化运行机制
1. 使用System.Version在内部,读取软件版本信息,并格式化输出。我们在外面配置软件版本,“关于”部分中版本自动更改。
获取格式化版本号
// Establish the version number string used for user display,
// such as the Splash and Help->About screens.
// To change the Application.ProductVersion make the
// changes in \WorldWind\AssemblyInfo.cs
// For alpha/beta versions, include " alphaN" or " betaN"
// at the end of the format string.
Version ver = new System.Version(Application.ProductVersion);
Release = string.Format("{0}.{1}.{2}.{3}", ver.Major, ver.Minor, ver.Build, ver.Revision);
2.判断该软件是否已经有个实例启动,如果有,则不启动。
判断是否已启动实例
// If World Wind is already running, pass any commandline
// arguments from this instance, and quit.
IntPtr handle = GetWWHandle(); //此处通过获取线程指针
if (!System.IntPtr.Zero.Equals(handle))
{
if(args.Length>0)
NativeMethods.SendArgs( handle, string.Join("\n",args) );
return;
}
3.判断计算机中已经存在的协议数目,并给当前主线程命名。主要是针对.Net FrameWork 1.1版的bug(不允许多于50个协议)。因为该软件要启用自己的新协议"worldwind://"。现在2.0以后不存在该问题啦。
//abort if 50 bindings problem present and user opts to go to the download page
if(BindingsCheck.FiftyBindingsWarning())
return;
// Name the main thread
System.Threading.Thread.CurrentThread.Name = "Main Thread";
4.解析Main(string[] args)中参数args。主要是在控制台中启动程序时同时赋予了参数的形式。
// ParseArgs may set values that are used elsewhere,
// such as startFullScreen and CurrentSettingsDirectory.
ParseArgs(args);
args中参数可能是:
"worldwind://":加载定位显示球体某处。
“/f” :全屏启动。
“/s=……”:指定加载“配置”的文件夹路径。
这里要注意的是,Main函数一般是没有参数的,如果我们以后要写可以在控制台下给启动程序传入参数,可以借鉴一下。
5.加载上次使用的配置信息,包括上次使用的WorldWind主窗体使用信息和上次使用的World球体显示信息。
加载配置
if(CurrentSettingsDirectory == null)
{
// load program settings from default directory
LoadSettings();
World.LoadSettings();
}
else
{
LoadSettings(CurrentSettingsDirectory);
World.LoadSettings(CurrentSettingsDirectory);
}
6.启动主程序,还有对线程异常事件处理和程序空闲处理(防止过度休眠)。
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
MainApplication app = new MainApplication();//该构造函数中完成星球球体的加载与渲染
Application.Idle += new EventHandler(app.WorldWindow.OnApplicationIdle);
Application.Run(app);
7.程序启动后,将状态配置保存起来。
// Save World settings
World.Settings.Save();
其中用到了,将World的配置对象WorldSettings(注意:不是其父类SettingsBase,只是调用其父类的Save方法)序列化为XML文件,并保存,以备下次启动前读取World(即球体)上次状态配置。
将WorldSettings序列化
public virtual void Save(string fileName)
{
XmlSerializer ser = null;
try
{
ser = new XmlSerializer(this.GetType());
using(TextWriter tw = new StreamWriter(fileName))
{
ser.Serialize(tw, this);
}
}
catch(Exception ex)
{
throw new System.Exception(String.Format("Saving settings class '{0}' to {1} failed", this.GetType().ToString(), fileName), ex);
}
}
8.保存程序整体的该次状态配置。(此处类似7,不详讲,只是这次序列化的对象是WorldWindSettings)。
// Save program settings
Settings.Save();
总结:主函数内容分析学习到此完成。我们之后会针对各功能分别分析,个个突破。
WorldWind中MainApplication类构造函数的分析
上面提到的第6步中,MainApplication类构造函数真正完成星球球体的加载与渲染。该构造函数依次作以下几个事情:
1、通过变量Settings.ConfigurationWizardAtStartup来判断是否在程序第一次启动时加载配置助手窗体,该配置助手完成一些一些与欢迎界面Welcome、大气层渲染Atmosphere、缓冲区配置Cache、网络代理Proxy、配置参数最后汇总核查FinalPage等相关信息的配置。
if(Settings.ConfigurationWizardAtStartup)
{
// If the settings file doesn't exist, then we are using the
// default settings, and the default is to show the Configuration
// Wizard at startup. We only want that to happen the first time
// World Wind is started, so change the setting to false(the user
// can change it to true if they want).
if(!File.Exists(Settings.FileName))
{
Settings.ConfigurationWizardAtStartup = false;
}
ConfigurationWizard.Wizard wizard = new ConfigurationWizard.Wizard( Settings );
wizard.TopMost = true;
wizard.ShowInTaskbar = true;
wizard.ShowDialog();
// TODO: should settings be saved now, in case of program crashes,
// and so that XML file on disk matches in-memory settings?
}
2、加载闪屏
using( this.splashScreen = new Splash() )
{
this.splashScreen.Owner = this;
this.splashScreen.Show();
this.splashScreen.SetText("Initializing...");
Application.DoEvents();
InitializeComponent();
// ParseArgs may have set the "startFullScreen" flag
if(startFullScreen && !FullScreen)
{
FullScreen = true;
}
3、设置缓冲区大小和网络下载错误
// set Upper and Lower limits for Cache size control, in bytes
long CacheUpperLimit = (long)Settings.CacheSizeMegaBytes * 1024L * 1024L;
long CacheLowerLimit = (long)Settings.CacheSizeMegaBytes * 768L * 1024L; //75% of upper limit
//Set up the cache
worldWindow.Cache = new Cache(
Settings.CachePath,
CacheLowerLimit,
CacheUpperLimit,
Settings.CacheCleanupInterval,
Settings.TotalRunTime );
WorldWind.Net.WebDownload.Log404Errors = World.Settings.Log404Errors;
4、获取XML配置文件所在的路径,获取该路径下所有星球的XML配置文件,请这些星球的名称作为“File”菜单下的菜单项,以便在各个星球之间切换。并利用这些星球的参数构造每一个星球对象,将其注意加载到哈希表对象availableWorldList中(哈希表对象是在文件WorldWind.cs中定义的,专门用来保存各类星球球体对象,利用哈希表的直接索引定位特性)。
DirectoryInfo worldsXmlDir = new DirectoryInfo( Settings.ConfigPath );
if (!worldsXmlDir.Exists)
throw new ApplicationException(
string.Format(CultureInfo.CurrentCulture,
"World Wind configuration directory '{0}' could not be found.", worldsXmlDir.FullName));
FileInfo[] worldXmlDescriptorFiles = worldsXmlDir.GetFiles("*.xml");
int worldIndex = 0;
menuItemFile.MenuItems.Add(0, new System.Windows.Forms.MenuItem("-"));
foreach (FileInfo worldXmlDescriptorFile in worldXmlDescriptorFiles)
{
try
{
Log.Write(Log.Levels.Debug+1, "CONF", "checking world " + worldXmlDescriptorFile.FullName + " ...");
World w = WorldWind.ConfigurationLoader.Load(worldXmlDescriptorFile.FullName, worldWindow.Cache);
if(!availableWorldList.Contains(w.Name))
this.availableWorldList.Add(w.Name, worldXmlDescriptorFile.FullName);
w.Dispose();
System.Windows.Forms.MenuItem mi = new System.Windows.Forms.MenuItem(w.Name, new System.EventHandler(OnWorldChange));
menuItemFile.MenuItems.Add(worldIndex, mi);
worldIndex++;
}
catch( Exception caught )
{
splashScreen.SetError( worldXmlDescriptorFile + ": " + caught.Message );
Log.Write(caught);
}
}
Log.Write(Log.Levels.Debug, "CONF", "loading startup world...");
OpenStartupWorld();
此步骤中,第一处加红标注的World w = WorldWind.ConfigurationLoader.Load(worldXmlDescriptorFile.FullName, worldWindow.Cache); 代码根据每个星球所对应的保存在XML中的参数逐个构造星球对象。此处将仅仅调用ConfigurationLoader.Load()函数构造好的各个星球对象加入到哈希表对象availableWorldList中,然后调用第二处加红标注的OpenStartupWorld()函数来加载渲染默认启动时的星球。而OpenStartupWorld()则通过调用OpenWorld(curWorldFile)函数,OpenWorld(curWorldFile)函数内部再根据Settings.DefaultWorld(其默认值为“Earth”)参数调用ConfigurationLoader.Load()函数构造、加载和渲染指定的星球,否则加载渲染默认启动时的星球。OpenWorld( curWorldFile )函数还将在点击“File”菜单下的各个星球名称菜单项激活星球之间切换事件时被调用。
5、设置“View”菜单下的"vertical exaggeration"菜单项的竖直放大倍数子菜单项。如下图所示。
// Set up vertical exaggeration sub-menu
float[] verticalExaggerationMultipliers = { 0.0f, 1.0f, 1.5f, 2.0f, 3.0f, 5.0f, 7.0f, 10.0f };
foreach (float multiplier in verticalExaggerationMultipliers)
{
MenuItem curItem = new MenuItem(multiplier.ToString("f1",CultureInfo.CurrentCulture) + "x", new EventHandler(this.menuItemVerticalExaggerationChange));
curItem.RadioCheck = true;
this.menuItemVerticalExaggeration.MenuItems.Add(curItem);
if(Math.Abs(multiplier-World.Settings.VerticalExaggeration)<0.1f)
curItem.Checked = true;
}
图2 竖直放大倍数菜单项
所要加载的星球的XML文件内容如下(以Earth.xml为例):
<?xml version="1.0" encoding="UTF-8"?>
<World Name="Earth" EquatorialRadius="6378137.0" LayerDirectory="Earth" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="WorldXmlDescriptor.xsd">
<TerrainAccessor Name="SRTM">
<TerrainTileService>
<ServerUrl>http://worldwind25.arc.nasa.gov/wwelevation/wwelevation.aspx</ServerUrl>
<DataSetName>srtm30pluszip</DataSetName>
<LevelZeroTileSizeDegrees>20.0</LevelZeroTileSizeDegrees>
<NumberLevels>12</NumberLevels>
<SamplesPerTile>150</SamplesPerTile>
<DataFormat>Int16</DataFormat>
<FileExtension>bil</FileExtension>
<CompressonType>zip</CompressonType>
</TerrainTileService>
<LatLonBoundingBox>
<North>
<Value>90.0</Value>
</North>
<South>
<Value>-90.0</Value>
</South>
<West>
<Value>-180.0</Value>
</West>
<East>
<Value>180.0</Value>
</East>
</LatLonBoundingBox>
</TerrainAccessor>
</World>
本篇博文在本人调试源代码的基础上,梳理了WorldWind主函数的处理和加载流程,在博文成文的过程中较多的参考和借鉴了无痕客的博文《WorldWind学习系列二:擒贼先擒王篇1》和《WorldWind学习系列二:擒贼先擒王篇2》,在此,向无痕客表示感谢。
其他参考链接:WORLD WIND界面结构探究,WorldWindcs代码分析
WorldWind源码剖析系列:星球球体的加载与渲染的更多相关文章
- thinkphp5源码剖析系列1-类的自动加载机制
前言 tp5想必大家都不陌生,但是大部分人都停留在应用的层面,我将开启系列随笔,深入剖析tp5源码,以供大家顺利进阶.本章将从类的自动加载讲起,自动加载是tp框架的灵魂所在,也是成熟php框架的必备功 ...
- WorldWind源码剖析系列:星球类World
星球类World代表通用的星球类,因为可能需要绘制除地球之外的其它星球,如月球.火星等.该类的类图如下. 需要说明的是,在WorldWind中星球球体的渲染和经纬网格的渲染时分别绘制的.经纬网格的渲染 ...
- WorldWind源码剖析系列:星球表面渲染类WorldSurfaceRenderer
星球表面渲染类WorldSurfaceRenderer描述如何渲染星球类(如地球)表面影像纹理.该类的类图如下. 星球类World包含的主要的字段.属性和方法如下: public const int ...
- WorldWind源码剖析系列:表面影像类SurfaceImage
表面影像类SurfaceImage描述星球类(如地球)表面纹理影像.该类的类图如下. 表面影像类SurfaceImage包含的主要的字段.属性和方法如下: string m_ImageFilePath ...
- WorldWind源码剖析系列:设置类SettingsBase
PluginSDK中的星球设置类WorldSettings 和WorldWind.程序设置类WorldWindSettings均继承自父类SettingsBase.类图如下所示.其中父类Setting ...
- WorldWind源码剖析系列:表面瓦片类SurfaceTile
表面瓦片类SurfaceTile描述星球类(如地球)表面纹理影像的瓦片模型.其类图如下. 表面瓦片类SurfaceTile包含的主要的字段.属性和方法如下: int m_Level;//该瓦片所属金字 ...
- WorldWind源码剖析系列:窗口定制控件类WorldWindow
在WorldWindow定制控件是从Control类派生出来的,需要自己操纵GDI+绘制所需要的界面效果,这种自定义控件比较耗费精力,需要比较深厚的GDI+和DirectX 3D开发功底.(区别于用户 ...
- WorldWind源码剖析系列:影像存储类ImageStore、Nlt影像存储类NltImageStore和WMS影像存储类WmsImageStore
影像存储类ImageStore 影像存储类ImageStore提供了计算本地影像路径和远程影像影像URL访问的各种接口,是WmsImageStore类和NltImageStore类的基类.当划分完层次 ...
- WorldWind源码剖析系列:WorldWind实时确定、更新、初始化和渲染地形和纹理数据
WorldWind实时确定.更新.初始化和渲染地形和纹理数据 当用户点击WorldWind中的地球时,首先响应的是WorldWindow.OnPaint()函数,后续程序的调用流程如下图所示. 零散知 ...
随机推荐
- spring中获取ApplicationContext对象的技巧,含源码说明
第一步,实现接口ApplicationContextAware,重写setApplicationContext方法,下方代码标红的地方,绿色部分 可以通过声明来进行存储到本类中. @Component ...
- 移动端开发在iOS系统中 new Date() 返回 NaN 的问题
问题: 通过 new Date() 函数将后台返回的时间('2021-11-25')获取时间戳.在 chrome 浏览器的手机模拟器中没有出现问题,但在 iPhone 真机测试的时候,显示的结果不符合 ...
- layer弹出框确定前验证:弹出消息框(弹出两个layer)
作者QQ:1095737364 QQ群:123300273 欢迎加入! layer 弹出框中经常遇到要弹出表单进行修改数据, 因此在弹出框中的表单需要验证数据, 就需要在弹出一个layer, 默认的设 ...
- ubuntu16.04安装matlab2016b
一.matlab2016b版本下载 在ubuntu下安装matlab2016b,需要三个文件,分别是:Matlab+2016b+Linux64+Crack.rar .R2016b_glnxa64_dv ...
- OSGI企业应用开发(八)整合Spring和Mybatis框架(一)
到目前为止,我们已经学习了如何使用Blueprint將Spring框架整合到OSGI应用中,并学习了Blueprint&Gemini Blueprint的一些使用细节.本篇文章开始,我们將My ...
- Hive创建指向HBase表的表
create [external] table t1(id int, value string) stored by 'org.apache.hadoop.hive.hbase.HBaseStorag ...
- 解决 web.xml is missing and <failOnMissingWebXml> is set to true 报错
在学习maven模块化构建项目的时候遇到了如下报错信息: web.xml is missing and <failOnMissingWebXml> is set to true. 这时候需 ...
- linux下opencv编译
.tar.gz cd opencv-/ cd .. mkdir my_build_dir cd my_build_dir cmake ../opencv- -DWITH_GTK_2_X=ON -DCM ...
- Javascript执行流总结
面对各种各样的JavaScript代码,我们有时候难免会犯错.可当自己仔细研究一下,哦原来是这么回事.有时候怎么会想为什么Javascript程序会是这样执行的呢?为什么没有得到自己预期的答案呢?自己 ...
- LeetCode题解之Leaf-Similar Trees
1.题目描述 2.问题分析 将叶子节点的值放入vector,然后比较. 3.代码 bool leafSimilar(TreeNode* root1, TreeNode* root2) { vector ...