1. 程式人生 > >Simple2D-22(重構)紋理池

Simple2D-22(重構)紋理池

opengl offsetx http 進行 true 現在 窗口 sed fse

  以前 Simple2D 使用 TextureManager,現在將它改為 TexturePool (紋理池)。主要是負責加載和管理紋理,這次為 TexturePool 添加紋理集的功能,紋理集就是將大量的圖片拼合成一張紋理。

  紋理集的制作

  你可以使用軟件 TexturePacher 來創建紋理集:

技術分享

  將圖片文件拖曳到左邊的窗口,然後將 Output 的 DataFormat 設置為 cocos2d,最後選擇 Data File 和 Texture File 的輸出路徑,點擊工具欄的 Publish 按鈕後得到兩個文件 xxx.plist 和 xxx.png,再將這兩個文件放置在 Assert 文件夾即可。

  解析 Plist 文件

  由於 Plist 文件時 xml 格式的,所以可以使用 Tinyxml 庫來解析,其中只需要讀取 plist 文件的三個信息即可:

技術分享

  1、紋理文件名,與小圖相關聯的標簽

  2、小圖的大小和小圖在大圖中的位置偏移,用來計算紋理坐標

  3、小圖是否旋轉,TexturePacher 在合並小圖時為了合理分配空間位置,必要時會對小圖旋轉 90o,計算紋理坐標時要進行旋轉

  OpenGL 為每個紋理分配一個唯一的 ID,而紋理集的多張小圖都來自於一張紋理,為了管理這些紋理,需要兩個結構:TextureUnique 和 Texture2D。TextureUnique 對於著一張紋理,而 Texture2D 則對應小圖:

    struct Texture2D
    {
        TextureUnique* textureUnique;

        int     width;
        int     height;
        Vec2 uv[4];
    };

  Texture2D 保存圖片的大小(該大小是小圖的大小,不是紋理的大小)、紋理坐標和 TextureUnique 對象。

  TexturePool 使用 ParsePlistFile( ) 函數來解析 plist 文件:

bool TexturePool::ParsePlistFile(const std::string
& filename, std::vector<PlistParseData>& ppd_list) { tinyxml2::XMLDocument doc; auto path = PathHelper::fullPath(filename); if ( doc.LoadFile(path.c_str()) != tinyxml2::XML_NO_ERROR ) { LOG_WRITE_DEBUG("不存在 plist 文件:%s", filename.c_str()); return false; } tinyxml2::XMLElement* frame_ele = nullptr; tinyxml2::XMLElement* context_ele = nullptr; tinyxml2::XMLNode* plist_node = doc.RootElement(); plist_node = plist_node->FirstChildElement(); frame_ele = plist_node->FirstChildElement(); tinyxml2::XMLElement* begin_node = frame_ele->NextSiblingElement()->FirstChildElement(); std::string left, right; while ( begin_node ) { PlistParseData ppd; ppd.filename = begin_node->GetText(); context_ele = begin_node->NextSiblingElement(); context_ele = context_ele->FirstChildElement("string"); std::string size = context_ele->GetText(); /* {{xx, xx},{xx, xx}} */ left = size.substr(2, size.find_first_of("}") - 2); right = left.substr(left.find_first_of(",") + 1, left.size() - left.find_first_of(",")); left = left.substr(0, left.find_first_of(",")); ppd.offsetx = atoi(left.c_str()); ppd.offsety = atoi(right.c_str()); right = size.substr(size.find_last_of("{") + 1, size.size() - size.find_last_of("{") - 3); left = right.substr(0, right.find_first_of(",")); right = right.substr(right.find_first_of(",") + 1, right.size() - right.find_first_of(",")); ppd.width = atoi(left.c_str()); ppd.height = atoi(right.c_str()); context_ele = context_ele->NextSiblingElement(); context_ele = context_ele->NextSiblingElement(); context_ele = context_ele->NextSiblingElement(); context_ele = context_ele->NextSiblingElement(); std::string rotate = context_ele->Name(); ppd.rotate = (rotate.compare("true") == 0); begin_node = begin_node->NextSiblingElement(); begin_node = begin_node->NextSiblingElement(); ppd_list.push_back(ppd); } /* 獲取圖像文件名 */ frame_ele = frame_ele->NextSiblingElement(); frame_ele = frame_ele->NextSiblingElement(); frame_ele = frame_ele->NextSiblingElement(); tinyxml2::XMLElement* metadata = frame_ele->FirstChildElement("string"); std::string texture_name = metadata->GetText(); metadata = metadata->NextSiblingElement("string"); std::string texture_size = metadata->GetText(); int dot = texture_size.find_first_of(","); PlistParseData ppd; ppd.filename = texture_name; ppd.width = atoi(texture_size.substr(1, dot - 1).c_str()); ppd.height = atoi(texture_size.substr(dot + 1, texture_size.size() - dot - 2).c_str()); ppd_list.push_back(ppd); return true; }

  將解析得到的小圖數據保存到 PlistParseData 結構中,然後得到一個 PlistParseData 數組:

        struct PlistParseData
        {
            std::string filename;
            bool rotate;

            int offsetx;
            int offsety;

            int width;
            int height;
        };

  通過 PlistParseData 數組就可以創建 TextureUnique 和 Texture2D 對象了:

bool TexturePool::LoadFileFromPlist(const std::string& filename)
    {
        std::vector<PlistParseData> ppd_list;
        if ( ParsePlistFile(filename, ppd_list) == false ) {
            LOG_WRITE("解析文件 %s 失敗!", filename.c_str());
            return false;
        }

        TextureUnique* texture_unique = new TextureUnique(ppd_list.back().filename.c_str());

        for ( int i = 0; i < ppd_list.size() - 1; i++ ) {
            PlistParseData& ppd = ppd_list[i];

            Texture2D* texture_2d = new Texture2D;
            texture_2d->textureUnique = texture_unique;
            texture_2d->width = ppd.width;
            texture_2d->height = ppd.height;

            /* 計算紋理坐標 */
            Vec2 p1, p2;
            if ( ppd.rotate ) {
                p1.x = ( float ) ppd.offsetx / texture_unique->width;
                p1.y = 1 - ( float ) (ppd.offsety + ppd.width) / texture_unique->height;

                p2.x = ( float ) (ppd.offsetx + ppd.height) / texture_unique->width;
                p2.y = 1 - ( float ) ppd.offsety / texture_unique->height;

                texture_2d->uv[3].set(p1.x, p1.y);
                texture_2d->uv[0].set(p1.x, p2.y);
                texture_2d->uv[1].set(p2.x, p2.y);
                texture_2d->uv[2].set(p2.x, p1.y);
            }
            else {
                p1.x = ( float ) ppd.offsetx / texture_unique->width;
                p1.y = 1- ( float ) (ppd.offsety + ppd.height) / texture_unique->height;

                p2.x = ( float ) (ppd.offsetx + ppd.width) / texture_unique->width;
                p2.y = 1- ( float ) ppd.offsety / texture_unique->height;

                texture_2d->uv[0].set(p1.x, p1.y);
                texture_2d->uv[1].set(p1.x, p2.y);
                texture_2d->uv[2].set(p2.x, p2.y);
                texture_2d->uv[3].set(p2.x, p1.y);
            }
            vTextureMap.insert(std::make_pair(ppd.filename, texture_2d));
        }
        return true;
    }

  將得到的 Texture2D 對象保存到一個數組中,最後通過 TexturePool 提供的函數 Texture2D* GetTexture(const std::string& filename) 獲取 Texture2D 對象。而 TextureUnique 則用於紋理的刪除,但 TexturePool 並沒有提供紋理的刪除操作,也就是你無法再不需要紋理時刪除紋理,只能在程序結束後刪除。

  Texture2D 是 Sprite、Painter 和 ImGui 使用的圖片渲染對象,而 TextureUnique 只是在 TexturePool 內部使用。

  源碼下載:Simple2D-20.rar

Simple2D-22(重構)紋理池