openGL 提升渲染性能 之 頂點數組 VBO IBO VAO


使用openGL圖形庫繪制,都需要通過openGL接口向圖像顯卡提交頂點數據,顯卡根據提交的數據繪制出相應的圖形。

 

openGL繪制方式有:直接模式,顯示列表,頂點數組,頂點索引。

 

直接模式:最簡單,最直接的模式,但是性能是最差的,因為每繪制一個基本圖元都需要提交一次數據;

glBegin(GL_TRIANGLE_STRIP);  

glColor3ub(255, 0, 0);  

glVertex3f(-0.5f, 0.5f, 0.0f);  

glColor3ub(0, 255, 0);  

glVertex3f(-0.5f, -0.5f, 0.0f);  

glColor3ub(0, 0, 255);  

glVertex3f(0.5f, 0.5f, 0.0f);  

glColor3ub(255, 0, 255);  

glVertex3f(0.5f, -0.5f, 0.0f);  

glEnd();  

上面就是用直接模式繪制一個三角形帶的所有openGL命令,如果要繪制無數個三角形,那么函數調用的開銷是巨大的,而且這樣的寫法讓頂點數據非常不容易擴展和修改,所以基本上這種模式不可能用在實際的用途中。

 

顯示列表:直接模式在每次繪制的時候,都需要將頂點數組從cpu端重新發送到gpu端,如果每次數據都沒有任何變化,這種重復的發送就顯得沒有意義而且低效。顯示列表就是為了解決這個重復發送的性能問題,顯示列表相當於把一組繪制命令存儲在服務器端(gpu端),每次只需要發送一個調用命令,而不需要重復發送所有頂點數據,就可以執行已經預定好的繪制命令了。雖然顯示列表解決了不用重發發送頂點數據的問題,但是缺點也是顯而易見的,就是顯示列表一旦定義好,就無法被修改,因此顯示列表只適用於那些不會被修改的繪制命令。而且顯示列表和直接模式依然具有相同的問題,就是函數調用開銷和難以擴展和修改。只是相當於命令直接在gpu端執行,減少了從cpu發送gpu的過程而已。

定義一個顯示列表:

glNewList (listName, GL_COMPILE);

glColor3f (1.0, 0.0, 0.0); 

glBegin (GL_TRIANGLES);

glVertex2f (0.0, 0.0);

glVertex2f (1.0, 0.0); 

glVertex2f (0.0, 1.0);

glEnd ();

glTranslatef (1.5, 0.0, 0.0);

glEndList (); 

執行一個顯示列表:

glCallList (listName);

刪除一個顯示列表:

glDeleteLists(listName, 1);

 

頂點數組:由於直接模式的局限性,openGL提供了另一種更加高效的繪制模式,頂點數組。

頂點數組顧名思義就是允許我們將我們的頂點數據放置到一個數組中,一次性提交給顯卡進行繪制。這樣只需要極少量的函數調用,而且數組全部聚合在一起,也更加容易修改和擴展。

 // 先定義頂點位置,顏色數組,紋理數組的頂點相關數據:

GLfloat vertexes[] = {
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 512, 0.0f, 1.0f,
1024, 512, 0.0f, 1.0f,
1024, 0.0f, 0.0f, 1.0f
};
GLfloat colores[] = {
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
GLfloat texCoordes[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};

// 0,1,2代表着色器的input的location,分別代表頂點位置,頂點顏色,頂點紋理坐標(這里使用的是可編程管線,如果使用固定管線,指定方式也是類似的)

// 為0,1,2指定相應的數據

glEnableVertexAttribArray(0); // 固定管線使用glEnableClientState(GL_VERTEX_ARRAY)
glEnableVertexAttribArray(1); // 固定管線使用glEnableClientState(GL_COLOR_ARRAY)
glEnableVertexAttribArray(2); // 固定管線使用glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, vertexes); // 固定管線使用glVertexPointer(4, GL_FLOAT, 0, vertexes);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colores); // 固定管線使用glColorPointer(4, GL_FLOAT, 0, colores);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, texCoordes); // 固定管線使用glTexCoordPointer(2, GL_FLOAT, 0, texCoordes);

// 繪制一個四邊形

glDrawArrays(GL_QUADS, 0, 4);

 

這里將位置,顏色,紋理分為三個數組分別存放,我們也可把所有數據放在一個數組中,稱之為交錯數組:

GLfloat data[] = {
0.0f, 0.0f, 0.0f, 1.0f,    1.0f, 1.0f, 1.0f, 1.0f,   0.0f, 1.0f,
0.0f, 512, 0.0f, 1.0f,    1.0f, 1.0f, 1.0f, 1.0f,   0.0f, 0.0f,
1024, 512, 0.0f, 1.0f,  1.0f, 1.0f, 1.0f, 1.0f,   1.0f, 0.0f,
1024, 0.0f, 0.0f, 1.0f,  1.0f, 1.0f, 1.0f, 1.0f,   1.0f, 1.0f
};

glEnableVertexAttribArray(0); 
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 10 * sizeof(GLfloat), vertexes); // 第四個參數指的是兩個位置數據在數組中間距,第五個參數指的是第一個位置數據在數組中的起始位置
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 10 * sizeof(GLfloat), colores + 4); // 第四個參數指的是兩個顏色數據在數組中間距,第五個參數指的是第一個顏色數據在數組中的起始位置
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 10 * sizeof(GLfloat), texCoordes + 8); // 第四個參數指的是兩個紋理坐標數據在數組中間距,第五個參數指的是第一個紋理坐標數據在數組中的起始位置

// 繪制一個四邊形

glDrawArrays(GL_QUADS, 0, 4);

 

除此之外,交錯無數也可以直接使用:

  glInterleavedArrays (GLenum format, GLsizei stride, const GLvoid *pointer);

進行一次性指定,但是數組中數據的構成規則必須和format指定的規則一致。例如:glInterleavedArrays(GL_V2F,0,data); 表示數據組合規則是僅僅2個浮點數的位置數據

 

頂點索引:頂點數組已經可以為我們提供了方便指定繪制數據的方式,大幅提升了渲染的性能。但是依然還存在一個問題:如果我們需要繪制一個模型,這個模型由無數三角形片構成,大部分三角形都是連續拼接的沒有空隙,所以每相鄰的兩個三角形可能會有擁有一個或者兩個完全相同頂點(位置,顏色,紋理坐標都相同)。如果我們使用頂點數組的數據來構成這個模型,我們將為相鄰的兩個三角形片分別制定6個頂點數據,其中有可能會出現最多兩對完全相同的頂點,而這些頂點其實是可以共享的,但卻出現了冗余數據。在模型復雜的情況下,冗余數據也是巨大的。於是openGL為我們指定了一種更加靈活的方式,頂點索引數組,即我們只需要創建好必要的頂點數據,頂點數據在數組中的排列也不受圖元繪制方式的限制,理論上可以隨意排列。然后用索引去對應每一個頂點數據,繪制圖元需要提交頂點數據的時候,直接指定頂點索引即可,因為頂點索引會一一映射到頂點數據,這樣就消除了冗余的頂點數據,而且以更加靈活的方式進行渲染。

// 指定頂點數據,注意渲染順序的迎風面是逆時針還是順時針,這里繪制4個頂點,分別對應頂點數組中的前4個頂點

GLuint indexes = {0, 1, 2, 3};

// 用頂點索引進行繪制的繪制調用

glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, indexes);

 

VBO: 頂點數組加上頂點索引,似乎已經可以完美解決大部分問題,但是圖形渲染對性能的追求是永無止境的。雖然使用頂點數組和頂點索引,我們可以一次性提交所有繪制數據,並只需要調用一次繪制命令。但是在數據很大的情況下,我們依然都要從cpu端向gpu端提交大量數據,如果這些數據又幾乎不會發生改變,那么這種操作將是極大的性能浪費。

為了減少這種耗時但又無意義的工作,openGL為我們提供了VBO(頂點緩沖對象)來改善這種問題。

使用VBO,可以將我們的頂點數據存放在圖像顯卡的內存中,而不需要存放在cpu端的內存中,就不需要在每次繪制時,發送大量頂點數據到gpu端了。

// 生成VBO,並為VBO綁定頂點數據

size_t dataSize = sizeof(GLfloat) * vertexCount * 4; // 在圖像顯卡中需要分配的內存大小
GLuint vbos[1] = { 0 }; // VBO名字

glGenBuffers(1, vbos); // 生成一個可用的VBO名字
if (vbos[0] > 0) // 如果名字可用
{
vertexVBO = vbos[0];

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); // 綁定當前的VBO,GL_ARRAY_BUFFER是VBO使用的固定參數

glBufferData(GL_ARRAY_BUFFER, dataSize, vertexes, GL_STATIC_DRAW); // 將位置數據綁定到當前的VBO上,dataSize是需要的內存大小,vertexes是頂點的位置數據

// GL_STATIC_DRAW 是一個性能提示參數,這個參數指示了當前VBO的用途,該參數必須是GL_STREAM_DRAWGL_STATIC_DRAW, or GL_DYNAMIC_DRAW之一。openGL會根據該指示,盡可能將數據放置在性能最優的內存中,可能是顯存,AGP內存,或者cpu內存中。

// GL_STATIC_DRAW:數據指定一次,並多次被用於繪制。

// GL_STREAM_DRAW:數據指定一次,最多幾次用於繪制。

// GL_DYNAMIC_DRAW:數組多次指定,多次用於繪制。

delete[] vertexes;
m_vertexes = nullptr;

CHECK_GL_ERROR();
}

// 使用VBO進行繪制,和使用頂點數組類似

#define BUFFER_OFFSET(offset) ((GLvoid*)(NULL + offset)) // 數據在緩沖區中的偏移位置,和頂點數組指針位置效果類似

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); // 綁定位置VBO
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定位置數據
glBindBuffer(GL_ARRAY_BUFFER, colorVBO); // 綁定顏色VBO
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定顏色數據
glBindBuffer(GL_ARRAY_BUFFER, textureVBO); // 綁定紋理VBO
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定紋理數據
glBindBuffer(GL_ARRAY_BUFFER, 0);

glDrawArrays(GL_QUADS, 0, m_vertexCount); // 繪制

 

IBO: 索引緩沖對象,和VBO一樣,只是存儲的是索引數組。

glGenBuffers(1, &IBO);
if (sphereIBO > 0)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); // 參數必須使用GL_ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexCount, indexes, GL_STATIC_DRAW);

CHECK_GL_ERROR();

delete[] indexes;
indexes = nullptr;
}

// 繪制

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_QUADS, indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

 

VAO: 有了VBO和IBO,已經可以很好的解決問題了,但是使用VAO可以使我們的開發更加靈活。VAO其實就是可以綁定VBO和IBO的一個包裝對象,我們把有關聯的VBO和IBO一起綁定到一個VAO上,我們每次只需要使用VAO就可以進行繪制了。

// 生成VAO

glGenVertexArrays(1, &VAO); // 生成一個VAO
if (VAO > 0) // 如果VAO可用
{
glBindVertexArray(VAO); // 綁定到當前的VAO

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); // 綁定一個IBO到當前的VAO上

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); // 綁定位置VBO到當前的VAO上
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定位置數據
glBindBuffer(GL_ARRAY_BUFFER, colorVBO); // 綁定顏色VBO到當前的VAO上
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定顏色數據
glBindBuffer(GL_ARRAY_BUFFER, textureVBO); // 綁定紋理VBO到當前的VAO上
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定紋理數據

CHECK_GL_ERROR_DEBUG();
}

// 繪制

glBindVertexArray(VAO);
glDrawElements(GL_QUADS, indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glBindVertexArray(0);

 

下面是基於cocos2d的完整的代碼:

Sphere.h:

#ifndef __SPHERE__
#define __SPHERE__

#include "cocos2d.h"

USING_NS_CC;

class Sphere : public cocos2d::Node
{
public:
    static Sphere* create();

    virtual ~Sphere();

    bool init();

    virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags);

    virtual void visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags);

private:
    GLProgram* m_program;
    GLProgram* m_program_planview;
    GLuint m_textureName;

    GLfloat* m_vertexes;
    GLfloat* m_colors;
    GLfloat* m_texcoordes;
    GLuint* m_indexes;
    unsigned int m_vertexCount;
    unsigned int m_indexCount;

    float m_sphereLatitude; // 緯度
    float m_sphereLongitude; // 經度
    unsigned int m_sphereLatitudeCount;
    unsigned int m_sphereLongitudeCount;
    unsigned int m_sphereQuadCount;

    float m_sphereRadius; // 半徑
    float m_spherePerimeter; // 周長
    float m_planViewWidth;
    float m_planViewHeight;

    Mat4 m_sphereTransform;
    float m_rotateXAngle;
    float m_rotateYAngle;
    float m_rotateZAngle;
    float m_sphereTranslateX;
    float m_sphereTranslateY;

    float m_planViewTranslateX;
    float m_planViewTranslateY;

    bool m_isContinue;
    float m_continueRotateY;
    float m_continueRotateX;
    float m_decreateDetal;

    std::thread* m_thread_1;
    std::thread* m_thread_2;
    bool m_isThread1Done;
    bool m_isThread2Done;

    std::vector<float> m_offsetDataList;
    std::vector<float> m_offsetDataListCopy;

    GLuint m_sphereVAO;
    GLuint m_sphereIBO;
    GLuint m_sphereVertexVBO;
    GLuint m_sphereColorVBO;
    GLuint m_sphereTextureVBO;

    bool initProgram();
    bool initVertexData();

    void createQuaternion(float rotateX, float rotateY, float rotateZ, Quaternion& quat);

    void generateVertexesSphere(float radius);
    void generateVertexesSphereNew(float radius);
    void generateTexture();
    void generateVAO();
    void generateIBO();
    void generateVBO();

    void transformSphere(float touchOffsetX, float touchOffsetY);

    void drawBg(const Mat4& transform);
    void drawSphere(const Mat4& transform, GLfloat* vertexes);
    void drawPlanView(const Mat4& transform, GLfloat* vertexes);

    void update();

    bool onTouchBegan(Touch* touch, Event* event);
    void onTouchMoved(Touch* touch, Event* event);
    void onTouchEnded(Touch* touch, Event* event);

    void onThread1Proc();
    void onThread2Proc();

    void noticeToTransform(float touchOffsetX, float touchOffsetY);
};

#endif

Sphere.cpp:

#include "SphereNew.h"
#include "math.h"
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <thread>
#include <functional>

#define MPI 3.1415926f
#define HMPI (3.1415926f / 2.0f)
#define DMPI (3.1415926f * 2.0f)
#define A_TO_R(angle) (3.1415926f / 180.0f * (float)(angle))

#define TEXTURE_WIDTH 1024
#define TEXTURE_HEIGHT 512

#define BUFFER_OFFSET(offset) ((GLvoid*)(NULL + offset))

const GLchar* ccPositionTextureColor_v = " \
attribute vec4 a_position; \n\
attribute vec2 a_texCoord; \n\
attribute vec4 a_color; \n\
\n\
#ifdef GL_ES \n\
varying lowp vec4 v_fragmentColor; \n\
varying mediump vec2 v_texCoord; \n\
#else \n\
varying vec4 v_fragmentColor; \n\
varying vec2 v_texCoord; \n\
#endif \n\
\n\
void main() \n\
{ \n\
    gl_Position = CC_MVPMatrix * a_position; \n\
    v_fragmentColor = a_color; \n\
    v_texCoord = a_texCoord; \n\
} \n\
";

const GLchar* ccPositionTextureColorForSphere_v = " \
uniform vec2 translate; \n\
uniform float radius; \n\
attribute vec4 a_position; \n\                                                                                                                                                                                attribute vec2 a_texCoord; \n\
attribute vec4 a_color; \n\
\n\
#ifdef GL_ES \n\
varying lowp vec4 v_fragmentColor; \n\
varying mediump vec2 v_texCoord; \n\
#else \n\
varying vec4 v_fragmentColor; \n\
varying vec2 v_texCoord; \n\
#endif \n\
const float pi = 3.1415926; \n\
const float hpi = 1.5707963; \n\
\n\
void main() \n\
{ \n\
vec4 _position = CC_MVMatrix * a_position; \n\
float _angle1 = atan(_position.x, _position.z); \n\
float _angle2 = atan(_position.z, _position.y); \n\
float _xOffset = _angle1 * radius; \n\
float _yOffset = _position.y * hpi; \n\
_position.x = _xOffset + translate.x; \n\
_position.y = _yOffset + translate.y; \n\
_position.z = 0.0f; \n\
gl_Position = CC_PMatrix * _position; \n\
\n\
v_fragmentColor = a_color; \n\
v_texCoord = a_texCoord; \n\
} \n\
";

const GLchar* ccPositionTextureColor_f = " \
#ifdef GL_ES \n\
precision lowp float; \n\
#endif \n\
\n\
varying vec4 v_fragmentColor; \n\
varying vec2 v_texCoord; \n\
\n\
void main() \n\
{ \n\
vec4 color = v_fragmentColor * texture2D(CC_Texture0, v_texCoord); \n\
color.a = 1.0; \n\
gl_FragColor = color; \n\
} \n\
";

static std::condition_variable cv_transform;
static std::condition_variable cv_notice;
static std::condition_variable cv_transform_sphere;
static std::condition_variable cv_transform_planview;
static std::mutex mx_transform;
static std::mutex mx_transform_planview;
static std::mutex mx_transform_sphere;
static bool b_transform_sphere = false;
static bool b_transform_planview = false;

static float _touchOffsetX = 0.0f;
static float _touchOffsetY = 0.0f;
static float _touchOffsetXCopy = 0.0f;
static float _touchOffsetYCopy = 0.0f;
static Mat4 _sphereTransformationCopy;

Sphere* Sphere::create()
{
    Sphere *pRet = new(std::nothrow) Sphere();

    if (pRet && pRet->init())
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = nullptr;
        return nullptr;
    }
}

bool Sphere::init()
{
    if (!this->initProgram())
        return false;

    if (!this->initVertexData())
        return false;

    this->generateVertexesSphereNew(m_sphereRadius);
    this->generateTexture();
    this->generateVBO();
    this->generateIBO();
    this->generateVAO();

    this->transformSphere(0, 0);

    auto eventListener = EventListenerTouchOneByOne::create();
    eventListener->setSwallowTouches(true);
    eventListener->onTouchBegan = CC_CALLBACK_2(Sphere::onTouchBegan, this);
    eventListener->onTouchMoved = CC_CALLBACK_2(Sphere::onTouchMoved, this);
    eventListener->onTouchEnded = CC_CALLBACK_2(Sphere::onTouchEnded, this);
    CCDirector::sharedDirector()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(eventListener, this);

    /*
    m_isThread1Done = false;
    m_isThread2Done = false;
    m_thread_1 = new std::thread(std::bind(&Sphere::onThread1Proc, this));
    m_thread_2 = new std::thread(std::bind(&Sphere::onThread2Proc, this));
    */
    m_thread_1 = nullptr;
    m_thread_2 = nullptr;

    return true;
}

bool Sphere::initProgram()
{
    bool suc = false;

    m_program = new CCGLProgram();
    if (m_program)
    {
        if (m_program->initWithVertexShaderByteArray(ccPositionTextureColor_v, ccPositionTextureColor_f))
        {
            if (m_program->link())
            {
                m_program->updateUniforms();
                suc = true;
            }
        }
    }
    if (!suc)
    {
        delete m_program;
        m_program = NULL;
    }

    suc = false;

    m_program_planview = new CCGLProgram();
    if (m_program_planview)
    {
        if (m_program_planview->initWithVertexShaderByteArray(ccPositionTextureColorForSphere_v, ccPositionTextureColor_f))
        {
            if (m_program_planview->link())
            {
                m_program_planview->updateUniforms();
                suc = true;
            }
        }
    }
    if (!suc)
    {
        delete m_program_planview;
        m_program_planview = NULL;
    }

    return suc;
}

bool Sphere::initVertexData()
{
    m_vertexes = nullptr;
    m_colors = nullptr;
    m_texcoordes = nullptr;
    m_indexes = nullptr;
    m_vertexCount = 0;
    m_indexCount = 0;

    m_sphereTranslateX = 512.0f;
    m_sphereTranslateY = 412.0f;
    m_rotateYAngle = 0.0f;
    m_rotateXAngle = 0.0f;
    m_rotateZAngle = 0.0f;

    m_planViewTranslateX = 512.0f;
    m_planViewTranslateY = 150.0f;
    m_planViewWidth = 400.0f;
    m_planViewHeight = 200.0f;

    m_sphereRadius = 100.0f;
    m_spherePerimeter = 2.0f * 3.1415926f * m_sphereRadius;

    m_isContinue = false;
    m_continueRotateX = 0.0f;
    m_continueRotateY = 0.0f;
    m_decreateDetal = 0.01f;

    m_sphereLatitude = 1.0f;
    m_sphereLongitude = 1.0f;
    m_sphereLatitudeCount = ceil(180.0f / m_sphereLatitude);
    m_sphereLongitudeCount = ceil(360.0f / m_sphereLongitude);
    m_sphereQuadCount = m_sphereLatitudeCount * m_sphereLongitudeCount;

    m_sphereTransform.setIdentity();
    //m_sphereTransform.rotateY(A_TO_R(90.0f));

    m_sphereVAO = 0;
    m_sphereIBO = 0;
    m_sphereVertexVBO = 0;
    m_sphereColorVBO = 0;
    m_sphereTextureVBO = 0;

    return true;
}

Sphere::~Sphere()
{
    if (m_thread_1)
    {
        m_isThread1Done = true;
        m_thread_1->join();
        delete m_thread_1;
    }

    if (m_thread_2)
    {
        m_isThread2Done = true;
        b_transform_planview = true;
        cv_transform_planview.notify_all();
        m_thread_2->join();
        delete m_thread_2;
    }

    if (m_vertexes) 
        delete[] m_vertexes;
    if (m_colors) 
        delete[] m_colors;
    if (m_texcoordes) 
        delete[] m_texcoordes;
    if (m_indexes)
        delete[] m_indexes;

    if (m_sphereVertexVBO > 0)
        glDeleteBuffers(1, &m_sphereVertexVBO);
    if (m_sphereColorVBO > 0)
        glDeleteBuffers(1, &m_sphereColorVBO);
    if (m_sphereTextureVBO > 0)
        glDeleteBuffers(1, &m_sphereTextureVBO);
    if (m_sphereIBO > 0)
        glDeleteBuffers(1, &m_sphereIBO);
    if (m_sphereVAO > 0)
        glDeleteVertexArrays(1, &m_sphereVAO);
}

void Sphere::generateVertexesSphere(float radius)
{
    float rx = 0.0f, ry = 0.0f, rz = 0.0f;

    m_vertexes = new GLfloat[m_sphereQuadCount * 16];
    m_colors = new GLfloat[m_sphereQuadCount * 16];
    m_texcoordes = new GLfloat[m_sphereQuadCount * 8];
    m_indexes = new GLuint[m_sphereQuadCount * 4];

    unsigned int offset = 4 * 4;
    unsigned int textureOffset = 4 * 2;
    unsigned int stride = m_sphereLatitudeCount / 2 * m_sphereLongitudeCount * offset;
    unsigned int textureStride = m_sphereLatitudeCount / 2 * m_sphereLongitudeCount * textureOffset;

    float unitU = m_sphereLongitude / 360.0f;
    float unitV = m_sphereLatitude / 360.0f * 2;

    GLuint vertexIndex = 0;

    for (unsigned int latiIndex = 0; latiIndex < m_sphereLatitudeCount / 2; ++latiIndex)
    {
        // 緯度
        float latiAngle = m_sphereLatitude * (latiIndex + 1);
        float latiRadian1 = A_TO_R(latiAngle - m_sphereLatitude);
        float latiRadian2 = A_TO_R(latiAngle);

        for (unsigned int longiIndex = 0; longiIndex < m_sphereLongitudeCount; ++longiIndex)
        {
            // 經度
            float longiAngle = m_sphereLongitude * (longiIndex + 1);
            float longiRadian1 = A_TO_R(longiAngle - m_sphereLongitude);
            float longiRadian2 = A_TO_R(longiAngle);

            unsigned int index = latiIndex * m_sphereLongitudeCount + longiIndex;

            // vertex
            // 上半球
            m_vertexes[index * offset + 0] = rx + radius * cos(latiRadian1) * cos(longiRadian1);
            m_vertexes[index * offset + 1] = ry + radius * sin(latiRadian1);
            m_vertexes[index * offset + 2] = rz + radius * cos(latiRadian1) *sin(longiRadian1);
            m_vertexes[index * offset + 3] = 1.0f;
            // 上半球
            m_vertexes[index * offset + 4] = rx + radius * cos(latiRadian1) * cos(longiRadian2);
            m_vertexes[index * offset + 5] = ry + radius * sin(latiRadian1);
            m_vertexes[index * offset + 6] = rz + radius * cos(latiRadian1) * sin(longiRadian2);
            m_vertexes[index * offset + 7] = 1.0f;
            // 上半球
            m_vertexes[index * offset + 8] = rx + radius * cos(latiRadian2) * cos(longiRadian2);
            m_vertexes[index * offset + 9] = ry + radius * sin(latiRadian2);
            m_vertexes[index * offset + 10] = rz + radius * cos(latiRadian2)  * sin(longiRadian2);
            m_vertexes[index * offset + 11] = 1.0f;
            // 上半球
            m_vertexes[index * offset + 12] = rx + radius * cos(latiRadian2) * cos(longiRadian1);
            m_vertexes[index * offset + 13] = ry + radius * sin(latiRadian2);
            m_vertexes[index * offset + 14] = rz + radius * cos(latiRadian2) * sin(longiRadian1);
            m_vertexes[index * offset + 15] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 0] = m_vertexes[index * offset + 12];
            m_vertexes[stride + index * offset + 1] = -1 * m_vertexes[index * offset + 13];
            m_vertexes[stride + index * offset + 2] = m_vertexes[index * offset + 14];
            m_vertexes[stride + index * offset + 3] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 4] = m_vertexes[index * offset + 8];
            m_vertexes[stride + index * offset + 5] = -1 * m_vertexes[index * offset + 9];
            m_vertexes[stride + index * offset + 6] = m_vertexes[index * offset + 10];
            m_vertexes[stride + index * offset + 7] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 8] = m_vertexes[index * offset + 4];
            m_vertexes[stride + index * offset + 9] = -1 * m_vertexes[index * offset + 5];
            m_vertexes[stride + index * offset + 10] = m_vertexes[index * offset + 6];
            m_vertexes[stride + index * offset + 11] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 12] = m_vertexes[index * offset + 0];
            m_vertexes[stride + index * offset + 13] = -1 * m_vertexes[index * offset + 1];
            m_vertexes[stride + index * offset + 14] = m_vertexes[index * offset + 2];
            m_vertexes[stride + index * offset + 15] = 1.0f;

            // color
            //上半球
            m_colors[index * offset + 0] = 1.0f;
            m_colors[index * offset + 1] = 1.0f;
            m_colors[index * offset + 2] = 1.0f;
            m_colors[index * offset + 3] = 0.6f;
            m_colors[index * offset + 4] = 1.0f;
            m_colors[index * offset + 5] = 1.0f;
            m_colors[index * offset + 6] = 1.0f;
            m_colors[index * offset + 7] = 0.6f;
            m_colors[index * offset + 8] = 1.0f;
            m_colors[index * offset + 9] = 1.0f;
            m_colors[index * offset + 10] = 1.0f;
            m_colors[index * offset + 11] = 0.6f;
            m_colors[index * offset + 12] = 1.0f;
            m_colors[index * offset + 13] = 1.0f;
            m_colors[index * offset + 14] = 1.0f;
            m_colors[index * offset + 15] = 0.6f;
            // 下半球
            m_colors[stride + index * offset + 0] = 1.0f;
            m_colors[stride + index * offset + 1] = 1.0f;
            m_colors[stride + index * offset + 2] = 1.0f;
            m_colors[stride + index * offset + 3] = 0.6f;
            m_colors[stride + index * offset + 4] = 1.0f;
            m_colors[stride + index * offset + 5] = 1.0f;
            m_colors[stride + index * offset + 6] = 1.0f;
            m_colors[stride + index * offset + 7] = 0.6f;
            m_colors[stride + index * offset + 8] = 1.0f;
            m_colors[stride + index * offset + 9] = 1.0f;
            m_colors[stride + index * offset + 10] = 1.0f;
            m_colors[stride + index * offset + 11] = 0.6f;
            m_colors[stride + index * offset + 12] = 1.0f;
            m_colors[stride + index * offset + 13] = 1.0f;
            m_colors[stride + index * offset + 14] = 1.0f;
            m_colors[stride + index * offset + 15] = 0.6f;

            // texture
            float startU = (m_sphereLongitudeCount - longiIndex) * unitU;
            float startV = (m_sphereLatitudeCount / 2 - latiIndex) * unitV;
            // 上半球
            m_texcoordes[index * textureOffset + 0] = startU - unitU;
            m_texcoordes[index * textureOffset + 1] = startV;
            m_texcoordes[index * textureOffset + 2] = startU;
            m_texcoordes[index * textureOffset + 3] = startV;
            m_texcoordes[index * textureOffset + 4] = startU;
            m_texcoordes[index * textureOffset + 5] = (startV + unitV);
            m_texcoordes[index * textureOffset + 6] = startU - unitU;
            m_texcoordes[index * textureOffset + 7] = (startV + unitV);
            // 下半球
            m_texcoordes[textureStride + index * textureOffset + 0] = startU;
            m_texcoordes[textureStride + index * textureOffset + 1] = 1.0f - (startV + unitV);
            m_texcoordes[textureStride + index * textureOffset + 2] = startU - unitU;
            m_texcoordes[textureStride + index * textureOffset + 3] = 1.0f - (startV + unitV);
            m_texcoordes[textureStride + index * textureOffset + 4] = startU - unitU;;
            m_texcoordes[textureStride + index * textureOffset + 5] = 1.0f - startV;
            m_texcoordes[textureStride + index * textureOffset + 6] = startU;
            m_texcoordes[textureStride + index * textureOffset + 7] = 1.0f - startV;

            m_indexes[vertexIndex++] = index * offset / 4;
            m_indexes[vertexIndex++] = index * offset / 4 + 1;
            m_indexes[vertexIndex++] = index * offset / 4 + 2;
            m_indexes[vertexIndex++] = index * offset / 4 + 3;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4 + 1;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4 + 2;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4 + 3;

            m_vertexCount += 8;
            m_indexCount += 8;
        }
    }
}

void Sphere::generateVertexesSphereNew(float radius)
{
    unsigned int quadCount = m_sphereLatitudeCount * m_sphereLongitudeCount;
    unsigned int vertexCount = (m_sphereLatitudeCount + 1) * (m_sphereLongitudeCount + 1);

    m_vertexes = new GLfloat[vertexCount * 4];
    m_colors = new GLfloat[vertexCount * 4];
    m_texcoordes = new GLfloat[vertexCount * 2];
    m_indexes = new GLuint[quadCount * 4];

    float unitU = m_sphereLongitude / 360.0f;
    float unitV = m_sphereLatitude / 180.0f;

    for (unsigned int latiIndex = 0; latiIndex <= m_sphereLatitudeCount; ++latiIndex)
    {
        // 緯度
        float latiAngle = m_sphereLatitude * latiIndex;
        float latiRadian = A_TO_R(latiAngle);

        latiRadian = HMPI - latiRadian;

        for (unsigned int longiIndex = 0; longiIndex <= m_sphereLongitudeCount; ++longiIndex)
        {
            // 經度
            float longiAngle = m_sphereLongitude * longiIndex;
            float longiRadian = A_TO_R(longiAngle);

            unsigned int index = latiIndex * (m_sphereLongitudeCount + 1) + longiIndex;

            m_vertexes[index * 4] = m_sphereRadius * cos(latiRadian) * sin(longiRadian);
            m_vertexes[index * 4 + 1] = m_sphereRadius * sin(latiRadian);
            m_vertexes[index * 4 + 2] = m_sphereRadius * cos(latiRadian) * cos(longiRadian);
            m_vertexes[index * 4 + 3] = 1.0f;

            m_colors[index * 4] = 1.0f;
            m_colors[index * 4 + 1] = 1.0f;
            m_colors[index * 4 + 2] = 1.0f;
            m_colors[index * 4 + 3] = 1.0f;

            m_texcoordes[index * 2] = longiIndex * unitU;
            m_texcoordes[index * 2 + 1] = latiIndex * unitV;
        }
    }
    m_vertexCount = vertexCount;

    for (unsigned int i = 0; i < m_sphereLatitudeCount; ++i)
    {
        for (unsigned int j = 0; j < m_sphereLongitudeCount; ++j)
        {
            unsigned index = i * m_sphereLongitudeCount + j;

            m_indexes[index * 4] = i * (m_sphereLongitudeCount + 1) + j;
            m_indexes[index * 4 + 1] = i * (m_sphereLongitudeCount + 1) + j + 1;
            m_indexes[index * 4 + 2] = (i + 1) * (m_sphereLongitudeCount + 1) + j + 1;
            m_indexes[index * 4 + 3] = (i + 1) * (m_sphereLongitudeCount + 1) + j;
        }
    }
    m_indexCount = quadCount * 4;
}

void Sphere::generateTexture()
{
    CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("images/diqiu.jpg");
    if (texture)
        m_textureName = texture->getName();
    else
        assert(0, "create texture failed");
}

void Sphere::generateVAO()
{
    glGenVertexArrays(1, &m_sphereVAO);
    if (m_sphereVAO > 0)
    {
        glBindVertexArray(m_sphereVAO);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);

        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glEnableVertexAttribArray(2);

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

        CHECK_GL_ERROR_DEBUG();
    }
    else
    {
        assert(0);
    }

    glBindVertexArray(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void Sphere::generateIBO()
{
    glGenBuffers(1, &m_sphereIBO);
    if (m_sphereIBO > 0)
    {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_indexCount, m_indexes, GL_STATIC_DRAW);

        CHECK_GL_ERROR_DEBUG();

        delete[] m_indexes;
        m_indexes = nullptr;
    }

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

void Sphere::generateVBO()
{
    size_t dataSize = sizeof(GLfloat) * m_vertexCount * 4;
    GLuint vbos[3] = { 0, 0, 0 };

    glGenBuffers(3, vbos);
    if (vbos[0] > 0)
    {
        m_sphereVertexVBO = vbos[0];

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glBufferData(GL_ARRAY_BUFFER, dataSize, m_vertexes, GL_STATIC_DRAW);

        delete[] m_vertexes;
        m_vertexes = nullptr;

        CHECK_GL_ERROR_DEBUG();
    }
    else
    {
        assert(0);
    }
    if (vbos[1] > 0)
    {
        m_sphereColorVBO = vbos[1];

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glBufferData(GL_ARRAY_BUFFER, dataSize, m_colors, GL_STATIC_DRAW);

        delete[] m_colors;
        m_colors = nullptr;

        CHECK_GL_ERROR_DEBUG();
    }
    if (vbos[2] > 0)
    {
        m_sphereTextureVBO = vbos[2];

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_vertexCount * 2, m_texcoordes, GL_STATIC_DRAW);

        delete[] m_texcoordes;
        m_texcoordes = nullptr;

        CHECK_GL_ERROR_DEBUG();
    }

    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void Sphere::drawBg(const Mat4& transform)
{
    CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("images/bg.jpg");
    GL::bindTexture2D(texture->getName());
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // GLProgram* program = CCShaderCache::sharedShaderCache()->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE);
    m_program->use();
    m_program->setUniformsForBuiltins(transform);

    CCSize& size = CCDirector::sharedDirector()->getVisibleSize();
    GLfloat vertexes[] = {
        0.0f, 0.0f, 0.0f, 1.0f,
        0.0f, 512, 0.0f, 1.0f,
        1024, 512, 0.0f, 1.0f,
        1024, 0.0f, 0.0f, 1.0f
    };
    GLfloat colores[] = {
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f
    };
    GLfloat texCoordes[] = {
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
        1.0f, 1.0f
    };

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, vertexes);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colores);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, texCoordes);

    glDrawArrays(GL_QUADS, 0, 4);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);

    CHECK_GL_ERROR_DEBUG();
}

void Sphere::drawSphere(const Mat4& transform, GLfloat* vertexes)
{
    GL::bindTexture2D(m_textureName);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    Mat4 _transform;
    _transform.setIdentity();
    _transform.translate(m_sphereTranslateX, m_sphereTranslateY, 0.0f);
    _transform.multiply(m_sphereTransform);

    m_program->use();
    m_program->setUniformsForBuiltins(_transform);

    CHECK_GL_ERROR_DEBUG();

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    if (m_sphereVAO == 0)
    {
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        CHECK_GL_ERROR_DEBUG();

        if (m_sphereIBO == 0)
        {
            if (m_indexes)
                glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, m_indexes);
            else
                glDrawArrays(GL_QUADS, 0, m_vertexCount);
        }
        else
        {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);
            glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
    }
    else
    {
        glBindVertexArray(m_sphereVAO);
        glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
        glBindVertexArray(0);
    }

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);

    CHECK_GL_ERROR_DEBUG();
}

void Sphere::drawPlanView(const Mat4& transform, GLfloat* vertexes)
{
    GL::bindTexture2D(m_textureName);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

    Mat4 _transform;
    _transform.setIdentity();
    //_transform.translate(m_planViewTranslateX, m_planViewTranslateY, 0.0f);
    _transform.multiply(m_sphereTransform);

    m_program_planview->use();
    GLfloat _translate[] = { m_planViewTranslateX, m_planViewTranslateY };
    GLuint location_radius = m_program_planview->getUniformLocation("radius");
    m_program_planview->setUniformLocationWith1f(location_radius, m_sphereRadius);
    GLuint location_translate = m_program_planview->getUniformLocation("translate");
    m_program_planview->setUniformLocationWith2f(location_translate, _translate[0], _translate[1]);
    m_program_planview->setUniformsForBuiltins(_transform);

    /*
    m_program->use();
    m_program->setUniformsForBuiltins(_transform);
    */

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    if (m_sphereVAO == 0)
    {
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        if (m_sphereIBO == 0)
        {
            if (m_indexes)
                glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, m_indexes);
            else
                glDrawArrays(GL_QUADS, 0, m_vertexCount);
        }
        else
        {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);
            glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
    }
    else
    {
        glBindVertexArray(m_sphereVAO);
        glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
        glBindVertexArray(0);
    }

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);

    CHECK_GL_ERROR_DEBUG();
}

void Sphere::draw(Renderer *renderer, const Mat4& transform, uint32_t flags)
{
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CW);

    GL::blendFunc(BlendFunc::ALPHA_NON_PREMULTIPLIED.src, BlendFunc::ALPHA_NON_PREMULTIPLIED.dst);

    this->drawBg(transform);

    {
        if (mx_transform_sphere.try_lock())
        {
            this->drawSphere(transform, m_vertexes);
        
            mx_transform_sphere.unlock();
        }
    }

    {
        if (mx_transform_planview.try_lock())
        {
            this->drawPlanView(transform, m_vertexes);

            mx_transform_planview.unlock();
        }
    }

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
}

void Sphere::visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags)
{
    this->update();

    Node::visit(renderer, parentTransform, parentFlags);
}

void Sphere::transformSphere(float touchOffsetX, float touchOffsetY)
{
    m_rotateYAngle += touchOffsetX / m_spherePerimeter * 360.0f;
    // m_rotateXAngle -= touchOffsetY / m_spherePerimeter * 360.0f;
    // m_rotateZAngle -= touchOffsetX / m_spherePerimeter * 360.0f;

    if (m_rotateYAngle >= 90.0f && m_rotateYAngle <= 270.0f)
        m_rotateXAngle += touchOffsetY / m_spherePerimeter * 360.0f;
    else
        m_rotateXAngle -= touchOffsetY / m_spherePerimeter * 360.0f;

    if (m_rotateYAngle < 0.0f)
        m_rotateYAngle += 360.0f;
    if (m_rotateYAngle > 360.0f)
        m_rotateYAngle -= 360.0f;

    if (m_rotateXAngle < 0.0f)
        m_rotateXAngle += 360.0f;
    if (m_rotateXAngle >= 360.0f)
        m_rotateXAngle -= 360.0f;

    /*
    cocos2d::log("m_rotateXAngle:%f", m_rotateXAngle);
    cocos2d::log("m_rotateYAngle:%f", m_rotateYAngle);
    cocos2d::log("m_rotateZAngle:%f", m_rotateZAngle);
    */

    Quaternion quat1;
    this->createQuaternion(m_rotateXAngle, 0, 0, quat1);
    Quaternion quat2;
    this->createQuaternion(0, m_rotateYAngle, 0, quat2);
    Quaternion quat3;
    this->createQuaternion(0, 0, m_rotateZAngle, quat3);
    Quaternion quat4;
    this->createQuaternion(m_rotateXAngle, m_rotateYAngle, m_rotateZAngle, quat4);

    m_sphereTransform.setIdentity();
    //m_sphereTransform.rotate(quat4);
    //m_sphereTransform.rotate(quat3);
    //m_sphereTransform.rotate(quat2);
    //m_sphereTransform.rotate(quat1);
    m_sphereTransform.rotate(quat3);
    m_sphereTransform.rotate(quat2);
    m_sphereTransform.rotate(quat1);

    // m_sphereTransform.rotateX(A_TO_R(m_rotateXAngle));
    // m_sphereTransform.rotateY(A_TO_R(m_rotateYAngle));
}

void Sphere::createQuaternion(float rotateX, float rotateY, float rotateZ, Quaternion& quat)
{
    float halfRadx = CC_DEGREES_TO_RADIANS(rotateX / 2.f), halfRady = CC_DEGREES_TO_RADIANS(rotateY / 2.f), halfRadz = CC_DEGREES_TO_RADIANS(rotateZ / 2.f);
    float coshalfRadx = cosf(halfRadx), sinhalfRadx = sinf(halfRadx), coshalfRady = cosf(halfRady), sinhalfRady = sinf(halfRady), coshalfRadz = cosf(halfRadz), sinhalfRadz = sinf(halfRadz);
    quat.x = sinhalfRadx * coshalfRady * coshalfRadz - coshalfRadx * sinhalfRady * sinhalfRadz;
    quat.y = coshalfRadx * sinhalfRady * coshalfRadz + sinhalfRadx * coshalfRady * sinhalfRadz;
    quat.z = coshalfRadx * coshalfRady * sinhalfRadz - sinhalfRadx * sinhalfRady * coshalfRadz;
    quat.w = coshalfRadx * coshalfRady * coshalfRadz + sinhalfRadx * sinhalfRady * sinhalfRadz;
}

void Sphere::update()
{
    if (m_isContinue)
    {
        float _rotateX = fabsf(m_continueRotateX);
        float _rotateY = fabsf(m_continueRotateY);
        
        _rotateX -= m_decreateDetal;
        if (_rotateX < 0.0f)
            m_continueRotateX = 0.0f;
        else
            m_continueRotateX += (m_continueRotateX >= 0.0f) ? (-m_decreateDetal) : m_decreateDetal;

        _rotateY -= m_decreateDetal;
        if (_rotateY < 0.0f)
            m_continueRotateY = 0.0f;
        else
            m_continueRotateY += (m_continueRotateY >= 0.0f) ? (-m_decreateDetal) : m_decreateDetal;

        if (m_continueRotateX == 0.0f && m_continueRotateY == 0.0f)
        {
            m_isContinue = false;
        }
        else
        {
            this->transformSphere(m_continueRotateY, m_continueRotateX);

            // this->noticeToTransform(m_continueRotateY, m_continueRotateX);
        }
    }
}

void Sphere::onThread1Proc()
{
    do {
        if (m_isThread1Done)
            break;

        float x = 0.0f, y = 0.0f;
        {
            std::lock_guard<std::mutex> lock(mx_transform_sphere);

            if (b_transform_sphere)
            {
                x = _touchOffsetXCopy = _touchOffsetX;
                y = _touchOffsetYCopy = _touchOffsetY;

                this->transformSphere(x, y);

                _sphereTransformationCopy = m_sphereTransform;

                b_transform_sphere = false;
                b_transform_planview = true;
                cv_transform_planview.notify_all();
            }
            else
            {
                std::this_thread::sleep_for(std::chrono::microseconds(1));
            }
        }
    }
    while (1);
}

void Sphere::onThread2Proc()
{
    do {
        if (m_isThread2Done)
            break;

        {
            std::unique_lock<std::mutex> lock(mx_transform_planview);
            cv_transform_planview.wait(lock, [this](){
                return b_transform_planview;
            });
            b_transform_planview = false;
        }

        {
            std::lock_guard<std::mutex> lock(mx_transform_planview);
        }
    } 
    while (1);
}

void Sphere::noticeToTransform(float touchOffsetX, float touchOffsetY)
{
    {
        std::lock_guard<std::mutex> lock(mx_transform_sphere);

        _touchOffsetX = touchOffsetX;
        _touchOffsetY = touchOffsetY;

        b_transform_sphere = true;
    }
}

bool Sphere::onTouchBegan(Touch* touch, Event* event)
{
    const cocos2d::Vec2& touchPoint = touch->getLocation();

    float _x = touchPoint.x - m_sphereTranslateX, _y = touchPoint.y - m_sphereTranslateY;
    float _dis = _x * _x + _y * _y;

    if (_dis <= m_sphereRadius * m_sphereRadius)
    {
        m_isContinue = false;
        return true;
    }
    else
    {
        return false;
    }
}

void Sphere::onTouchMoved(Touch* touch, Event* event)
{
    const cocos2d::Vec2& touchPoint = touch->getLocation();
    const cocos2d::Vec2& preTouchPoint = touch->getPreviousLocation();
    float touchOffsetX = touchPoint.x - preTouchPoint.x;
    float touchOffsetY = touchPoint.y - preTouchPoint.y;

    this->transformSphere(touchOffsetX, touchOffsetY);

    // this->noticeToTransform(touchOffsetX, touchOffsetY);
    
    m_continueRotateY = touchOffsetX / m_spherePerimeter * 360.0f;
    m_continueRotateX = touchOffsetY / m_spherePerimeter * 360.0f;
}

void Sphere::onTouchEnded(Touch* touch, Event* event)
{
    m_isContinue = true;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM