WorldWind源码剖析系列:星球球体的加载与渲染

WorldWind源码剖析系列:星球球体的加载与渲染

WorldWind中主函数Main()的分析

在文件WorldWind.cs中主函数Main()依次作以下几个事情:

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();

总结:主函数内容分析学习到此完成。我们之后会针对各功能分别分析,个个突破。

WorldWindMainApplication类构造函数的分析

上面提到的第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中的参数逐个构造星球对象。

`第二处加红标注的OpenStartupWorld();则根据Settings.DefaultWorld(DefaultWorld默认值为“Earth”)参数加载和渲染指定的星球,否咋加载渲染默认的星球。

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;

}

   所要加载的星球的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》,在此,向无痕客表示感谢。

时间: 2024-12-09 16:01:51

WorldWind源码剖析系列:星球球体的加载与渲染的相关文章

thinkphp5源码剖析系列1-类的自动加载机制

前言 tp5想必大家都不陌生,但是大部分人都停留在应用的层面,我将开启系列随笔,深入剖析tp5源码,以供大家顺利进阶.本章将从类的自动加载讲起,自动加载是tp框架的灵魂所在,也是成熟php框架的必备功能 入口 // [ 应用入口文件 ] namespace think; // 加载基础文件 require __DIR__ . '/../thinkphp/base.php'; base.php <?php // +------------------------------------------

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

星球类World代表通用的星球类,因为可能需要绘制除地球之外的其它星球,如月球.火星等.该类的类图如下. 需要说明的是,在WorldWind中星球球体的渲染和经纬网格的渲染时分别绘制的.经纬网格的渲染过程请参见文章<WorldWind源码剖析系列:星球经纬度格网的绘制>,是通过Form.OnPaint()函数激活.刷新和绘制的.星球球体的渲染过程请参见文章<WorldWind源码剖析系列:星球球体的加载与渲染>.而星球类World是绘制过程中从XML配置文件中读取参数构造的用来代表

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

星球表面渲染类WorldSurfaceRenderer描述如何渲染星球类(如地球)表面影像纹理.该类的类图如下. 星球类World包含的主要的字段.属性和方法如下: public const int RenderSurfaceSize = 256;//定义渲染表面尺寸的常量 RenderToSurface m_Rts = null;//D3D定义的类型 const int m_NumberRootTilesHigh = 5;//根瓦片高度数,即纬度方向上划分的瓦片数.经度方向上划分的瓦片数为该值

WorldWind源码剖析系列:表面影像类SurfaceImage

表面影像类SurfaceImage描述星球类(如地球)表面纹理影像.该类的类图如下. 表面影像类SurfaceImage包含的主要的字段.属性和方法如下: string m_ImageFilePath;//影像文件的路径 double m_North;//影像文件的北部边界 double m_South;//影像文件的南部边界 double m_West;//影像文件的西部边界 double m_East; //影像文件的东部边界 Texture m_Texture = null;//用影像文件

WorldWind源码剖析系列:设置类SettingsBase

PluginSDK中的星球设置类WorldSettings 和WorldWind.程序设置类WorldWindSettings均继承自父类SettingsBase.类图如下所示.其中父类SettingsBase内嵌了枚举型LocationType,子类WorldSettings的定义文件中附带了定义了测量模式的枚举型. 父类SettingsBase主要是对设置文件相关的设置文件名.版本.路径.载入.保存等于文件处理相关的顶层抽象.主要是以XML文件保存的,采用.NET平台提供的XmlSerial

WorldWind源码剖析系列:表面瓦片类SurfaceTile

表面瓦片类SurfaceTile描述星球类(如地球)表面纹理影像的瓦片模型.其类图如下. 表面瓦片类SurfaceTile包含的主要的字段.属性和方法如下: int m_Level;//该瓦片所属金字塔影像的层级 double m_North;//该瓦片北边界 double m_South; //该瓦片南边界 double m_West; //该瓦片西边界 double m_East; //该瓦片东边界 bool m_Initialized = false; //该瓦片是否已被初始化 Devic

WorldWind源码剖析系列:影像存储类ImageStore

影像存储类ImageStore 影像存储类ImageStore提供了计算本地影像路径和远程影像影像URL访问的各种接口,是WmsImageStore类和NltImageStore类的基类.该类的类图如下. 影像存储类基类ImageStore提供的主要字段.属性和方法简要描述如下: protected string     m_dataDirectory;//影像数据目录 protected double m_levelZeroTileSizeDegrees = 36; //金字塔影像的零级瓦片大

WorldWind源码剖析系列:挂件类Widgets

WorldWindow用户定制控件类中所包含的的挂件类Widgets控件主要有如下图所示的派生类.它们的类图如下所示. 鉴于挂件类Widgets及其派生类,相对简单,基本上都是些利用DirectX3D进行绘图和处理图标纹理影像等的操作,此处不再对各个类的主要的字段.属性和方法进行描述了.感兴趣的读者可以直接阅读源码.建议阅读源码之前读者应具备一定的DirectX3D开发基础. 挂件PictureBox类被WavingFlags.TimeController等插件引擎子类所引用. 挂件Button

WorldWind源码剖析系列:WorldWind实时确定、更新、初始化和渲染地形和纹理数据

WorldWind实时确定.更新.初始化和渲染地形和纹理数据 当用户点击WorldWind中的地球时,首先响应的是WorldWindow.OnPaint()函数,后续程序的调用流程如下图所示. 零散知识点: 1.         地形瓦片类TerrainTile引用了地形瓦片服务类TerrainTileService,在TerrainTile的Initialize()函数中实例化并发起了地形下载请求类TerrainDownloadRequest对象.而在TerrainTileService类中也