自主三维GIS引擎笔记-实现三维球045

这篇具有很好参考价值的文章主要介绍了自主三维GIS引擎笔记-实现三维球045。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最小GIS迷你地球实现(实现一套最小的三维GIS球体)  V1.0.0.0版本

数据加代码比较大(主要是数据,数据有1G多,代码约5000行),无法上传,如需要微信联系(17381925156)

效果图:

自主三维GIS引擎笔记-实现三维球045

 

相机推进后:

自主三维GIS引擎笔记-实现三维球045

1 . 功能目标

1.1 实现基本的卫片数据浏览

1.2 实现高程数据的浏览

1.3   实现基本的三维操作,平移,旋转缩放功能

2  必备知识

2.1  图形学基础(OpenGL基础知识),采用OpenGL3.0标准,纹理数组,shader,Instance绘制

2.2  C++编程基础,vector,list,string,map,function,sort,c++ 17,匿名函数(lamda表达式),智能指针

2.3  多线程支持,多线程采用生产者与消费者模型,信号量,互斥体,队列

2.4  地理信息基本支持,卫片,高程,投影,转换,坐标系

3  软件结构图

 

自主三维GIS引擎笔记-实现三维球045

3.1 场景管理

3.1.1  相机实现

自主三维GIS引擎笔记-实现三维球045

实现第一人称相机,主要是实现功能,

定点旋转

        virtual void    rotateViewXByCenter(real angle,real3  pos)
        {
            if (!(_flag & FLAG_ROT_X))
            {
                return;
            }
            //! 计算眼睛到鼠标点的方向
            real3   vDir    =   pos - _eye;
            real    len1    =   length(vDir);
            real    len     =   0;
            vDir    =   normalize(vDir);
            matrix4r mat(1);
            mat.rotate(angle, _right);

            vDir    =   vDir * mat;
            _eye    =   pos - vDir * len1;

            _dir    =   _dir * mat;
            _up     =   _up * mat;
#ifndef COORD_LH
            _right  =   CELL::normalize(cross(_dir,_up));
            _up     =   CELL::normalize(cross(_right,_dir));
#else
            _right  =   CELL::normalize(cross(_up,_dir));
            _up     =   CELL::normalize(cross(_dir,_right));
#endif
            len     =   CELL::length(_eye - _target);

            _target     =   _eye + _dir * len;
            update();

        }

 

定点缩放

/**
 *    指定点推进摄像机
 */
virtual void    scaleCameraByPos(const real3& pos,real persent)
{

    real3   dir     =   CELL::normalize(pos - _eye);

    real    dis     =   CELL::length(pos - _eye) * persent;

    real    disCam  =   CELL::length(_target - _eye) * persent;

    real3   dirCam  =   CELL::normalize(_target - _eye);

    _eye    =   pos - dir * dis;
    _target =   _eye + dirCam * disCam;

    update();
}

 

精确移动

virtual void    moveLeft(real fElapsed)
{
    _eye     -=  normalize(_right) * _speed * fElapsed;
    _target  -=  normalize(_right) * _speed * fElapsed;
}

virtual void    moveRight(real fElapsed)
{
    _eye     +=  normalize(_right) * _speed * fElapsed;
    _target  +=  normalize(_right) * _speed * fElapsed;
}


virtual void    moveFront(real fElapsed)
{
    _eye    +=  _dir * _speed * fElapsed;
    _target +=  _dir * _speed * fElapsed;
}

virtual void    moveBack(real fElapsed)
{
    _eye    -=  _dir * _speed * fElapsed;
    _target -=  _dir * _speed * fElapsed;
}
/**
 *    往上看,则向下移动摄像机
 */
 virtual void   moveUp(real fElapsed)
 {
     _eye       +=  _up * _speed * fElapsed;
     _target    +=  _up * _speed * fElapsed;
 }
 /**
  *    向下看,则摄像机向上移动
  */
 virtual void   moveDown(real fElapsed)
 {
     _eye       -=  _up * _speed * fElapsed;
     _target    -=  _up * _speed * fElapsed;
 }
 /**
  *    根据给定的方向移动摄像机
  */
  virtual   void    moveDir(real3 dir,real fElapsed)
  {
    _eye    += dir * _speed * fElapsed;
    _target += dir * _speed * fElapsed;
      
  }

 

实现屏幕到世界坐标投影转换 

/**
*   窗口坐标转化为世界坐标
*/
real3  screenToWorld(const real2& screen)
{
    real4  screens(screen.x,screen.y,0,1);
    real4  world;
    unProject(screens,world);
    return  real3(world.x,world.y,world.z);
}

 

世界坐标到屏幕坐标投影转换

/**
*   世界坐标转化为窗口坐标
*/
real2  worldToScreen( const real3& world)
{
    real4  worlds(world.x,world.y,world.z,1);
    real4  screens;
    project(worlds,screens);
    return  real2(screens.x,screens.y);
}

 

根据屏幕坐标点创建射线

Ray createRayFromScreen(int x,int y)
{
    real4  minWorld;
    real4  maxWorld;

    real4  screen(real(x),real(y),0,1);
    real4  screen1(real(x),real(y),1,1);

    unProject(screen,minWorld);
    unProject(screen1,maxWorld);
    Ray     ray;
    ray.setOrigin(real3(minWorld.x,minWorld.y,minWorld.z));

    real3  dir(maxWorld.x - minWorld.x,maxWorld.y - minWorld.y, maxWorld.z - minWorld.z);
    ray.setDirection(normalize(dir));
    return  ray;
}

 

3.1.2  裁剪实现

自主三维GIS引擎笔记-实现三维球045

根据投影矩阵,观察矩阵构造视锥

1. 支持判断点是否在视锥内

2. 支持判断是否球体在视锥内

3. 支持判断包围盒是否在视锥内。

template<class T>
class   tfrustum
{
public:
    enum
    {
        FRUSTUM_LEFT    =   0,
        FRUSTUM_RIGHT   =   1,
        FRUSTUM_TOP     =   2,
        FRUSTUM_BOTTOM  =   3,
        FRUSTUM_FAR     =   4,
        FRUSTUM_NEAR    =   5,
    };
public:
    /**
    *   project * modleview
    */
    void    loadFrustum(const tmat4x4<T> &mvp)
    {
        const T*  dataPtr =   mvp.data();
        _planes[FRUSTUM_LEFT  ] =   Plane<T>(dataPtr[12] - dataPtr[0], dataPtr[13] - dataPtr[1], dataPtr[14] - dataPtr[2],  dataPtr[15] - dataPtr[3]);
        _planes[FRUSTUM_RIGHT ] =   Plane<T>(dataPtr[12] + dataPtr[0], dataPtr[13] + dataPtr[1], dataPtr[14] + dataPtr[2],  dataPtr[15] + dataPtr[3]);

        _planes[FRUSTUM_TOP   ] =   Plane<T>(dataPtr[12] - dataPtr[4], dataPtr[13] - dataPtr[5], dataPtr[14] - dataPtr[6],  dataPtr[15] - dataPtr[7]);
        _planes[FRUSTUM_BOTTOM] =   Plane<T>(dataPtr[12] + dataPtr[4], dataPtr[13] + dataPtr[5], dataPtr[14] + dataPtr[6],  dataPtr[15] + dataPtr[7]);

        _planes[FRUSTUM_FAR   ] =   Plane<T>(dataPtr[12] - dataPtr[8], dataPtr[13] - dataPtr[9], dataPtr[14] - dataPtr[10], dataPtr[15] - dataPtr[11]);
        _planes[FRUSTUM_NEAR  ] =   Plane<T>(dataPtr[12] + dataPtr[8], dataPtr[13] + dataPtr[9], dataPtr[14] + dataPtr[10], dataPtr[15] + dataPtr[11]);
    }
    bool    pointInFrustum(const tvec3<T> &pos) const
    {
        for (int i = 0; i < 6; i++)
        {
            if (_planes[i].distance(pos) <= 0) 
                return false;
        }
        return true;
    }
    bool    sphereInFrustum(const tvec3<T> &pos, const float radius) const
    {
        for (int i = 0; i < 6; i++)
        {
            if (_planes[i].distance(pos) <= -radius) 
                return false;
        }
        return true;
    }
    bool    cubeInFrustum(T minX,T maxX,T minY,T maxY,T minZ,T maxZ) const
    {
        for (int i = 0; i < 6; i++)
        {
            if (_planes[i].distance(tvec3<T>(minX, minY, minZ)) > 0) continue;
            if (_planes[i].distance(tvec3<T>(minX, minY, maxZ)) > 0) continue;
            if (_planes[i].distance(tvec3<T>(minX, maxY, minZ)) > 0) continue;
            if (_planes[i].distance(tvec3<T>(minX, maxY, maxZ)) > 0) continue;
            if (_planes[i].distance(tvec3<T>(maxX, minY, minZ)) > 0) continue;
            if (_planes[i].distance(tvec3<T>(maxX, minY, maxZ)) > 0) continue;
            if (_planes[i].distance(tvec3<T>(maxX, maxY, minZ)) > 0) continue;
            if (_planes[i].distance(tvec3<T>(maxX, maxY, maxZ)) > 0) continue;
            return false;
        }
        return true;
    }

    const Plane<T> &getPlane(const int plane) const 
    { 
        return _planes[plane];
    }
public:
    Plane<T>    _planes[6];
};

 

3.1.2 浏览实现

输入输出接口定义,实现对鼠标键盘事件的抽象,后续可以实现跨平台处理

class   CELLInput
{
public:
    /// <summary>
    /// 鼠标左键按下
    /// </summary>
    virtual void    onLButtonDown(int x,int y)  =   0;
    /// <summary>
    /// 鼠标左键提起
    /// </summary>
    virtual void    onLButtonUp(int x, int y) = 0;
    virtual void    onLButtonDbClick(int x, int y)  =   0;
    virtual void    onRButtonDbClick(int x, int y)  =   0;
    virtual void    onMButtonDbClick(int x, int y)  =   0;

    /// <summary>
    /// 鼠标移动事件
    /// </summary>

    virtual void    onRButtonDown(int x, int y) =   0;
    /// <summary>
    /// 鼠标移动事件
    /// </summary>
    virtual void    onRButtonUp(int x, int y)   =   0;

    /// <summary>
    /// 鼠标移动事件
    /// </summary>

    virtual void    onMButtonDown(int x, int y) = 0;
    /// <summary>
    /// 鼠标移动事件
    /// </summary>
    virtual void    onMButtonUp(int x, int y) = 0;


    /// <summary>
    /// 鼠标移动事件
    /// </summary>
    virtual void    onMouseMove(int x,int y)    =   0;

    /// <summary>
    /// 鼠标移动事件
    /// </summary>
    virtual void    onMouseWheel(int delta) = 0;
    /// <summary>
    /// 键盘事件
    /// </summary>
    virtual void    onKeyDown(int key)  =   0;
    /// <summary>
    /// 键盘事件
    /// </summary>
    virtual void    onKeyUp(int key)    =   0;

};

 

 具体业务代码实现:处理windows事件

LRESULT         eventProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONDOWN:
        _frame->onLButtonDown(LOWORD(lParam),HIWORD(lParam));
        break;
    case WM_LBUTTONUP:
        _frame->onLButtonUp(LOWORD(lParam),HIWORD(lParam));
        break;
    case WM_RBUTTONDOWN:
        _frame->onRButtonDown(LOWORD(lParam),HIWORD(lParam));
        break;
    case WM_RBUTTONUP:
        _frame->onRButtonUp(LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_MBUTTONDOWN:
        _frame->onMButtonDown(LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_MBUTTONUP:
        _frame->onMButtonUp(LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_MOUSEMOVE:
        _context._mouseX = LOWORD(lParam);
        _context._mouseY = HIWORD(lParam);
        _frame->onMouseMove(LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_MOUSEWHEEL:

        _frame->onMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam));
        break;
    case WM_KEYDOWN:
        _frame->onKeyDown((int)wParam);
        break;
    case WM_KEYUP:
        _frame->onKeyUp((int)wParam);
        break;
    case WM_LBUTTONDBLCLK:
        _frame->onLButtonDbClick(LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_RBUTTONDBLCLK:
        _frame->onLButtonDbClick(LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_MBUTTONDBLCLK:
        _frame->onMButtonDbClick(LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_SIZE:
        {
            if(IsWindow(_hWnd))
            { 
                RECT    rect;
                GetClientRect(_hWnd,&rect);
                _context._width     =   rect.right - rect.left;
                _context._height    =   rect.bottom - rect.top;
            }
        }
        break;
    case WM_DESTROY:
        _threadRun  =   false;
        CELLThread::join();
        PostQuitMessage(0);
        break;
    default:
        return  DefWindowProc(hWnd, message, wParam, lParam);
    }
    return  S_OK;

 

具体动作实现:

void CELLFrameBigMap::update(CELLContext& )
{
    _context._device->setViewPort(0,0,_context._width,_context._height);
    _context._screenPrj =   CELL::ortho<real>(0.0f,(real)_context._width,(real)_context._height,0,-1000.0f,1000.0f);

    _context._camera.setViewSize(real2(_context._width,_context._height));

    _context._camera.perspective(45.0,real(_context._width)/real(_context._height),10.0,FSIZE * 10);

    _context._camera.update();
    _context._mvp   =   _context._camera._matProj * _context._camera._matView * _context._camera._matWorld ;
    _context._vp    =   _context._camera._matProj * _context._camera._matView;
    
    _context._timePerFrame  =   (float)_timeStamp.getElapsedSecond();
    _timeStamp.update();

    matrix4r    matVP   =   _context._vp.transpose();
    _context._frustum.loadFrustum(matVP);

    if (_context._keyState[VK_UP])
    {
        _context._camera.moveFront(_context._timePerFrame);
    }
    if (_context._keyState[VK_DOWN])
    {
        _context._camera.moveBack(_context._timePerFrame);
    }
    if (_context._keyState[VK_LEFT])
    {
        _context._camera.moveLeft(_context._timePerFrame);
    }
    if (_context._keyState[VK_RIGHT])
    {
        _context._camera.moveRight(_context._timePerFrame);
    }

    _terrain.update(_context);
   
}

void CELLFrameBigMap::onFrameStart(CELLContext& context)
{
    context._device->clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    context._device->clearColor(0, 0, 0, 1);
    context._device->disableRenderState(GL_CULL_FACE);
    context._device->enableRenderState(GL_DEPTH_TEST);
    _terrain.render(context);

}

void CELLFrameBigMap::onFrameEnd(CELLContext& context)
{
}

void CELLFrameBigMap::onLButtonDown(int x, int y)
{
    getPointsFromScreen(x,y,_basePoint);
    _bLbuttonDown   =   true;
    _lbuttonDown    =   int2(x,y);
}

void CELLFrameBigMap::onLButtonUp(int x, int y)
{
    _lbuttonDown    =   int2(x,y);
    _bLbuttonDown   =   false;
}

void    CELLFrameBigMap::onLButtonDbClick(int x, int y)
{
    /// _basePoint
    real3   vDir    =   CELL::normalize(-_basePoint);
    real    dist    =   CELL::length(_context._camera.getEye() - _context._camera.getTarget());
    _context._camera.setTarget(_basePoint);
    real3   vEye    =   _basePoint - dist * vDir;
    _context._camera.setEye(vEye);
    _context._camera.calcDir();
    _context._camera.rotateViewXByCenter(0,_basePoint);
    _context._camera.update();


}
void    CELLFrameBigMap::onRButtonDbClick(int x, int y)
{
}
void    CELLFrameBigMap::onMButtonDbClick(int x, int y) 
{
}


void CELLFrameBigMap::onRButtonDown(int , int )
{

}

void CELLFrameBigMap::onRButtonUp(int , int )
{

}

void CELLFrameBigMap::onMButtonDown(int x, int y)
{
    _mButtonDown    =   int2(x,y);
    _bMButtonDown   =   true;
}

void CELLFrameBigMap::onMButtonUp(int x, int y)
{
    _mButtonDown    =   int2(x,y);
    _bMButtonDown   =   false;
}

void CELLFrameBigMap::onMouseMove(int x, int y)
{
    if (_bLbuttonDown)
    {
        int2    curPoint(x, y);
        int2    offset = curPoint - _lbuttonDown;
        _lbuttonDown = curPoint;
        _context._camera.rotateViewYByCenter(offset.x, _basePoint);
        _context._camera.rotateViewXByCenter(offset.y, _basePoint);
    }
    if (_bMButtonDown)
    {
        int2    ofScreen    =   int2(x,y) - _mButtonDown;
        _mButtonDown        =   int2(x,y);
        moveScene(_basePoint,ofScreen);
    }
    
}

void CELLFrameBigMap::onMouseWheel(int delta)
{
    double  persent =   1;
    if (delta > 0)
    {
        persent =   0.9;
    }
    else
    {
        persent =   1.1;
    }
    _context._camera.scaleCameraByPos(_basePoint,persent);
}

 

3.2 投影管理

3.2.1  墨卡托投影实现

自主三维GIS引擎笔记-实现三维球045

 

定义

Web Mercator 坐标系使用的投影方法不是严格意义的墨卡托投影,而是一个被 EPSG(European Petroleum Survey Group)

称为伪墨卡托的投影方法,这个伪墨卡托投影方法的大名是 Popular Visualization Pseudo Mercator,PVPM。因为这个坐标

系统是 Google Map 最先使用的,或者更确切地说,是Google 最先发明的。在投影过程中,将表示地球的参考椭球体近似的

作为正球体处理(正球体半径 R = 椭球体半长轴 a)。后来,Web Mercator 在 Web 地图领域被广泛使用,这个坐标系就名声

大噪。尽管这个坐标系由于精度问题一度不被GIS专业人士接受,但最终 EPSG 还是给了 WKID:3857。以整个世界范围,

赤道作为标准纬线,本初子午线作为中央经线,两者交点为坐标原点,向东向北为正,向西向南为负。

X轴:由于赤道半径为6378137米,则赤道周长为2PIr = 20037508.3427892,因此X轴的取值范围:[-20037508.3427892,20037508.3427892]。

Y轴:由墨卡托投影的公式可知,当纬度φ接近90°时,y值趋向于无穷。

为了方便实现一级地图在长度上一分为二,也就是在高一级精度上用4张切片地图显示,最好将xy轴显示的取值范围统一,

为此Y轴的取值范围也限定在[-20037508.3427892,20037508.3427892]之间。

通过计算纬度最大值就是85.0511287798066°

另外值得一提的是,现在网上大部分地图瓦片瓦片都是基于Web墨卡托的,就是第0层1一张图片,第1层2*2张图片...,如果使用OpenLayers可以很好的调用公共服务器

编号

EPSG:3857
EPSG:102100
EPSG:900913 (谷歌自己起的,没有被EPSG接受,900913=google)

转换实现

 1 class   GlobalMercator
 2     {
 3     protected:
 4         int     tileSize ;
 5         real    initialResolution;
 6         real    originShift;
 7 
 8     public:
 9         GlobalMercator()
10         {
11             tileSize            =   256;
12             initialResolution   =   2 * PI * 6378137 / tileSize;  
13             originShift         =   2 * PI * 6378137 / 2.0;
14         }
15 
16         real2     LatLonToMeters(real lat, real lon )
17         {
18             //"Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:900913" 
19             real    mx  = lon * originShift / 180.0 ;
20             real    my  = log( tan((90 + lat) *PI / 360.0 )) / (PI / 180.0);
21                     my  = my * originShift / 180.0;
22             return real2(mx, my);
23         }
24 
25         real2     MetersToLatLon(int mx,int my )
26         {
27             real    lon =   (mx / originShift) * 180.0;
28             real    lat =   (my / originShift) * 180.0;
29                     lat =   real(180 / PI * (2 * atan( exp( lat * PI / 180.0)) - PI / 2.0));
30             return  real2(lon,lat);
31         }
32 
33         real     resolution(int zoom )
34         {
35             //  return (2 * math.pi * 6378137) / (self.tileSize * 2**zoom)
36             return  initialResolution / (pow(2,real(zoom))); 
37         }
38         real2     PixelsToMeters(int px, int py, int zoom)
39         {
40             real    res =   resolution( zoom );
41             real    mx  =   px * res - originShift;
42             real    my  =   py * res - originShift;
43             return  real2(mx,my);
44         }
45         int2    MetersToPixels(real mx, real my, int zoom)
46         {
47             real    res =   resolution( zoom );
48             int     px  =   int((mx + originShift) / res) ;
49             int     py  =   int((my + originShift) / res);
50             return  int2(px,py);
51         }
52 
53         int2    PixelsToTile(int px, int py)
54         {
55             int tx = int( ceil( px / real(tileSize) ) - 1 );
56             int ty = int( ceil( py / real(tileSize) ) - 1 );
57             return  int2(tx,ty);
58         }
59 
60         int2 MetersToTile(real mx, real my, int zoom)
61         {
62             int2 vPt = MetersToPixels( mx, my, zoom);
63             return PixelsToTile(vPt.x,vPt.y) ;
64         }
65         int2    LongLatToTile(real dLon,real dLat,int zoom)
66         {
67             real2 vMeter  =   LatLonToMeters(dLat,dLon);
68 
69             return  MetersToTile(vMeter.x,vMeter.y,zoom);
70         }
71 
72     };
  1  class   CELLMercator :public CELLSpRef
  2     {
  3     protected:
  4         GlobalMercator  _proj;
  5     public:
  6 
  7         CELLMercator(void)
  8         {
  9         
 10         }
 11 
 12         ~CELLMercator(void)
 13         {
 14         }
 15         /**
 16         *   将经度转化为x-tile key
 17         */
 18         static  int     long2tilex(real lon, int z) 
 19         { 
 20             return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); 
 21         }
 22         /**
 23         *   将纬度转化为y-tile key
 24         */
 25         static  int     lat2tiley(real lat, int z)
 26         { 
 27             return  (int)(floor((1.0 - log( tan(lat * PI/180.0) + 1.0 / cos(lat * PI/180.0)) / PI) / 2.0 * pow(2.0, z)));   
 28         }
 29         /**
 30         *   给定 x-tile 获取经度
 31         */
 32         static  real  tilex2long(int x, int z) 
 33         {
 34             return x / pow(2.0, z) * 360.0 - 180;
 35         }
 36         /**
 37         *   给定 y-tile 获取纬度
 38         */
 39         static  real  tiley2lat(int y, int z) 
 40         {
 41             real n = PI - 2.0 * PI * y / pow(2.0, z);
 42             return 180.0 / PI * atan(0.5 * (exp(n) - exp(-n)));
 43         }
 44 
 45         static  real2  tile2lonlat(int x,int y, int z) 
 46         {
 47             real    p   =   pow(2.0, z);
 48             real    n   =   PI - 2.0 * PI * y / p;
 49             real    lat =   180.0 / PI * atan(0.5 * (exp(n) - exp(-n)));
 50             real    lon =   x / p * 360.0 - 180;
 51             return  real2(lon,lat);
 52         }
 53         real2   tile2World(int x,int y, int z)
 54         {
 55             real2   lonLat  =   tile2lonlat(x,y,z);
 56             return  _proj.LatLonToMeters(lonLat.x,lonLat.y);
 57         }
 58         /**
 59         *   根据经纬度与级别返回瓦片Key
 60         */
 61         virtual int3    getKey(unsigned level, real rLong,real rLat)
 62         {
 63             //! 注意,这里不做显示,是为了处理地图滚动,即地图的显示范围可以不进行限制
 64             #if 1
 65             if (rLong <= -180)
 66             {
 67                 rLong   =   -179.9;
 68             }
 69             if (rLong >= 180)
 70             {
 71                 rLong   =   179.9;
 72             }
 73             if (rLat < -85)
 74             {
 75                 rLat   =   -85;
 76             }
 77             if (rLat > 85)
 78             {
 79                 rLat   =   85;
 80             }
 81             #endif
 82             int levelTileNumber =   0;
 83             levelTileNumber =   1<<level;
 84 
 85             int    xTile   =    long2tilex(rLong,level);
 86 
 87             int    yTile   =    lat2tiley(rLat,level); 
 88 
 89             return  int3(xTile,yTile,level);
 90         }
 91     public:
 92         /**
 93         *   经纬度转化为世界坐标
 94         */
 95         virtual real3   longLatToWorld(const real3& longLatx) override
 96         {
 97             //return  longLatx;
 98             real3   longLat =   longLatx;
 99             longLat.x   =   tmin<real>(179.9999f,longLatx.x);
100             longLat.x   =   tmax<real>(-179.9999f,longLat.x);
101 
102             longLat.y   =   tmin<real>(85.0f,longLatx.y);
103             longLat.y   =   tmax<real>(-85.0f,longLat.y);
104             auto    res =   _proj.LatLonToMeters(longLatx.y,longLatx.x);
105             return  real3(res.x,0,res.y);
106         }
107 
108         /**
109         *   世界坐标转化为经纬度
110         */
111         virtual real3   worldToLongLat(const real3& world) override
112         {
113             const   real      worldMin = -20037504.0f * 1000;
114             const   real      worldMax = 20037504.0f * 1000;
115             real    dWordX  = (real)tmin<real>(world.x, worldMax);
116                     dWordX  = (real)tmax<real>((real)dWordX, worldMin);
117 
118             real    dWordY  = (real)tmin<real>(world.y, worldMax);
119                     dWordY  = (real)tmax<real>((real)dWordY, worldMin);
120             auto    res     =   _proj.MetersToLatLon((int)dWordX, (int)dWordY);
121             return  real3(res.x,res.y,0);
122         }
123 
124         /**
125         *   得到当前级别下tile的个数
126         */
127         int     getTileNumber(int lev)
128         {
129             return  (int)pow(2,real(lev));
130         }
131     };

 

 

3.2.2  wgs84投影实现

定义

WGS的最新版本为WGS 84(也称作WGS 1984、EPSG:4326),1984年定义、最后修订于2004年。

投影过程如下图所示:

球体被切成一个个小圆弧,一共60个投影带,分别为01,02.........60

自主三维GIS引擎笔记-实现三维球045

自主三维GIS引擎笔记-实现三维球045

 

分为60个投影带,每一个投影带投影通用编码分别为:32601,32650,32660,从上图我们可以看到从南到北,

同一个投影带又被划分为分别为X,W,V等小格子:在同一个投影带内,比如50投影带内,通用编码为32650,依次又形成了,

50X,50W,50V,50S等小格子 

代码实现

 1 class   CELLWgs842d :public CELLSpRef
 2     {
 3     public:
 4 
 5         /**
 6         *   经纬度转化为世界坐标
 7         */
 8         virtual real3   longLatToWorld(const real3& longLatx) override
 9         {
10             real3   world;
11             world.x =   longLatx.x * WGS_84_RADIUS_EQUATOR;
12             world.y =   longLatx.z;
13             world.z =   longLatx.y * WGS_84_RADIUS_EQUATOR;
14             return  world;
15         }
16 
17         /**
18         *   世界坐标转化为经纬度
19         */
20         virtual real3   worldToLongLat(const real3& world) override
21         {
22             real3   lonlat;
23             lonlat.x    =   world.x  / WGS_84_RADIUS_EQUATOR;
24             lonlat.y    =   world.y  / WGS_84_RADIUS_EQUATOR;
25             lonlat.y    =   world.z;
26             return  lonlat;
27         }
28 
29         /**
30         *   根据经纬度与级别返回瓦片Key
31         */
32         virtual int3    getKey(unsigned level, real rLong,real rLat) override
33         {
34             unsigned    xTiles  =   2<<level;
35             unsigned    yTiles  =   1<<level;
36             double      xWidth  =   TWO_PI / xTiles;
37             double      yHeight =   PI / yTiles;
38             double      xCoord  =   (rLong + PI) / xWidth;
39             if (xCoord >= xTiles)
40             {
41                 xCoord  =   xTiles - 1;
42             }
43 
44             double      yCoord  =   (HALF_PI - rLat) / yHeight;
45             if (yCoord >= yTiles)
46             {
47                 yCoord  =   yTiles - 1;
48             }
49 
50             return  int3(int(xCoord),int(yCoord),level);
51         }
52     };

 

3.3.2 wgs84球体投影实现

定义:

自主三维GIS引擎笔记-实现三维球045自主三维GIS引擎笔记-实现三维球045自主三维GIS引擎笔记-实现三维球045

实现

 1 class   CELLWgs84Sphere :public CELLSpRef
 2     {
 3     protected:
 4         tellipsoidModelIn<double> _spr;
 5     public:
 6 
 7         /**
 8         *   经纬度转化为世界坐标
 9         */
10         virtual real3   longLatToWorld(const real3& longLatx) override
11         {
12             return  _spr.longlatAltToWorld(longLatx);
13         }
14 
15         /**
16         *   世界坐标转化为经纬度
17         */
18         virtual real3   worldToLongLat(const real3& world) override
19         {
20             return  _spr.worldToLonglatAlt(world);
21         }
22 
23         /**
24         *   根据经纬度与级别返回瓦片Key
25         */
26         virtual int3    getKey(unsigned level, real rLong,real rLat) override
27         {
28             unsigned    xTiles  =   2<<level;
29             unsigned    yTiles  =   1<<level;
30             double      xWidth  =   TWO_PI / xTiles;
31             double      yHeight =   PI / yTiles;
32             double      xCoord  =   (rLong + PI) / xWidth;
33             if (xCoord >= xTiles)
34             {
35                 xCoord  =   xTiles - 1;
36             }
37 
38             double      yCoord  =   (HALF_PI - rLat) / yHeight;
39             if (yCoord >= yTiles)
40             {
41                 yCoord  =   yTiles - 1;
42             }
43 
44             return  int3(int(xCoord),int(yCoord),level);
45         }

 

3.3 四叉树

自主三维GIS引擎笔记-实现三维球045

结构定义

 1 class   CELLQuadTree :public CELLObject
 2     {
 3     public:
 4         enum ChildId
 5         {
 6             CHILD_LT,
 7             CHILD_RT,
 8             CHILD_LB,
 9             CHILD_RB,
10         };
11         enum 
12         {
13             FLAG_HAS_IMAGE  =   1<<0,
14             FLAG_HAS_DEM    =   1<<1,
15             FLAG_HAS_CULL   =   1<<2,
16             FLAG_RENDER     =   1<<3,
17         };
18     public:
19         typedef std::vector<CELLQuadTree*>  ArrayNode;
20         typedef CELLTerrainInterface        TerrainApi;
21         using   NodePtr =   std::shared_ptr<CELLQuadTree>;
22     public:
23         TerrainApi*     _terrain;
24         /// 数据标志
25         uint            _flag;
26         /// 对应瓦片id
27         TileId          _tileId;
28         real2           _vStart;
29         real2           _vEnd;
30         matrix4r        _local = matrix4r(1);
31 
32         float2          _uvStart;
33         float2          _uvEnd;
34         /// 对应瓦片的范围(世界坐标)
35         aabb3dr         _aabb;
36         /// 位置
37         ChildId         _cornerId;
38         /// 当前瓦片的父节点
39         CELLQuadTree*   _parent;
40         /// 瓦片的孩子节点
41         NodePtr         _childs[4];
42         /// 纹理id
43         uint            _textureId;
44         /// dem数据
45         DemData         _demData;
46         VertexBufferId  _vbo;
47         IndexBufferId   _ibo;
48 };

 

3.3.1  对象管理

GIS 系统中会频繁大量加载卸载地图数据(动态加卸载),时间长了会产生大量的内存碎片,影像加载绘制效率,为了提升性能

系统使用内存池以及对象池来优化瓦片数据。

引入基类定义,方便扩展新的特性

class   CELLPool :public CELLObject
{
public:
};

using   PoolPtr   =   std::shared_ptr<CELLPool>;

内存池定义如下:

使用模板实现,采用单例设计模式对外提供内存管理能力

template<size_t blackSize>
class   CELLPoolMemory :public CELLPool
{
public:
    struct  Data
    {
        char _data[blackSize];
    };
    using       ObjectList  =   std::list<void*>;
protected:
    ObjectList  _objests;
public:
    ~CELLPoolMemory()
    {
        destroy();
    }
    void    init(size_t nCnt)
    {
        for (size_t i = 0; i < nCnt; i++)
        {
            auto    obj   =   new Data();
            _objests.push_back(obj);
        }
    }
    void*   alloc()
    {
        if (_objests.empty())
        {
            return  new Data();
        }
        else
        {
            void*   ptr =   _objests.back();
            _objests.pop_back();
            return  ptr;
        }
    }

    /// <summary>
    /// 回收函数
    /// </summary>
    void    free(void* obj)
    {
        _objests.push_back(obj);
    }

    void    destroy()
    {
        for (auto var : _objests)
        {
            delete  var;
        }
        _objests.clear();
    }
public:
    static  CELLPoolMemory<blackSize>&  instance()
    {
        static  CELLPoolMemory<blackSize> sInstance;
        return  sInstance;
    }
};

对象池定义如下:

使用模板实现,采用单例设计模式对外提供内存管理能力

    template<class T>
    class   CELLPoolObject :public CELLPool
    {
    public:
        using   TPtr        =   T*;
        using   ObjectList  =   std::list<TPtr>;
        using   SPtr        =   std::shared_ptr<T>;
    protected:
        ObjectList  _objests;
   
    public:
        /// <summary>
        /// 初始化
        /// </summary>
        template<class ...Args>
        void    init(size_t nCnt,Args ...args)
        {
            for (size_t i = 0; i < nCnt; i++)
            {
                TPtr    ptr =   new T(args...);
                _objests.push_back(ptr);
            }
        }
        /// <summary>
        /// 分配函数
        /// </summary>
        template<class ...Args>
        SPtr    alloc(Args ...args)
        {
            if (_objests.empty())
            {
                return  SPtr(new T(args...),deleteObject);
            }
            else
            {
                TPtr    ptr =   _objests.back();
                _objests.pop_back();
                return  SPtr(ptr,deleteObject);
            }
        }

        /// <summary>
        /// 回收函数
        /// </summary>
        void    free(TPtr obj)
        {
            _objests.push_back(obj);
        }

        void    destroy()
        {
            _objests.clear();
        }
    public:
        static  inline  void    deleteObject(T* obj)
        {
            instance().free(obj);
        }
        static  CELLPoolObject<T>&  instance()
        {
            static  CELLPoolObject<T> sInstance;
            return  sInstance;
        }
    };

 

3.3.2  网络数据生成

 主要是生成可以绘制的三角网络数据,为了统一接口,实现如下;

struct  V3U3N4
{
    float   x, y, z;
    float   h;
    float   u, v;
};


template<ushort row,ushort col>
class   CELLDemMesh :public CELLObject
{
protected:
    V3U3N4      _data[row * col];
    ushort3     _faces[(row -1) * (col - 1) * 2];
    int         _col    =   0;
    int         _row    =   0;
    aabb3dr     _aabb;
public:
    CELLDemMesh()
    {
        _row    =   row;
        _col    =   col;
    }

    void    redefine(int c,int r)
    {
        _col    =   c;
        _row    =   r;
    }
    int     getRow() const
    {
        return  _row;
    }
    int     getCol() const
    {
        return  _col;
    }
    V3U3N4* vertData()
    {
        return  _data;
    }
    const V3U3N4* vertData() const
    {
        return  _data;
    }
    int     vertCount() const
    {
        return  _row * _col;
    }
    int     vertByte() const
    {
        return  vertCount() * sizeof(V3U3N4);
    }
    ushort3*    faceData() 
    {
        return  _faces;
    }
    const ushort3*    faceData() const
    {
        return  _faces;
    }
    int         faceCount() const
    {
        return  (_row -1) * (_col - 1) * 2;
    }
    int         faceByte() const
    {
        return  faceCount() * sizeof(ushort3);
    }
    void    fillFace()
    {
        uint    fSize   =   (_row -1) * (_col - 1) * 2;
        uint    index   =   0;
        for (ushort r = 0; r < _row - 1; ++r)
        {
            for (ushort c = 0; c < _col - 1; ++c)
            {
                _faces[index + 0][0]    =   (_col * r) + c;
                _faces[index + 0][1]    =   (_col * r) + c + 1;
                _faces[index + 0][2]    =   (_col * (r + 1)) + c;

                _faces[index + 1][0]    =   (_col * r) + c + 1;
                _faces[index + 1][1]    =   _col * (r + 1) + c + 1;
                _faces[index + 1][2]    =   _col * (r + 1) + c;
                index += 2;
            }
        }
    }
    void    fillVertex(const real2& vStart,const real2& vEnd,bool bInitY,CELLSpRef* spRef)
    {
        real2   vSize   =   vEnd - vStart;
        real2   vGrid   =   real2(vSize.x/(_col -1),vSize.y/(_row - 1));
        _aabb.setNull();
        real3   tmp[row * col];
        if (bInitY)
        {
            for (ushort r = 0; r < _row; ++r)
            {
                for (ushort c = 0; c < _col; ++c)
                {
                    int     idx     =   r * _col + c;
                    real3   vWorld  =   spRef->longLatToWorld(real3(vStart.x + c * vGrid.x,vStart.y + r * vGrid.y,0));
                    tmp[idx]        =   vWorld;
                    /// _data[idx].x    =   vWorld.x;
                    /// _data[idx].y    =   vWorld.y;
                    /// _data[idx].z    =   vWorld.z;
                    _aabb.merge(vWorld);
                }
            }
        }
        else
        {
            for (ushort r = 0; r < _row; ++r)
            {
                for (ushort c = 0; c < _col; ++c)
                {
                    int     idx     =   r * _col + c;
                    real3   vWorld  =   spRef->longLatToWorld(real3(vStart.x + c * vGrid.x,vStart.y + r * vGrid.y,_data[idx].h));
                    tmp[idx]        =   vWorld;
                    /// _data[idx].x    =   vWorld.x;
                    /// _data[idx].y    =   vWorld.y;
                    /// _data[idx].z    =   vWorld.z;
                    _aabb.merge(vWorld);
                }
            }
        }
        real3   vCenter     =   _aabb.getCenter();
        for (size_t i = 0; i < row * col; i++)
        {
            real3   vLocal  =   tmp[i] - vCenter;
            _data[i].x    =   vLocal.x;
            _data[i].y    =   vLocal.y;
            _data[i].z    =   vLocal.z;
        }
    }

    void    fillUV(const real2& vStart,const real2& vEnd,int layer)
    {
        real2   uvSize  =   vEnd - vStart;
        real2   uvStep  =   real2(uvSize.x/(_col -1),uvSize.y/(_row - 1));
        for (ushort r = 0; r < _row; ++r)
        {
            for (ushort c = 0; c < _col; ++c)
            {
                int     idx     =   r * _col + c;
                _data[idx].u    =   (vStart.x + real(c) * uvStep.x);
                _data[idx].v    =   (vStart.y + real(r) * uvStep.y);
            }
        }
    }

    void    fillHeight(CELLFiledHeight* pHeight)
    {
        float*  pData   =   pHeight->data();
        for (size_t i = 0 ;i < _row * _col ; ++ i)
        {
            _data[i].h  =   pData[i];
        }
    }
    aabb3dr     getAabb() const
    {
        return  _aabb;
    }
    /// <summary>
    /// 瓦片与射线相交测试
    /// </summary>
    bool    intersect(const CELL::Ray& ray,const matrix4r& mat,real& dist)
    {
        int     faceCnt =   faceCount();
        real    time    =   FLT_MAX;
        bool    bPick   =   false;

        auto    result  =   ray.intersects(_aabb);
        if (!result.first)
            return  false;

        for (int i = 0; i < faceCnt; i++)
        {
            int     i1      =   _faces[i].x;
            int     i2      =   _faces[i].y;
            int     i3      =   _faces[i].z;

            real3   v1      =   mat * real3(_data[i1].x,_data[i1].y,_data[i1].z);
            real3   v2      =   mat * real3(_data[i2].x,_data[i2].y,_data[i2].z);
            real3   v3      =   mat * real3(_data[i3].x,_data[i3].y,_data[i3].z);
            real    cur     =   FLT_MAX;
            real    u       =   0;
            real    v       =   0;

            bool    res =   CELL::intersectTriangle<real>(ray.getOrigin(),ray.getDirection(),v1,v2,v3,&cur,&u,&v);
            if (res)
            {
                time    =   CELL::tmin(time,cur);
                bPick   =   true;
            }
        }
        if (bPick)
        {
            dist    =   time;
        }
        return  bPick;
    }
};

using   DemData =   CELLDemMesh<DEM_W,DEM_H>;

 

3.3.3 裂分算法实现(核心部分实现)

 

CELLCamera& camera      =   context._camera;
real3       vWCenter    =   _aabb.getCenter();
real3       vWSize      =   _aabb.getSize();

real        fSize       =   CELL::length(vWSize) * 0.5;
real        dis         =   CELL::length(vWCenter - camera._eye);

if (context._frustum.cubeInFrustum(
                                 _aabb._minimum.x
                                ,_aabb._maximum.x
                                ,_aabb._minimum.y
                                ,_aabb._maximum.y
                                ,_aabb._minimum.z
                                ,_aabb._maximum.z))
{
    _flag   &=  ~FLAG_HAS_CULL;
}
else
{
    _flag   |=  FLAG_HAS_CULL;
}

if (dis/fSize < 3 && hasNoFlag(FLAG_HAS_CULL))
{
    if(!hasChild() && hasImage())
    {
        real2   vLlCenter   =   lonLatCenter();
        real2   vLLHalf     =   lonLatRange() * 0.5;
        real3   vCenter     =   real3(vLlCenter.x,  0,  vLlCenter.y); 
        real3   vSize       =   real3(vLLHalf.x,    0,  vLLHalf.y); 

        if (_terrain->getTaskCount() > 20)
        {
            return  ;
        }
        _childs[CHILD_LT]   =   NodePtr(new  CELLQuadTree(
            _terrain
            , this
            ,real2(vCenter.x - vSize.x,vCenter.z)
            ,real2(vCenter.x,vCenter.z + vSize.z)
            ,(int)_tileId._lev + 1
            ,CHILD_LT
        ));

        _childs[CHILD_RT] = NodePtr(new CELLQuadTree(
            _terrain
            ,this
            , real2(vCenter.x, vCenter.z)
            , real2(vCenter.x + vSize.x, vCenter.z + vSize.z)
            , (int)_tileId._lev + 1
            , CHILD_RT
        ));


        _childs[CHILD_LB] = NodePtr(new CELLQuadTree(
            _terrain
            , this
            , real2(vCenter.x - vSize.x, vCenter.z - vSize.z)
            , real2(vCenter.x, vCenter.z)
            , (int)_tileId._lev + 1
            , CHILD_LB
        ));
        _childs[CHILD_RB] = NodePtr(new CELLQuadTree(
            _terrain
            , this
            , real2(vCenter.x, vCenter.z - vSize.z)
            , real2(vCenter.x + vSize.x, vCenter.z)
            , (int)_tileId._lev + 1
            , CHILD_RB
        ));
    }
    else
    {
        for (int i = 0; i < 4; ++i)
        {
            if (_childs[i] && hasNoFlag(FLAG_HAS_CULL))
            {
                _childs[i]->update(context);
            }
            else
            {
                _flag   &=  FLAG_RENDER;
            }
        }
    }
}
else
{
    for (int i = 0 ;i < 4 ; ++ i)
    {
        _childs[i]  =   nullptr;
    }
}

 

3.4 图层管理

 

3.5 文件格式管理

为了统一接口,提炼一个通用的基类如下代码所示:

class   ExtDesc
{
public:
    std::string _ext;
    std::string _version;
    std::string _desc;
};
using   ExtDescs    =   std::vector<ExtDesc>;
class   CELLFormat :public CELLObject
{
public:
    friend  class   CELLFormatMgr;
protected:
    ExtDescs    _descs;
public:
    /// <summary>
    /// 判断是否支持对应扩展名的文件
    /// </summary>
    bool    support(const std::string& ext,const std::string& version = "1.0.0.0")
    {
        for (auto& var : _descs)
        {
            if (var._ext != ext )
                continue;
            if (var._version != version )
                continue;
            return  true;
        }
        return  false;
    }
    /// <summary>
    /// 判断是否有子节点
    /// </summary>
    virtual ObjectPtr   read(const char* fileName)  =   0;

};
using   FormatPtr   =   std::shared_ptr<CELLFormat>;

 

3.5.1  dem文件读取实现

自主三维GIS引擎笔记-实现三维球045

图1为5米格网的DEM,图2为ALOS 12.5米分辨率的DEM,图3为ASTER GDEM V3 30米分辨率的DEM,图4为SRTM3 90米分辨率的DEM。

自主三维GIS引擎笔记-实现三维球045

大多数项目上使用的是30米的数据源,可以从网上上下载,高程数据可以理解成一个二维数据,每一个元素存储的是高度信息(海拔)

自主三维GIS引擎笔记-实现三维球045

GIS中加载考虑到速度,也会把瓦片切分成标准的瓦片,本节是读取cesium高程数据代码:

3.5.2 terrain文件读取实现

FILE*   pFile   =   fopen(fileName,"rb");
            if (pFile == nullptr)
                return  nullptr;
            uint16_t            data[65 * 65]   =   {0};
            CELL::CELLFieldHeight*  pHeight = dynamic_cast<CELL::CELLFieldHeight*>(user);
            if (pHeight == nullptr)
            {
                pHeight =   new CELL::FEFieldHeight();
            }
            pHeight->create(65,65,0);
            fread(data,1,sizeof(data),pFile);
            fclose(pFile);
            float*  pData =   pHeight->data();

            for (size_t i = 0 ;i < 65 * 65;++i)
            {
                pData[i]    =   float(data[i]) * 0.2f;
            }

            for (int j = 0, k = 65; j < 65 / 2; j++)
            {
                for (int i = 0; i < 65; i++)
                {
                    auto    temp = pData[j * k + i];
                    pData[j * k + i]  = pData[(65 - j - 1) * k + i];
                    pData[(65 - j - 1) * k + i] = temp;
                }
            }
            return  pHeight;

 

3.6 任务系统实现

采用生产者与消费者模式:

自主三维GIS引擎笔记-实现三维球045

 

一对一模式:

自主三维GIS引擎笔记-实现三维球045

多对多模式:

自主三维GIS引擎笔记-实现三维球045

系统中瓦片加载的典型应用如下图描述,这里注意,创建纹理必须在主线程中完成。

自主三维GIS引擎笔记-实现三维球045

 

主要功能,支持多线程中执行任务

支持取消任务

支持任务执行失败通知

支持任务执行成功通知

 

    class   CELLTaskObserver
    {
    public:
        /// <summary>
        /// 任务取消通知
        /// </summary>
        virtual void    onTaskCancel(CELLTask* task) = 0;

        /// <summary>
        /// 任务完成通知
        /// </summary>
        virtual void    onTaskExe(CELLTask* task) = 0;
        /// <summary>
        /// 任务完成通知
        /// </summary>
        virtual void    onTaskFinish(CELLTask* task)    =   0;
    };
    class   CELLTaskSystem
    {
    public:
        class   TaskThread :public CELLThread
        {
        public:
            bool            _exitFlag;
            CELLTaskSystem* _system;
        public:
            TaskThread(CELLTaskSystem* pSystem)
            {
                _system     =   pSystem;
                _exitFlag   =   true;
            }
        public:
            virtual void    join()
            {
                _exitFlag   =   true;
                CELLThread::join();

            }
            virtual bool    onCreate()
            {
                _exitFlag   =   false;
                return  false;
            }
            virtual bool    onRun()
            {
                while (!_exitFlag)
                {
                    _system->run();
                }
                return  false;
            }
            virtual bool    onDestroy()
            {
                return  false;
            }
        };

        typedef std::vector<CELLThread*>    ArrayThread;
        typedef std::list<TaskPtr>          ArrayTask;
    public:
        CELLTaskObserver*   _observer;
        ArrayThread         _threads;
        ArrayTask           _tasks;
        CELLSemaphore       _semphore;
        mutable CELLMutex   _mutex;
    public:
        CELLTaskSystem()
            :_observer(0)
        {}
        /// <summary>
        /// 
        /// </summary>
        virtual ~CELLTaskSystem()
        {}
        /// <summary>
        /// 设置观察者指针
        /// </summary>
        virtual void    setObserver(CELLTaskObserver* observer)
        {
            _observer   =   observer;
        }
        /// <summary>
        /// 启动任务处理
        /// </summary>
        virtual void    start(int threadNum = 4)
        {
            destroy();
            for (int i = 0 ;i < threadNum ; ++ i)
            {
                TaskThread* pThread =   new TaskThread(this);
                pThread->start();
                _threads.push_back(pThread);
            }
        }

        /// <summary>
        /// 销毁
        /// </summary>
        virtual void    destroy()
        {
            for (size_t i = 0 ;i < _threads.size() ; ++ i)
            {
                _threads[i]->join();
                delete  _threads[i];
            }
            _threads.clear();
        }
        /// <summary>
        /// 添加任务接口
        /// </summary>
        virtual void    addTask(CELLTask* task)
        {
            {
                CELLMutex::ScopeLock lk(_mutex);
                _tasks.push_back(task->toPtr<CELLTask>());
            }
            _semphore.set(1);
        }
        /// <summary>
        /// 获取任务队列中的任务个数
        /// </summary>
        virtual size_t  getTaskCount() const
        {
            CELLMutex::ScopeLock lk(_mutex);
            return  _tasks.size();
        }
        
    public:
        virtual void    run()
        {
            if(!_semphore.wait())
            {
                return;
            }
            TaskPtr pTask   =   nullptr;
            {
                CELLMutex::ScopeLock lk(_mutex);
              
                if (_tasks.empty())
                {
                    return;
                }
                /// 1. 取数据
                pTask   =   _tasks.front();
                _tasks.pop_front();
            }
            /// 2. 执行过程
            /// 3. 通知过程

            if (_observer && pTask)
            {
                _observer->onTaskExe(pTask.get());
                _observer->onTaskFinish(pTask.get());
            }
        }
    };

 文章来源地址https://www.toymoban.com/news/detail-710836.html

到了这里,关于自主三维GIS引擎笔记-实现三维球045的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【GIS开发】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)

    🍺三维数字地球系列相关文章如下🍺: 1 【小沐学GIS】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)第一期 2 【小沐学GIS】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)第二期 3 【小沐学GIS】基于OpenSceneGraph(OSG)绘制三维数字地球Earth 4 【小沐学GIS】基于C++绘制太阳系

    2023年04月17日
    浏览(64)
  • 三维GIS开发:利用Cesium加载 M3D 地质体模型(附代码)

    实现步骤 Step 1.  引用开发库 : 本示例引用 local 本地【include-cesium-local.js】开发库,完成此步骤后才可调用三维 WebGL 的功能; Step 2.  创建布局 : 创建 id=\\\'GlobeView\\\' 的 div 作为三维视图的容器,并设置其样式; Step 3.  构造三维场景控件 : 实例化 Cesium.WebSceneControl 对象,完成

    2024年02月10日
    浏览(40)
  • OpenLayers 开源的Web GIS引擎

    介绍:开源的Web GIS引擎,使用了JavaScript、最新的HTML5技术及CSS技术,支持dom,canvas和webgl三种渲染方式。除了支持网页端,还支持移动端。在地图数据源方面,支持各种类型的瓦片地图,既支持在线的,也支持离线的。比如OSM, Bing, MapBox, Stamen, MapQuest等等;还支持各种矢量地

    2024年02月03日
    浏览(45)
  • Pix4Dmapper空间三维模型的应用实例:GIS选址分析

      本文介绍基于 无人机影像建模完成后的结果 ,利用 ArcMap 软件进行 空间选址分析 ,从而实现空间三维模型应用的方法。 目录 1 空间分析目标确立 2 基于基本约束条件的选址求解 2.1 坡度计算与提取 2.2 海拔提取 2.3 LAS数据初探 2.4 淹没分析 2.5 区域相交 2.6 面积约束 3 基于

    2024年02月04日
    浏览(39)
  • K210追小球程序与STM32最小系统板通信(自主学习)

    本人先通过学习OPENMV,再延申到K210中。(主要是OPENMV我还没买屏幕,但是K210有)在OPENMV官网中,有相关追小球的函数,但是是用OPENMV单片机来进行调试的。在网上找到的相关源码,加载到OPENMV后发现帧数很慢。 学校实训的要求是要做出能追一定物体的平衡小车,我的小车的

    2024年02月16日
    浏览(41)
  • AMRT 3D 数字孪生引擎(轻量化图形引擎、GIS/BIM/3D融合引擎):智慧城市、智慧工厂、智慧建筑、智慧校园。。。

    1、提供强大完整的工具链 AMRT3D包含开发引擎、资源管理、场景编辑、UI搭建、项目预览和发布等项目开发所需的全套功能,并整合了动画路径、精准测量、动态天气、视角切换和动画特效等工具。 2、轻量化技术应用与个性化定制 AMRT3D适用于快速开发数字孪生3D可视化项目、

    2024年04月22日
    浏览(104)
  • 编程笔记 html5&css&js 045 网页布局

    网页布局有很多种方式,一般分为以下几个部分:头部区域、菜单导航区域、内容区域、底部区域。 网页布局有很多种方式,一般分为以下几个部分:头部区域、菜单导航区域、内容区域、底部区域。 头部区域位于整个网页的顶部,一般用于设置网页的标题或者网页的 log

    2024年01月19日
    浏览(44)
  • 【虚幻引擎UE】UE4/UE5 GIS辅助类插件推荐及使用介绍

    此插件将虚幻引擎连接到Speckle,允许从Speckle接收版本化的3D数据,可以实现Revit、Rhino、Blender、Sketchup、Unity、虚幻引擎、AutoCAD等之间的完全互操作协作。 这是一个涵盖了在展厅、工业产品或其他3D场景中导航的许多可能性的系统。在窗口、触摸屏、手机和Html5上。 只需快速

    2024年02月07日
    浏览(93)
  • 用一个文件,实现迷你 Web 框架

    当下网络就如同空气一样在我们的周围,它以无数种方式改变着我们的生活,但要说网络的核心技术变化甚微。 随着开源文化的蓬勃发展,诞生了诸多优秀的开源 Web 框架,让我们的开发变得轻松。但同时也让我们不敢停下学习新框架的脚步,其实万变不离其宗,只要理解了

    2024年02月08日
    浏览(19)
  • Open3D点云数据处理(二十):最小二乘直线拟合(三维)

    专栏目录:Open3D点云数据处理(Python) 最小二乘三维直线拟合的原理是通过最小化数据点到直线距离的平方和,找到最优的直线模型来拟合给定数据集。这个距离是指数据点到直线的垂线距离。 三维直线通常表示为两个平面的交线,形如 { A

    2024年02月12日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包