原文转自:http://blog.csdn.net/jk276993857/article/details/5735447
* 一、主函数
* WW程序的入口在WorldWind.cs的Main函数中,其中,args可通过命令行等方式进行
* 传递参数,例如程序目录等。详见ParseArgs(args)。在主函数中,包括以下部分:
* 1. 获得该应用程序的版本号
* 2. 如果已经运行了WW,将该实例参数传出,并退出
* 3. 如果服务器上有超过50个用户同时在线则退出
* 4. 命名主线程
* 5. 解析由命令行输入的args参数
* 6. 载入配置文件 (重点***)
* 7. 添加线程异常事件处理句柄
* 8. 创建主应用程序实例 (重点*****)
* 9. 保存World设置
* 10.在保存程序设置之前对用户资格证书进行加密
* 11.保存程序设置
*
* 二、创建主应用程序实例
* 在主程序Main()中主要通过以下代码创建主应用程序实例
MainApplication app = new MainApplication();
Application.Idle += new EventHandler(app.WorldWindow.OnApplicationIdle);
Application.Run(app);
* 1. 初始化MainApplication
* 1)配置向导ConfigurationWizard
* 如果设置文件(WorldWind.xml)不存在,我们将启用默认设置,那就是
* 在启动时显示配置向导。我们只希望在第一次启动WW时启动该向导,因
* 此一旦启动后就把该设置成假(如果用户需要也可设置成真)
* 该Flag的初始定义见WorldWindSettings.cs中的Miscellaneous settings
*
* 2)初始化欢迎界面splashScreen
* Splash类中主要有两个元素需要关注:BackgroundImage和SetText
* BackgroundImage已经嵌入Splash资源中,如果需要通过自定义图片,可
* BackgroundImage = Image.FromFile(splashImageFile.FullName);
* SetText()在接下来的初始化过程中,记录初始化阶段,包括:
* splashScreen.SetText("Initializing...");
* OpenStartupWorld()中
* splashScreen.SetText("Initializing " + startupWorldName + "..." );
* OpenWorld()中
* splashScreen.SetText("Initializing menus...");
* InitializePluginCompiler()中
* splashScreen.SetText("Initializing plugins...");
* compiler.LoadStartupPlugins()中
* worldWind.SplashScreen.SetText("Initializing plugin " + pi.Name);
* 自定义一个欢迎界面是软件的包装,对于程序员来说其重点不在美观,而在
* 于将初始化的内容明晰化地表现,进度条和进度文字都很直观表现,但千万要
* 考虑到用户的感受,让用户等得久不要紧,关键是在等待的过程中,欢迎界
* 面需要给用户一点信息,让它知道程序正在正常运行,并且运行到哪了。
*
* 3)初始化窗口界面InitializeComponent()
* 这部分主要包括了MenuItem(菜单栏)、WebBrower(浏览器)、WorldWindow(地球窗口)、
* ToolBar(工具栏)。对于MenuItem和Toolbar的功能和促发事件在此不做详述
* 参见:#region MenuItem and Toolbar item Click methods
*
* 在此主要说明webBrowserPanel和worldWindow
* this.Controls.Add(splitContainer);
* this.splitContainer.Panel1.Controls.Add(this.webBrowserPanel);
* this.splitContainer.Panel2.Controls.Add(this.worldWindow);
* 其中WebBrowserPanel主要包含了System.Windows.Forms.WebBrowser类
* worldWindow窗口包含了public class WorldWindow : Control, IGlobe类
* 关于WorldWindow的将在下面单独进行说明
* 此处只是进行了初始化this.worldWindow = new WorldWind.WorldWindow();
// Now perform the rendering m_Device3d initialization
// Skip DirectX initialization in design mode
// 对渲染设备m_Device3d进行初始化,忽略DX的初始模式下的初始化
if(!IsInDesignMode())
this.InitializeGraphics();
// Post m_Device3d creation initialization
// 在m_Device3d创建之后进行的其他参数初始化
// 此处的drawArgs类相当重要,用于绘制参数的设置
// Widgets用于各窗口的传递、绘制、鼠标事件等
this.drawArgs = new DrawArgs(m_Device3d, this );
this.m_RootWidget = new WorldWind.Widgets.RootWidget(this);
this.m_NewRootWidget = new WorldWind.NewWidgets.RootWidget(this);
//this.m_RootWidget.ChildWidgets.Add(layerManager);
DrawArgs.RootWidget = this.m_RootWidget;
DrawArgs.NewRootWidget = this.m_NewRootWidget;
// 用于帧率控制的时间器,事件委托
m_FpsTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_FpsTimer_Elapsed);
m_FpsTimer.Start();
TimeKeeper.Start();
// WorldWind.Widgets.LayerManager layerManager = new WorldWind.Widgets.LayerManager();
// m_RootWidget.ChildWidgets.Add(layerManager);
*
* WorldWind初始化中,InitializeGraphics()创建了D3D设备m_Device3d
* m_Device3d = new Device(adapterOrdinal, dType, this, flags, m_presentParams);
*
*
* 4) 设置缓存
worldWindow.Cache = new Cache(
Settings.CachePath,
CacheLowerLimit,
CacheUpperLimit,
Settings.CacheCleanupInterval,
Settings.TotalRunTime
* 5)读取Xml星球配置文件
* 在星球的初始化中,主要是构建初始场景和大气层,World newWorld = new World()
* 以及将可渲染图层添加入列表中,getRenderablesFromLayerDirectory()
public static World Load(string filename, Cache cache)
{
try
{
XmlReader docReader = XmlReader.Create(filename, readerSettings);
XPathDocument docNav = new XPathDocument(docReader);
XPathNavigator nav = docNav.CreateNavigator();
// 根据Xml文件进行读取星球配置,此处不采用反序列化
// 通过节点及属性进行读取,与文件格式相关,可参见Earth.xml
XPathNodeIterator worldIter = nav.Select("/World[@Name]");
if (worldIter.Count > 0)
{
worldIter.MoveNext();
string worldName = worldIter.Current.GetAttribute("Name", "");
double equatorialRadius = ParseDouble(worldIter.Current.GetAttribute("EquatorialRadius", ""));
string layerDirectory = worldIter.Current.GetAttribute("LayerDirectory", "");
if (layerDirectory.IndexOf(":") < 0)
{
layerDirectory = Path.Combine(Path.GetDirectoryName(filename), layerDirectory);
}
TerrainAccessor[] terrainAccessor = getTerrainAccessorsFromXPathNodeIterator(worldIter.Current.Select("TerrainAccessor"),
System.IO.Path.Combine(cache.CacheDirectory, worldName));
// 初始化星球,此处仅对外层大气层和根瓦片层进行初始化格网
World newWorld = new World(
worldName,
new Microsoft.DirectX.Vector3(0, 0, 0),
new Microsoft.DirectX.Quaternion(0, 0, 0, 0),
equatorialRadius,
cache.CacheDirectory,
(terrainAccessor != null ? terrainAccessor[0] : null)//TODO: Oops, World should be able to handle an array of terrainAccessors
);
// 从图层文件夹中获取可渲染物体,例如^Placenames.xml、@Images.xml等
newWorld.RenderableObjects = getRenderablesFromLayerDirectory(layerDirectory, newWorld, cache);
return newWorld;
}
}
return null;
}
* 此处主要是World的构造函数 World newWorld = new World()其基类是RenderableObject
* 其中构建的可渲染物体包括
this.m_projectedVectorRenderer = new ProjectedVectorRenderer(this);
--UpdateRootTiles();
--ProjectedVectorTile newTile = new ProjectedVectorTile()
--Renderable.ImageLayer 才是最终的可渲染的物体
*
m_outerSphere.Init()
--mesh.Vertices = CreateMesh();
注意此处的外大气层不是RenderableObject,它的渲染和更新不通过接口完成
*
* 6)打开启动星球
* OpenStartupWorld()
* 获取startupWorldName,再OpenWorld( curWorldFile );
* worldWindow.CurrentWorld = WorldWind.ConfigurationLoader.Load(worldXmlFile, worldWindow.Cache);
* 此处对于ConfigurationLoader.Load与上面所讲内容一致
* 但存在的疑问是,这里重复操作了,可以通过一定的优化,以加快初始化速度。
*
* 7)加载项,在OpenStartupWorld()->OpenWorld()中最后几行
// 初始化加载编译器,包括VB、C#、JS编译器
InitializePluginCompiler();
// 添加图层目录按钮
// this.worldWindow.CurrentWorld.RenderableObjects.ChildObjects
// 是从加载地球时getRenderablesFromLayerDirectory()函数加载进来的
// 另一方面,从加载项的载入时添加的图层,例如3DStars等
foreach (RenderableObject worldRootObject in this.worldWindow.CurrentWorld.RenderableObjects.ChildObjects)
{
this.AddLayerMenuButtons(this.worldWindow, worldRootObject);
}
// 添加内部加载项目录按钮,其中InternalPlugin是从InitializePluginCompiler
// 中的FindPlugins(Assembly.GetExecutingAssembly());函数得到的
this.AddInternalPluginMenuButtons();
* 1.7.1 初始化加载项,分为三步a构造函数、b寻找可加载项,c进行加载
* 注意此处已经将Settings中设置的启动加载项与之后的m_Plugins关联起来
private void InitializePluginCompiler()
{
Log.Write(Log.Levels.Debug, "CONF", "initializing plugin compiler...");
this.splashScreen.SetText("Initializing plugins...");
string pluginRoot = Path.Combine(DirectoryPath, "Plugins");
// 构造函数
compiler = new PluginCompiler(this, pluginRoot);
//#if DEBUG
// Search for plugins in worldwind.exe (plugin development/debugging aid)
// 寻找WW内部的加载项
compiler.FindPlugins(Assembly.GetExecutingAssembly());
//#endif
// 寻找Debug/Plugin/目录下的其他加载项
// 根据文件扩展名来判定,可加载c#/vb/js等类型或.dll预编译的加载项
compiler.FindPlugins();
// 进行加载
compiler.LoadStartupPlugins();
}
* 1.7.1.1 构造函数中,最重要的是定义了三个编译器,C#/VB/JS
AddCodeProvider(new Microsoft.CSharp.CSharpCodeProvider() );
AddCodeProvider(new Microsoft.VisualBasic.VBCodeProvider() );
AddCodeProvider(new Microsoft.JScript.JScriptCodeProvider() );
* 1.7.1.2 寻找可加载项包括内部和外部,内部的通过主程序里的模块加载
* 外部的通过Debug/Plugin/文件中进行读取
* 其中FindPlugins()主要调用AddPlugin(string path)函数
* 此处仅根据扩展名添加加载项的路径
string extension = Path.GetExtension(filename).ToLower();
if(HasCompiler(extension) || IsPreCompiled(extension))
{
PluginInfo plugin = new PluginInfo();
plugin.FullPath = filename;
m_plugins.Add(plugin);
}
* 1.7.1.3 进行加载LoadStartupPlugins(),其中核心函数为Load(PluginInfo pi)
public void Load(PluginInfo pi)
{
if(pi.Plugin == null)
{
// Try to find a suitable compiler
// 根据扩展名查找合适的编译器
string extension = Path.GetExtension(pi.FullPath).ToLower();
Assembly asm = null;
if(extension==".dll")
{
// Load pre-compiled assembly
// 对于.dll文件则直接加载预编译项
asm = Assembly.LoadFile(pi.FullPath);
}
else
{
// 根据扩展名加载合适的编译器,并编译
CodeDomProvider cdp = (CodeDomProvider)codeDomProviders[extension];
if(cdp==null)
return;
asm = Compile(pi, cdp);
}
// 获取Plugin的接口
pi.Plugin = GetPluginInterface(asm);
}
string pluginPath = MainApplication.DirectoryPath;
if( pi.FullPath != null && pi.FullPath.Length > 0)
pluginPath = Path.GetDirectoryName(pi.FullPath);
// 加载Plugin,其内部Load可重载
pi.Plugin.PluginLoad(worldWind, pluginPath);
* 1.7.2 AddLayerMenuButtons添加图层目录按钮
* this.worldWindow.CurrentWorld.RenderableObjects.ChildObjects
* 是从加载地球时getRenderablesFromLayerDirectory()函数加载进来的
* 另一方面,从加载项的载入时添加的图层,例如3DStars等,即通过上面加载
* Plugin内部的Load重载
*
* 1.7.3 添加内部加载项目录按钮,
* 其中InternalPlugin是从InitializePluginCompiler中的
* FindPlugins(Assembly.GetExecutingAssembly());函数得到的
* 核心函数是使用worldWindow.MenuBar.AddToolsMenuButton()函数完成
* 此处,只添加三个加载项目的目录按钮,而其他还有20个目录按钮在不同
* 地方进行加载,可通过搜索MenuBar.AddToolsMenuButton进行查找.
* 8) 设置垂直放大子目录
* 9) 载入其他默认配置,以更改菜单栏状态
* 10)强制初始绘制,以免向用户显示随机帧缓存内容
* Render()内容将在后续说明
*
* 小结:通过对MainApplication实例的初始化,基本将主程序的窗口界面、启动
* 星球、加载项、配置信息初始化完成,并可进行第一帧的绘制。此后的
* 过程即为不断绘制并更新当前窗体内容。更新是通过鼠标、键盘、菜单
* 栏、工具栏的状态改变来更新相应的参数。而绘制函数所需要的所有参
* 数都已经在此完成,在绘制函数中,只需要不断循环即可。接下来,通过
* 对Render函数的学习,以及对鼠标、键盘、菜单栏、工具栏的学习,即可
* 逐步掌握WW的入门。而进阶学习还将要有更深入的了解才能有质的飞越。