• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            小星星的天空

            O(∩_∩)O 小月亮的fans ^_^

              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
              16 隨筆 :: 0 文章 :: 61 評論 :: 0 Trackbacks

            2009年10月20日 #

            多態(Polymorphism)是面向對象的核心概念,本文以C++為例,討論多態的具體實現。C++中多態可以分為基于繼承和虛函數的動態多態以及基于模板的靜態多態,如果沒有特別指明,本文中出現的多態都是指前者,也就是基于繼承和虛函數的動態多態。至于什么是多態,在面向對象中如何使用多態,使用多態的好處等等問題,如果大家感興趣的話,可以找本面向對象的書來看看。
                為了方便說明,下面舉一個簡單的使用多態的例子(From [1] ):

            class Shape
            {
            protected:
              int m_x;    // X coordinate
              int m_y;  // Y coordinate
            public:
              // Pure virtual function for drawing
              virtual void Draw() = 0;  

              // A regular virtual function
              virtual void MoveTo(int newX, int newY);

             // Regular method, not overridable.
              void Erase();

              // Constructor for Shape
              Shape(int x, int y); 

             // Virtual destructor for Shape
              virtual ~Shape();
            };
            // Circle class declaration
            class Circle : public Shape
            {
            private:
               int m_radius;    // Radius of the circle
            public:
               // Override to draw a circle
               virtual void Draw();    

               // Constructor for Circle
               Circle(int x, int y, int radius);

              // Destructor for Circle
               virtual ~Circle();
            };
            // Shape constructor implementation
            Shape::Shape(int x, int y)
            {
               m_x = x;
               m_y = y;
            }
            // Shape destructor implementation
            Shape::~Shape()
            {
            //...
            }
             // Circle constructor implementation
            Circle::Circle(int x, int y, int radius) : Shape (x, y)
            {
               m_radius = radius;
            }

            // Circle destructor implementation
            Circle::~Circle()
            {
            //...
            }

            // Circle override of the pure virtual Draw method.
            void Circle::Draw()
            {
               glib_draw_circle(m_x, m_y, m_radius);
            }

            main()
            {
              // Define a circle with a center at (50,100) and a radius of 25
              Shape *pShape = new Circle(50, 100, 25);

              // Define a circle with a center at (5,5) and a radius of 2
              Circle aCircle(5,5, 2);

              // Various operations on a Circle via a Shape pointer
              //Polymorphism
              pShape->Draw();
              pShape->MoveTo(100, 100);

              pShape->Erase();
              delete pShape;

             // Invoking the Draw method directly
              aCircle.Draw();
            }   

                 例子中使用到多態的代碼以黑體標出了,它們一個很明顯的特征就是通過一個基類的指針(或者引用)來調用不同子類的方法。
                 那么,現在的問題是,這個功能是怎樣實現的呢?我們可以先來大概猜測一下:對于一般的方法調用,到了匯編代碼這一層次的時候,一般都是使用 Call funcaddr 這樣的指令進行調用,其中funcaddr是要調用函數的地址。按理來說,當我使用指針pShape來調用Draw的時候,編譯器應該將Shape::Draw的地址賦給funcaddr,然后Call 指令就可以直接調用Shape::Draw了,這就跟用pShape來調用Shape::Erase一樣。但是,運行結果卻告訴我們,編譯器賦給funcaddr的值卻是Circle::Drawde的值。這就說明,編譯器在對待Draw方法和Erase方法時使用了雙重標準。那么究竟是誰有這么大的法力,使編譯器這個鐵面無私的判官都要另眼相看呢?virtual!!
                
            Clever!!正是virtual這個關鍵字一手導演了這一出“乾坤大挪移”的好戲。說道這里,我們先要明確兩個概念:靜態綁定和動態綁定。
                1、靜態綁定(static bingding),也叫早期綁定,簡單來說就是編譯器在編譯期間就明確知道所要調用的方法,并將該方法的地址賦給了Call指令的funcaddr。因此,運行期間直接使用Call指令就可調用到相應的方法。
                2、動態綁定(dynamic binding),也叫晚期綁定,與靜態綁定不同,在編譯期間,編譯器并不能明確知道究竟要調用的是哪一個方法,而這,要知道運行期間使用的具體是哪個對象才能決定。
                好了,有了這兩個概念以后,我們就可以說,virtual的作用就是告訴編譯器:我要進行動態綁定!編譯器當然會尊重你的意見,而且為了完成你這個要求,編譯器還要做很多的事情:編譯器自動在聲明了virtual方法的類中插入一個指針vptr和一個數據結構VTable(vptr用以指向VTable;VTable是一個指針數組,里面存放著函數的地址),并保證二者遵守下面的規則:
                1、VTable中只能存放聲明為virtual的方法,其它方法不能存放在里面。在上面的例子中,Shape的VTable中就只有Draw,MoveTo和~Shape。方法Erase的地址并不能存放在VTable中。此外,如果方法是純虛函數,如 Draw,那么同樣要在VTable中保留相應的位置,但是由于純虛函數沒有函數體,因此該位置中并不存放Draw的地址,而是可以選擇存放一個出錯處理的函數的地址,當該位置被意外調用時,可以用出錯函數進行相應的處理。
                2、派生類的VTalbe中記錄的從基類中繼承下來的虛函數地址的索引號必須跟該虛函數在基類VTable中的索引號保持一致。如在上例中,如果在Shape的VTalbe中,Draw為 1 號, MoveTo 2 號,~Shape為 3 號,那么,不管這些方法在Circle中是按照什么順序定義的,Circle的VTable中都必須保證Draw為 1 號,MoveTo為 2號。至于 3號,這里是~Circle。為什么不是~Shape啊?嘿嘿,忘啦,析構函數不會繼承的。
                3、vptr是由編譯器自動插入生成的,因此編譯器必須負責為其進行初始化。初始化的時間選在對象創建時,而地點就在構造函數中。因此,編譯器必須保證每個類至少有一個構造函數,若沒有,自動為其生成一個默認構造函數。
                 4、vptr通常放在對象的起始處,也就是Addr(obj) == Addr(obj.vptr)。
                你看,天下果然沒有免費的午餐,為了實現動態綁定,編譯器要為我們默默干了這么多的臟話累活。如果你想體驗一下編譯器的辛勞,那么可以嘗試用C語言模擬一下上面的行為,【1】中就有這么一個例子。好了,現在萬事具備,只欠東風了。編譯,連接,載入,GO!當程序執行到 pShape->Draw()的時候,上面的設施也開始起作用了。。
                前面已經提到,晚期綁定時之所以不能確定調用哪個函數,是因為具體的對象不確定。好了,當運行到pShape->Draw()時,對象出來了,它由pShape指針標出。我們找到這個對象后,就可以找到它里面的vptr(在對象的起始處),有了vptr后,我們就找到了VTable,調用的函數就在眼前了。。等等,VTable中方法那么多,我究竟使用哪個呢?不用著急,編譯器早已為我們做好了記錄:編譯器在創建VTable時,已經為每個virtual函數安排好了座次,并且把這個索引號記錄了下來。因此,當編譯器解析到pShape->Draw()的時候,它已經悄悄的將函數的名字用索引號來代替了。這時候,我們通過這個索引號就可以在VTable中得到一個函數地址,Call it!
                在這里,我們就體會到為什么會有第二條規定了,通常,我們都是用基類的指針來引用派生類的對象,但是不管具體對象是哪個派生類的,我們都可以使用相同的索引號來取得對應的函數實現。
                 現實中有一個例子其實跟這個蠻像的:報警電話有110,119,120(VTable中不同的方法)。不同地方的人撥打不同的號碼所產生的結果都是不一樣的。譬如,在三環外的一個人(具體對象)跟一環內的一個人(另外一個具體對象)打119,最后調用的消防隊肯定是不一樣的,這就是多態了。這是怎么實現的呢,每個人都知道一個報警中心(VTable,里面有三個方法 110,119,120)。如果三環外的一個人需要火警搶險(一個具體對象)時,它就撥打119,但是他肯定不知道最后是哪一個消防隊會出現的。這得有報警中心來決定,報警中心通過這個具體對象(例子中就是具體位置了)以及他說撥打的電話號碼(可以理解成索引號),報警中心可以確定應該調度哪一個消防隊進行搶險(不同的動作)。
                 這樣,通過vptr和VTable的幫助,我們就實現了C++的動態綁定。當然,這僅僅是單繼承時的情況,多重繼承的處理要相對復雜一點,下面簡要說一下最簡單的多重繼承的情況,至于虛繼承的情況,有興趣的朋友可以看看 Lippman的《Inside the C++ Object Model》,這里暫時就不展開了。(主要是自己還沒搞清楚,況且現在多重繼承都不怎么使用了,虛繼承應用的機會就更少了)
                 首先,我要先說一下多重繼承下對象的內存布局,也就是說該對象是如何存放本身的數據的。

            class Cute
            {
            public:
             int i;
             virtual void cute(){ cout<<"Cute cute"<<endl; }
            };
            class Pet
            {
            public:
               int j;
               virtual void say(){ cout<<"Pet say"<<endl;  }
            };
            class Dog : public Cute,public Pet
            {
            public:
             int z;
             void cute(){ cout<<"Dog cute"<<endl; }
             void say(){ cout<<"Dog say"<<endl;  }
            };

                在上面這個例子中,一個Dog對象在內存中的布局如下所示:                    

            Dog

            Vptr1

            Cute::i

            Vptr2

            Pet::j

            Dog::z


                 也就是說,在Dog對象中,會存在兩個vptr,每一個跟所繼承的父類相對應。如果我們要想實現多態,就必須在對象中準確地找到相應的vptr,以調用不同的方法。但是,如果根據單繼承時的邏輯,也就是vptr放在指針指向位置的起始處,那么,要在多重繼承情況下實現,我們必須保證在將一個派生類的指針隱式或者顯式地轉換成一個父類的指針時,得到的結果指向相應派生類數據在Dog對象中的起始位置。幸好,這工作編譯器已經幫我們完成了。上面的例子中,如果Dog向上轉換成Pet的話,編譯器會自動計算Pet數據在Dog對象中的偏移量,該偏移量加上Dog對象的起始位置,就是Pet數據的實際地址了。

            int main()
            {
             Dog* d = new Dog();
             cout<<"Dog object addr : "<<d<<endl;
             Cute* c = d;
             cout<<"Cute type addr : "<<c<<endl;
             Pet* p = d;
             cout<<"Pet type addr : "<<p<<endl;
             delete d;
            }
            output:
            Dog object addr : 0x3d24b0
            Cute type addr : 0x3d24b0
            Pet type addr : 0x3d24b8   // 正好指向Dog對象的vptr2處,也就是Pet的數據

                  好了,既然編譯器幫我們自動完成了不同父類的地址轉換,我們調用虛函數的過程也就跟單繼承統一起來了:通過具體對象,找到vptr(通常指針的起始位置,因此Cute找到的是vptr1,而Pet找到的是vptr2),通過vptr,我們找到VTable,然后根據編譯時得到的VTable索引號,我們取得相應的函數地址,接著就可以馬上調用了。

                  在這里,順便也提一下兩個特殊的方法在多態中的特別之處吧:第一個是構造函數,在構造函數中調用虛函數是不會有多態行為的,例子如下:

            class Pet
            {
            public:
               Pet(){ sayHello(); }
               void say(){ sayHello(); }

               virtual void sayHello()
               {
                 cout<<"Pet sayHello"<<endl;
               }
              
            };
            class Dog : public Pet
            {
            public:
               Dog(){};
               void sayHello()
               {
                 cout<<"Dog sayHello"<<endl;
               }
            };
            int main()
            {
             Pet* p = new Dog();
             p->sayHello();
             delete p;
            }
            output:
            Pet sayHello //直接調用的是Pet的sayHello()
            Dog sayHello //多態

                 第二個就是析構函數,使用多態的時候,我們經常使用基類的指針來引用派生類的對象,如果是動態創建的,對象使用完后,我們使用delete來釋放對象。但是,如果我們不注意的話,會有意想不到的情況發生。

            class Pet
            {
            public:
               ~Pet(){ cout<<"Pet destructor"<<endl;  }
              //virtual ~Pet(){ cout<<"Pet virtual destructor"<<endl;  }
            };
            class Dog : public Pet
            {
            public:
               ~Dog(){ cout<<"Dog destructor"<<endl;};
               //virtual ~Dog(){ cout<<"Dog virtual destructor"<<endl;  }
            };
            int main()
            {
             Pet* p = new Dog();
             delete p;
            }
            output:
            Pet destructor  //糟了,Dog的析構函數沒有調用,memory leak!

            如果我們將析構函數改成virtual以后,結果如下
            Dog virtual destructor
            Pet virtual destructor   // That's OK!

                所以,如果一個類設計用來被繼承的話,那么它的析構函數應該被聲明為virtual的。

            posted @ 2009-10-20 21:36 Little Star 閱讀(375) | 評論 (0)編輯 收藏

            通常在C的編程中,我們經常使用memset函數將一塊連續的內存區域清零或設置為其它指定的值,最近在移植一段java代碼到C++的時候,不當使用memset函數花費了我幾個小時的調試時間。對于虛函數的底層機制很多資料都有較詳細闡述,但對我個人而言,這次的調試讓我感觸頗深。

            先來看一段代碼,在繼承的類Advance之中,有很多屬性字段,我希望將其清成0或NULL,于是在構造函數中我通過memset將當前類的所有屬性置0。

            class Base{

            public:

            virtual void kickoff() = 0;

            };
            class Advance:public Base{

            public:

            Advance(){

            memset(this, 0, sizeof(Advance));

            }

            void kickoff(){

            count++;

            //... do something else;

            }

            private:

            int attr1, attr2;

            char* label;

            int count;

            //... other attributes, they should be initiated to 0 or NULL at beginning.

            };

            int _tmain(int argc, _TCHAR* argv[])

            {
            Base* ptr = new Advance();
            ptr->kickoff();
            return 0;
            }

            這樣看似能正常運行,但運行程序時,你會發現類似于下面的錯誤:

            TestVirtual.exe 中的 0x00415390 處未處理的異常: 0xC0000005: 讀取位置 0x00000000 時發生訪問沖突

            同時斷點停留在ptr->kickoff()處,從錯誤提示我們可以得知無法調用kickoff方法,這個方法的指針沒有被正確初始化,但為什么呢?

            指出問題之前,先看看這段文獻上的關于虛函數機制的說明:

            函數賴以生存的底層機制:vptr + vtable。虛函數的運行時實現采用了VPTR/VTBL的形式,這項技術的基礎:
            ①編譯器在后臺為每個包含虛函數的類產生一個靜態函數指針數組(虛函數表),在這個類或者它的基類中定義的每一個虛函數都有一個相應的函數指針。
            ②每個包含虛函數的類的每一個實例包含一個不可見的數據成員vptr(虛函數指針),這個指針被構造函數自動初始化,指向類的vtbl(虛函數表)
            ③當客戶調用虛函數的時候,編譯器產生代碼反指向到vptr,索引到vtbl中,然后在指定的位置上找到函數指針,并發出調用。

            這里的問題,就出在

            memset(this, 0, sizeof(Advance));

            上面,虛函數指針應該在進入構造函數賦值體之前自動初始化的,而memset卻又將已經初始化好的指針清0了,這就是為什么會產生上面的訪問零址的錯誤。將上面的memset語句去除程序就可以正常運行了。

            所以,從上面的問題中,我們可以看出在構造函數體內調用memset將整個對象清0是很有風險的,當沒有虛函數的時候上面程序可以正常運行(可以試著將Base類的純虛函數聲明改成非虛函數再運行程序)。初始化類的屬性對象時,比較穩妥的辦法還是手動逐個進行初使化
            posted @ 2009-10-20 21:11 Little Star 閱讀(2810) | 評論 (7)編輯 收藏

            2009年10月13日 #




                   shadow map 以前早就研究過,不過一次不小心把以前做的東西都弄丟了,今天重新做了一下,
            加到了系統里,給大家看下效果:)



            shadow map算法原理很簡單,先簡單介紹下算法給新人:
            1.以光源所在位置為觀察點渲染場景(可以只渲染需要產生陰影的物體)將渲染后的深度值保存深度圖(一張事先準備好的紋理)。
            在此步需要注意的是 此次渲染用到的模型觀視投影矩陣(以后簡稱mvp)需要保存一下,下一步要用到。
            2.正常渲染場景,將頂點坐標乘以步驟一時候的mvp,將坐標變換到以光源為觀察點的坐標系里,比較z值和從深度圖中讀出來的
            值得大小判斷遮擋,有遮擋的話將輸出顏色減弱或者換成別的隨筆你了。其中有個地方需要注意,如何從深度圖紋理中讀數據,
            這個我是這么解決的:float2 suv = ((spos.xy/spos.z))//其中spos是變換到光源坐標系下的頂點數據,得到的suv經過處理后可以
            當做深度圖的紋理坐標值,讀取方法為float4 shadow = tex2D(t11,(suv+1.0)*0.5),其中用到一個【-1,1】到【0,1】的變換。
            剩下的就是比較了  :
                        float sz =  1 - spos.z/(gDepthSize);//將深度值變換到【0,1】區間,gDepthSize是獲得深度紋理時渲染場景的最深值  
                        //增加陰影
                        if((sz < shadow.x))//sz是就是
                        {
                            color = color*(1 - shadow);
                            color.w = 1.0;
                            //color = float4(sz,sz,sz,1);
                        }
            //----------------------------------------------------------------------------------------------------------------------------
            關于shadow map 算法的缺點,跟大家討論一下:
            永遠的困擾shadow map的失真問題,當光源照射場景稍大的時候失真現象就會很嚴重,有些改進算法,但都覺得治標不治本。
            如果說我整個場景有很多到處跑的人,那他們的陰影效果要怎樣做呢???

            感覺shadow map用在生成當前角色的陰影挺好,如果是大范圍的不大適合。很多靜態的物體可以先把陰影事先計算好,用的時候
            直接讀取,沒有必要每幀都重新計算。

            //----------------------------------------------------------------------------------------------------------------------------
            shadow map 最大的好處是可以處理透明紋理的陰影,以為我的場景的樹是用透明紋理畫上去的,如果得到的陰影是個矩
            形那就很怪了,幸好shadow map 沒有這個問題!!!



            posted @ 2009-10-13 23:34 Little Star 閱讀(3637) | 評論 (6)編輯 收藏



                     今天考慮程序的優化問題,突然想到說現在vetex shader已經可以訪問紋理資源了,可以把訪問高度圖的操作轉移
            到vetex shader中去做計算,GPU訪問紋理的速度要比CPU訪問內存快多了吧(我是這么認為的)。不過遇見一個問題。
            用tex2D訪問出來的值怪怪的,不是我要的高度紋理值,后來發現如果fragment shader里面訪問了別的紋理會影響到vetex里
            面的,上網google了一下,好多人都說得用 tex2DLod才行,試了下一點好轉的跡象都沒有,又有人說紋理得是float的,參照
            著改了下,還是不行。(很多時候在網上查到的都不好用,當然也有好用的,有點廢話,哈哈)
                    后來突然發現我的sampler都沒有跟寄存器綁定,綁定了以后就好了,很奇怪,難道說只調用cgGLSetTextureParameter
            并不能實現紋理的綁定?   現在我把所有sampler都跟一個寄存器綁定后就一切正常了。

            就像這樣:
            //--------------------------------------------------------------------------------
            sampler2D t10 :TEXUNIT0;//地面高度圖,    其他的sampler也需要綁定到寄存器才會好用

            OutPut xS_v(float4 ipos:POSITION,
                        float2 tex:TEXCOORD0,
                        float3 normal:NORMAL)
            {
                OutPut OUT;                            //out是關鍵字
                ipos.y =tex2D(t10,tex).z*256;
            //---------------------------------------------------------------------------------


            cg語言運行出來的結果經常怪怪的,有一次我故意寫錯了一句話,結果編譯也能通過,只是顯示出來的完全不
            是我想要的,有沒有高人指點一下,多謝!!


            不過把訪問高度圖改到用GPU訪問紋理后,效果還是很明顯的,速度提升了差不多一倍。




            posted @ 2009-10-13 09:02 Little Star 閱讀(2482) | 評論 (4)編輯 收藏

            2009年10月12日 #


            最近的工作:

            1.實現了刀客的動作控制。(md2文件,GPU實現幀動畫)。
            2.試驗了水面效果(有反射紋理映射,水面法線貼圖計算光照和擾動)。
            3.試驗了billboard效果。
            4.修改程序中的一些bug。

            有兩個個問題:
            1.OpenGL如何提高效率,歡迎指教!
            2.flt文件如何簡化,有好用的工具么,我的都是從3ds導過來的,面數太多了,不大適用。

            發幾張孬圖,貽笑大方!



            感覺水面反射加了樹的倒影反而變得怪怪的!!

            再來幾張前幾天截得圖


            這是我的小花園,大樹的紋理爛透了。




            小河流水 嘩啦啦……





            去掉地形的效果,感覺還蠻漂亮的,呵呵

            大家有什么看法盡量留言,您的關注就是對我最大的幫助!!

            posted @ 2009-10-12 00:48 Little Star 閱讀(4527) | 評論 (9)編輯 收藏

            2009年9月25日 #

            normal map



            Parallax Mapping



            對應的網格圖



            趁著人少發兩張等著挨磚頭的效果圖。

            看了一天的資料,證實了原來的想法是對的。
            normal map  是 bump map 的改進,主要是GPU可編程為其創造了條件。
            parallax map 是 normal map 的改進版本,相當于是對normal map 的修正,沒什么心意。

            Displacement Mapping貌似挺牛,明天仔細研究下 :-)
            posted @ 2009-09-25 03:33 Little Star 閱讀(2047) | 評論 (2)編輯 收藏

            2009年9月24日 #



            先來張調整了參數的roam網格圖片,把面片數約束在1w,這下roam的效果就明顯了吧!
            發現地勢如果不是特別平坦的話,很難找到一個滿意的參數,既能減少面片數,又能取得很好的顯示效果。
            如果有大片大片的平地,那就簡單了。



            這是使用cg語言實現的 texture blend 使用四張紋理加一個細節紋理混合而成,
            增加了像素級的 Phone 光照模型。光照的顏色有點怪怪的。

            posted @ 2009-09-24 02:09 Little Star 閱讀(1300) | 評論 (3)編輯 收藏

            2009年9月21日 #



            面片數跟幀數永遠的讓人很矛盾。這樣以后突變就很不明顯了,肉眼幾乎發現不了,不過感覺有退化成普通的lod的趨勢。
            當然,遠處的網格還是會有跳動現象,不過那已經很遠了,一般不會有人注意到發生了什么。
            可是這樣大大增加了網格數量,應用下視景體剪除效果會比這個好很多吧!

            posted @ 2009-09-21 01:00 Little Star 閱讀(1526) | 評論 (2)編輯 收藏

            2009年9月20日 #



            這個是我實現出來的 roam terrain 地形
            問題是,當我在地形上漫游時,網格必然會發生變化。不同層次的網格之間跳躍的很厲害,讓人覺得地形很不真實,
            有沒有人有好的辦法能解決?

            posted @ 2009-09-20 00:29 Little Star 閱讀(475) | 評論 (0)編輯 收藏

            2009年9月17日 #

            1.測試一下 flt文件讀取和現實效果。
            2.測試下引擎框架,尋找bug。


            這張是剛開始時沒加載上外部文件時的



            這張是爆炸效果圖


            這張是著火效果



            被導彈追擊

            最后來張夜色美景




            誠盼牛人指點不足!
            posted @ 2009-09-17 00:35 Little Star 閱讀(1868) | 評論 (7)編輯 收藏

            僅列出標題  下一頁
            国产亚州精品女人久久久久久| 亚洲国产精品一区二区三区久久| 久久国产一区二区| 久久久久久国产精品免费无码 | 99久久无码一区人妻a黑| 午夜不卡久久精品无码免费| 久久久久亚洲AV无码永不| 国产成人精品久久亚洲| 午夜精品久久久内射近拍高清| 国产aⅴ激情无码久久| 久久久久亚洲AV无码麻豆| 久久伊人影视| 久久精品无码一区二区三区| 亚洲v国产v天堂a无码久久| 久久亚洲AV成人出白浆无码国产| 久久精品国产亚洲7777| 久久人妻AV中文字幕| 88久久精品无码一区二区毛片 | 亚洲人成网站999久久久综合| 亚洲狠狠婷婷综合久久蜜芽| 7国产欧美日韩综合天堂中文久久久久 | 久久精品国产亚洲AV香蕉| 伊人久久大香线焦综合四虎| 97久久国产综合精品女不卡| 精品国产热久久久福利| 久久精品成人免费网站| 久久久久亚洲AV片无码下载蜜桃 | 国产日韩欧美久久| 久久er99热精品一区二区| 狠狠色婷婷久久一区二区| 久久se这里只有精品| 国产99久久久国产精品~~牛| 国产精品久久久亚洲| 欧美午夜精品久久久久免费视| 欧美久久一区二区三区| 久久精品国产亚洲精品| 久久91精品国产91久久户| 麻豆精品久久久一区二区| 94久久国产乱子伦精品免费 | 亚洲国产精品18久久久久久| 久久九九久精品国产|