zoukankan      html  css  js  c++  java
  • WorldWind学习系列十一:Virtual Earth插件学习

        学习WorldWind有很长时间了,理论学习算是基本完成了。我体会是WW的学习主要分为两大步:WW框架体系学习和WW插件学习。学习WW插件逐步深入后,必然要首先学习Direct3D编程,这也算是我的经验之谈吧。今天Virtual Earth插件学习完成,也标志着我可以从WW理论转向WW实践啦。虽然我总结介绍的是Virtual Earth插件,但是希望网友阅读下面的内容前,最好能够先深入学习Direct3D编程、BMNG和Globe Icon插件的底层渲染,这些都是学习Virtual Earth的基础。

      Virtual Earth插件包括以下几个类:

      VirtualEarthForm 窗体类

      VirtualEarthPlugin插件类,继承自Plugin(重要)

      VeReprojectTilesLayer 渲染对象类,继承自Renderable Object(重要)

      VeTile 瓦片对象类(真正实现大部分功能的)(重要)

      Projection投影变换类(重要)

      Search类

      PushPin类

      我们先看看VirtualEarthPlugin,所有的插件类必须继承自Plugin.cs,必须重写Load()和Unload()方法。这两个方法分别实现插件的加载和卸载。

      Load()方法一般是实现添加菜单和添加工具按钮,跟前面介绍插件都很类似的,自己开发插件时模仿着这套路写就行。

      public override void Load()
            {
                
    try
                {
              //判断当前World为Earth(注:其他星体可通过名字判断)
                    
    if (ParentApplication.WorldWindow.CurrentWorld.IsEarth)
                    { 
    //初始化VE插件控制窗体
                        m_Form 
    = new VirtualEarthForm(ParentApplication);
                        m_Form.Owner 
    = ParentApplication;
               
    //添加VE插件菜单
                        m_MenuItem 
    = new MenuItem("MicroSoft VirtualEarth");
                        m_MenuItem.Click 
    += new EventHandler(menuItemClicked);
                        ParentApplication.PluginsMenu.MenuItems.Add(m_MenuItem);

                        
    //#if DEBUG
                        string imgPath = Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + "\\Plugins\\VirtualEarth\\VirtualEarthPlugin.png";
                        
    //#else
                        
    //                    _pluginDir = this.PluginDirectory;
                        
    //                    string imgPath = this.PluginDirectory + @"\VirtualEarthPlugin.png";
                        
    //#endif
                        if (File.Exists(imgPath) == false)
                        {
                            Utility.Log.Write(
    new Exception("imgPath not found " + imgPath));
                        }
                //添加工具按钮,会出现工具栏里
                        m_ToolbarItem 
    = new WorldWind.WindowsControlMenuButton(
                            
    "MicroSoft VirtualEarth",
                            imgPath,
                            m_Form);

                        ParentApplication.WorldWindow.MenuBar.AddToolsMenuButton(m_ToolbarItem);

                        
    base.Load();
                    }
                }
                
    catch (Exception ex)
                {
                    Utility.Log.Write(ex);
                    
    throw;
                }
            }

      Unload()方法就是释放插件窗体,并移除插件菜单项和插件工具按钮。

      我们再来看看VeReprojectTilesLayer 类,像其他插件类一样,要重载Initialize()、Update()、Render()方法,但是,VE插件重点在Upadate()方法,当然他还有其他自己特色的方法。

      Initialize()方法主要是一些该类全局对象的实例化,如:投影、VE控制窗体及VeTile初始化等。

      Render()方法主要是实现插件的三维渲染功能的,但是VE的Render真正实现是调用VeTile类中的Render()方法。

    VE插件渲染代码
     /// <summary>
            
    /// Draws the layer
            
    /// </summary>
            public override void Render(DrawArgs drawArgs)
            {
                
    try
                {
                    
    if (this.isOn == false)
                    {
                        
    return;
                    }

                    
    if (this.isInitialized == false)
                    {
                        
    return;
                    }

                    
    if (drawArgs.device == null)
                        
    return;

                    
    if (veTiles != null && veTiles.Count > 0)
                    {
                        
    //render mesh and tile(s)
                        bool disableZBuffer = false//TODO where do i get this setting
                        
    //foreach(VeTile veTile in veTiles)
                        
    //{
                        
    //    veTile.Render(drawArgs, disableZBuffer);
                        
    //}

                        
    // camera jitter fix
                        drawArgs.device.Transform.World = Matrix.Translation(
                               (
    float)-drawArgs.WorldCamera.ReferenceCenter.X,
                            (
    float)-drawArgs.WorldCamera.ReferenceCenter.Y,
                            (
    float)-drawArgs.WorldCamera.ReferenceCenter.Z
                        );

                        
    // Clear ZBuffer between layers (as in WW)
                        drawArgs.device.Clear(ClearFlags.ZBuffer, 01.0f0);

                        
    // Render tiles(这里的GetZoomLevelByTrueViewRange方法是个知识点,该方法是根据WorldCamera视角范围,求取当前球体的缩放层次)
                int zoomLevel = GetZoomLevelByTrueViewRange(drawArgs.WorldCamera.TrueViewRange.Degrees);
            
             //真正的渲染处理是由VeTile类的Render()方法实现的
              
    int tileDrawn = VeTile.Render(drawArgs, disableZBuffer, veTiles, zoomLevel); // Try current level first
             if(tileDrawn == 0) VeTile.Render(drawArgs, disableZBuffer, veTiles, prevLvl); // If nothing drawn, render previous level

                        
    //camera jitter fix
                        drawArgs.device.Transform.World = drawArgs.WorldCamera.WorldMatrix;

                        
    //Render logo
                        RenderDownloadProgress(drawArgs, null0);

                    }

                    
    //else pushpins only
                    
    //render PushPins
                    if (pushPins != null && pushPins.Count > 0)
                    {

                        RenderPushPins(drawArgs);

                    }
                }
                
    catch (Exception ex)
                {
                    Utility.Log.Write(ex);
                }
            }

       VE中获取缩放级别的方法是很巧妙的,根据原作者文章知:VE的缩放级别是1-19级甚至更低,是以2的阶乘递减的。方法我们自己开发时可以重用,但是它的思想还是要好好体会的。

      注:缩放级别为 180 、90、45、22.5、……

    根据视角范围获取缩放级别
            public int GetZoomLevelByTrueViewRange(double trueViewRange)
            {
                
    int maxLevel = 3; //视角范围为45度
                
    int minLevel = 19;
                
    int numLevels = minLevel - maxLevel + 1;
                
    int retLevel = maxLevel;
                
    for (int i = 0; i < numLevels; i++)
                {
                    retLevel 
    = i + maxLevel;

                    
    double viewAngle = 180;
                    
    for (int j = 0; j < i; j++)
                    {
                        viewAngle 
    = viewAngle / 2.0;
                    }
                    
    if (trueViewRange >= viewAngle)
                    {
                        
    break;
                    }
                }
                
    return retLevel;
            }

      VE插件最最关键的方法为Update(),因为VE实质上就是不断地根据缩放级别更新影像数据,其实就是构建要渲染绘制的对象集合,最后由Render()方法完成渲染绘制。所以该方法是VE中最复杂的,当然也就是VE的处理精华所在,也就是我们研究学习的重点啦。(说这些,主要是告诉大家这里是重点,要好好研究和关注)

    Update()代码
            /// <summary>
            
    /// Update layer (called from worker thread)
            
    /// </summary>
            public override void Update(DrawArgs drawArgs)
            {

                
    try
                {
                    
    if (this.isOn == false)
                    {
                        
    return;
                    }

                    
    //NOTE for some reason Initialize is not getting called from the Plugin Menu Load/Unload
                    
    //it does get called when the plugin loads from Startup
                    
    //not sure what is going on, so i'll just call it manually
                    if (this.isInitialized == false)
                    {
                        
    this.Initialize(drawArgs);
                        
    return;
                    }

                    
    //get lat, lon 获取经纬度、倾斜角度、高度等
                    double lat = drawArgs.WorldCamera.Latitude.Degrees;
                    
    double lon = drawArgs.WorldCamera.Longitude.Degrees;
                    
    double tilt = drawArgs.WorldCamera.Tilt.Degrees;
                    
    //determine zoom level
                    double alt = drawArgs.WorldCamera.Altitude;
                    
    //could go off distance, but this changes when view angle changes
                    
    //Angle fov = drawArgs.WorldCamera.Fov; //stays at 45 degress
                    
    //Angle viewRange = drawArgs.WorldCamera.ViewRange; //off of distance, same as TVR but changes when view angle changes
                   //获取当前视角范围      
                    Angle tvr = drawArgs.WorldCamera.TrueViewRange; //off of altitude
                    
    //smallest altitude = 100m
                    
    //tvr = .00179663198575926
                    
    //start altitude = 12756273m
                    
    //tvr = 180

                    
    //WW _levelZeroTileSizeDegrees 获取缩放级别,上面已经介绍啦
                    
    //180 90 45 22.5 11.25 5.625 2.8125 1.40625 .703125 .3515625 .17578125 .087890625 0.0439453125 0.02197265625 0.010986328125 0.0054931640625
                    int zoomLevel = GetZoomLevelByTrueViewRange(tvr.Degrees);
     
             //只要到一定缩放级别时,才启用VE(这里我们可以学习,来控制一些图层的显示)
                    
    //dont start VE tiles until a certain zoom level
                    if (zoomLevel < veForm.StartZoomLevel)
                    {
                        
    this.RemoveAllTiles();
                
    this.ForceRefresh();
                        
    return;
                    }

                    
    //WW tiles
                    
    //double tileDegrees = GetLevelDegrees(zoomLevel);
                    
    //int row = MathEngine.GetRowFromLatitude(lat, tileDegrees);
                    
    //int col = MathEngine.GetColFromLongitude(lon, tileDegrees);

                    
    //VE tiles
                    double metersY;
                    
    double yMeters;
                    
    int yMetersPerPixel;
                    
    int row;
                    
    /*
                    //WRONG - doesn't stay centered away from equator
                    //int yMeters = LatitudeToYAtZoom(lat, zoomLevel); //1024
                    double sinLat = Math.Sin(DegToRad(lat));
                    metersY = earthRadius / 2 * Math.Log((1 + sinLat) / (1 - sinLat)); //0
                    yMeters = earthHalfCirc - metersY; //20037508.342789244
                    yMetersPerPixel = (int) Math.Round(yMeters / MetersPerPixel(zoomLevel));
                    row = yMetersPerPixel / pixelsPerTile;
                    
    */
                    
    //CORRECT
                    
    //int xMeters = LongitudeToXAtZoom(lon, zoomLevel); //1024
            //计算弧长,(earthRadius 地球半径,DegToRad(lon)经度的度值转弧度值)
            //DegToRad()为角度转弧度的方法,很简单。弧长=半径*弧度值。(这里球体的弧度值认为有负值)
                    double metersX = earthRadius * DegToRad(lon); //0
                  //因为从西到东算列数,所以要从-180度算列所在的弧度的总长度的      
                    double xMeters = earthHalfCirc + metersX; //20037508.342789244
             //获取总的像素数。MetersPerPixel(zoomLevel))是计算每像素代表的米数。(知识点)
                    int xMetersPerPixel = (int)Math.Round(xMeters / MetersPerPixel(zoomLevel));
             //获取列数,从西-》东(-180-》180)来算列数
                    
    int col = xMetersPerPixel / pixelsPerTile;
              
                    
    //reproject - overrides row above
                    
    //this correctly keeps me on the current tile that is being viewed
            //使用UV结构体来存放经纬度,然后调用proj.Forward(uvCurrent)实现投影变换
                    UV uvCurrent = new UV(DegToRad(lon), DegToRad(lat));
                    uvCurrent 
    = proj.Forward(uvCurrent);
                    metersY 
    = uvCurrent.V;
             //这里为啥是“—”??看过该插件原作者的文章才会知道:原来VE的行数是从北向南算的,而WW是从南向北计算行数的。
                    yMeters 
    = earthHalfCirc - metersY;
             //获取总像素数
                    yMetersPerPixel 
    = (int)Math.Round(yMeters / MetersPerPixel(zoomLevel));
             //获取行数
                    row 
    = yMetersPerPixel / pixelsPerTile;
    说明:计算行列数,是为了后面获取切片后图片,并将正确的图片作为纹理渲染到正确的位置上,当然这过程还有很多处理,我会一一分析的。
                    
    //update mesh if VertEx changes
                    if (prevVe != World.Settings.VerticalExaggeration)
                    {
                        
    lock (veTiles.SyncRoot)
                        {
                            VeTile veTile;
                            
    for (int i = 0; i < veTiles.Count; i++)
                            {
                                veTile 
    = (VeTile)veTiles[i];
                                
    if (veTile.VertEx != World.Settings.VerticalExaggeration)
                                {
                  //创建网格MESH(稍后重点分析)
                             veTile.CreateMesh(
    this.Opacity, World.Settings.VerticalExaggeration);
                                }
                            }
                        }
                    }
                    prevVe 
    = World.Settings.VerticalExaggeration;

                    
    //if within previous bounds and same zoom level, then exit
                    if (row == prevRow && col == prevCol && zoomLevel == prevLvl && tilt == preTilt)
                    {
                        
    return;
                    }

                    
    //System.Diagnostics.Debug.WriteLine("CHANGE");

                    
    lock (veTiles.SyncRoot)
                    {
                        VeTile veTile;
               //使之前存放的veTile,标记为“暂时不需要”,这里没有删除
                        
    for (int i = 0; i < veTiles.Count; i++)
                        {
                            veTile 
    = (VeTile)veTiles[i];
                            veTile.IsNeeded 
    = false;
                        }
                    }

                    
    //metadata
                    ArrayList alMetadata = null;
                    
    if (veForm.IsDebug == true)
                    {
                        alMetadata 
    = new ArrayList();
                        alMetadata.Add(
    "yMeters " + yMeters.ToString());
                        alMetadata.Add(
    "metersY " + metersY.ToString());
                        alMetadata.Add(
    "yMeters2 " + yMeters.ToString());
                        alMetadata.Add(
    "vLat " + uvCurrent.V.ToString());
                        
    //alMetadata.Add("xMeters " + xMeters.ToString());
                        
    //alMetadata.Add("metersX " + metersX.ToString());
                        
    //alMetadata.Add("uLon " + uvCurrent.U.ToString());
                    }
             
    //添加目前VeTile(这是重点,稍后分析)
                    
    //add current tiles first
                    AddVeTile(drawArgs, row, col, zoomLevel, alMetadata);
                    
    //then add other tiles outwards in surrounding circles
              //添加周边的VeTile
                    AddNeighborTiles(drawArgs, row, col, zoomLevel, null1);
                    AddNeighborTiles(drawArgs, row, col, zoomLevel, 
    null2);
                    AddNeighborTiles(drawArgs, row, col, zoomLevel, 
    null3);
            
    // Extend tile grid if camera tilt above some values
                    //根据倾斜角度决定是否继续添加相邻Tile单位          
                    if(tilt > 45) AddNeighborTiles(drawArgs, row, col, zoomLevel, null4);
                    
    if(tilt > 60) AddNeighborTiles(drawArgs, row, col, zoomLevel, null5);


                    
    //if(prevLvl > zoomLevel) //zooming out
                    
    //{
                    
    //}            

                    
    lock (veTiles.SyncRoot)
                    {
                        VeTile veTile;
                //移除不需要的veTile图片,为啥是现在移除??(思考一下有啥好处)
                        
    for (int i = 0; i < veTiles.Count; i++)
                        {
                            veTile 
    = (VeTile)veTiles[i];
                            
    if (veTile.IsNeeded == false)
                            {
                                veTile.Dispose();
                                veTiles.RemoveAt(i);
                    i
    --;
                            }
                        }
                    }
            //保存当前基本的行列、缩放级别、倾斜角度
                    prevRow 
    = row;
                    prevCol 
    = col;
                    prevLvl 
    = zoomLevel;
            preTilt 
    = tilt;
                }
                
    catch (Exception ex)
                {
                    Utility.Log.Write(ex);
                }

            }

     我们来看看AddVeTile(drawArgs, row, col, zoomLevel, alMetadata);方法,因为该方法是AddNeighborTiles(drawArgs, row, col, zoomLevel, null1);方法的基础。

    AddVeTile()方法代码

     private void AddVeTile(DrawArgs drawArgs, int row, int col, int zoomLevel, ArrayList alMetadata)
            {
                
    //TODO handle column wrap-around
                //haven't had to explicitly handle this yet

                
    bool tileFound = false;
                
    lock (veTiles.SyncRoot)
                {
                    
    foreach (VeTile veTile in veTiles)
                    {
                        
    if (veTile.IsNeeded == true)
                        {
                            
    continue;
                        }

      //判断当前的veTile对象,是否是当前缩放级别下的行列对应的瓦片,这里涉及到VeTile类的IsEqual()方法
                        
    if (veTile.IsEqual(row, col, zoomLevel) == true)
                        {
                            veTile.IsNeeded = true;
                            tileFound = true;
                            
    break;
                        }
                    }
                }
                
    if (tileFound == false)
                {
                    
    //exit if zoom level has changed

                   //获取当前新的缩放级别
                    int curZoomLevel = GetZoomLevelByTrueViewRange(drawArgs.WorldCamera.TrueViewRange.Degrees);
                    
    if (curZoomLevel != zoomLevel)
                    {
                        
    return;
                    }

             //之前没下载过,需要创建新的VeTile对象。CreateVeTile方法很重要
                    VeTile newVeTile = CreateVeTile(drawArgs, row, col, zoomLevel, alMetadata);
                    newVeTile.IsNeeded = true;
                    
    lock (veTiles.SyncRoot)
                    {
                        veTiles.Add(newVeTile);
                    }
                }
            }

    CreateVeTile()方法实现,相当底层啦!我自己研究了很长时间,希望能给大家说明白。即使不明白,知道是干啥知道咋用就行啦,不用深究原理的。

            private VeTile CreateVeTile(DrawArgs drawArgs, int row, int col, int zoomLevel, ArrayList alMetadata)
            {

               //初始化VeTile对象
                VeTile newVeTile = new VeTile(row, col, zoomLevel);

                
    //metadata
                if (alMetadata != null)
                {
                    
    foreach (string metadata in alMetadata)
                    {
                        newVeTile.AddMetaData(metadata);
                    }
                }
           //获取纹理图片,即从服务器端下载图片用作纹理(重点,稍后详细分析)
                
    //thread to download new tile(s) or just load from cache
                newVeTile.GetTexture(drawArgs, pixelsPerTile);

                
    //handle the diff projection

          //每个像素代表的距离
                double metersPerPixel = MetersPerPixel(zoomLevel);

            //获取当前级别总的网格边数
                
    double totalTilesPerEdge = Math.Pow(2, zoomLevel);

                //总长度(我认为:就是地球周长)
                
    double totalMeters = totalTilesPerEdge * pixelsPerTile * metersPerPixel;

                //(我认为:halfMeters就是地球周长的一半)
                
    double halfMeters = totalMeters / 2;

                
    //do meters calculation in VE space
                //the 0,0 origin for VE is in upper left

                //VE空间坐标系下,计算距离,原点在左上角

           //首先求出WW坐标系下的,NW(这里就是上面求行列数的逆运算啦)
                double N = row * (pixelsPerTile * metersPerPixel);
                
    double W = col * (pixelsPerTile * metersPerPixel);
                
    //now convert it to +/- meter coordinates for Proj.4
                //the 0,0 origin for Proj.4 is 0 lat, 0 lon
                //-22 to 22 million, -11 to 11 million

                //在以经纬度为0 0为原点的坐标系下的新的N W,坐标系方向轴为 N——S; W——E      
                N = halfMeters - N;
                W = W - halfMeters;

                //计算出单元格的 E S
                
    double E = W + (pixelsPerTile * metersPerPixel);
                
    double S = N - (pixelsPerTile * metersPerPixel);
           

           //给新的瓦片单元格的UL UR LL LR赋值,其实就是单元网格的四角的坐标(理解这点很重要,是为了创建Mesh用)
                newVeTile.UL = new UV(W, N);
                newVeTile.UR = new UV(E, N);
                newVeTile.LL = new UV(W, S);
                newVeTile.LR = new UV(E, S);

                
    //create mesh
                byte opacity = this.Opacity; //from RenderableObject
                float verticalExaggeration = World.Settings.VerticalExaggeration;

               //重点CreateMesh()方法,里面也涉及数学,不是太容易理解的
                newVeTile.CreateMesh(opacity, verticalExaggeration);
                newVeTile.CreateDownloadRectangle(drawArgs, World.Settings.DownloadProgressColor.ToArgb());

                
    return newVeTile;
            }

    VeTile类,是真正实现渲染方法,也主要用于球体和平面坐标的转换处理、计算,算是VE插件的核心啦,这里面涉及大量的数学知识,主要编程为Direct3D编程。我们现在来看看CreateMesh()方法。

    注:如果你看懂CreateMesh(),就说明你已经理解VE大部分啦

       //NOTE this is a mix from Mashi's Reproject and WW for terrain
            public void CreateMesh(byte opacity, float verticalExaggeration)
            {
                
    this.vertEx = verticalExaggeration;

                
    int opacityColor = System.Drawing.Color.FromArgb(opacity, 000).ToArgb();

                meshPointCount 
    = 32//64; //96 // How many vertices for each direction in mesh (total: n^2)
                
    //vertices = new CustomVertex.PositionColoredTextured[meshPointCount * meshPointCount];

                
    // Build mesh with one extra row and col around the terrain for normal computation and struts
                vertices = new CustomVertex.PositionNormalTextured[(meshPointCount + 2* (meshPointCount + 2)];

                
    int upperBound = meshPointCount - 1;
                
    float scaleFactor = (float)1 / upperBound;
                
    //using(Projection proj = new Projection(m_projectionParameters))
                
    //{
                double uStep = (UR.U - UL.U) / upperBound;
                
    double vStep = (UL.V - LL.V) / upperBound;
                UV curUnprojected 
    = new UV(UL.U - uStep, UL.V + vStep);
           
    //将平面坐标投影转换为WW球面坐标
                
    // figure out latrange (for terrain detail)
                UV geoUL = _proj.Inverse(m_ul);
                UV geoLR 
    = _proj.Inverse(m_lr);
                
    double latRange = (geoUL.U - geoLR.U) * 180 / Math.PI;
           
    //将弧度转为角度(这里没再使用下面四个变量,原方法用到啦,不研究!)
                North = geoUL.V * 180 / Math.PI;
                South 
    = geoLR.V * 180 / Math.PI;
                West 
    = geoUL.U * 180 / Math.PI;
                East 
    = geoLR.U * 180 / Math.PI;
           
    //半径
                float meshBaseRadius = (float)_layerRadius;

                UV geo;
                Vector3 pos;
                
    double height = 0;
                
    for (int i = 0; i < meshPointCount + 2; i++)
                {
                    
    for (int j = 0; j < meshPointCount + 2; j++)
                    { 
    //将平面坐标(proj 4)投影转换为WW球面坐标
                        geo = _proj.Inverse(curUnprojected);
                       
    //将弧度转为角度
                        
    // Radians -> Degrees
                        geo.U *= 180 / Math.PI;
                        geo.V 
    *= 180 / Math.PI;

                        
    if (_terrainAccessor != null)
                        {
                            
    if (_veForm.IsTerrainOn == true)
                            {
                                
    //height = heightData[i, j] * verticalExaggeration;
                                
    //original : need to fetch altitude on a per vertex basis (in VE space) to have matching tile borders (note PM)
                                height = verticalExaggeration * _terrainAccessor.GetElevationAt(geo.V, geo.U, Math.Abs(upperBound / latRange));
                            }
                            
    else
                            {
                                height 
    = 0;
                            }
                        }
                
    //将空间坐标转换为笛卡尔坐标
                        pos = MathEngine.SphericalToCartesian(
                            geo.V,
                            geo.U,
                            _layerRadius 
    + height); 

                    
    //构建Mesh顶点集合
                        int idx = i * (meshPointCount + 2+ j;
                        vertices[idx].X 
    = pos.X;
                        vertices[idx].Y 
    = pos.Y;
                        vertices[idx].Z 
    = pos.Z;
                        
    //double sinLat = Math.Sin(geo.V);
                        
    //vertices[idx].Z = (float) (pos.Z * sinLat);

                        vertices[idx].Tu 
    = (j - 1* scaleFactor;
                        vertices[idx].Tv 
    = (i - 1* scaleFactor;
                        
    //vertices[idx].Color = opacityColor;
                        curUnprojected.U += uStep;
                    }
                    curUnprojected.U 
    = UL.U - uStep;
                    curUnprojected.V 
    -= vStep;
                }
                
    //}
    //构建索引集合,分组存放Mesh点集合中的点。学Direct3D编程,看懂下面的很容易的
                int slices = meshPointCount + 1;
                indices 
    = new short[2 * slices * slices * 3];
                
    for (int i = 0; i < slices; i++)
                {
                    
    for (int j = 0; j < slices; j++)
                    {
                        indices[(
    2 * 3 * i * slices) + 6 * j] = (short)(i * (meshPointCount + 2+ j);
                        indices[(
    2 * 3 * i * slices) + 6 * j + 1= (short)((i + 1* (meshPointCount + 2+ j);
                        indices[(
    2 * 3 * i * slices) + 6 * j + 2= (short)(i * (meshPointCount + 2+ j + 1);

                        indices[(
    2 * 3 * i * slices) + 6 * j + 3= (short)(i * (meshPointCount + 2+ j + 1);
                        indices[(
    2 * 3 * i * slices) + 6 * j + 4= (short)((i + 1* (meshPointCount + 2+ j);
                        indices[(
    2 * 3 * i * slices) + 6 * j + 5= (short)((i + 1* (meshPointCount + 2+ j + 1);
                    }
                }

                
    // Compute normals and fold struts
                calculate_normals();
                fold_struts(
    false, meshBaseRadius);
            }

     

          

      (未完待续……

      

  • 相关阅读:
    稳扎稳打Silverlight(47) 4.0UI之操作剪切板, 隐式样式, CompositeTransform, 拖放外部文件到程序中
    返璞归真 asp.net mvc (9) asp.net mvc 3.0 新特性之 View(Razor)
    返璞归真 asp.net mvc (6) asp.net mvc 2.0 新特性
    稳扎稳打Silverlight(48) 4.0其它之打印, 动态绑定, 增强的导航系统, 杂七杂八
    精进不休 .NET 4.0 (9) ADO.NET Entity Framework 4.1 之 Code First
    稳扎稳打Silverlight(42) 4.0控件之Viewbox, RichTextBox
    稳扎稳打Silverlight(53) 4.0通信之对WCF NetTcpBinding的支持, 在Socket通信中通过HTTP检索策略文件, HTTP请求中的ClientHttp和BrowserHttp
    稳扎稳打 Silverlight 4.0 系列文章索引
    稳扎稳打Silverlight(54) 4.0通信之对UDP协议的支持: 通过 UdpAnySourceMulticastClient 实现 ASM(Any Source Multicast),即“任意源多播”
    返璞归真 asp.net mvc (8) asp.net mvc 3.0 新特性之 Model
  • 原文地址:https://www.cnblogs.com/wuhenke/p/1631739.html
Copyright © 2011-2022 走看看