3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

技术文章

發布時間:2024/3/12 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 技术文章 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

CSocket 編程之阻塞和非阻塞模式

我通過幾個采用 CSocket 類編寫并基于 Client/Server (客戶端 / 服務端)的網絡聊天和傳輸文件的程序 ( 詳見: 源代碼參考 ) ,在調試這些程序的過程中,追蹤深入至 CSocket 類核心源碼 Sockcore.cpp , 對于CSocket 類的運行機制可謂是一覽無遺,并且對于阻塞和非阻塞方式下的 socket 程序的編寫也是稍有體會。

閱讀本文請先注意:

  這里的阻塞和非阻塞的概念僅適用于 Server 端 socket 程序。socket 意為套接字,它與 Socket 不同,請注意首字母的大小寫。
  客戶端與服務端的通信簡單來講:服務端 socket 負責監聽,應答,接收和發送消息,而客戶端 socket 只是連接,應答,接收,發送消息。此外,如果你對于采用 CSocket 類編寫 Client/Server 網絡程序的原理不是很了解,請先查詢一下( 詳見:參考書籍和在線幫助 )。
在此之前,有必要先講述一下: 網絡傳輸服務提供者, ws2_32.dll , socket 事件 和 socket window 。

1、網絡傳輸服務提供者(網絡傳輸服務進程), Socket 事件, Socket Window

  網絡傳輸服務提供者 ( transport service provider )是以 DLL 的形式存在的,在 windows 操作系統啟動時由服務進程 svchost.exe 加載。當 socket 被創建時,調用 API 函數 Socket (在 ws2_32.dll 中), Socket 函數會傳遞三個參數 : 地址族,套接字類型 ( 注 2 ) 和協議,這三個參數決定了是由哪一個類型的 網絡傳輸服務提供者 來啟動網絡傳輸服務功能。所有的網絡通信正是由網絡傳輸服務提供者完成 , 這里將 網絡傳輸服務提供者 稱為 網絡傳輸服務進程 更有助于理解,因為前文已提到 網絡傳輸服務提供者 是由 svchost.exe 服務進程所加載的。
  下圖描述了網絡應用程序、 CSocket ( WSock32.dll )、 Socket API(ws2_32.dll) 和 網絡傳輸服務進程 之間的接口層次關系:



當 Client 端 socket 與 Server 端 socket 相互通信時,兩端均會觸發 socket 事件。這里僅簡要說明兩個 socket 事件:
  • FD_CONNECT: 連接事件 , 通常 Client 端 socket 調用 socket API 函數 Connect 時所觸發,這個事件發生在 Client 端。
  • FD_ACCEPT :正在引入的連接事件,通常 Server 端 socket 正在接收來自 Client 端 socket 連接時觸發,這個事件發生在 Server 端。

  網絡傳輸服務進程 將 socket 事件 保存至 socket 的事件隊列中。此外, 網絡傳輸服務進程 還會向 socket window 發送消息 WM_SOCKET_NOTIFY , 通知有 socket 事件 產生,見下文對 socket window 的詳細說明。
  調用 CSocket::Create 函數后,socket 被創建。 socket 創建過程中調用 CAsyncSocket::AttachHandle(SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead) 。該函數的作用是:

  • 將 socket 實例句柄和 socket 指針添加至 當前模塊狀態 ( 注 1 )的一個映射表變量 m_pmapSocketHandle 中。
  • 在 AttachHandle 過程中,會 new 一個 CSocketWnd 實例 ( 基于 CWnd 派生 ) ,這里將這個實例稱之為 socket window ,進一步理解為它是存放所有 sockets 的消息池 ( window 消息),請仔細查看,這里 socket 后多加了一個 s ,表示創建的多個 socket 將共享一個 消息池 。
  • 當 Client 端 socket 與 Server 端相互通信時 , 此時 網絡傳輸服務進程 向 socket window 發送消息 WM_SOCKET_NOTIFY ,需要說明的是 CSocketWnd 窗口句柄保存在 當前模塊狀態 的 m_hSocketWindow 變量中。

2、阻塞模式

  阻塞模式下 Server 端與 Client 端之間的通信處于同步狀態下。在 Server 端直接實例化 CSocket 類,調用 Create 方法創建 socket ,然后調用方法 Listen 開始偵聽,最后用一個 while 循環阻塞調用 Accept 函數用于等待來自 Client 端的連接,如果這個 socket 在主線程(主程序)中運行,這將導致主線程的阻塞。因此,需要創建一個新的線程以運行 socket 服務。
調試跟蹤至 CSocket::Accept 函數源碼:

while(!Accept(...)) { // The socket is marked as nonblocking and no connections are present to be accepted. if (GetLastError() == WSAEWOULDBLOCK)
 PumpMessage(FD_ACCEPT); else return FALSE; }   它不斷調用 CAsyncSocket::Accept ( CSocket 派生自 CAsyncSocket 類)判斷 Server 端 socket 的事件隊列中是否存在正在引入的連接事件 - FD_ACCEPT (見 1 ),換句話說,就是判斷是否有來自 Client 端 socket 的連接請求。
  如果當前 Server 端 socket 的事件隊列中存在正在引入的連接事件, Accept 返回一個非 0 值。否則, Accept 返回 0,此時調用 GetLastError 將返回錯誤代碼 WSAEWOULDBLOCK ,表示隊列中無任何連接請求。注意到在循環體內有一句代碼: PumpMessage(FD_ACCEPT);

  PumpMessage 作為一個消息泵使得 socket window 中的消息能夠維持在活動狀態。實際跟蹤進入 PumpMessage 中,發現這個消息泵與 Accept 函數的調用并不相關,它只是使很少的 socket window 消息(典型的是 WM_PAINT 窗口重繪消息)處于活動狀態,而絕大部分的 socket window 消息被阻塞,被阻塞的消息中含有 WM_SOCKET_NOTIFY。
  很顯然,如果沒有來自 Client 端 socket 的連接請求, CSocket 就會不斷調用 Accept 產生循環阻塞,直到有來自 Client 端 socket 的連接請求而解除阻塞。
  阻塞解除后,表示 Server 端 socket 和 Client 端 socket 已成功連接, Server 端與 Client 端彼此相互調用 Send 和 Receive 方法開始通信。

3、非阻塞模式

  在非阻塞模式下 利用 socket 事件 的消息機制, Server 端與 Client 端之間的通信處于異步狀態下。
  通常需要從 CSocket 類派生一個新類,派生新類的目的是重載 socket 事件 的消息函數,然后在 socket 事件 的消息函數中添入合適的代碼以完成 Client 端與 Server 端之間的通信,與阻塞模式相比,非阻塞模式無需創建一個新線程。
  這里將討論當 Server 端 socket 事件 - FD_ACCEPT 被觸發后,該事件的處理函數 OnAccept 是如何進一步被觸發的。其它事件的處理函數如 OnConnect, OnReceive 等的觸發方式與此類似。
在 1 中已提到 Client/Server 端通信時, Server 端 socket 正在接收來自 Client 端 socket 連接請求,這將會觸發 FD_ACCEPT 事件,同時 Server 端的 網絡傳輸服務進程 向 Server 端的 socket window (CSocketWnd )發送事件通知消息 WM_SOCKET_NOTIFY , 通知有 FD_ACCEPT 事件產生 , CsocketWnd 在收到事件通知消息后,調用消息處理函數 OnSocketNotify:

LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam) { CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam); CSocket::ProcessAuxQueue(); return 0L ; }   消息參數 wParam 是 socket 的句柄, lParam 是 socket 事件 。這里稍作解釋一下,CSocketWnd 類是作為 CSocket 類的 友元類 ,這意味著它可以訪問 CSocket 類中的保護和私有成員函數和變量, AuxQueueAdd 和 ProcessAuxQueue 是 CSocket 類的靜態成員函數,如果你對友元不熟悉,請迅速找本有關 C++ 書看一下友元的使用方法吧!
ProcessAuxQueue 是實質處理 socket 事件的函數,在該函數中有這樣一句代碼: CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);

  其實也就是由 socket 句柄得到發送事件通知消息的 socket 指針 pSocket:從 m_pmapSocketHandle 中查找(見 1 )!
  最后, WSAGETSELECTEVENT(lParam) 會取出事件類型,在一個簡單的 switch 語句中判斷事件類型并調用事件處理函數。在這里,事件類型是 FD_ACCEPT ,當然就調用 pSocket->OnAccept !

結束語
  Server 端 socket 處于阻塞調用模式下,它必須在一個新創建的線程中工作,防止主線程被阻塞。
  當有多個 Client 端 socket 與 Server 端 socket 連接及通信時, Server 端采用阻塞模式就顯得不適合了,應該采用非阻塞模式 , 利用 socket 事件 的消息機制來接受多個 Client 端 socket 的連接請求并進行通信。
  在非阻塞模式下,利用 CSocketWnd 作為所有 sockets 的消息池,是實現 socket 事件 的消息機制的關鍵技術。文中存在用詞不妥和可能存在的技術問題,請大家原諒,也請批評指正,謝謝!

注:

  • 當前模塊狀態——用于保存當前線程和模塊狀態的一個結構,可以通過 AfxGetThreadModule() 獲得。AFX_MODULE_THREAD_STATE 在 CSocket 重新定義為 _AFX_SOCK_THREAD_STATE 。
  • socket 類型——在 TCP/IP 協議中, Client/Server 網絡程序采用 TCP 協議:即 socket 類型為 SOCK_STREAM ,它是可靠的連接方式。在這里不采用 UDP 協議:即 socket 類型為 SOCK_DGRAM ,它是不可靠的連接方式。
  • 源代碼參考:

  • http://www.codeproject.com/internet/SocketFileTransfer.asp 采用 CSocket 類編寫的基于 Client/Server 的網絡文件傳輸程序,它是基于阻塞模式的 Client/Server 端網絡程序典型示例。
  • http://www.codeguru.com/Cpp/I-N/network/messaging/article.php/c5453 采用 CSocket 類編寫的基于 Client/Server 的網絡聊天程序,它是基于非阻塞模式的 Client/Server 端網絡程序典型示例。
  • 23:28?| 添加評論| 閱讀評論 (1)| 固定鏈接| 引用通告 (0)| 寫入日志

    WinSock學習筆記

    Socket(套接字

    ◆先看定義:
    typedef unsigned int u_int;
    typedef u_int SOCKET;◆Socket相當于進行網絡通信兩端的插座,只要對方的Socket和自己的Socket有通信聯接,雙方就可以發送和接收數據了。其定義類似于文件句柄的定義。

    ◆Socket有五種不同的類型:

    1、流式套接字(stream socket)
    定義:

    #define SOCK_STREAM 1 流式套接字提供了雙向、有序的、無重復的以及無記錄邊界的數據流服務,適合處理大量數據。它是面向聯結的,必須建立數據傳輸鏈路,同時還必須對傳輸的數據進行驗證,確保數據的準確性。因此,系統開銷較大。

    2、 數據報套接字(datagram socket)

    定義:

    #define SOCK_DGRAM 2 數據報套接字也支持雙向的數據流,但不保證傳輸數據的準確性,但保留了記錄邊界。由于數據報套接字是無聯接的,例如廣播時的聯接,所以并不保證接收端是否正在偵聽。數據報套接字傳輸效率比較高。

    3、原始套接字(raw-protocol interface)

    定義:

    #define SOCK_RAW 3 原始套接字保存了數據包中的完整IP頭,前面兩種套接字只能收到用戶數據。因此可以通過原始套接字對數據進行分析。
    其它兩種套接字不常用,這里就不介紹了。

    ◆Socket開發所必須需要的文件(以WinSock V2.0為例):

    頭文件:Winsock2.h

    庫文件:WS2_32.LIB

    動態庫:W32_32.DLL

    一些重要的定義

    1、數據類型的基本定義:這個大家一看就懂。

    typedef unsigned char u_char;
    typedef unsigned short u_short;
    typedef unsigned int u_int;
    typedef unsigned long u_long;2、 網絡地址的數據結構,有一個老的和一個新的的,請大家留意,如果想知道為什么,
    請發郵件給Bill Gate。其實就是計算機的IP地址,不過一般不用用點分開的IP地
    址,當然也提供一些轉換函數。

    ◆ 舊的網絡地址結構的定義,為一個4字節的聯合:

    struct in_addr {
    union {
    struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
    struct { u_short s_w1,s_w2; } S_un_w;
    u_long S_addr;
    } S_un;
    #define s_addr S_un.S_addr /* can be used for most tcp & ip code */
    //下面幾行省略,反正沒什么用處。
    };其實完全不用這么麻煩,請看下面:

    ◆ 新的網絡地址結構的定義:
    非常簡單,就是一個無符號長整數 unsigned long。舉個例子:IP地址為127.0.0.1的網絡地址是什么呢?請看定義:

    #define INADDR_LOOPBACK 0x7f0000013、 套接字地址結構

    (1)、sockaddr結構:

    struct sockaddr {
    u_short sa_family; /* address family */
    char sa_data[14]; /* up to 14 bytes of direct address */
    };sa_family為網絡地址類型,一般為AF_INET,表示該socket在Internet域中進行通信,該地址結構隨選擇的協議的不同而變化,因此一般情況下另一個與該地址結構大小相同的sockaddr_in結構更為常用,sockaddr_in結構用來標識TCP/IP協議下的地址。換句話說,這個結構是通用socket地址結構,而下面的sockaddr_in是專門針對Internet域的socket地址結構。

    (2)、sockaddr_in結構

    struct sockaddr_in {
    short sin_family;
    u_short sin_port;
    struct in_addr sin_addr;
    char sin_zero[8];
    };sin _family為網絡地址類型,必須設定為AF_INET。sin_port為服務端口,注意不要使用已固定的服務端口,如HTTP的端口80等。如果端口設置為0,則系統會自動分配一個唯一端口。sin_addr為一個unsigned long的IP地址。sin_zero為填充字段,純粹用來保證結構的大小。

    ◆ 將常用的用點分開的IP地址轉換為unsigned long類型的IP地址的函數:

    unsigned long inet_addr(const char FAR * cp )用法:

    unsigned long addr=inet_addr("192.1.8.84")◆ 如果將sin_addr設置為INADDR_ANY,則表示所有的IP地址,也即所有的計算機。

    #define INADDR_ANY (u_long)0x000000004、 主機地址:

    先看定義:

    struct hostent {
    char FAR * h_name; /* official name of host */
    char FAR * FAR * h_aliases; /* alias list */
    short h_addrtype; /* host address type */
    short h_length; /* length of address */
    char FAR * FAR * h_addr_list; /* list of addresses */
    #define h_addr h_addr_list[0] /* address, for backward compat */
    };
    h_name為主機名字。
    h_aliases為主機別名列表。
    h_addrtype為地址類型。
    h_length為地址類型。
    h_addr_list為IP地址,如果該主機有多個網卡,就包括地址的列表。另外還有幾個類似的結構,這里就不一一介紹了。

    5、 常見TCP/IP協議的定義:

    #define IPPROTO_IP 0
    #define IPPROTO_ICMP 1
    #define IPPROTO_IGMP 2
    #define IPPROTO_TCP 6
    #define IPPROTO_UDP 17
    #define IPPROTO_RAW 255 具體是什么協議,大家一看就知道了。

    套接字的屬性

    為了靈活使用套接字,我們可以對它的屬性進行設定。

    1、 屬性內容:

    //允許調試輸出
    #define SO_DEBUG 0x0001 /* turn on debugging info recording */
    //是否監聽模式
    #define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
    //套接字與其他套接字的地址綁定
    #define SO_REUSEADDR 0x0004 /* allow local address reuse */
    //保持連接
    #define SO_KEEPALIVE 0x0008 /* keep connections alive */
    //不要路由出去
    #define SO_DONTROUTE 0x0010 /* just use interface addresses */
    //設置為廣播
    #define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */
    //使用環回不通過硬件
    #define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
    //當前拖延值
    #define SO_LINGER 0x0080 /* linger on close if data present */
    //是否加入帶外數據
    #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */
    //禁用LINGER選項
    #define SO_DONTLINGER (int)(~SO_LINGER)
    //發送緩沖區長度
    #define SO_SNDBUF 0x1001 /* send buffer size */
    //接收緩沖區長度
    #define SO_RCVBUF 0x1002 /* receive buffer size */
    //發送超時時間
    #define SO_SNDTIMEO 0x1005 /* send timeout */
    //接收超時時間
    #define SO_RCVTIMEO 0x1006 /* receive timeout */
    //錯誤狀態
    #define SO_ERROR 0x1007 /* get error status and clear */
    //套接字類型
    #define SO_TYPE 0x1008 /* get socket type */2、 讀取socket屬性:

    int getsockopt(SOCKET s, int level, int optname, char FAR * optval, int FAR * optlen)s為欲讀取屬性的套接字。level為套接字選項的級別,大多數是特定協議和套接字專有的。如IP協議應為 IPPROTO_IP。

    optname為讀取選項的名稱
    optval為存放選項值的緩沖區指針。
    optlen為緩沖區的長度用法:

    int ttl=0; //讀取TTL值
    int rc = getsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl));
    //來自MS platform SDK 20033、 設置socket屬性:

    int setsockopt(SOCKET s,int level, int optname,const char FAR * optval, int optlen)s為欲設置屬性的套接字
    level為套接字選項的級別,用法同上。
    optname為設置選項的名稱
    optval為存放選項值的緩沖區指針。
    optlen為緩沖區的長度

    用法:

    int ttl=32; //設置TTL值
    int rc = setsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); 套接字的使用步驟

    1、啟動Winsock:對Winsock DLL進行初始化,協商Winsock的版本支持并分配必要的
    資源。(服務器端和客戶端)

    int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData )

    wVersionRequested為打算加載Winsock的版本,一般如下設置:
    wVersionRequested=MAKEWORD(2,0)
    或者直接賦值:wVersionRequested=2

    LPWSADATA為初始化Socket后加載的版本的信息,定義如下:
    typedef struct WSAData {
    WORD wVersion;
    WORD wHighVersion;
    char szDescription[WSADESCRIPTION_LEN+1];
    char szSystemStatus[WSASYS_STATUS_LEN+1];
    unsigned short iMaxSockets;
    unsigned short iMaxUdpDg;
    char FAR * lpVendorInfo;
    } WSADATA, FAR * LPWSADATA;如果加載成功后數據為:

    wVersion=2表示加載版本為2.0。
    wHighVersion=514表示當前系統支持socket最高版本為2.2。
    szDescription="WinSock 2.0"
    szSystemStatus="Running"表示正在運行。
    iMaxSockets=0表示同時打開的socket最大數,為0表示沒有限制。
    iMaxUdpDg=0表示同時打開的數據報最大數,為0表示沒有限制。
    lpVendorInfo沒有使用,為廠商指定信息預留。該函數使用方法:

    WORD wVersion=MAKEWORD(2,0);
    WSADATA wsData;
    int nResult= WSAStartup(wVersion,&wsData);
    if(nResult !=0)
    {
    //錯誤處理
    }2、創建套接字:(服務器端和客戶端)

    SOCKET socket( int af, int type, int protocol );
    af為網絡地址類型,一般為AF_INET,表示在Internet域中使用。
    type為套接字類型,前面已經介紹了。
    protocol為指定網絡協議,一般為IPPROTO_IP。用法:

    SOCKET sock=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
    if(sock==INVALID_SOCKET)
    {
    //錯誤處理
    }3、套接字的綁定:將本地地址綁定到所創建的套接字上。(服務器端和客戶端)

    int bind( SOCKET s, const struct sockaddr FAR * name, int namelen )
    s為已經創建的套接字
    name為socket地址結構,為sockaddr結構,如前面討論的,我們一般使用sockaddr_in
    結構,在使用再強制轉換為sockaddr結構。
    namelen為地址結構的長度。
    用法:

    sockaddr_in addr;
    addr. sin_family=AF_INET;
    addr. sin_port= htons(0); //保證字節順序
    addr. sin_addr.s_addr= inet_addr("192.1.8.84")
    int nResult=bind(s,(sockaddr*)&addr,sizeof(sockaddr));
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }4、 套接字的監聽:(服務器端)

    int listen(SOCKET s, int backlog )s為一個已綁定但未聯接的套接字
    backlog為指定正在等待聯接的最大隊列長度,這個參數非常重要,因為服務器一般可
    以提供多個連接
    用法:

    int nResult=listen(s,5) //最多5個連接
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }5、套接字等待連接::(服務器端)

    SOCKET accept( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen )s為處于監聽模式的套接字
    sockaddr為接收成功后返回客戶端的網絡地址。
    addrlen為網絡地址的長度。

    用法:

    sockaddr_in addr;
    SOCKET s_d=accept(s,(sockaddr*)&addr,sizeof(sockaddr));
    if(s==INVALID_SOCKET)
    {
    //錯誤處理
    }6、套接字的連結:將兩個套接字連結起來準備通信。(客戶端)

    int connect(SOCKET s, const struct sockaddr FAR * name, int namelen )s為欲連結的已創建的套接字
    name為欲連結的socket地址。
    namelen為socket地址的結構的長度。

    用法:

    sockaddr_in addr;
    addr. sin_family=AF_INET;
    addr. sin_port=htons(0); //保證字節順序
    addr. sin_addr.s_addr= htonl(INADDR_ANY) //保證字節順序
    int nResult=connect(s,(sockaddr*)&addr,sizeof(sockaddr));
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }7、套接字發送數據:(服務器端和客戶端)

    int send(SOCKET s, const char FAR * buf, int len, int flags )s為服務器端監聽的套接字
    buf為欲發送數據緩沖區的指針。
    len為發送數據緩沖區的長度。
    flags為數據發送標記。
    返回值為發送數據的字符數。

    ◆這里講一下這個發送標記,下面8中討論的接收標記也一樣:

    flag取值必須為0或者如下定義的組合:0表示沒有特殊行為。

    #define MSG_OOB 0x1 /* process out-of-band data */
    #define MSG_PEEK 0x2 /* peek at incoming message */
    #define MSG_DONTROUTE 0x4 /* send without using routing tables */
    MSG_OOB表示數據應該帶外發送,所謂帶外數據就是TCP緊急數據。
    MSG_PEEK表示使有用的數據復制到緩沖區內,但并不從系統緩沖區內刪除。
    MSG_DONTROUTE表示不要將包路由出去。

    用法:

    char buf[]="xiaojin";
    int nResult=send(s,buf,strlen(buf));
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }8、 套接字的數據接收:(客戶端)

    int recv( SOCKET s, char FAR * buf, int len, int flags )s為準備接收數據的套接字
    buf為準備接收數據的緩沖區。
    len為準備接收數據緩沖區的大小。
    flags為數據接收標記。
    返回值為接收的數據的字符數。

    用法:

    char mess[1000];
    int nResult =recv(s,mess,1000,0);
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }9、中斷套接字連接:通知服務器端或客戶端停止接收和發送數據。(服務器端和客戶端)

    int shutdown(SOCKET s, int how)s為欲中斷連接套接字
    How為描述禁止哪些操作,取值為:SD_RECEIVE、SD_SEND、SD_BOTH。

    #define SD_RECEIVE 0x00
    #define SD_SEND 0x01
    #define SD_BOTH 0x02用法:

    int nResult= shutdown(s,SD_BOTH);
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }10、 關閉套接字:釋放所占有的資源。(服務器端和客戶端)

    int closesocket( SOCKET s )s為欲關閉的套接字

    用法:

    int nResult=closesocket(s);
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }


    與socket有關的一些函數介紹

    1、讀取當前錯誤值:每次發生錯誤時,如果要對具體問題進行處理,那么就應該調用這個函數取得錯誤代碼。
    int WSAGetLastError(void );
    #define h_errno WSAGetLastError()
    錯誤值請自己閱讀Winsock2.h。

    2、將主機的unsigned long值轉換為網絡字節順序(32位):為什么要這樣做呢?因為不同的計算機使用不同的字節順序存儲數據。因此任何從Winsock函數對IP地址和端口號的引用和傳給Winsock函數的IP地址和端口號均時按照網絡順序組織的。

    u_long htonl(u_long hostlong);
    舉例:htonl(0)=0
    htonl(80)= 1342177280
    3、將unsigned long數從網絡字節順序轉換位主機字節順序,是上面函數的逆函數。
    u_long ntohl(u_long netlong);
    舉例:ntohl(0)=0
    ntohl(1342177280)= 80
    4、將主機的unsigned short值轉換為網絡字節順序(16位):原因同2:
    u_short htons(u_short hostshort);
    舉例:htonl(0)=0
    htonl(80)= 20480
    5、將unsigned short數從網絡字節順序轉換位主機字節順序,是上面函數的逆函數。
    u_short ntohs(u_short netshort);
    舉例:ntohs(0)=0
    ntohsl(20480)= 80
    6、將用點分割的IP地址轉換位一個in_addr結構的地址,這個結構的定義見筆記(一),實際上就是一個unsigned long值。計算機內部處理IP地址可是不認識如192.1.8.84之類的數據。
    unsigned long inet_addr( const char FAR * cp );
    舉例:inet_addr("192.1.8.84")=1409810880
    inet_addr("127.0.0.1")= 16777343
    如果發生錯誤,函數返回INADDR_NONE值。

    7、將網絡地址轉換位用點分割的IP地址,是上面函數的逆函數。
    char FAR * inet_ntoa( struct in_addr in );
    舉例:char * ipaddr=NULL;
    char addr[20];
    in_addr inaddr;
    inaddr. s_addr=16777343;
    ipaddr= inet_ntoa(inaddr);
    strcpy(addr,ipaddr); 這樣addr的值就變為127.0.0.1。
    注意意不要修改返回值或者進行釋放動作。如果函數失敗就會返回NULL值。

    8、獲取套接字的本地地址結構:
    int getsockname(SOCKET s, struct sockaddr FAR * name, int FAR * namelen );
    s為套接字
    name為函數調用后獲得的地址值
    namelen為緩沖區的大小。
    9、獲取與套接字相連的端地址結構:

    int getpeername(SOCKET s, struct sockaddr FAR * name, int FAR * namelen );
    s為套接字
    name為函數調用后獲得的端地址值
    namelen為緩沖區的大小。
    10、獲取計算機名:

    int gethostname( char FAR * name, int namelen );
    name是存放計算機名的緩沖區
    namelen是緩沖區的大小
    用法:
    char szName[255];
    memset(szName,0,255);
    if(gethostname(szName,255)==SOCKET_ERROR)
    {
    //錯誤處理
    }
    返回值為:szNmae="xiaojin"
    11、根據計算機名獲取主機地址:
    struct hostent FAR * gethostbyname( const char FAR * name );

    name為計算機名。
    用法:
    hostent * host;
    char* ip;
    host= gethostbyname("xiaojin");
    if(host->h_addr_list[0])
    {
    struct in_addr addr;
    memmove(&addr, host->h_addr_list[0],4);
    //獲得標準IP地址
    ip=inet_ ntoa (addr);
    }

    返回值為:hostent->h_name="xiaojin"
    hostent->h_addrtype=2 //AF_INET
    hostent->length=4
    ip="127.0.0.1"
    Winsock 的I/O操作:

    1、 兩種I/O模式
    阻塞模式:執行I/O操作完成前會一直進行等待,不會將控制權交給程序。套接字 默認為阻塞模式。可以通過多線程技術進行處理。
    非阻塞模式:執行I/O操作時,Winsock函數會返回并交出控制權。這種模式使用 起來比較復雜,因為函數在沒有運行完成就進行返回,會不斷地返回 WSAEWOULDBLOCK錯誤。但功能強大。
    為了解決這個問題,提出了進行I/O操作的一些I/O模型,下面介紹最常見的三種:

    2、select模型:

      通過調用select函數可以確定一個或多個套接字的狀態,判斷套接字上是否有數據,或
    者能否向一個套接字寫入數據。
    int select( int nfds, fd_set FAR * readfds, fd_set FAR * writefds,
    fd_set FAR *exceptfds, const struct timeval FAR * timeout );
    ◆先來看看涉及到的結構的定義:
    a、 d_set結構:

    #define FD_SETSIZE 64?
    typedef struct fd_set {
    u_int fd_count; /* how many are SET? */
    SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
    } fd_set; fd_count為已設定socket的數量
    fd_array為socket列表,FD_SETSIZE為最大socket數量,建議不小于64。這是微軟建
    議的。

    B、timeval結構:
    struct timeval {
    long tv_sec; /* seconds */
    long tv_usec; /* and microseconds */
    };
    tv_sec為時間的秒值。
    tv_usec為時間的毫秒值。
    這個結構主要是設置select()函數的等待值,如果將該結構設置為(0,0),則select()函數
    會立即返回。

    ◆再來看看select函數各參數的作用:
    nfds:沒有任何用處,主要用來進行系統兼容用,一般設置為0。

    readfds:等待可讀性檢查的套接字組。

    writefds;等待可寫性檢查的套接字組。

    exceptfds:等待錯誤檢查的套接字組。

    timeout:超時時間。

    函數失敗的返回值:調用失敗返回SOCKET_ERROR,超時返回0。
    readfds、writefds、exceptfds三個變量至少有一個不為空,同時這個不為空的套接字
    種至少有一個socket,道理很簡單,否則要select干什么呢。 舉例:測試一個套接字是否可讀:
    fd_set fdread;
    //FD_ZERO定義
    // #define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
    FD_ZERO(&fdread);
    FD_SET(s,&fdread); //加入套接字,詳細定義請看winsock2.h
    if(select(0,%fdread,NULL,NULL,NULL)>0
    {
    //成功
    if(FD_ISSET(s,&fread) //是否存在fread中,詳細定義請看winsock2.h
    {
    //是可讀的
    }
    }◆I/O操作函數:主要用于獲取與套接字相關的操作參數。

    int ioctlsocket(SOCKET s, long cmd, u_long FAR * argp ); s為I/O操作的套接字
    cmd為對套接字的操作命令。
    argp為命令所帶參數的指針。

    常見的命令:
    //確定套接字自動讀入的數據量
    #define FIONREAD _IOR(''''f'''', 127, u_long) /* get # bytes to read */
    //允許或禁止套接字的非阻塞模式,允許為非0,禁止為0
    #define FIONBIO _IOW(''''f'''', 126, u_long) /* set/clear non-blocking i/o */
    //確定是否所有帶外數據都已被讀入
    #define SIOCATMARK _IOR(''''s'''', 7, u_long) /* at oob mark? */
    3、WSAAsynSelect模型:
    WSAAsynSelect模型也是一個常用的異步I/O模型。應用程序可以在一個套接字上接收以
    WINDOWS消息為基礎的網絡事件通知。該模型的實現方法是通過調用WSAAsynSelect函
    數 自動將套接字設置為非阻塞模式,并向WINDOWS注冊一個或多個網絡時間,并提供一
    個通知時使用的窗口句柄。當注冊的事件發生時,對應的窗口將收到一個基于消息的通知。

    int WSAAsyncSelect( SOCKET s, HWND hWnd, u_int wMsg, long lEvent); s為需要事件通知的套接字
    hWnd為接收消息的窗口句柄
    wMsg為要接收的消息
    lEvent為掩碼,指定應用程序感興趣的網絡事件組合,主要如下:
    #define FD_READ_BIT 0
    #define FD_READ (1 << FD_READ_BIT)
    #define FD_WRITE_BIT 1
    #define FD_WRITE (1 << FD_WRITE_BIT)
    #define FD_OOB_BIT 2
    #define FD_OOB (1 << FD_OOB_BIT)
    #define FD_ACCEPT_BIT 3
    #define FD_ACCEPT (1 << FD_ACCEPT_BIT)
    #define FD_CONNECT_BIT 4
    #define FD_CONNECT (1 << FD_CONNECT_BIT)
    #define FD_CLOSE_BIT 5
    #define FD_CLOSE (1 << FD_CLOSE_BIT)
    用法:要接收讀寫通知:
    int nResult= WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);
    if(nResult==SOCKET_ERROR)
    {
    //錯誤處理
    }
    取消通知:

    int nResult= WSAAsyncSelect(s,hWnd,0,0);
    當應用程序窗口hWnd收到消息時,wMsg.wParam參數標識了套接字,lParam的低字標明
    了網絡事件,高字則包含錯誤代碼。

    4、WSAEventSelect模型
    WSAEventSelect模型類似WSAAsynSelect模型,但最主要的區別是網絡事件發生時會被發
    送到一個事件對象句柄,而不是發送到一個窗口。

    使用步驟如下:
    a、 創建事件對象來接收網絡事件:

    #define WSAEVENT HANDLE
    #define LPWSAEVENT LPHANDLE
    WSAEVENT WSACreateEvent( void );
    該函數的返回值為一個事件對象句柄,它具有兩種工作狀態:已傳信(signaled)和未傳信
    (nonsignaled)以及兩種工作模式:人工重設(manual reset)和自動重設(auto reset)。默認未
    未傳信的工作狀態和人工重設模式。

    b、將事件對象與套接字關聯,同時注冊事件,使事件對象的工作狀態從未傳信轉變未
    已傳信。

    int WSAEventSelect( SOCKET s,WSAEVENT hEventObject,long lNetworkEvents ); s為套接字
    hEventObject為剛才創建的事件對象句柄
    lNetworkEvents為掩碼,定義如上面所述

    c、I/O處理后,設置事件對象為未傳信
    BOOL WSAResetEvent( WSAEVENT hEvent );Hevent為事件對象

    成功返回TRUE,失敗返回FALSE。

    d、等待網絡事件來觸發事件句柄的工作狀態:

    DWORD WSAWaitForMultipleEvents( DWORD cEvents,
    const WSAEVENT FAR * lphEvents, BOOL fWaitAll,
    DWORD dwTimeout, BOOL fAlertable );lpEvent為事件句柄數組的指針
    cEvent為為事件句柄的數目,其最大值為WSA_MAXIMUM_WAIT_EVENTS
    fWaitAll指定等待類型:TRUE:當lphEvent數組重所有事件對象同時有信號時返回;
    FALSE:任一事件有信號就返回。
    dwTimeout為等待超時(毫秒)
    fAlertable為指定函數返回時是否執行完成例程

    對事件數組中的事件進行引用時,應該用WSAWaitForMultipleEvents的返回值,減去
    預聲明值WSA_WAIT_EVENT_0,得到具體的引用值。例如:

    nIndex=WSAWaitForMultipleEvents(…);
    MyEvent=EventArray[Index- WSA_WAIT_EVENT_0];e、判斷網絡事件類型:

    int WSAEnumNetworkEvents( SOCKET s,
    WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents );s為套接字
    hEventObject為需要重設的事件對象
    lpNetworkEvents為記錄網絡事件和錯誤代碼,其結構定義如下:

    typedef struct _WSANETWORKEVENTS {
    long lNetworkEvents;
    int iErrorCode[FD_MAX_EVENTS];
    } WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;f、關閉事件對象句柄:

    BOOL WSACloseEvent(WSAEVENT hEvent);調用成功返回TRUE,否則返回FALSE。
    23:19?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    mfc 添加自定義消息

    Windows?應用程序所要做的每項工作幾乎都是基于消息處理的,?Windows?系統消息分為常用?Windows?消息,控件通知消息和命令。然而,有時我們需要定義自己的消息來通知程序什么事情發生了,這就是用戶自定義消息。?ClassWizard?并沒有提供增加用戶自定義消息的功能,所以要使用用戶自定義消息,必須手工編寫代碼。然后?ClassWizard?才可以象處理其它消息一樣處理你自定義的消息。具體做法如下詳解:?

      ?第一步:定義消息。一個消息實際上是開發?Windows95?應用程序時,?Microsoft?推薦用戶自定義消息至少是?WM_USER+100?,因為很多新控件也要使用?WM_USER?消息。?

      ?第二步:實現消息處理函數。該函數使用?WPRAM?和?LPARAM?參數并返回?LPESULT?。?

      ?LPESULT?CMainFrame::OnMyMessage(WPARAM?wParam,?LPARAM?lParam){//?TODO:?處理用戶自定義消息?AfxMessageBox("?處理用戶自定義消息?");?return?0;}?

      ?第三步:在類頭文件的?AFX_MSG?塊中說明消息處理函數:?
      ?class?CMainFrame:public?CMDIFrameWnd{

      ?...

      ?//?一般消息映射函數?

      ?protected:

      ?//?{{AFX_MSG(CMainFrame)

      ?afx_msg?int?OnCreate(LPCREATESTRUCT?lpCreateStruct);

      ?afx_msg?void?OnTimer(UINT?nIDEvent);

      ?afx_msg?LRESULT?OnMyMessage(WPARAM?wParam,?LPARAM?lParam);

      ?//}}AFX_MSG

      ?DECLARE_MESSAGE_MAP()}


      ?第四步:在用戶類的消息塊中,使用?ON_MESSAGE?宏指令將消息映射到消息處理函數中。?
      ?BEGIN_MESSAGE_MAP(CMainFrame,?CMDIFrameWnd)

      ?//{{AFX_MSG_MAP(CMainFrame)

      ?ON_WM_CREATE()

      ?ON_WM_TIMER()

      ?ON_MESSAGE(WM_MY_MESSAGE,?OnMyMessage)

      ?//}}AFX_MSG_MAPEND_MESSAGE_MAP()


      ?這樣,一個用戶自定義消息就可以使用了,如果用戶需要一個整個系統唯一的消息,可以調用?SDK?函數?RegisterWindowMessage?并使用?ON_REGISTER_MESSAGE?宏指令取代?ON_MESSAGE?宏指令,其余步驟同上。?

      ?VC++?為程序員提供了一套功能強大、方便快捷的編程工具,它可以幫你方便的生成窗口、菜單等用戶界面,可惜就是做出來的東西都一樣,沒有一點個性。下面,就介紹一些方法,讓我們可以按照自己的設計定制出更加符合自己程序風格的窗口。 如果用戶需要一個定義整個系統唯一的消息,可以調用SDK函數RegisterWindowMessage定義消息:

    static UINT WM_MY_MESSAGE=RegisterWindowMessage("User");

      并使用ON_REGISTERED_MESSAGE宏指令取代ON_MESSAGE宏指令,其余步驟同上。

      當需要使用自定義消息時,可以在相應類中的函數中調用函數PostMessage或SendMessage發送消息PoseMessage(WM_MY_MESSAGE,O,O); 如果向其他進程發送消息可通過如下方法發送消息:

    DWORD result;
    SendMessageTimeout(wnd->m_hWnd, // 目標窗口
    WM_MY_MESSAGE, // 消息
    0, // WPARAM
    0, // LPARAM
    SMTO_ABORTIFHUNG |
    SMTO_NORMAL,
    TIMEOUT_INTERVAL,
    &result);

      以避免其它進程如果被阻塞而造成系統死等狀態。

      可是如果需要向其它類(如主框架、子窗口、視類、對話框、狀態條、工具條或其他控件等)發送消息時,上述方法顯得無能為力,而在編程過程中往往需要獲取其它類中的某個識別信號,MFC框架給我們造成了種種限制,但是可以通過獲取某個類的指針而向這個類發送消息,而自定義消息的各種動作則在這個類中定義,這樣就可以自由自在的向其它類發送消息了。

      下面舉的例子敘述了向視類和框架類發送消息的方法:

      在主框架類中向視類發送消息:

      視類中定義消息:

    ON_REGISTERED_MESSAGE(WM_MY_MESSAGE,OnMyMessage) //定義消息映射
    視類定義消息處理函數:

    // 消息處理函數
    LRESULT CMessageView::OnMyMessage(WPARAM wParam, LPARAM lParam)
    {
    // TODO: 處理用戶自定義消息
    ...
    return 0;
    }

    //發送消息的測試函數
    void CMainFrame::OnTest()
    {
    CView * active = GetActiveView();//獲取當前視類指針
    if(active != NULL)
    active->PostMessage(WM_MY_MESSAGE,0,0);
    }

      在其它類中向視類發送消息:

    //發送消息的測試函數
    void CMainFrame::OnTest()
    {
    CMDIFrameWnd *pFrame;
    CMDIChildWnd *pChild;
    CView *pView;
    //獲取主窗口指針
    pFrame =(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
    // 獲取子窗口指針
    pChild = (CMDIChildWnd *) pFrame->GetActiveFrame();
    //獲取視類指針
    pView = pChild->GetActiveView();
    if(pView != NULL)
    pView->PostMessage(WM_MY_MESSAGE,0,0);//發送消息
    }

    ?

     在視類中向主框架發送消息:

      首先在主框架中定義相關的消息,方法同上,然后在發送消息的函數中添加代碼如下

    //發送消息的測試函數
    void CMessageView::OnTest()
    {
    CFrameWnd * active = GetActiveFrame();//獲取當前主窗口框架指針
    if(active != this)
    active->PostMessage(WM_MY_MESSAGE,0,0);
    return 0;
    }

      在其它類中向不同的類發送消息可依次方法類推,這樣我們的程序就可以的不受限制向其它類和進程發送消息,而避免了種種意想不到的風險。

      下面一個例子程序為多文檔程序里在一對話框中向視類發送消息,詳述了發送自定義消息的具體過程。

     實現步驟:

      第一步:在VC++中新建工程Message,所有ClassWizard步驟選項均為缺省,完成。

      第二步:在主菜單中添加測試菜單為調出對話框,在框架類中建立相應函數OnTest()

      第三步:在資源中建立對話框,通過ClassWizard添加新類TestDialog,添加測試按鈕,

      在對話框類中建立相應函數OnDialogTest()

    //通過對話框按鈕發送消息的函數
    void TestDialog::OnDialogTest()
    {
    CMDIFrameWnd *pFrame;
    CMDIChildWnd *pChild;
    CView *pView;
    //獲取主窗口指針
    pFrame =(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
    // 獲取子窗口指針
    pChild = (CMDIChildWnd *) pFrame->GetActiveFrame();
    //獲取視類指針
    pView = pChild->GetActiveView();
    if(active != NULL)
    active->PostMessage(WM_MY_MESSAGE,0,0);//發送消息
    }

      在Message.h頭文件中添加如下語句:

    static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");

      第四步:在視類中添加自定義消息:

      在頭文件MessageView.h中添加消息映射

    protected:
    //{{AFX_MSG(CMessageView)
    //}}AFX_MSG
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); //此行為添加代碼
    DECLARE_MESSAGE_MAP()
    在視類文件MessageView.cpp中的消息映射中添加自定義消息映射
    BEGIN_MESSAGE_MAP(CMessageView, CView)
    //{{AFX_MSG_MAP(CMessageView)
    //}}AFX_MSG_MAP
    // Standard printing commands
    ON_REGISTERED_MESSAGE(WM_MY_MESSAGE,OnMyMessage) //此行添加代碼定義唯一消息
    END_MESSAGE_MAP()

      添加相應的0消息處理函數

    LRESULT CMessageView::OnMyMessage(WPARAM wParam, LPARAM lParam)
    {
    CRect rect;
    GetClientRect(&rect);
    InvalidateRect(&rect);
    test=!test;
    return 0;
    }

      在MessageView.h中添加布爾變量 public:BOOL test;

      在視類構造函數中初始化 test變量:test=FALSE;

      修改CMessageView::OnDraw()函數

    void CMessageView::OnDraw(CDC* pDC)
    {
    CMessageDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // 以下程序顯示消息響應效果
    if(test)
    pDC->TextOut(0,0,"消息響應!");
    }

      第五步:顯示測試對話框

      在MainFrame類中包含對話框頭文件:

    #include "TestDialog.h";
    OnTest()函數中添加代碼
    void CMainFrame::OnTest()
    {
    TestDialog dialog;
    dialog.DoModal();
    }

      運行程序,在測試菜單打開對話框,點擊測試按鈕即可看到結果。

    23:11?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    vc 實現毫秒定時器

    定時器使用方法

      定時器在VC中的使用頻繁,以下討論定義器的使用方法。

      定時器的原型是:

      WINUSERAPI UINT WINAPI SetTimer ( HWND hWnd , UINT nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc);

    ??? hWnd????? 是欲設置定時器的窗體句柄。定時時間到時,系統會向該窗體發送WM_TIMER消息。

    ??? nIDEvent????? 定時器標識符。在一個窗體內可以使用多個定時器,不同的定時器根據nIDEvent來區分。

    ??? uElapse???????? 定時時間,單位是毫秒。

    ??? lpTimerFunc 定時器的回調函數。如果該值為NULL,定時時間到時,定時器發送的消息WM_TIMER由窗體映像該消息的函數處理;否則由回調函數處理,說白一點,這里的回調函數就是取代OnTimer的處理函數。

    ???????? 通常,我們在使用定時器時,只用到三個參數,即
    ??? UINT CWnd::SetTimer(?
    ????????????UINT nIDEvent,?
    ?????UINT nElapse,?
    ??????void (CALLBACK EXPORT* lpfnTimer)(?HWND, UINT, UINT, DWORD)?
    ??? );

    ??? 其實,這個函數只是MFCAPI的封裝,其實現函數為:

    ??? _AFXWIN_INLINE UINT CWnd::SetTimer(UINT nIDEvent, UINT nElapse,

    ??????? void (CALLBACK* lpfnTimer)(HWND, UINT, UINT, DWORD))

    ??{
    ???????????? ASSERT(::IsWindow(m_hWnd));
    ??? ???? return ::SetTimer(m_hWnd, nIDEvent, nElapse,(TIMERPROC)lpfnTimer);?
    ??? }

    ??? 由此可見,CWnd::SetTimer只是將API函數SetTimer的第一個參數設置成它自己的句柄而已。


    ???
    有了上面的認識,對定時器的使用就清楚了,下面舉例說明定時器的具體使用。

    ??? 1.打開VC,新建一基于對話框的工程,工程名為Test在對話框上添加一按鈕,將其ID改為IDC_BUTTON_STARTCaption改為Start. 映像該按鈕的BN_CLICKED消息,void CTestDlg::OnButtonStart();

    ??? 2.再在對話框上添加一按鈕,IDID_BUTTON_STOP,Caption改為Stop,映像消息為void CTestDlg::OnButtonStop();

    ??? 3.添加一個Lable,ID改為IDC_STATIC_TIME,用于記數,表明定時器函數的執行。

      4.映像對話框的WM_TIMER消息,void CTestDlg::OnTimer(UINT nIDEvent);


    ???
    以上的實現函數如下所示:

    void CTestDlg::OnButtonStart()

    {

    ??? SetTimer(1,1000,NULL);//啟動定時器1,定時時間是1

    }

    ?

    void CTestDlg::OnButtonStop()

    {

    ??? KillTimer(1);??????? //關閉定時器1

    }

    ?

    void CTestDlg::OnTimer(UINT nIDEvent)

    {

    ??? static int nTimer=0;

    ??? CString strTmp="";

    ??? strTmp.Format("Timer:??? %d",nTimer++);

    ??? CWnd *pWnd=GetDlgItem(IDC_STATIC_TIME);

    ??? pWnd->SetWindowText(strTmp);? //Lable<SPAN lang=ZH-CN style="FONT-SIZE: 10pt; FONT-FAMILY: SimSun; mso-ascii-font-family: 細明體; mso-hansi-font-family: 細明體; mso-fareast-language: ZH-CN

    23:08?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志 2006/4/15

    opengl----使用3dmax建模后怎樣把模型導入

    //? importmodel.h/ #include <math.h>
    #include <vector>
    #include <windows.h>??// Header File For Windows
    #include <stdio.h>???// Header File For Standard Input/Output
    #include <gl/gl.h>???// Header File For The OpenGL32 Library
    #include <gl/glu.h>???// Header File For The GLu32 Library
    #include <gl/glaux.h>??// Header File For The Glaux Library
    #include <math.h>
    //? 基本塊(Primary Chunk),位于文件的開始
    #define PRIMARY?????? 0x4D4D
    //? 主塊(Main Chunks)
    #define OBJECTINFO??? 0x3D3D??// 網格對象的版本號
    #define VERSION?????? 0x0002??// .3ds文件的版本
    #define EDITKEYFRAME? 0xB000??// 所有關鍵幀信息的頭部
    //? 對象的次級定義(包括對象的材質和對象)
    #define MATERIAL?? 0xAFFF??// 保存紋理信息
    #define OBJECT??? 0x4000??// 保存對象的面、頂點等信息
    //? 材質的次級定義
    #define MATNAME?????? 0xA000??// 保存材質名稱
    #define MATDIFFUSE??? 0xA020??// 對象/材質的顏色
    #define MATMAP??????? 0xA200??// 新材質的頭部
    #define MATMAPFILE??? 0xA300??// 保存紋理的文件名
    #define OBJ_MESH?? 0x4100??// 新的網格對象
    #define MAX_TEXTURES? 100???// 最大的紋理數目
    //? OBJ_MESH的次級定義
    #define OBJ_VERTICES? 0x4110??// 對象頂點
    #define OBJ_FACES?? 0x4120??// 對象的面
    #define OBJ_MATERIAL? 0x4130??// 對象的材質
    #define OBJ_UV??? 0x4140??// 對象的UV紋理坐標 #define MAP_W?????? 32?????? // size of map along x-axis 32
    #define MAP_SCALE?? 24.0f???? // the scale of the terrain map
    #define MAP???MAP_W*MAP_SCALE/2
    #define KEY_DOWN(vk_code)((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
    #define RAND_COORD(x)?? ((float)rand()/RAND_MAX * (x))
    #define FRAND?? (((float)rand()-(float)rand())/RAND_MAX) using namespace std; class CVector3??//定義3D點的類,用于保存模型中的頂點
    {public:?float x, y, z;
    }; class CVector2??//定義2D點類,用于保存模型的UV紋理坐標
    {public:?float x, y;
    };
    struct tFace??//面的結構定義
    {?int vertIndex[3];???// 頂點索引
    ?int coordIndex[3];???// 紋理坐標索引
    };
    struct tMatInfo//材質信息結構體
    {?char? strName[255];???// 紋理名稱
    ?char? strFile[255];???// 如果存在紋理映射,則表示紋理文件名稱
    ?BYTE? color[3];????// 對象的RGB顏色
    ?int?? texureId;????// 紋理ID
    ?float uTile;????// u 重復
    ?float vTile;????// v 重復
    ?float uOffset;?????? // u 紋理偏移
    ?float vOffset;????// v 紋理偏移
    } ;
    struct t3DObject?//對象信息結構體
    {?int? numOfVerts;???// 模型中頂點的數目
    ?int? numOfFaces;???// 模型中面的數目
    ?int? numTexVertex;???// 模型中紋理坐標的數目
    ?int? materialID;???// 紋理ID
    ?bool bHasTexture;???// 是否具有紋理映射
    ?char strName[255];???// 對象的名稱
    ?CVector3? *pVerts;???// 對象的頂點
    ?CVector3? *pNormals;??// 對象的法向量
    ?CVector2? *pTexVerts;??// 紋理UV坐標
    ?tFace *pFaces;????// 對象的面信息
    };
    struct t3DModel?//模型信息結構體
    {?int numOfObjects;???// 模型中對象的數目
    ?int numOfMaterials;???// 模型中材質的數目
    ?vector<tMatInfo>pMaterials;?// 材質鏈表信息
    ?vector<t3DObject> pObject;?// 模型中對象鏈表信息
    };
    struct tChunk?//保存塊信息的結構
    {?unsigned short int ID;??// 塊的ID??
    ?unsigned int length;??// 塊的長度
    ?unsigned int bytesRead;??// 需要讀的塊數據的字節數
    }; class CLoad3DS// CLoad3DS類處理所有的裝入代碼
    {
    public:
    ?CLoad3DS();????????// 初始化數據成員
    ?virtual ~CLoad3DS();
    ?void show3ds(int j0,float tx,float ty,float tz,float size);//顯示3ds模型
    ?void Init(char *filename,int j);
    private:
    ?bool Import3DS(t3DModel *pModel, char *strFileName);// 裝入3ds文件到模型結構中
    ?void CreateTexture(UINT textureArray[],LPSTR strFileName,int textureID);//? 從文件中創建紋理
    ?int? GetString(char *);????????// 讀一個字符串
    ?void ReadChunk(tChunk *);???????// 讀下一個塊
    ?void ReadNextChunk(t3DModel *pModel, tChunk *);??// 讀下一個塊
    ?void ReadNextObjChunk(t3DModel *pModel,t3DObject *pObject,tChunk *);// 讀下一個對象塊
    ?void ReadNextMatChunk(t3DModel *pModel, tChunk *);?// 讀下一個材質塊
    ?void ReadColor(tMatInfo *pMaterial, tChunk *pChunk);// 讀對象顏色的RGB值
    ?void ReadVertices(t3DObject *pObject, tChunk *);?// 讀對象的頂點
    ?void ReadVertexIndices(t3DObject *pObject,tChunk *);// 讀對象的面信息
    ?void ReadUVCoordinates(t3DObject *pObject,tChunk *);// 讀對象的紋理坐標
    ?void ReadObjMat(t3DModel *pModel,t3DObject *pObject,tChunk *pPreChunk);// 讀賦予對象的材質名稱
    ?void ComputeNormals(t3DModel *pModel);????// 計算對象頂點的法向量
    ?void CleanUp();??????????// 關閉文件,釋放內存空間
    ?FILE?*m_FilePointer;????????// 文件指針
    ?tChunk?*m_CurrentChunk;
    ?tChunk?*m_TempChunk;
    }; // ///ImportModel.cpp #include "StdAfx.h"
    //#include "Set3ds.h"
    #include "ImportModel.h"
    #include <windows.h>??// Header File For Windows
    #include <stdio.h>???// Header File For Standard Input/Output
    #include <gl/gl.h>???// Header File For The OpenGL32 Library
    #include <gl/glu.h>???// Header File For The GLu32 Library
    #include <gl/glaux.h>??// Header File For The Glaux Library
    #include <math.h>
    UINT g_Texture[10][MAX_TEXTURES] = {0};?
    t3DModel g_3DModel[10];?
    ????????
    int?? g_ViewMode?? = GL_TRIANGLES;
    bool? g_bLighting???? = true;?? CLoad3DS::CLoad3DS()//? 構造函數的功能是初始化tChunk數據
    {?m_CurrentChunk = new tChunk;?// 初始化并為當前的塊分配空間
    ?m_TempChunk = new tChunk;??// 初始化一個臨時塊并分配空間
    }
    CLoad3DS::~CLoad3DS()
    {?CleanUp();// 釋放內存空間
    ?for(int j = 0; j <10;j++)
    ?for(int i = 0; i < g_3DModel[j].numOfObjects; i++)
    ?{?delete [] g_3DModel[j].pObject[i].pFaces;// 刪除所有的變量
    ??delete [] g_3DModel[j].pObject[i].pNormals;
    ??delete [] g_3DModel[j].pObject[i].pVerts;
    ??delete [] g_3DModel[j].pObject[i].pTexVerts;
    ?}
    }

    void CLoad3DS::Init(char *filename,int j)//
    {?Import3DS(&g_3DModel[j], filename);???// 將3ds文件裝入到模型結構體中
    ?for(int i =0; i<g_3DModel[j].numOfMaterials;i++)
    ?{if(strlen(g_3DModel[j].pMaterials[i].strFile)>0)// 判斷是否是一個文件名
    ?CreateTexture(g_Texture[j], g_3DModel[j].pMaterials[i].strFile, i);//使用紋理文件名稱來裝入位圖???
    ? g_3DModel[j].pMaterials[i].texureId = i;// 設置材質的紋理ID
    ?}
    }
    //? 從文件中創建紋理
    void CLoad3DS::CreateTexture(UINT textureArray[], LPSTR strFileName, int textureID)
    {?AUX_RGBImageRec *pBitmap = NULL;
    ?if(!strFileName) return;???????// 如果無此文件,則直接返回
    ?pBitmap = auxDIBImageLoad(strFileName);????// 裝入位圖,并保存數據
    ?if(pBitmap == NULL)??exit(0);?????// 如果裝入位圖失敗,則退出
    ?// 生成紋理
    ?glGenTextures(1, &textureArray[textureID]);
    ?// 設置像素對齊格式
    ?glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
    ?glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);
    ?gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pBitmap->sizeX, pBitmap->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pBitmap->data);
    ?glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
    ?glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
    ?if (pBitmap)??????????// 釋放位圖占用的資源
    ?{?if (pBitmap->data)?free(pBitmap->data);???????
    ??free(pBitmap);?????
    ?}
    }
    void CLoad3DS::show3ds(int j0,float tx,float ty,float tz,float size) //顯示3ds模型
    {
    ?glPushAttrib(GL_CURRENT_BIT);//保存現有顏色屬實性
    ?glPushMatrix();
    ?glDisable(GL_TEXTURE_2D);
    ?::glTranslatef( tx, ty, tz);
    ?::glScaled(size,size,size);
    ?glRotatef(90, 0, 1.0f, 0);
    ?// 遍歷模型中所有的對象
    ?for(int i = 0; i < g_3DModel[j0].numOfObjects; i++)
    ?{if(g_3DModel[j0].pObject.size() <= 0) break;// 如果對象的大小小于0,則退出
    ? t3DObject *pObject = &g_3DModel[j0].pObject[i];// 獲得當前顯示的對象
    ? if(pObject->bHasTexture)// 判斷該對象是否有紋理映射
    ?? {?glEnable(GL_TEXTURE_2D);// 打開紋理映射
    ???glBindTexture(GL_TEXTURE_2D, g_Texture[j0][pObject->materialID]);
    ?? }
    ? else?glDisable(GL_TEXTURE_2D);// 關閉紋理映射
    //這里原來有錯,不行正確調用模型的貼圖,g_Texture應該為2維數組?
    ? glColor3ub(255, 255, 255); glBegin(g_ViewMode);//開始以g_ViewMode模式繪制?????
    ? for(int j = 0; j < pObject->numOfFaces; j++)??// 遍歷所有的面
    ? {for(int tex = 0; tex < 3; tex++)?????// 遍歷三角形的所有點
    ??{int index = pObject->pFaces[j].vertIndex[tex];?// 獲得面對每個點的索引
    ?? glNormal3f(pObject->pNormals[index].x,pObject->pNormals[index].y,?
    ?????????? pObject->pNormals[index].z);??// 給出法向量
    ?? if(pObject->bHasTexture)??????// 如果對象具有紋理
    ?? {?if(pObject->pTexVerts)??????// 確定是否有UVW紋理坐標
    ????glTexCoord2f(pObject->pTexVerts[index].x,pObject->pTexVerts[index].y);
    ?? }
    ?? else
    ?? {?if(g_3DModel[j0].pMaterials.size() && pObject->materialID>= 0)
    ???{?BYTE *pColor = g_3DModel[j0].pMaterials[pObject->materialID].color;
    ????glColor3ub(pColor[0],pColor[1],pColor[2]);
    ???}
    ?? }
    ?? glVertex3f(pObject->pVerts[index].x,pObject->pVerts[index].y,pObject->pVerts[index].z);
    ??}
    ? }
    ?glEnd();// 繪制結束
    ?}
    ?glEnable(GL_TEXTURE_2D);
    ?glPopMatrix();
    ?glPopAttrib();//恢復前一屬性
    }
    //
    //? 打開一個3ds文件,讀出其中的內容,并釋放內存
    bool CLoad3DS::Import3DS(t3DModel *pModel, char *strFileName)
    {?char strMessage[255] = {0};
    ?// 打開一個3ds文件
    ?m_FilePointer = fopen(strFileName, "rb");
    ?// 確保所獲得的文件指針合法
    ?if(!m_FilePointer)
    ?{?sprintf(strMessage, "Unable to find the file: %s!", strFileName);
    ??MessageBox(NULL, strMessage, "Error", MB_OK);
    ??return false;
    ?}
    ?// 當文件打開之后,首先應該將文件最開始的數據塊讀出以判斷是否是一個3ds文件
    ?// 如果是3ds文件的話,第一個塊ID應該是PRIMARY
    ?// 將文件的第一塊讀出并判斷是否是3ds文件
    ?ReadChunk(m_CurrentChunk);
    ?// 確保是3ds文件
    ?if (m_CurrentChunk->ID != PRIMARY)
    ?{?sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName);
    ??MessageBox(NULL, strMessage, "Error", MB_OK);
    ??return false;
    ?}
    ?// 現在開始讀入數據,ReadNextChunk()是一個遞歸函數
    ?// 通過調用下面的遞歸函數,將對象讀出
    ?ReadNextChunk(pModel, m_CurrentChunk);
    ?// 在讀完整個3ds文件之后,計算頂點的法線
    ?ComputeNormals(pModel);
    ?// 釋放內存空間
    //?CleanUp();
    ?return true;
    }
    //? 下面的函數釋放所有的內存空間,并關閉文件
    void CLoad3DS::CleanUp()
    {?// 遍歷場景中所有的對象
    ?fclose(m_FilePointer);??????// 關閉當前的文件指針
    ?delete m_CurrentChunk;??????// 釋放當前塊
    ?delete m_TempChunk;???????// 釋放臨時塊
    } //? 下面的函數讀出3ds文件的主要部分
    void CLoad3DS::ReadNextChunk(t3DModel *pModel, tChunk *pPreChunk)
    {?t3DObject newObject = {0};?????// 用來添加到對象鏈表
    ?tMatInfo newTexture = {0};????// 用來添加到材質鏈表
    ?unsigned int version = 0;?????// 保存文件版本
    ?int buffer[50000] = {0};?????// 用來跳過不需要的數據
    ?m_CurrentChunk = new tChunk;????// 為新的塊分配空間??
    ?//? 下面每讀一個新塊,都要判斷一下塊的ID,如果該塊是需要的讀入的,則繼續進行
    ?//? 如果是不需要讀入的塊,則略過
    ?// 繼續讀入子塊,直到達到預定的長度
    ?while (pPreChunk->bytesRead < pPreChunk->length)
    ?{?// 讀入下一個塊
    ??ReadChunk(m_CurrentChunk);
    ??// 判斷塊的ID號
    ??switch (m_CurrentChunk->ID)
    ??{
    ??case VERSION:???????// 文件版本號
    ???// 在該塊中有一個無符號短整型數保存了文件的版本
    ???// 讀入文件的版本號,并將字節數添加到bytesRead變量中
    ???m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    ???// 如果文件版本號大于3,給出一個警告信息
    ???if (version > 0x03)
    ????MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
    ???break;
    ??case OBJECTINFO:??????// 網格版本信息
    ???// 讀入下一個塊
    ???ReadChunk(m_TempChunk);
    ???// 獲得網格的版本號
    ???m_TempChunk->bytesRead += fread(&version, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
    ???// 增加讀入的字節數
    ???m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;
    ???// 進入下一個塊
    ???ReadNextChunk(pModel, m_CurrentChunk);
    ???break;
    ??case MATERIAL:???????// 材質信息
    ???// 材質的數目遞增
    ???pModel->numOfMaterials++;
    ???// 在紋理鏈表中添加一個空白紋理結構
    ???pModel->pMaterials.push_back(newTexture);
    ???// 進入材質裝入函數
    ???ReadNextMatChunk(pModel, m_CurrentChunk);
    ???break;
    ??case OBJECT:???????// 對象的名稱
    ???// 該塊是對象信息塊的頭部,保存了對象了名稱
    ???// 對象數遞增
    ???pModel->numOfObjects++;
    ???// 添加一個新的tObject節點到對象鏈表中
    ???pModel->pObject.push_back(newObject);
    ???// 初始化對象和它的所有數據成員
    ???memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
    ???// 獲得并保存對象的名稱,然后增加讀入的字節數
    ???m_CurrentChunk->bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
    ???// 進入其余的對象信息的讀入
    ???ReadNextObjChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), m_CurrentChunk);
    ???break;
    ??case EDITKEYFRAME:
    ???// 跳過關鍵幀塊的讀入,增加需要讀入的字節數
    ???m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    ???break;
    ??default:
    ???//? 跳過所有忽略的塊的內容的讀入,增加需要讀入的字節數
    ???m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    ???break;
    ??}
    ??// 增加從最后塊讀入的字節數
    ??pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
    ?}
    ?// 釋放當前塊的內存空間
    ?delete m_CurrentChunk;
    ?m_CurrentChunk = pPreChunk;
    }
    //? 下面的函數處理所有的文件中對象的信息
    void CLoad3DS::ReadNextObjChunk(t3DModel *pModel, t3DObject *pObject, tChunk *pPreChunk)
    {?int buffer[50000] = {0};?????// 用于讀入不需要的數據
    ?// 對新的塊分配存儲空間
    ?m_CurrentChunk = new tChunk;
    ?// 繼續讀入塊的內容直至本子塊結束
    ?while (pPreChunk->bytesRead < pPreChunk->length)
    ?{?// 讀入下一個塊
    ??ReadChunk(m_CurrentChunk);
    ??// 區別讀入是哪種塊
    ??switch (m_CurrentChunk->ID)
    ??{
    ??case OBJ_MESH:?????// 正讀入的是一個新塊
    ???// 使用遞歸函數調用,處理該新塊
    ???ReadNextObjChunk(pModel, pObject, m_CurrentChunk);
    ???break;
    ??case OBJ_VERTICES:????// 讀入是對象頂點
    ???ReadVertices(pObject, m_CurrentChunk);
    ???break;
    ??case OBJ_FACES:?????// 讀入的是對象的面
    ???ReadVertexIndices(pObject, m_CurrentChunk);
    ???break;
    ??case OBJ_MATERIAL:????// 讀入的是對象的材質名稱
    ???// 該塊保存了對象材質的名稱,可能是一個顏色,也可能是一個紋理映射。同時在該塊中也保存了
    ???// 紋理對象所賦予的面
    ???// 下面讀入對象的材質名稱
    ???ReadObjMat(pModel, pObject, m_CurrentChunk);???
    ???break;
    ??case OBJ_UV:??????// 讀入對象的UV紋理坐標
    ???// 讀入對象的UV紋理坐標
    ???ReadUVCoordinates(pObject, m_CurrentChunk);
    ???break;
    ??default:?
    ???// 略過不需要讀入的塊
    ???m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    ???break;
    ??}
    ??// 添加從最后塊中讀入的字節數到前面的讀入的字節中
    ??pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
    ?}
    ?// 釋放當前塊的內存空間,并把當前塊設置為前面塊
    ?delete m_CurrentChunk;
    ?m_CurrentChunk = pPreChunk;
    }
    //? 下面的函數處理所有的材質信息
    void CLoad3DS::ReadNextMatChunk(t3DModel *pModel, tChunk *pPreChunk)
    {?int buffer[50000] = {0};?????// 用于讀入不需要的數據
    ?// 給當前塊分配存儲空間
    ?m_CurrentChunk = new tChunk;
    ?// 繼續讀入這些塊,知道該子塊結束
    ?while (pPreChunk->bytesRead < pPreChunk->length)
    ?{?// 讀入下一塊
    ??ReadChunk(m_CurrentChunk);
    ??// 判斷讀入的是什么塊
    ??switch (m_CurrentChunk->ID)
    ??{
    ??case MATNAME:???????// 材質的名稱
    ???// 讀入材質的名稱
    ???m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strName, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    ???break;
    ??case MATDIFFUSE:??????// 對象的R G B顏色
    ???ReadColor(&(pModel->pMaterials[pModel->numOfMaterials - 1]), m_CurrentChunk);
    ???break;
    ??case MATMAP:???????// 紋理信息的頭部
    ???// 進入下一個材質塊信息
    ???ReadNextMatChunk(pModel, m_CurrentChunk);
    ???break;
    ??case MATMAPFILE:??????// 材質文件的名稱
    ???// 讀入材質的文件名稱
    ???m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strFile, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    ???break;
    ??default:?
    ???// 掠過不需要讀入的塊
    ???m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    ???break;
    ??}
    ??// 添加從最后塊中讀入的字節數
    ??pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
    ?}
    ?// 刪除當前塊,并將當前塊設置為前面的塊
    ?delete m_CurrentChunk;
    ?m_CurrentChunk = pPreChunk;
    }
    //? 下面函數讀入塊的ID號和它的字節長度
    void CLoad3DS::ReadChunk(tChunk *pChunk)
    {?// 讀入塊的ID號,占用了2個字節。塊的ID號象OBJECT或MATERIAL一樣,說明了在塊中所包含的內容
    ?pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
    ?// 然后讀入塊占用的長度,包含了四個字節
    ?pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
    }
    //? 下面的函數讀入一個字符串
    int CLoad3DS::GetString(char *pBuffer)
    {?int index = 0;
    ?// 讀入一個字節的數據
    ?fread(pBuffer, 1, 1, m_FilePointer);
    ?// 直到結束
    ?while (*(pBuffer + index++) != 0) {
    ??// 讀入一個字符直到NULL
    ??fread(pBuffer + index, 1, 1, m_FilePointer);
    ?}
    ?// 返回字符串的長度
    ?return strlen(pBuffer) + 1;
    }
    //? 下面的函數讀入RGB顏色
    void CLoad3DS::ReadColor(tMatInfo *pMaterial, tChunk *pChunk)
    {?// 讀入顏色塊信息
    ?ReadChunk(m_TempChunk);
    ?// 讀入RGB顏色
    ?m_TempChunk->bytesRead += fread(pMaterial->color, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
    ?// 增加讀入的字節數
    ?pChunk->bytesRead += m_TempChunk->bytesRead;
    }
    //? 下面的函數讀入頂點索引
    void CLoad3DS::ReadVertexIndices(t3DObject *pObject, tChunk *pPreChunk)
    {?unsigned short index = 0;?????// 用于讀入當前面的索引
    ?// 讀入該對象中面的數目
    ?pPreChunk->bytesRead += fread(&pObject->numOfFaces, 1, 2, m_FilePointer);
    ?// 分配所有面的存儲空間,并初始化結構
    ?pObject->pFaces = new tFace [pObject->numOfFaces];
    ?memset(pObject->pFaces, 0, sizeof(tFace) * pObject->numOfFaces);
    ?// 遍歷對象中所有的面
    ?for(int i = 0; i < pObject->numOfFaces; i++)
    ?{?for(int j = 0; j < 4; j++)
    ??{?// 讀入當前面的第一個點
    ???pPreChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
    ???if(j < 3)
    ???{?// 將索引保存在面的結構中
    ????pObject->pFaces[i].vertIndex[j] = index;
    ???}
    ??}
    ?}
    }
    //? 下面的函數讀入對象的UV坐標
    void CLoad3DS::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreChunk)
    {?// 為了讀入對象的UV坐標,首先需要讀入UV坐標的數量,然后才讀入具體的數據
    ?// 讀入UV坐標的數量
    ?pPreChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);
    ?// 分配保存UV坐標的內存空間
    ?pObject->pTexVerts = new CVector2 [pObject->numTexVertex];
    ?// 讀入紋理坐標
    ?pPreChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
    }
    //? 讀入對象的頂點
    void CLoad3DS::ReadVertices(t3DObject *pObject, tChunk *pPreChunk)
    {?// 在讀入實際的頂點之前,首先必須確定需要讀入多少個頂點。
    ?// 讀入頂點的數目
    ?pPreChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);
    ?// 分配頂點的存儲空間,然后初始化結構體
    ?pObject->pVerts = new CVector3 [pObject->numOfVerts];
    ?memset(pObject->pVerts, 0, sizeof(CVector3) * pObject->numOfVerts);
    ?// 讀入頂點序列
    ?pPreChunk->bytesRead += fread(pObject->pVerts, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
    ?// 現在已經讀入了所有的頂點。
    ?// 因為3D Studio Max的模型的Z軸是指向上的,因此需要將y軸和z軸翻轉過來。
    ?// 具體的做法是將Y軸和Z軸交換,然后將Z軸反向。
    ?// 遍歷所有的頂點
    ?for(int i = 0; i < pObject->numOfVerts; i++)
    ?{?// 保存Y軸的值
    ??float fTempY = pObject->pVerts[i].y;
    ??// 設置Y軸的值等于Z軸的值
    ??pObject->pVerts[i].y = pObject->pVerts[i].z;
    ??// 設置Z軸的值等于-Y軸的值
    ??pObject->pVerts[i].z = -fTempY;
    ?}
    }
    //? 下面的函數讀入對象的材質名稱
    void CLoad3DS::ReadObjMat(t3DModel *pModel, t3DObject *pObject, tChunk *pPreChunk)
    {?char strMaterial[255] = {0};???// 用來保存對象的材質名稱
    ?int buffer[50000] = {0};????// 用來讀入不需要的數據
    ?// 材質或者是顏色,或者是對象的紋理,也可能保存了象明亮度、發光度等信息。
    ?// 下面讀入賦予當前對象的材質名稱
    ?pPreChunk->bytesRead += GetString(strMaterial);
    ?// 遍歷所有的紋理
    ?for(int i = 0; i < pModel->numOfMaterials; i++)
    ?{?//如果讀入的紋理與當前的紋理名稱匹配
    ??if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0)
    ??{?// 設置材質ID
    ???pObject->materialID = i;
    ???// 判斷是否是紋理映射,如果strFile是一個長度大于1的字符串,則是紋理
    ???if(strlen(pModel->pMaterials[i].strFile) > 0) {
    ????// 設置對象的紋理映射標志
    ????pObject->bHasTexture = true;
    ???}?
    ???break;
    ??}
    ??else
    ??{?// 如果該對象沒有材質,則設置ID為-1
    ???pObject->materialID = -1;
    ??}
    ?}
    ?pPreChunk->bytesRead += fread(buffer, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
    }???
    //? 下面的這些函數主要用來計算頂點的法向量,頂點的法向量主要用來計算光照
    // 下面的宏定義計算一個矢量的長度
    #define Mag(Normal) (sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z))
    // 下面的函數求兩點決定的矢量
    CVector3 Vector(CVector3 vPoint1, CVector3 vPoint2)
    {?CVector3 vVector;???????
    ?vVector.x = vPoint1.x - vPoint2.x;???
    ?vVector.y = vPoint1.y - vPoint2.y;???
    ?vVector.z = vPoint1.z - vPoint2.z;???
    ?return vVector;????????
    }
    // 下面的函數兩個矢量相加
    CVector3 AddVector(CVector3 vVector1, CVector3 vVector2)
    {?CVector3 vResult;???????
    ?vResult.x = vVector2.x + vVector1.x;??
    ?vResult.y = vVector2.y + vVector1.y;??
    ?vResult.z = vVector2.z + vVector1.z;??
    ?return vResult;????????
    }
    // 下面的函數處理矢量的縮放
    CVector3 DivideVectorByScaler(CVector3 vVector1, float Scaler)
    {?CVector3 vResult;???????
    ?vResult.x = vVector1.x / Scaler;???
    ?vResult.y = vVector1.y / Scaler;???
    ?vResult.z = vVector1.z / Scaler;???
    ?return vResult;????????
    }
    // 下面的函數返回兩個矢量的叉積
    CVector3 Cross(CVector3 vVector1, CVector3 vVector2)
    {?CVector3 vCross;????????
    ?vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
    ?vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
    ?vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
    ?return vCross;????????
    }
    // 下面的函數規范化矢量
    CVector3 Normalize(CVector3 vNormal)
    {?double Magnitude;???????
    ?Magnitude = Mag(vNormal);?????// 獲得矢量的長度
    ?vNormal.x /= (float)Magnitude;????
    ?vNormal.y /= (float)Magnitude;????
    ?vNormal.z /= (float)Magnitude;????
    ?return vNormal;????????
    }
    //? 下面的函數用于計算對象的法向量
    void CLoad3DS::ComputeNormals(t3DModel *pModel)
    {?CVector3 vVector1, vVector2, vNormal, vPoly[3];
    ?// 如果模型中沒有對象,則返回
    ?if(pModel->numOfObjects <= 0)
    ??return;
    ?// 遍歷模型中所有的對象
    ?for(int index = 0; index < pModel->numOfObjects; index++)
    ?{?// 獲得當前的對象
    ??t3DObject *pObject = &(pModel->pObject[index]);
    ??// 分配需要的存儲空間
    ??CVector3 *pNormals??= new CVector3 [pObject->numOfFaces];
    ??CVector3 *pTempNormals?= new CVector3 [pObject->numOfFaces];
    ??pObject->pNormals??= new CVector3 [pObject->numOfVerts];
    ??// 遍歷對象的所有面
    ??for(int i=0; i < pObject->numOfFaces; i++)
    ??{?vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
    ???vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
    ???vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
    ???// 計算面的法向量
    ???vVector1 = Vector(vPoly[0], vPoly[2]);??// 獲得多邊形的矢量
    ???vVector2 = Vector(vPoly[2], vPoly[1]);??// 獲得多邊形的第二個矢量
    ???vNormal? = Cross(vVector1, vVector2);??// 獲得兩個矢量的叉積
    ???pTempNormals[i] = vNormal;?????// 保存非規范化法向量
    ???vNormal? = Normalize(vNormal);????// 規范化獲得的叉積
    ???pNormals[i] = vNormal;??????// 將法向量添加到法向量列表中
    ??}
    ??//? 下面求頂點法向量
    ??CVector3 vSum = {0.0, 0.0, 0.0};
    ??CVector3 vZero = vSum;
    ??int shared=0;
    ??// 遍歷所有的頂點
    ??for (i = 0; i < pObject->numOfVerts; i++)???
    ??{?for (int j = 0; j < pObject->numOfFaces; j++)?// 遍歷所有的三角形面
    ???{????????????// 判斷該點是否與其它的面共享
    ????if (pObject->pFaces[j].vertIndex[0] == i ||
    ?????pObject->pFaces[j].vertIndex[1] == i ||
    ?????pObject->pFaces[j].vertIndex[2] == i)
    ????{?vSum = AddVector(vSum, pTempNormals[j]);
    ?????shared++;????????
    ????}
    ???}?????
    ???pObject->pNormals[i] = DivideVectorByScaler(vSum, float(-shared));
    ???// 規范化最后的頂點法向
    ???pObject->pNormals[i] = Normalize(pObject->pNormals[i]);?
    ???vSum = vZero;????????
    ???shared = 0;??????????
    ??}
    ??// 釋放存儲空間,開始下一個對象
    ??delete [] pTempNormals;
    ??delete [] pNormals;
    ?}
    } // 使用模型: class basicpic
    {
    public:
    ?basicpic();
    ?virtual ~basicpic(); GLUquadricObj *obj; CLoad3DS* m_3ds;
    ?void Scene(int obj,float size);
    }; basicpic::basicpic()
    {
    ?m_3ds=new CLoad3DS();
    ?
    ?m_3ds->Init("炮管1組.3DS",0);
    ??? m_3ds->Init("炮蓋1.3DS",1);
    ?m_3ds->Init("炮蓋2.3DS",2);
    ?m_3ds->Init("炮蓋3.3DS",3);
    ?m_3ds->Init("炮蓋4.3DS",4);
    ?m_3ds->Init("導彈1.3DS",5);
    ?m_3ds->Init("導彈2.3DS",6);
    ?m_3ds->Init("導彈3.3DS",7);
    ?m_3ds->Init("導彈4.3DS",8);
    ?glEnable(GL_TEXTURE_2D); }
    void basicpic::Scene(int obj,float size)
    {
    ?m_3ds->show3ds(obj,0,0,0,size);
    } 到入時:用scene(模型號,大小);
    ?
    13:34?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    opengl--貼圖

    這段代碼用來加載位圖文件。如果文件不存在,返回 NULL 告知程序無法加載位圖。在我開始解釋這段代碼之前,關于用作紋理的圖像我想有幾點十分重要,并且您必須明白。此圖像的寬和高必須是2的n次方;寬度和高度最小必須是64象素;并且出于兼容性的原因,圖像的寬度和高度不應超過256象素。如果您的原始素材的寬度和高度不是64,128,256象素的話,使用圖像處理軟件重新改變圖像的大小。可以肯定有辦法能繞過這些限制,但現在我們只需要用標準的紋理尺寸。

    首先,我們創建一個文件句柄。句柄是個用來鑒別資源的數值,它使程序能夠訪問此資源。我們開始先將句柄設為 NULL
    AUX_RGBImageRec *LoadBMP(char *Filename) // 載入位圖圖象
    {
    FILE *File=NULL;
    // 文件句柄
    接下來檢查文件名是否已提供。因為 LoadBMP() 可以無參數調用,所以我們不得不檢查一下。您可不想什么都沒載入吧.....:)
    if (!Filename) // 確保文件名已提供。
    {
    return NULL;
    // 如果沒提供,返回 NULL
    }
    接著檢查文件是否存在。下面這一行嘗試打開文件。
    File=fopen(Filename,"r"); //嘗試打開文件
    如果我們能打開文件的話,很顯然文件是存在的。使用 fclose(File) 關閉文件。 auxDIBImageLoad(Filename) 讀取圖象數據并將其返回。
    if (File) // 文件存在么?
    {
    fclose(File);
    // 關閉句柄
    return auxDIBImageLoad(Filename);
    //載入位圖并返回指針
    }
    如果我們不能打開文件,我們將返回NULL。這意味著文件無法載入。程序在后面將檢查文件是否已載入。如果沒有,我們將退出程序并彈出錯誤消息。
    return NULL; // 如果載入失敗,返回 NULL
    }
    下一部分代碼載入位圖(調用上面的代碼)并轉換成紋理。
    int LoadGLTextures() // 載入位圖(調用上面的代碼)并轉換成紋理
    {
    然后設置一個叫做 Status 的變量。我們使用它來跟蹤是否能夠載入位圖以及能否創建紋理。 Status 缺省設為 FALSE (表示沒有載入或創建任何東東)。
    int Status=FALSE; // Status 狀態指示器
    現在我們創建存儲位圖的圖像記錄。次記錄包含位圖的寬度、高度和數據。
    AUX_RGBImageRec *TextureImage[1]; // 創建紋理的存儲空間
    清除圖像記錄,確保其內容為空。
    memset(TextureImage,0,sizeof(void *)*1); // 將指針設為 NULL
    現在載入位圖,并將其轉換為紋理。 TextureImage[0]=LoadBMP("Data/NeHe.bmp") 調用 LoadBMP() 的代碼。載入 Data 目錄下的 NeHe.bmp 位圖文件。如果一切正常,圖像數據將存放在 TextureImage[0] 中, Status 被設為 TRUE ,然后我們開始創建紋理。
    // 載入位圖,檢查有無錯誤,如果位圖沒找到則退出。
    if (TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
    {
    Status=TRUE;
    // 將 Status 設為 TRUE
    現在使用中 TextureImage[0] 的數據創建紋理。第一行 glGenTextures(1, &texture[0]) 告訴OpenGL我們想生成一個紋理名字(如果您想載入多個紋理,加大數字)。值得注意的是,開始我們使用 GLuint texture[1] 來創建一個紋理的存儲空間,您也許會認為第一個紋理就是存放在 &texture[1] 中的,但這是錯的。正確的地址應該是 &texture[0] 。同樣如果使用 GLuint texture[2] 的話,第二個紋理存放在 texture[1] 中。『譯者注:學C的,在這里應該沒有障礙,數組就是從零開始的嘛。』

    第二行 glBindTexture(GL_TEXTURE_2D, texture[0]) 告訴OpenGL將紋理名字 texture[0] 綁定到紋理目標上。2D紋理只有高度(在 Y 軸上)和寬度(在 X 軸上)。主函數將紋理名字指派給紋理數據。本例中我們告知OpenGL, &texture[0] 處的內存已經可用。我們創建的紋理將存儲在 &texture[0] 的 指向的內存區域。
    glGenTextures(1, &texture[0]); // 創建紋理

    // 使用來自位圖數據生成 的典型紋理

    glBindTexture(GL_TEXTURE_2D, texture[0]);
    下來我們創建真正的紋理。下面一行告訴OpenGL此紋理是一個2D紋理 ( GL_TEXTURE_2D )。數字代表圖像的詳細程度,通常就由它為零去了。數字是數據的成分數。因為圖像是由紅色數據,綠色數據,藍色數據三種組分組成。 TextureImage[0]->sizeX 是紋理的寬度。如果您知道寬度,您可以在這里填入,但計算機可以很容易的為您指出此值。 TextureImage[0]->sizey 是紋理的高度。數字是邊框的值,一般就是零。 GL_RGB 告訴OpenGL圖像數據由紅、綠、藍三色數據組成。
    GL_UNSIGNED_BYTE 意味著組成圖像的數據是無符號字節類型的。最后... TextureImage[0]->data 告訴OpenGL紋理數據的來源。此例中指向存放在 TextureImage[0] 記錄中的數據。
    // 生成紋理
    glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
    下面的兩行告訴OpenGL在顯示圖像時,當它比放大得原始的紋理大 ( GL_TEXTURE_MAG_FILTER )或縮小得比原始得紋理小( GL_TEXTURE_MIN_FILTER )時OpenGL采用的濾波方式。通常這兩種情況下我都采用 GL_LINEAR 。這使得紋理從很遠處到離屏幕很近時都平滑顯示。使用 GL_LINEAR 需要CPU和顯卡做更多的運算。如果您的機器很慢,您也許應該采用 GL_NEAREST 。過濾的紋理在放大的時候,看起來斑駁的很『譯者注:馬賽克啦』。您也可以結合這兩種濾波方式。在近處時使用 GL_LINEAR ,遠處時 GL_NEAREST
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 線形濾波
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    // 線形濾波
    }
    現在我們釋放前面用來存放位圖數據的內存。我們先查看位圖數據是否存放在處。如果是的話,再查看數據是否已經存儲。如果已經存儲的話,刪了它。接著再釋放 TextureImage[0] 圖像結構以保證所有的內存都能釋放。
    if (TextureImage[0]) // 紋理是否存在
    {
    if (TextureImage[0]->data)
    // 紋理圖像是否存在
    {
    free(TextureImage[0]->data);
    // 釋放紋理圖像占用的內存
    }

    free(TextureImage[0]);
    // 釋放圖像結構
    }
    最后返回狀態變量。如果一切OK,變量 Status 的值為 TRUE 。否則為 FALSE

    return Status; // 返回 Status
    }

    ?

    -----------------------------------------------------------

    int InitGL(GLvoid) // 此處開始對OpenGL進行所有設置
    {
    if (!LoadGLTextures())
    // 調用紋理載入子例程( 新增 )
    {
    return FALSE;
    // 如果未能載入,返回FALSE( 新增 )
    }

    glEnable(GL_TEXTURE_2D);
    // 啟用紋理映射( 新增 )

    .............

    ---------------------------------------------------------------------------------------------

    下一行代碼選擇我們使用的紋理。如果您在您的場景中使用多個紋理,您應該使用來 glBindTexture(GL_TEXTURE_2D, texture[ 所使用紋理對應的數字 ]) 選擇要綁定的紋理。當您想改變紋理時,應該綁定新的紋理。有一點值得指出的是,您不能 glBegin() glEnd() 之間綁定紋理,必須在 glBegin() 之前或 glEnd() 之后綁定。注意我們在后面是如何使用 glBindTexture 來指定和綁定紋理的。
    glBindTexture(GL_TEXTURE_2D, texture[0]); // 選擇紋理
    為了將紋理正確的映射到四邊形上,您必須將紋理的右上角映射到四邊形的右上角,紋理的左上角映射到四邊形的左上角,紋理的右下角映射到四邊形的右下角,紋理的左下角映射到四邊形的左下角。如果映射錯誤的話,圖像顯示時可能上下顛倒,側向一邊或者什么都不是。

    glTexCoord2f 的第一個參數是X坐標。 0.0f 是紋理的左側。 0.5f 是紋理的中點, 1.0f 是紋理的右側。 glTexCoord2f 的第二個參數是Y坐標。 0.0f 是紋理的底部。 0.5f 是紋理的中點, 1.0f 是紋理的頂部。

    所以紋理的左上坐標是 X:0.0f,Y:1.0f ,四邊形的左上頂點是 X: -1.0f,Y:1.0f 。其余三點依此類推。

    試著玩玩 glTexCoord2f 的X,Y坐標參數。把 1.0f 改為 0.5f 將只顯示紋理的左半部分,把 0.0f 改為 0.5f 將只顯示紋理的右半部分。
    glBegin(GL_QUADS);
    // 前面
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 紋理和四邊形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
    // 紋理和四邊形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
    // 紋理和四邊形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
    // 紋理和四邊形的左上
    // 后面
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
    // 紋理和四邊形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
    // 紋理和四邊形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
    // 紋理和四邊形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
    // 紋理和四邊形的左下
    // 頂面
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
    // 紋理和四邊形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
    // 紋理和四邊形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
    // 紋理和四邊形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);?
    // 紋理和四邊形的右上
    // 底面
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
    // 紋理和四邊形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
    // 紋理和四邊形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
    // 紋理和四邊形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
    // 紋理和四邊形的右下
    // 右面
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
    // 紋理和四邊形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
    // 紋理和四邊形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
    // 紋理和四邊形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
    // 紋理和四邊形的左下
    // 左面
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
    // 紋理和四邊形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
    // 紋理和四邊形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
    // 紋理和四邊形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
    // 紋理和四邊形的左上
    glEnd();


    13:25?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    opengl---設置光源

    接著設置用來創建光源的數組。我們將使用兩種不同的光。第一種稱為環境光。環境光來自于四面八方。所有場景中的對象都處于環境光的照射中。第二種類型的光源叫做漫射光。漫射光由特定的光源產生,并在您的場景中的對象表面上產生反射。處于漫射光直接照射下的任何對象表面都變得很亮,而幾乎未被照射到的區域就顯得要暗一些。這樣在我們所創建的木板箱的棱邊上就會產生的很不錯的陰影效果。
    創建光源的過程和顏色的創建完全一致。前三個參數分別是RGB三色分量,最后一個是alpha通道參數。
    因此,下面的代碼我們得到的是半亮(0.5f)的白色環境光。如果沒有環境光,未被漫射光照到的地方會變得十分黑暗。
    GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f }; //環境光參數 ( 新增 )
    下一行代碼我們生成最亮的漫射光。所有的參數值都取成最大值1.0f。它將照在我們木板箱的前面,看起來挺好。
    GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f }; // 漫射光參數 ( 新增 )
    最后我們保存光源的位置。前三個參數和glTranslate中的一樣。依次分別是XYZ軸上的位移。由于我們想要光線直接照射在木箱的正面,所以XY軸上的位移都是0.0f。第三個值是Z軸上的位移。為了保證光線總在木箱的前面,所以我們將光源的位置朝著觀察者(就是您哪。)挪出屏幕。我們通常將屏幕也就是顯示器的屏幕玻璃所處的位置稱作Z軸的0.0f點。所以Z軸上的位移最后定為2.0f。假如您能夠看見光源的話,它就浮在您顯示器的前方。當然,如果木箱不在顯示器的屏幕玻璃后面的話,您也無法看見箱子。『譯者注:我很欣賞NeHe的耐心。說真的有時我都打煩了,這么簡單的事他這么廢話干嘛?但如果什么都清楚,您還會翻著這樣的頁面看個沒完么?』
    最后一個參數取為1.0f。這將告訴OpenGL這里指定的坐標就是光源的位置,以后的教程中我會多加解釋。

    GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f }; // 光源位置

    現在開始設置光源。下面下面一行設置環境光的發光量,光源light1開始發光。這一課的開始處我們我們將環境光的發光量存放在LightAmbient數組中。現在我們就使用此數組(半亮度環境光)。
    glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // 設置環境光
    接下來我們設置漫射光的發光量。它存放在LightDiffuse數組中(全亮度白光)。
    glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); // 設置漫射光
    然后設置光源的位置。位置存放在 LightPosition 數組中(正好位于木箱前面的中心,X-0.0f,Y-0.0f,Z方向移向觀察者2個單位<位于屏幕外面>)。
    glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); // 光源位置
    最后,我們啟用一號光源。我們還沒有啟用GL_LIGHTING,所以您看不見任何光線。記住:只對光源進行設置、定位、甚至啟用,光源都不會工作。除非我們啟用GL_LIGHTING。

    glEnable(GL_LIGHT1); // 啟用一號光源

    ?

    until:glEnable(GL_LIGHTING); //啟用光源

    不想用的時候:glDisable(GL_LIGHTING); //禁用光源


    ?

    ?
    13:22?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    OpenGL--著色透明

    glColor3f(r,g,b)。括號中的三個參數依次是紅、綠、藍三色分量。取值范圍可以從0,0f到1.0f。 OpenGL中的絕大多數特效都與某些類型的(色彩)混合有關。混色的定義為,將某個象素的顏色和已繪制在屏幕上與其對應的象素顏色相互結合。至于如何結合這兩個顏色則依賴于顏色的alpha通道的分量值,以及/或者所使用的混色函數。Alpha通常是位于顏色值末尾的第4個顏色組成分量。前面這些課我們都是用GL_RGB來指定顏色的三個分量。相應的GL_RGBA可以指定alpha分量的值。更進一步,我們可以使用glColor4f()來代替glColor3f()。 透明: 先聲明:OpenGL::init(): glColor4f(1.0f,1.0f,1.0f,0.5f);?? // 全亮度,50% Alpha 混合( 新增)?
    glBlendFunc(GL_SRC_ALPHA,GL_ONE);? // 基于源象素alpha通道值的半透明混合函數( 新增) 使用的時候: if(flag_bp == true)
    ?{
    ??glEnable(GL_BLEND);???// Turn Blending On
    ???? glDisable(GL_DEPTH_TEST);?// Turn Depth Testing Off
    ?}
    ?Obj->Scene(0,1.0);//3dmax載入模型
    ?glPopMatrix(); glDisable(GL_BLEND);??// Turn Blending Off
    ?glEnable(GL_DEPTH_TEST);?// Turn Depth Testing On ps: flag_bp is true,Turn Blending On.or off.
    13:19?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    opengl---平移旋轉

    int DrawGLScene(GLvoid) // 此過程中包括所有的繪制代碼
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // 清除屏幕及深度緩存

    glLoadIdentity();
    // 重置視口
    當您調用glLoadIdentity()之后,您實際上講當前點移到了屏幕中心,X坐標軸從左至右,Y坐標軸從下至上,Z坐標軸從里至外。OpenGL屏幕中心的坐標值是X和Y軸上的0.0f點。中心左面的坐標值是負值,右面是正值。移向屏幕頂端是正值,移向屏幕底端是負值。移入屏幕深處是負值,移出屏幕則是正值。

    glTranslatef(x, y, z)沿著 X, Y 和 Z 軸移動。根據前面的次序,下面的代碼沿著X軸左移1.5個單位,Y軸不動(0.0f),最后移入屏幕6.0f個單位。注意在glTranslatef(x, y, z)中當您移動的時候,您并不是相對屏幕中心移動,而是相對與當前所在的屏幕位置
    glTranslatef(-1.5f,0.0f,-6.0f); // 左移 1.5 單位,并移入屏幕 6.0
    現在我們已經移到了屏幕的左半部分,并且將視圖推入屏幕背后足夠的距離以便我們可以看見全部的場景-創建三角形。glBegin(GL_TRIANGLES)的意思是開始繪制三角形,glEnd() 告訴OpenGL三角形已經創建好了。通常您會需要畫3個頂點,可以使用GL_TRIANGLES。在絕大多數的顯卡上,繪制三角形是相當快速的。如果要畫四個頂點,使用GL_QUADS的話會更方便。但據我所知,絕大多數的顯卡都使用三角形來為對象著色。最后,如果您想要畫更多的頂點時,可以使用GL_POLYGON。

    本節的簡單示例中,我們只畫一個三角形。如果要畫第二個三角形的話,可以在這三點之后,再加三行代碼(3點)。所有六點代碼都應包含在glBegin(GL_TRIANGLES) 和 glEnd()之間。在他們之間再不會有多余的點出現,也就是說,(GL_TRIANGLES) 和 glEnd()之間的點都是以三點為一個集合的。這同樣適用于四邊形。如果您知道實在繪制四邊形的話,您必須在第一個四點之后,再加上四點為一個集合的點組。另一方面,多邊形可以由任意個頂點,(GL_POLYGON)不在乎glBegin(GL_TRIANGLES) 和 glEnd()之間有多少行代碼。

    glBegin之后的第一行設置了多邊形的第一個頂點,glVertex 的第一個參數是X坐標,然后依次是Y坐標和Z坐標。第一個點是上頂點,然后是左下頂點和右下頂點。glEnd()告訴OpenGL沒有其他點了。這樣將顯示一個填充的三角形。


    {譯者:這里要注意的是存在兩種不同的坐標變換方式,glTranslatef(x, y, z)中的x, y, z是相對與您當前所在點的位移,但glVertex(x,y,z)是相對于glTranslatef(x, y, z)移動后的新原點的位移。因而這里可以認為glTranslate移動的是坐標原點,glVertex中的點是相對最新的坐標原點的坐標值。}
    glRotatef(Angle,Xvector,Yvector,Zvector)負責讓對象繞某個軸旋轉。這個命令有很多用處。 Angle 通常是個變量代表對象轉過的角度。 Xvector , Yvector Zvector 三個參數則共同決定旋轉軸的方向。比如(1,0,0)所描述的矢量經過X坐標軸的1個單位處并且方向向右。(-1,0,0)所描述的矢量經過X坐標軸的1個單位處,但方向向左。
    D. Michael Traub:提供了對 Xvector , Yvector Zvector 的上述解釋。
    為了更好的理解X, Y 和 Z的旋轉,我舉些例子...

    X軸-您正在使用一臺臺鋸。鋸片中心的軸從左至右擺放(就像OpenGL中的X軸)。尖利的鋸齒繞著X軸狂轉,看起來要么向上轉,要么向下轉。取決于鋸片開始轉時的方向。這與我們在OpenGL中繞著X軸旋轉什么的情形是一樣的。(譯者注:這會兒您要把臉蛋湊向顯示器的話,保準被鋸開了花 ^-^。)

    Y軸-假設您正處于一個巨大的龍卷風中心,龍卷風的中心從地面指向天空(就像OpenGL中的Y軸)。垃圾和碎片圍著Y軸從左向右或是從右向左狂轉不止。這與我們在OpenGL中繞著Y軸旋轉什么的情形是一樣的。

    Z軸-您從正前方看著一臺風扇。風扇的中心正好朝著您(就像OpenGL中的Z軸)。風扇的葉片繞著Z軸順時針或逆時針狂轉。這與我們在OpenGL中繞著Z軸旋轉什么的情形是一樣的。

    13:12?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    OpenGL--我的編碼框架

    // stdafx.h : include file for standard system include files,
    //? or project specific include files that are used frequently, but
    //????? are changed infrequently
    //

    #if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
    #define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000

    #define WIN32_LEAN_AND_MEAN??// Exclude rarely-used stuff from Windows headers

    #include <windows.h>?// Windows的頭文件

    #include <mmsystem.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    #include <assert.h>
    #include <gl/gl.h>??// OpenGL32庫的頭文件
    #include <gl/glu.h>??// GLu32庫的頭文件
    #include <gl/glaux.h>?// GLaux庫的頭文件

    #pragma comment( lib, "winmm.lib")
    #pragma comment( lib, "opengl32.lib")?// OpenGL32連接庫
    #pragma comment( lib, "glu32.lib")??// GLu32連接庫
    #pragma comment( lib, "glaux.lib")??// GLaux連接庫


    // TODO: reference additional headers your program requires here

    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.

    #endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)

    ----------------------------------------------------------------------------------

    // stdafx.cpp : source file that includes just the standard includes
    //?OpenGL的基本圖形.pch will be the pre-compiled header
    //?stdafx.obj will contain the pre-compiled type information

    #include "stdafx.h"

    // TODO: reference any additional headers you need in STDAFX.H
    // and not in this file
    ----------------------------------------------------------------------------------------
    #include "stdafx.h"
    #include "OpenGL.h"
    //
    OpenGL* m_OpenGL;
    HDC??hDC;??// GDI設備句柄,將窗口連接到 GDI( 圖形設備接口)
    HGLRC?hRC=NULL;?// 渲染描述句柄,將OpenGL調用連接到設備描述表
    HWND?hWnd=NULL;?// 保存 Windows 分配給程序的窗口句柄
    int??Width = 800;// 窗口寬
    int??Height= 600;// 窗口高
    int??bits? = 16;?// 顏色深度

    void GameLoop()
    {?? MSG msg;
    ??? BOOL fMessage;
    ??? PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
    ??? while(msg.message != WM_QUIT)?// 消息循環
    ??? {?? fMessage = PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE);
    ??????? if(fMessage)????//有消息
    ???{ TranslateMessage(&msg);
    ????????????? DispatchMessage(&msg);
    ???}
    ??????? else? m_OpenGL->Render();?//無消息
    ??? }
    }
    LRESULT WINAPI MsgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam )// 消息處理
    {?switch(message)
    ?{?case WM_CREATE:??????// 建立窗口
    ???hDC = GetDC(hWnd);????// 獲取當前窗口的設備句柄
    ???m_OpenGL->SetupPixelFormat(hDC);// 調用顯示模式安裝功能
    ???return 0;??break;
    ??case WM_CLOSE:??????// 關閉窗口
    ???m_OpenGL->CleanUp();???// 結束處理
    ???PostQuitMessage(0);
    ???return 0;??break;
    ??case WM_SIZE:??????// 窗口尺寸變化
    ???Height = HIWORD(lParam);??// 窗口的高
    ???Width? = LOWORD(lParam);??// 窗口的寬
    ???if (Height==0)?Height=1;??// 防止被0 除
    ???m_OpenGL->init(Width,Height);
    ???return 0;??break;
    ??case WM_DESTROY:?????// 退出消息
    ??????????? PostQuitMessage(0);
    ??????????? return 0;??break;
    ??????? case WM_KEYUP:??????// 按ESC退出,全屏模式必需要加入的退出方式。
    ??????????? switch (wParam)
    ??????????? { case VK_ESCAPE:
    ?????m_OpenGL->CleanUp();?// 結束處理
    ??????? PostQuitMessage(0);
    ??????? return 0;break;
    ??????????? }
    ??default:???break;
    ?}
    ?return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,INT )// WinMain程序入口
    {?? // 注冊窗口類
    ?bool fullScreen =TRUE;
    ?DWORD?dwExStyle;??// Window 擴展風格
    ?DWORD?dwStyle;??// Window 窗口風格
    ?RECT?windowRect;??// 窗口尺寸
    ?int??nX=0,nY=0;
    /*?
    ?if (MessageBox(NULL,"使用全屏模式嗎?", "將進入OpenGL,選擇顯示模式",
    ???????????? MB_YESNO|MB_ICONQUESTION|MB_SYSTEMMODAL)==IDNO)
    ??{fullScreen =false;}???// 選擇窗口模式
    ?if (fullScreen)??????// 選擇全屏模式
    ?{?DEVMODE dmScr;?????// 設備模式
    ??memset(&dmScr,0,sizeof(dmScr));?// 確保內存分配
    ??dmScr.dmSize=sizeof(dmScr);??// Devmode 結構的大小
    ??dmScr.dmPelsWidth = Width;??// 屏幕寬
    ??dmScr.dmPelsHeight= Height;??// 屏幕高
    ??dmScr.dmBitsPerPel= 16;???// 色彩深度
    ??dmScr.dmDisplayFrequency=75;?// 刷屏速度
    ??dmScr.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFREQUENCY;
    ??if (ChangeDisplaySettings(&dmScr, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
    ???{fullScreen=FALSE;}
    ??dwExStyle=WS_EX_APPWINDOW;??// Window 擴展風格
    ??dwStyle=WS_POPUP;????// Window 窗口風格
    ??ShowCursor(FALSE);????// 隱藏鼠標
    ?}
    ?else*/
    ?{?dwExStyle=WS_EX_APPWINDOW|WS_EX_WINDOWEDGE;?// 使窗口具有3D外觀
    ??dwStyle=WS_OVERLAPPEDWINDOW;????// 使用標準窗口
    ??//WS_OVERLAPPEDWINDOW是有標題欄,窗口菜單,最大、小化按鈕和可調整尺寸的窗口
    ??int wid=GetSystemMetrics(SM_CXSCREEN);??// 獲取當前屏幕寬
    ??int hei=GetSystemMetrics(SM_CYSCREEN);??// 獲取當前屏幕高
    ??nX=(wid-Width)/2;nY=(hei-Height)/2;???// 計算窗口居中用
    ?}
    //-------------------------------------------------------------------
    ?AdjustWindowRectEx(&windowRect,dwStyle,FALSE,dwExStyle);
    ?????????//根據窗口風格來調整窗口尺寸達到要求的大小
    ?char cc[]="tml";
    ??? WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
    ????????????????????? GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
    ????????????????????? cc, NULL };
    ??? RegisterClassEx( &wc );
    ?m_OpenGL=new OpenGL();//
    ?hWnd = CreateWindowEx(NULL,cc,"學OpenGL編3D游戲 [ 2.OpenGL的基本圖形 ])",
    ??????? dwStyle|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
    ??????? nX, nY,Width, Height,
    ??????? NULL,NULL,hInst,NULL);?// 創建窗口
    ?ShowWindow( hWnd, SW_SHOWDEFAULT );????// 顯示窗口
    ?UpdateWindow( hWnd );???????// 刷新窗口
    ?GameLoop();??????????// 進入消息循環
    ??? return 0;
    }

    --------------------------------------------------------------------------------------------------------

    // OpenGL.h: interface for the OpenGL class.
    //
    //

    #if !defined(AFX_OPENGL_H__17B7289C_7956_41C5_89B9_621E3C435389__INCLUDED_)
    #define AFX_OPENGL_H__17B7289C_7956_41C5_89B9_621E3C435389__INCLUDED_

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    #include "bsipic.h"
    class OpenGL?
    {?public:?OpenGL();
    ?virtual ~OpenGL();
    ?public:
    ?bsipic? m_bsipic;?// 定義bsipic類變量
    ?HDC??hDC;??// GDI設備描述表
    ?HGLRC?hRC;??// 永久著色描述表
    ?BOOL?SetupPixelFormat(HDC hDC);
    ?void?init(int Width, int Height);
    ?void?Render();
    ?void?CleanUp();
    ?void?play();
    };

    #endif // !defined(AFX_OPENGL_H__17B7289C_7956_41C5_89B9_621E3C435389__INCLUDED_)

    ------------------------------------------------------------------------------------------------------------

    // OpenGL.cpp: implementation of the OpenGL class.
    //
    #include "stdafx.h"
    #include "OpenGL.h"
    //
    #include "stdafx.h"
    #include "OpenGL.h"
    //
    extern HWND?hWnd;
    float?r;
    //
    OpenGL::OpenGL()
    {
    }
    OpenGL::~OpenGL()
    {?CleanUp();
    }
    BOOL OpenGL::SetupPixelFormat(HDC hDC0)//檢測安裝OpenGL
    {?int nPixelFormat;?????? // 象素點格式
    ?hDC=hDC0;
    ?PIXELFORMATDESCRIPTOR pfd = {
    ???? sizeof(PIXELFORMATDESCRIPTOR),??? // pfd結構的大小
    ???? 1,??????????????????????????????? // 版本號
    ???? PFD_DRAW_TO_WINDOW |????????????? // 支持在窗口中繪圖
    ???? PFD_SUPPORT_OPENGL |????????????? // 支持 OpenGL
    ???? PFD_DOUBLEBUFFER,???????????????? // 雙緩存模式
    ???? PFD_TYPE_RGBA,??????????????????? // RGBA 顏色模式
    ???? 16,?????????????????????????????? // 24 位顏色深度
    ???? 0, 0, 0, 0, 0, 0,???????????????? // 忽略顏色位
    ???? 0,??????????????????????????????? // 沒有非透明度緩存
    ???? 0,??????????????????????????????? // 忽略移位位
    ???? 0,??????????????????????????????? // 無累加緩存
    ???? 0, 0, 0, 0,?????????????????????? // 忽略累加位
    ???? 16,?????????????????????????????? // 32 位深度緩存????
    ???? 0,??????????????????????????????? // 無模板緩存
    ???? 0,??????????????????????????????? // 無輔助緩存
    ???? PFD_MAIN_PLANE,?????????????????? // 主層
    ???? 0,??????????????????????????????? // 保留
    ???? 0, 0, 0?????????????????????????? // 忽略層,可見性和損毀掩模
    ?};
    ?if (!(nPixelFormat = ChoosePixelFormat(hDC, &pfd)))
    ??{ MessageBox(NULL,"沒找到合適的顯示模式","Error",MB_OK|MB_ICONEXCLAMATION);
    ?????? return FALSE;
    ??}
    ?SetPixelFormat(hDC,nPixelFormat,&pfd);//設置當前設備的像素點格式
    ?hRC = wglCreateContext(hDC);????????? //獲取渲染描述句柄
    ?wglMakeCurrent(hDC, hRC);???????????? //激活渲染描述句柄

    ?return TRUE;
    }
    void OpenGL::init(int Width, int Height)
    {?glViewport(0,0,Width,Height);???// 設置OpenGL視口大小。?
    ?glMatrixMode(GL_PROJECTION);???// 設置當前矩陣為投影矩陣。
    ?glLoadIdentity();??????// 重置當前指定的矩陣為單位矩陣
    ?gluPerspective???????// 設置透視圖
    ??( 54.0f,???????// 透視角設置為 45 度
    ??? (GLfloat)Width/(GLfloat)Height,?// 窗口的寬與高比
    ??? 0.1f,????????// 視野透視深度:近點1.0f
    ??? 3000.0f???????// 視野透視深度:始點0.1f遠點1000.0f
    ??);
    ?// 這和照象機很類似,第一個參數設置鏡頭廣角度,第二個參數是長寬比,后面是遠近剪切。
    ?glMatrixMode(GL_MODELVIEW);????// 設置當前矩陣為模型視圖矩陣
    ?glLoadIdentity();??????// 重置當前指定的矩陣為單位矩陣
    //====================================================
    }
    void OpenGL::Render()//OpenGL圖形處理
    {?glClearColor(0.0f, 0.0f, 0.6f, 1.0f);??? // 設置刷新背景色
    ?glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);// 刷新背景
    ?glLoadIdentity();???????? // 重置當前的模型觀察矩陣
    ?play();
    ?glFlush();?????????? // 更新窗口
    ?SwapBuffers(hDC);???????? // 切換緩沖區
    ?r+=1;if(r>360) r=0;
    }
    void OpenGL::CleanUp()
    {? wglMakeCurrent(hDC, NULL);?????????????????????? //清除OpenGL
    ? wglDeleteContext(hRC);?????????????????????????? //清除OpenGL
    }
    void OpenGL::play()
    {?glPushMatrix();
    ?glPointSize(4);?
    ?glTranslatef (-5, 4,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(1.0f, 0.0f, 0.0f);m_bsipic.Point();?
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef ( 0, 4,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(0.0f, 1.0f, 0.0f);m_bsipic.Line();?
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef ( 5, 4,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(0.0f, 0.0f, 1.0f);m_bsipic.Triangle();
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef (-5, 0,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(1.0f, 1.0f, 0.0f);m_bsipic.Square();
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef ( 0, 0,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(0.0f, 1.0f, 1.0f);m_bsipic.Esquare();?
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef ( 5, 0,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(1.0f, 0.0f, 1.0f);m_bsipic.Park();
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef (-5,-4,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(1.0f, 1.0f, 1.0f);m_bsipic.Pillar();?
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef ( 0, -4,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(0.7f, 0.7f, 0.7f);auxSolidCone(1,1);
    ?glPopMatrix();
    ?glPushMatrix();
    ?glTranslatef ( 5,-4,-13);glRotatef(r,1.0,1.0,1.0);
    ?glColor3f(0.4f, 0.4f, 0.4f);auxWireTeapot(1);
    ?glPopMatrix();
    }

    ----------------------------------------------------------------------------------------------------

    // bsipic.h: interface for the bsipic class.
    //
    //

    #if !defined(AFX_BSIPIC_H__726BAD2E_C0FE_4F4C_8282_BD53CCED1A9D__INCLUDED_)
    #define AFX_BSIPIC_H__726BAD2E_C0FE_4F4C_8282_BD53CCED1A9D__INCLUDED_

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000

    class bsipic?
    {
    public:
    ?bsipic();
    ?virtual ~bsipic();
    ?void?Point();
    ?void?Line();
    ?void?Triangle();?
    ?void?Square();
    ?void?Esquare();
    ?void?Park();?
    ?void?Pillar();
    };

    #endif // !defined(AFX_BSIPIC_H__726BAD2E_C0FE_4F4C_8282_BD53CCED1A9D__INCLUDED_)

    ------------------------------------------------------------------------------------------------------------

    // bsipic.cpp: implementation of the bsipic class.

    //

    #include "stdafx.h"
    #include "bsipic.h"

    //
    bsipic::bsipic()
    {

    }

    bsipic::~bsipic()
    {

    }
    ///
    void bsipic::Point()//畫點
    { glBegin(GL_POINTS);//
    ?? glVertex3f( 0.0f, 1.0f,-1.0f);//a點
    ?? glVertex3f(-1.0f,-1.0f, 0.0f);//b點
    ?? glVertex3f( 1.0f,-1.0f, 0.0f);//c點
    ? glEnd();
    }
    void bsipic::Line()//畫線
    { glBegin(GL_LINE_LOOP); //
    ?? glVertex3f( 0.0f, 1.0f,-1.0f);//a點
    ?? glVertex3f(-1.0f,-1.0f, 0.0f);//b點
    ?? glVertex3f( 1.0f,-1.0f, 0.0f);//c點
    ? glEnd();
    }
    void bsipic::Triangle()//畫面
    { glBegin(GL_POLYGON);//
    ?glVertex3f( 0.0f, 1.0f,-1.0f);//a點
    ?glVertex3f(-1.0f,-1.0f, 0.0f);//b點
    ?glVertex3f( 1.0f,-1.0f, 0.0f);//c點
    ? glEnd();
    }
    void bsipic::Square()//畫正方面
    { glBegin(GL_POLYGON);//
    ?glVertex3f(0.0f,0.0f ,0.0f);//a點
    ?glVertex3f(1.0f,0.0f, 0.0f);//b點
    ?glVertex3f(1.0f,0.0f,-1.0f);//c點
    ?glVertex3f(0.0f,0.0f,-1.0f);//d點
    ? glEnd();
    }
    void bsipic::Esquare()//畫正方體
    { glBegin(GL_QUAD_STRIP);//
    ??? glVertex3f(0.0f,0.0f ,0.0f);//a0點
    ??? glVertex3f(0.0f,1.0f ,0.0f);//a1點
    ??? glVertex3f(1.0f,0.0f, 0.0f);//b0點
    ??? glVertex3f(1.0f,1.0f, 0.0f);//b1點
    ??? glVertex3f(1.0f,0.0f,-1.0f);//c0點
    ??? glVertex3f(1.0f,1.0f,-1.0f);//c1點
    ??? glVertex3f(0.0f,0.0f,-1.0f);//d0點
    ??? glVertex3f(0.0f,1.0f,-1.0f);//d1點
    ??? glVertex3f(0.0f,0.0f ,0.0f);//a0點
    ??? glVertex3f(0.0f,1.0f ,0.0f);//a1點
    ? glEnd();

    ? glBegin(GL_POLYGON);//
    ?glVertex3f(0.0f,0.0f ,0.0f);//a0點
    ?glVertex3f(1.0f,0.0f, 0.0f);//b0點
    ?glVertex3f(1.0f,0.0f,-1.0f);//c0點
    ?glVertex3f(0.0f,0.0f,-1.0f);//d0點
    ?glVertex3f(0.0f,1.0f ,0.0f);//a1點
    ?glVertex3f(1.0f,1.0f, 0.0f);//b1點
    ?glVertex3f(1.0f,1.0f,-1.0f);//c1點
    ?glVertex3f(0.0f,1.0f,-1.0f);//d1點
    ? glEnd();
    }
    void bsipic::Park ()//畫園
    { glBegin(GL_TRIANGLE_FAN);//
    ?? glVertex3f(0,0,0.0f );??
    ?? for(int i=0;i<=390;i+=30)
    ?? {float p=(float)(i*3.14/180);
    ??? glVertex3f((float)sin(p),(float)cos(p),0.0f );
    ?? }
    ? glEnd();
    }
    void bsipic::Pillar () //園柱
    {glBegin(GL_QUAD_STRIP);//
    ?? for(int i=0;i<=390;i+=30)
    ?? { float p=(float)(i*3.14/180);
    ?glVertex3f((float)sin(p)/2,(float)cos(p)/2,1.0f );
    ?glVertex3f((float)sin(p)/2,(float)cos(p)/2,0.0f );
    ?? }
    ?glEnd();
    }

    13:09?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志

    寫一些OPENGL-新手上路

    我們要畫出一個圖形首先要做的:(openGL編碼的框架)
    代碼的前4行包括了我們使用的每個庫文件的頭文件。如下所示:
    #include <windows.h>
    #include <gl/gl.h>
    #include <gl/glu.h>
    #include <gl/glaux.h>
    // Windows的頭文件
    // OpenGL32庫的頭文件
    // GLu32庫的頭文件
    // GLaux庫的頭文件
    接下來您需要設置您計劃在您的程序中使用的所有變量。本節中的例程將創建一個空的OpenGL窗口,因此我們暫時還無需設置大堆的變量。余下需要設置的變量不多,但十分重要。您將會在您以后所寫的每一個OpenGL程序中用到它們。
    第一行設置的變量是Rendering Context(著色描述表)。每一個OpenGL都被連接到一個著色描述表上。著色描述表將所有的OpenGL調用命令連接到Device Context(設備描述表)上。我將OpenGL的著色描述表定義為 hRC 。要讓您的程序能夠繪制窗口的話,還需要創建一個設備描述表,也就是第二行的內容。Windows的設備描述表被定義為 hDC 。DC將窗口連接到GDI(Graphics Device Interface圖形設備接口)。而RC將OpenGL連接到DC。第三行的變量 hWnd 將保存由Windows給我們的窗口指派的句柄。最后,第四行為我們的程序創建了一個Instance(實例)。
    HGLRC hRC=NULL;
    HDC hDC=NULL;
    HWND hWnd=NULL;
    HINSTANCE hInstance;
    // 永久著色描述表
    // 私有GDI設備描述表
    // 保存我們的窗口句柄
    // 保存程序的實例
    下面的第一行設置一個用來監控鍵盤動作的數組。有許多方法可以監控鍵盤的動作,但這里的方法很可靠,并且可以處理多個鍵同時按下的情況。

    active 變量用來告知程序窗口是否處于最小化的狀態。如果窗口已經最小化的話,我們可以做從暫停代碼執行到退出程序的任何事情。我喜歡暫停程序。這樣可以使得程序不用在后臺保持運行。

    fullscreen 變量的作用相當明顯。如果我們的程序在全屏狀態下運行, fullscreen 的值為TRUE,否則為FALSE。這個全局變量的設置十分重要,它讓每個過程都知道程序是否運行在全屏狀態下。
    bool keys[256];
    bool active=TRUE;
    bool fullscreen=TRUE;
    // 用于鍵盤例程的數組
    // 窗口的活動標志,缺省為TRUE
    // 全屏標志缺省設定成全屏模式
    現在我們需要先定義WndProc()。必須這么做的原因是CreateGLWindow()有對WndProc()的引用,但WndProc()在CreateGLWindow()之后才出現。在C語言中,如果我們想要訪問一個當前程序段之后的過程和程序段的話,必須在程序開始處先申明所要訪問的程序段。所以下面的一行代碼先行定義了WndProc(),使得CreateGLWindow()能夠引用WndProc()。
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // WndProc的定義
    ?
    下面的代碼的作用是重新設置OpenGL場景的大小,而不管窗口的大小是否已經改變(假定您沒有使用全屏模式)。甚至您無法改變窗口的大小時(例如您在全屏模式下),它至少仍將運行一次--在程序開始時設置我們的透視圖。OpenGL場景的尺寸將被設置成它顯示時所在窗口的大小。
    GLvoid ReSizeGLScene(GLsizei width, GLsizei height)
    {
    if (height==0)
    {
    height=1;
    }
    glViewport(0, 0, width, height);
    // 重置并初始化GL窗口大小

    // 防止被零除

    // 將Height設為1

    // 重置當前的視口(Viewport)
    下面幾行為透視圖設置屏幕。意味著越遠的東西看起來越小。這么做創建了一個現實外觀的場景。此處透視按照基于窗口寬度和高度的45度視角來計算。0.1f,100.0f是我們在場景中所能繪制深度的起點和終點。

    glMatrixMode(GL_PROJECTION)指明接下來的兩行代碼將影響projection matrix(投影矩陣)。投影矩陣負責為我們的場景增加透視。 glLoadIdentity()近似于重置。它將所選的矩陣狀態恢復成其原始狀態。調用 glLoadIdentity()之后我們為場景設置透視圖。
    glMatrixMode(GL_MODELVIEW)指明任何新的變換將會影響 modelview matrix(模型觀察矩陣)。模型觀察矩陣中存放了我們的物體訊息。最后我們重置模型觀察矩陣。如果您還不能理解這些術語的含義,請別著急。在以后的教程里,我會向大家解釋。只要知道如果您想獲得一個精彩的透視場景的話,必須這么做。
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    // 計算窗口的外觀比例
    gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }
    // 選擇投影矩陣
    // 重置投影矩陣


    // 選擇模型觀察矩陣
    // 重置模型觀察矩陣

    ?
    接下的代碼段中,我們將對OpenGL進行所有的設置。我們將設置清除屏幕所用的顏色,打開深度緩存,啟用smooth shading(陰影平滑),等等。這個例程直到OpenGL窗口創建之后才會被調用。此過程將有返回值。但我們此處的初始化沒那么復雜,現在還用不著擔心這個返回值。
    int InitGL(GLvoid)
    {
    // 此處開始對OpenGL進行所有設置
    ?
    下一行啟用smooth shading(陰影平滑)。陰影平滑通過多邊形精細的混合色彩,并對外部光進行平滑。我將在另一個教程中更詳細的解釋陰影平滑。
    glShadeModel(GL_SMOOTH); // 啟用陰影平滑
    下一行設置清除屏幕時所用的顏色。如果您對色彩的工作原理不清楚的話,我快速解釋一下。色彩值的范圍從0.0f到1.0f。0.0f代表最黑的情況,1.0f就是最亮的情況。glClearColor 后的第一個參數是Red Intensity(紅色分量),第二個是綠色,第三個是藍色。最大值也是1.0f,代表特定顏色分量的最亮情況。最后一個參數是Alpha值。當它用來清除屏幕的時候,我們不用關心第四個數字。現在讓它為0.0f。我會用另一個教程來解釋這個參數。

    通過混合三種原色(紅、綠、藍),您可以得到不同的色彩。希望您在學校里學過這些。因此,當您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您將用亮藍色來清除屏幕。如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的話,您將使用中紅色來清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。要得到白色背景,您應該將所有的顏色設成最亮(1.0f)。要黑色背景的話,您該將所有的顏色設為最暗(0.0f)。
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 黑色背景
    接下來的三行必須做的是關于depth buffer(深度緩存)的。將深度緩存設想為屏幕后面的層。深度緩存不斷的對物體進入屏幕內部有多深進行跟蹤。我們本節的程序其實沒有真正使用深度緩存,但幾乎所有在屏幕上顯示3D場景OpenGL程序都使用深度緩存。它的排序決定那個物體先畫。這樣您就不會將一個圓形后面的正方形畫到圓形上來。深度緩存是OpenGL十分重要的部分。
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    // 設置深度緩存
    // 啟用深度測試
    // 所作深度測試的類型
    接著告訴OpenGL我們希望進行最好的透視修正。這會十分輕微的影響性能。但使得透視圖看起來好一點。
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 真正精細的透視修正
    最后,我們返回TRUE。如果我們希望檢查初始化是否OK,我們可以查看返回的 TRUE或FALSE的值。如果有錯誤發生的話,您可以加上您自己的代碼返回FALSE。目前,我們不管它。
    return TRUE;
    }
    // 初始化 OK
    ?
    下一段包括了所有的繪圖代碼。任何您所想在屏幕上顯示的東東都將在此段代碼中出現。以后的每個教程中我都會在例程的此處增加新的代碼。如果您對OpenGL已經有所了解的話,您可以在 glLoadIdentity()調用之后,返回TRUE值之前,試著添加一些OpenGL代碼來創建基本的形。如果您是OpenGL新手,等著我的下個教程。目前我們所作的全部就是將屏幕清除成我們前面所決定的顏色,清除深度緩存并且重置場景。我們仍沒有繪制任何東東。

    返回TRUE值告知我們的程序沒有出現問題。如果您希望程序因為某些原因而中止運行,在返回TRUE值之前增加返回FALSE的代碼告知我們的程序繪圖代碼出錯。程序即將退出。
    int DrawGLScene(GLvoid)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);?
    glLoadIdentity();?
    return TRUE;?
    }
    // 從這里開始進行所有的繪制

    // 清除屏幕和深度緩存
    // 重置當前的模型觀察矩陣
    // 一切 OK
    ?
    下一段代碼只在程序退出之前調用。KillGLWindow() 的作用是依次釋放著色描述表,設備描述表和窗口句柄。我已經加入了許多錯誤檢查。如果程序無法銷毀窗口的任意部分,都會彈出帶相應錯誤消息的訊息窗口,告訴您什么出錯了。使您在您的代碼中查錯變得更容易些。
    GLvoid KillGLWindow(GLvoid)?
    {
    // 正常銷毀窗口
    ?
    我們在KillGLWindow()中所作的第一件事是檢查我們是否處于全屏模式。如果是,我們要切換回桌面。我們本應在禁用全屏模式前先銷毀窗口,但在某些顯卡上這么做可能會使得桌面崩潰。所以我們還是先禁用全屏模式。這將防止桌面出現崩潰,并在Nvidia和3dfx顯卡上都工作的很好!
    if (fullscreen)?
    {
    // 我們處于全屏模式嗎?
    ?
    我們使用ChangeDisplaySettings(NULL,0)回到原始桌面。將NULL作為第一個參數,0作為第二個參數傳遞強制Windows使用當前存放在注冊表中的值(缺省的分辨率、色彩深度、刷新頻率,等等)來有效的恢復我們的原始桌面。切換回桌面后,我們還要使得鼠標指針重新可見。
    ChangeDisplaySettings(NULL,0);?
    ShowCursor(TRUE);?
    }
    // 是的話,切換回桌面
    // 顯示鼠標指針

    ?
    接下來的代碼查看我們是否擁有著色描述表(hRC)。如果沒有,程序將跳轉至后面的代碼查看是否擁有設備描述表。
    if (hRC)?
    {
    // 我們擁有著色描述表嗎?
    ?
    如果存在著色描述表的話,下面的代碼將查看我們能否釋放它(將 hRC從hDC分開)。這里請注意我使用的的查錯方法。基本上我只是讓程序嘗試釋放著色描述表(通過調用wglMakeCurrent(NULL,NULL),然后我再查看釋放是否成功。巧妙的將數行代碼結合到了一行。
    if (!wglMakeCurrent(NULL,NULL))?
    {
    // 我們能否釋放DC和RC描述表?
    ?
    如果不能釋放DC和RC描述表的話,MessageBox()將彈出錯誤消息,告知我們DC和RC無法被釋放。NULL意味著消息窗口沒有父窗口。其右的文字將在消息窗口上出現。"SHUTDOWN ERROR"出現在窗口的標題欄上。MB_OK的意思消息窗口上帶有一個寫著OK字樣的按鈕。
    MB_ICONINFORMATION將在消息窗口中顯示一個帶圈的小寫的i(看上去更正式一些)。?
    MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    }
    下一步我們試著刪除著色描述表。如果不成功的話彈出錯誤消息。
    if (!wglDeleteContext(hRC))?
    {
    // 我們能否刪除RC?
    ?
    如果無法刪除著色描述表的話,將彈出錯誤消息告知我們RC未能成功刪除。然后hRC被設為NULL。
    MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    }
    hRC=NULL;???????????????????????????????????????????????????????????????????????????????????????????????????
    ? // 將RC設為 NULL
    }
    現在我們查看是否存在設備描述表,如果有嘗試釋放它。如果不能釋放設備描述表將彈出錯誤消息,然后hDC設為NULL。
    if (hDC && !ReleaseDC(hWnd,hDC))???????????????????????????????????????????????????????? // 我們能否釋放 DC?
    {
    MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    hDC=NULL;????????????????????????????????????????????????????????????????????????????????????????????????????
    // 將 DC 設為 NULL
    }
    現在我們來查看是否存在窗口句柄,我們調用 DestroyWindow( hWnd )來嘗試銷毀窗口。如果不能的話彈出錯誤窗口,然后hWnd被設為NULL。
    if (hWnd && !DestroyWindow(hWnd))??????????????????????????????????????????????????????? // 能否銷毀窗口?
    {
    MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    hWnd=NULL;?????????????????????????????????????????????????????????????????????????????????????????????????
    // 將 hWnd 設為 NULL
    }
    最后要做的事是注銷我們的窗口類。這允許我們正常銷毀窗口,接著在打開其他窗口時,不會收到諸如"Windows Class already registered"(窗口類已注冊)的錯誤消息。
    if (!UnregisterClass("OpenGL",hInstance))?????????????????????????????????????????????? ? // 能否注銷類?
    {
    MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    hInstance=NULL;?????????????????????????????????????????????????????????????????????????????????????????
    // 將 hInstance 設為 NULL
    }
    }
    接下來的代碼段創建我們的OpenGL窗口。我花了很多時間來做決定是否創建固定的全屏模式這樣不需要許多額外的代碼,還是創建一個容易定制的友好的窗口但需要更多的代碼。當然最后我選擇了后者。我經常在EMail中收到諸如此類的問題:怎樣創建窗口而不使用全屏幕?怎樣改變窗口的標題欄?怎樣改變窗口的分辨率或pixel format(象素格式)?以下的代碼完成了所有這一切!盡管最好要學學材質,這會讓您寫自己的OpenGL程序變得容易的多!

    正如您所見,此過程返回布爾變量(TRUE 或 FALSE)。他還帶有5個參數:窗口的標題欄,窗口的寬度,窗口的高度,色彩位數(16/24/32),和全屏標志(TRUE --全屏模式, FALSE--窗口模式 )。返回的布爾值告訴我們窗口是否成功創建。
    BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
    {
    當我們要求Windows為我們尋找相匹配的象素格式時,Windows尋找結束后將模式值保存在變量PixelFormat中。
    GLuint PixelFormat; // 保存查找匹配的結果
    wc用來保存我們的窗口類的結構。窗口類結構中保存著我們的窗口信息。通過改變類的不同字段我們可以改變窗口的外觀和行為。每個窗口都屬于一個窗口類。當您創建窗口時,您必須為窗口注冊類。
    WNDCLASS wc; // 窗口類結構
    dwExStyle和dwStyle存放擴展和通常的窗口風格信息。我使用變量來存放風格的目的是為了能夠根據我需要創建的窗口類型(是全屏幕下的彈出窗口還是窗口模式下的帶邊框的普通窗口);來改變窗口的風格。
    DWORD dwExStyle;
    DWORD dwStyle;
    // 擴展窗口風格
    // 窗口風格
    下面的5行代碼取得矩形的左上角和右下角的坐標值。我們將使用這些值來調整我們的窗口使得其上的繪圖區的大小恰好是我們所需的分辨率的值。通常如果我們創建一個640x480的窗口,窗口的邊框會占掉一些分辨率的值。
    RECT WindowRect;
    WindowRect.left=(long)0;
    WindowRect.right=(long)width;
    WindowRect.top=(long)0;
    WindowRect.bottom=(long)height;
    // 取得矩形的左上角和右下角的坐標值
    // 將Left?? 設為 0
    // 將Right? 設為要求的寬度
    // 將Top??? 設為 0
    // 將Bottom 設為要求的高度
    下一行代碼我們讓全局變量fullscreen等于fullscreenflag。如果我們希望在全屏幕下運行而將fullscreenflag設為TRUE,但沒有讓變量fullscreen等于fullscreenflag的話,fullscreen變量將保持為FALSE。當我們在全屏幕模式下銷毀窗口的時候,變量fullscreen的值卻不是正確的TRUE值,計算機將誤以為已經處于桌面模式而無法切換回桌面。上帝啊,但愿這一切都有意義。就是一句話,fullscreen的值必須永遠fullscreenflag的值,否則就會有問題。{CKER也覺得此處太廢話,懂的人都要不懂啦.....:(? }
    fullscreen=fullscreenflag; // 設置全局全屏標志
    下一部分的代碼中,我們取得窗口的實例,然后定義窗口類。

    CS_HREDRAW 和 CS_VREDRAW 的意思是無論何時,只要窗口發生變化時就強制重畫。CS_OWNDC為窗口創建一個私有的DC。這意味著DC不能在程序間共享。WndProc是我們程序的消息處理過程。由于沒有使用額外的窗口數據,后兩個字段設為零。然后設置實例。接著我們將hIcon設為NULL,因為我們不想給窗口來個圖標。鼠標指針設為標準的箭頭。背景色無所謂(我們在GL中設置)。我們也不想要窗口菜單,所以將其設為NULL。類的名字可以您想要的任何名字。出于簡單,我將使用"OpenGL"。
    hInstance = GetModuleHandle(NULL);
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = (WNDPROC) WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "OpenGL";
    // 取得我們窗口的實例
    // 移動時重畫,并為窗口取得DC
    // WndProc處理消息
    // 無額外窗口數據
    // 無額外窗口數據
    // 設置實例
    // 裝入缺省圖標
    // 裝入鼠標指針
    // GL不需要背景
    // 不需要菜單
    // 設定類名字
    現在注冊類名字。如果有錯誤發生,彈出錯誤消息窗口。按下上面的OK按鈕后,程序退出。
    if (!RegisterClass(&wc))??????????????????????????????????? // 嘗試注冊窗口類
    {
    MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;?????????????????????????????????????????????? file://退出并返回FALSE
    }
    查看程序應該在全屏模式還是窗口模式下運行。如果應該是全屏模式的話,我們將嘗試設置全屏模式。
    if (fullscreen)
    {
    // 要嘗試全屏模式嗎?
    ?
    下一部分的代碼看來很多人都會有問題要問關于.......切換到全屏模式。在切換到全屏模式時,有幾件十分重要的事您必須牢記。必須確保您在全屏模式下所用的寬度和高度等同于窗口模式下的寬度和高度。最最重要的是要在創建窗口之前設置全屏模式。這里的代碼中,您無需再擔心寬度和高度,它們已被設置成與顯示模式所對應的大小。
    DEVMODE dmScreenSettings;?
    memset(&dmScreenSettings,0,sizeof(dmScreenSettings));?
    dmScreenSettings.dmSize=sizeof(dmScreenSettings);?
    dmScreenSettings.dmPelsWidth = width;?
    dmScreenSettings.dmPelsHeight = height;?
    dmScreenSettings.dmBitsPerPel = bits;?
    dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
    // 設備模式
    // 確保內存分配
    // Devmode 結構的大小
    // 所選屏幕寬度
    // 所選屏幕高度
    // 每象素所選的色彩深度
    ?
    上面的代碼中,我們分配了用于存儲視頻設置的空間。設定了屏幕的寬,高,色彩深度。下面的代碼我們嘗試設置全屏模式。我們在dmScreenSettings中保存了所有的寬,高,色彩深度訊息。下一行使用ChangeDisplaySettings來嘗試切換成與dmScreenSettings所匹配模式。我使用參數CDS_FULLSCREEN來切換顯示模式,因為這樣做不僅移去了屏幕底部的狀態條,而且它在來回切換時,沒有移動或改變您在桌面上的窗口。
    // 嘗試設置顯示模式并返回結果。注: CDS_FULLSCREEN 移去了狀態條。
    if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
    {
    如果模式未能設置成功,我們將進入以下的代碼。如果不能匹配全屏模式,彈出消息窗口,提供兩個選項:在窗口模式下運行或退出。
    // 若模式失敗,提供兩個選項:退出或在窗口內運行。
    if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By/nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
    {
    如果用戶選擇窗口模式,變量fullscreen 的值變為FALSE,程序繼續運行。
    fullscreen=FALSE;
    }
    else
    {
    // 選擇窗口模式(Fullscreen=FALSE)


    ?
    如果用戶選擇退出,彈出消息窗口告知用戶程序將結束。并返回FALSE告訴程序窗口未能成功創建。程序退出。
    // Pop Up A Message Box Letting User Know The Program Is Closing.
    MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
    return FALSE;???????????????????????????????????????????????????????????????
    //退出并返回 FALSE
    }
    }
    }
    由于全屏模式可能失敗,用戶可能決定在窗口下運行,我們需要在設置屏幕/窗口之前,再次檢查fullscreen的值是TRUE或FALSE。
    if (fullscreen)
    {
    // 仍處于全屏模式嗎?
    ?
    如果我們仍處于全屏模式,設置擴展窗體風格為WS_EX_APPWINDOW,這將強制我們的窗體可見時處于最前面。再將窗體的風格設為WS_POPUP。這個類型的窗體沒有邊框,使我們的全屏模式得以完美顯示。
    最后我們禁用鼠標指針。當您的程序不是交互式的時候,在全屏模式下禁用鼠標指針通常是個好主意。
    dwExStyle=WS_EX_APPWINDOW;
    dwStyle=WS_POPUP;
    ShowCursor(FALSE);
    }
    else
    {
    // 擴展窗體風格
    // 窗體風格
    // 隱藏鼠標指針


    ?
    如果我們使用窗口而不是全屏模式,我們在擴展窗體風格中增加了 WS_EX_WINDOWEDGE,增強窗體的3D感觀。窗體風格改用 WS_OVERLAPPEDWINDOW,創建一個帶標題欄、可變大小的邊框、菜單和最大化/最小化按鈕的窗體。
    dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    dwStyle=WS_OVERLAPPEDWINDOW;
    }
    // 擴展窗體風格
    // 窗體風格
    ?
    下一行代碼根據創建的窗體類型調整窗口。調整的目的是使得窗口大小正好等于我們要求的分辨率。通常邊框會占用窗口的一部分。使用AdjustWindowRectEx 后,我們的OpenGL場景就不會被邊框蓋住。實際上窗口變得更大以便繪制邊框。全屏模式下,此命令無效。
    AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // 調整窗口達到真正要求的大小
    下一段代碼開始創建窗口并檢查窗口是否成功創建。我們將傳遞CreateWindowEx()所需的所有參數。如擴展風格、類名字(與您在注冊窗口類時所用的名字相同)、窗口標題、窗體風格、窗體的左上角坐標(0,0 是個安全的選擇)、窗體的寬和高。我們沒有父窗口,也不想要菜單,這些參數被設為NULL。還傳遞了窗口的實例,最后一個參數被設為NULL。

    注意我們在窗體風格中包括了 WS_CLIPSIBLINGS 和 WS_CLIPCHILDREN。要讓OpenGL正常運行,這兩個屬性是必須的。他們阻止別的窗體在我們的窗體內/上繪圖。
    if (!(hWnd=CreateWindowEx( dwExStyle,
    "OpenGL",
    title,
    WS_CLIPSIBLINGS |
    WS_CLIPCHILDREN |
    dwStyle,
    0, 0,
    WindowRect.right-WindowRect.left,
    WindowRect.bottom-WindowRect.top,
    NULL,
    NULL,
    hInstance,
    NULL)))
    // 擴展窗體風格
    // 類名字
    // 窗口標題
    // 必須的窗體風格屬性
    // 必須的窗體風格屬性
    // 選擇的窗體屬性
    // 窗口位置
    // 計算調整好的窗口寬度
    // 計算調整好的窗口高度
    // 無父窗口
    // 無菜單
    // 實例
    // 不向WM_CREATE傳遞任何東東
    下來我們檢查看窗口是否正常創建。如果成功, hWnd保存窗口的句柄。如果失敗,彈出消息窗口,并退出程序。
    {
    KillGLWindow();?????????????????????????????????????????????????????????????
    // 重置顯示區
    MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;???????????????????????????????????????????????????????????????
    // 返回 FALSE
    }
    下面的代碼描述象素格式。我們選擇了通過RGBA(紅、綠、藍、alpha通道)支持OpenGL和雙緩存的格式。我們試圖找到匹配我們選定的色彩深度(16位、24位、32位)的象素格式。最后設置16位Z-緩存。其余的參數要么未使用要么不重要(stencil buffer模板緩存和accumulation buffer聚集緩存除外)。
    static PIXELFORMATDESCRIPTOR pfd=
    {
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW |
    PFD_SUPPORT_OPENGL |
    PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    bits,
    0, 0, 0, 0, 0, 0,
    0,
    0,
    0,
    0, 0, 0, 0,
    16,
    0,
    0,
    PFD_MAIN_PLANE,
    0,
    0, 0, 0
    };
    //pfd 告訴窗口我們所希望的東東

    file://上訴格式描述符的大小
    // 版本號
    // 格式必須支持窗口
    // 格式必須支持OpenGL
    // 必須支持雙緩沖
    // 申請 RGBA 格式
    // 選定色彩深度
    // 忽略的色彩位
    // 無Alpha緩存
    // 忽略Shift Bit
    // 無聚集緩存
    // 忽略聚集位
    // 16位 Z-緩存 (深度緩存)
    // 無模板緩存
    // 無輔助緩存
    // 主繪圖層
    // 保留
    // 忽略層遮罩
    ?
    如果前面創建窗口時沒有錯誤發生,我們接著嘗試取得OpenGL設備描述表。若無法取得DC,彈出錯誤消息程序退出(返回FALSE)。
    if (!(hDC=GetDC(hWnd)))????????????????????????????????????????????????????? //取得設備描述表了么?
    {
    KillGLWindow();?????????????????????????????????????????????????????????????
    // 重置顯示區
    MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;???????????????????????????????????????????????????????????????
    // 返回 FALSE
    }
    設法為OpenGL窗口取得設備描述表后,我們嘗試找到對應與此前我們選定的象素格式的象素格式。如果Windows不能找到的話,彈出錯誤消息,并退出程序(返回FALSE)。
    if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))???????????????????????????? // Windows 找到相應的象素格式了嗎?
    {
    KillGLWindow();????????????????????????????????????????????????????????????
    // 重置顯示區
    MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;??????????????????????????????????????????????????????????????
    // 返回 FALSE
    }
    Windows 找到相應的象素格式后,嘗試設置象素格式。如果無法設置,彈出錯誤消息,并退出程序(返回FALSE)。
    if(!SetPixelFormat(hDC,PixelFormat,&pfd))?????????????????????????????????? // 能夠設置象素格式么?
    {
    KillGLWindow();????????????????????????????????????????????????????
    ??????? // 重置顯示區
    MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;?????????????????????????????????????????????????
    ????????????
    // 返回 FALSE
    }
    正常設置象素格式后,嘗試取得著色描述表。如果不能取得著色描述表的話,彈出錯誤消息,并退出程序(返回FALSE)。
    if (!(hRC=wglCreateContext(hDC)))?????????????????????????????????????????? // 能否取得著色描述表?
    {
    KillGLWindow();
    ???????????????????????????????????????????????????? ??????? // 重置顯示區
    MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
    ????????????????????????????????????????????????? ???????????? // 返回 FALSE
    }
    如果到現在仍未出現錯誤的話,我們已經設法取得了設備描述表和著色描述表。接著要做的是激活著色描述表。如果無法激活,彈出錯誤消息,并退出程序(返回FALSE)。
    if(!wglMakeCurrent(hDC,hRC))?????????????????????????????????????????????? // 嘗試激活著色描述表
    {
    KillGLWindow();????????????????????????????????????????????????????
    ??????? // 重置顯示區
    MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;?????????????????????????????????????????????????
    ???????????? // 返回 FALSE
    }
    一切順利的話,OpenGL窗口已經創建完成,接著可以顯示它啦。將它設為前端窗口(給它更高的優先級),并將焦點移至此窗口。然后調用ReSizeGLScene 將屏幕的寬度和高度設置給透視OpenGL屏幕。
    ShowWindow(hWnd,SW_SHOW);
    SetForegroundWindow(hWnd);
    SetFocus(hWnd);
    ReSizeGLScene(width, height);
    // 顯示窗口
    // 略略提高優先級
    // 設置鍵盤的焦點至此窗口
    // 設置透視 GL 屏幕
    跳轉至 InitGL(),這里可以設置光照、紋理、等等任何需要設置的東東。您可以在 InitGL()內部自行定義錯誤檢查,并返回 TRUE (一切正常)或FALSE (有什么不對)。例如,如果您在InitGL()內裝載紋理并出現錯誤,您可能希望程序停止。如果您返回 FALSE的話,下面的代碼會彈出錯誤消息,并退出程序。
    if (!InitGL())????????????????????????????????????????????????????????????? // 初始化新建的GL窗口
    {
    KillGLWindow();????????????????????????????????????????????????????????????
    // 重置顯示區
    MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;??????????????????????????????????????????????????????????????
    // 返回 FALSE
    }
    到這里可以安全的推定創建窗口已經成功了。我們向WinMain()返回TRUE,告知WinMain()沒有錯誤,以防止程序退出。
    return TRUE; // 成功
    }
    下面的代碼處理所有的窗口消息。當我們注冊好窗口類之后,程序跳轉到這部分代碼處理窗口消息。
    LRESULT CALLBACK WndProc( HWND hWnd,?
    UINT uMsg,?
    WPARAM wParam,?
    LPARAM lParam)?
    {
    // 窗口的句柄
    // 窗口的消息
    // 附加的消息內容
    // 附加的消息內容
    ?
    下來的代碼比對uMsg的值,然后轉入case處理,uMsg 中保存了我們要處理的消息名字。
    switch (uMsg)?
    {
    // 檢查Windows消息
    ?
    如果uMsg等于WM_ACTIVE,查看窗口是否仍然處于激活狀態。如果窗口已被最小化,將變量active設為FALSE。如果窗口已被激活,變量active的值為TRUE。
    case WM_ACTIVATE:?
    {
    if (!HIWORD(wParam))?
    {
    active=TRUE;?
    }
    else
    {
    active=FALSE;?
    }
    return 0;?
    }
    // 監視窗口激活消息

    // 檢查最小化狀態

    // 程序處于激活狀態



    // 程序不再激活

    // 返回消息循環
    ?
    如果消息是WM_SYSCOMMAND(系統命令),再次比對wParam。如果wParam 是 SC_SCREENSAVE 或 SC_MONITORPOWER的話,不是有屏幕保護要運行,就是顯示器想進入節電模式。返回0可以阻止這兩件事發生。
    case WM_SYSCOMMAND:
    {
    switch (wParam)?
    {
    case SC_SCREENSAVE:?
    case SC_MONITORPOWER:?
    return 0;?
    }
    break;?
    }
    // 中斷系統命令Intercept System Commands

    // 檢查系統調用Check System Calls

    // 屏保要運行?
    // 顯示器要進入節電模式?
    // 阻止發生

    // 退出
    ?
    如果 uMsg是WM_CLOSE,窗口將被關閉。我們發出退出消息,主循環將被中斷。變量done被設為TRUE,WinMain()的主循環中止,程序關閉。
    case WM_CLOSE:
    {
    PostQuitMessage(0);?
    return 0;?
    }
    //收到Close消息?

    // 發出退出消息

    ? 
    如果鍵盤有鍵按下,通過讀取wParam的信息可以找出鍵值。我將鍵盤數組keys[ ]相應的數組組成員的值設為TRUE。這樣以后就可以查找key[ ]來得知什么鍵被按下。允許同時按下多個鍵。
    case WM_KEYDOWN:?
    {
    keys[wParam] = TRUE;?
    return 0;?
    }
    // 有鍵按下么?

    // 如果是,設為TRUE
    // 返回

    ? 
    同樣,如果鍵盤有鍵釋放,通過讀取wParam的信息可以找出鍵值。然后將鍵盤數組keys[ ]相應的數組組成員的值設為FALSE。這樣查找key[ ]來得知什么鍵被按下,什么鍵被釋放了。鍵盤上的每個鍵都可以用0-255之間的一個數來代表。舉例來說,當我們按下40所代表的鍵時,keys[40]的值將被設為TRUE。放開的話,它就被設為FALSE。這也是key數組的原理。
    case WM_KEYUP:?
    {
    keys[wParam] = FALSE;?
    return 0;?
    }
    // 有鍵放開么?

    // 如果是,設為FALSE
    // 返回

    ?
    當調整窗口時,uMsg 最后等于消息WM_SIZE。讀取lParam的LOWORD 和HIWORD可以得到窗口新的寬度和高度。將他們傳遞給ReSizeGLScene(),OpenGL場景將調整為新的寬度和高度。
    case WM_SIZE:?
    {
    ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));??
    return 0;?
    }
    }
    // 調整OpenGL窗口大小

    // LoWord=Width,HiWord=Height
    // 返回


     
    其余無關的消息被傳遞給DefWindowProc,讓Windows自行處理。
    //向 DefWindowProc傳遞所有未處理的消息。
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
    }
    下面是我們的Windows程序的入口。將會調用窗口創建例程,處理窗口消息,并監視人機交互。
    int WINAPI WinMain( HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,?
    int nCmdShow)?
    {
    //實例
    // 前一個實例
    // 命令行參數
    // 窗口顯示狀態
    ? 
    我們設置兩個變量。msg 用來檢查是否有消息等待處理。done的初始值設為FALSE。這意味著我們的程序仍未完成運行。只要程序done保持FALSE,程序繼續運行。一旦done的值改變為TRUE,程序退出。
    MSG msg;?
    BOOL done=FALSE;?
    // Windowsx消息結構
    // 用來退出循環的Bool 變量
    這段代碼完全可選。程序彈出一個消息窗口,詢問用戶是否希望在全屏模式下運行。如果用戶單擊NO按鈕,fullscreen變量從缺省的TRUE改變為FALSE,程序也改在窗口模式下運行。
    // 提示用戶選擇運行模式
    if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
    {
    fullscreen=FALSE;
    file://窗口模式

    }
    接著創建OpenGL窗口。CreateGLWindow函數的參數依次為標題、寬度、高度、色彩深度,以及全屏標志。就這么簡單!我很欣賞這段代碼的簡潔。如果未能創建成功,函數返回FALSE。程序立即退出。
    // 創建OpenGL窗口
    if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
    {
    return 0;
    // 失敗退出

    }
     
    下面是循環的開始。只要done保持FALSE,循環一直進行。
    while(!done) // 保持循環直到 done=TRUE
    {
     
    我們要做的第一件事是檢查是否有消息在等待。使用PeekMessage()可以在不鎖住我們的程序的前提下對消息進行檢查。許多程序使用GetMessage(),也可以很好的工作。但使用GetMessage(),程序在收到paint消息或其他別的什么窗口消息之前不會做任何事。
    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
    ?//有消息在等待嗎?
    ? 
    下面的代碼查看是否出現退出消息。如果當前的消息是由PostQuitMessage(0)引起的WM_QUIT,done變量被設為TRUE,程序將退出。
    if (msg.message==WM_QUIT)
    {
    done=TRUE;
    }
    else
    {
    //收到退出消息?

    // 是,則done=TRUE

    // 不是,處理窗口消息
     
    如果不是退出消息,我們翻譯消息,然后發送消息,使得WndProc() 或 Windows能夠處理他們。
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    }
    else
    {
    // 翻譯消息
    // 發送消息


    // 如果沒有消息
     
    如果沒有消息,繪制我們的OpenGL場景。代碼的第一行查看窗口是否激活。如果按下ESC鍵,done變量被設為TRUE,程序將會退出。
    // 繪制場景。監視ESC鍵和來自DrawGLScene()的退出消息
    if (active)
    {
    if (keys[VK_ESCAPE])
    {
    done=TRUE;
    }
    else
    {

    // 程序激活的么?

    // ESC 按下了么?

    // ESC 發出退出信號

    // 不是退出的時候,刷新屏幕
     
    如果程序是激活的且ESC沒有按下,我們繪制場景并交換緩存(使用雙緩存可以實現無閃爍的動畫)。我們實際上在另一個看不見的"屏幕"上繪圖。當我們交換緩存后,我們當前的屏幕被隱藏,現在看到的是剛才看不到的屏幕。這也是我們看不到場景繪制過程的原因。場景只是即時顯示。
    DrawGLScene();
    SwapBuffers(hDC);
    }
    }
    // 繪制場景
    // 交換緩存 (雙緩存)

    下面的一點代碼是最近新加的(05-01-00)。允許用戶按下F1鍵在全屏模式和窗口模式間切換。
    if (keys[VK_F1])
    {
    keys[VK_F1]=FALSE;
    KillGLWindow();
    fullscreen=!fullscreen;
    // 重建 OpenGL 窗口
    if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
    {
    return 0;
    }
    }
    }
    }
    //? F1鍵按下了么?

    // 若是,使對應的Key數組中的值為 FALSE
    // 銷毀當前的窗口
    // 切換 全屏 / 窗口 模式



    // 如果窗口未能創建,程序退出



     
    If the done variable is no longer FALSE, the program quits. We kill the OpenGL window properly so that everything is freed up, and we exit the program.
    如果done變量不再是FALSE,程序退出。正常銷毀OpenGL窗口,將所有的內存釋放,退出程序。
    // 關閉程序
    KillGLWindow();
    return (msg.wParam);
    }

    // 銷毀窗口
    // 退出程序

    ?


    ?

    13:08?| 添加評論| 閱讀評論 (1)| 固定鏈接| 引用通告 (0)| 寫入日志 2006/4/2

    韜光養晦

    成大事者必備的9種手段
      
        每個人做人辦事的手段都是不一樣的,可以講,一個人就有一種手段,一個人就有一種靠自己手段獲得成功的途徑。無數事實表明,有些人就是太過于自信,想念自己確認的手段能夠解決任何問題,但不知道這種往往是起不到任何作用。因此,他們總覺得離成功的目標不是越來越近,而實際上越來越遠。
      
        人生的計劃和行動,是需要靠章法來完成的,而不是靠一些怪招去謀劃的。這就好比在拳擊臺比賽一樣:兩個拳手相互較量,激戰正酣,進退躲閃、撲讓攻守,都有相當靈活的步伐和拳路,他們的一招一式都是為成功而做準備的,這一招一式就叫手段。可惜的是,有很多人并不能看到這一招一式的寓意。
      
        手段是成功的保證,沒有手段的行動和計劃一定是事倍功半的,孫悟空與牛魔王一比高低,靠的是什么?靠的是他七十二變的手段:“飛人”喬丹叱咤NBA賽場靠什么?靠的是他靈活自如、左右盤帶,飛身灌藍的手段。一名話,沒有手段,你永遠吃不到成功的甜果。
      
        手段從何而來?對于那些成大事者來說,他們善于總結自己、反思自己、比較自己,從而避實就虛,找到自己人生的強項——自己究竟能干什么和不能干什么,并付出實際的行動。這個過程就是確立自己成大事手段的過程。不明白這一點,一個人永遠就會在錯誤的方向走下去。
      
        成大事的九種手段:
      
        1、敢于決斷——克服猶豫不定的習性
      
        很多人之所以一事無成,最大的毛病就是缺乏敢于決斷的手段,總是左顧右盼、思前想后,從而錯失成功的最佳時機。成大事者在看到事情的成功可能性到來時,敢于做出重大決斷,因此取得先機。
      
        2、挑戰弱點——徹底改變自己的缺陷
      
        人人都有弱點,不能成大事者總是固守自己的弱點,一生都不會發生重大轉變;能成大事者總是善于從自己的弱點上開刀,去把自己變成一個能力超強的人。一個連自己的缺陷都不能糾正的人,只能是失敗者!
      
        3、突破困境——從失敗中撮成功的資本
      
        人生總要面臨各種困境的挑戰,甚至可以說困境就是“鬼門關”。一般人會在困境面前渾身發抖,而成大事者則能把困境變為成功的有力跳板。
      
        4、抓住機遇——善于選擇、善于創造
      
        機遇就是人生最大的財富。有些人浪費機遇輕而易舉,所以一個個有巨大潛力的機遇都悄然溜跑,成大事都是絕對不允許溜走,并且能縱身撲向機遇。
      
        5、發揮強項——做自己最擅長的事情
      
        一個能力極弱的人肯定難以打開人生局面,他必定是人生舞臺上重量級選手的犧牲品;成大事者關于在自己要做的事情上,充分施展才智,一步一步地拓寬成功之路。
      
        6、調整心態——切忌讓情緒傷害自己
      
        心態消極的人,無論如何都挑不起生活和重擔,因為他們無法直面一個個人生挫折,成大事者則關于高速心態,即使在毫無希望時,也能看到一線成功的亮光。
      
        7、立即行動——只說不做,徒勞無益
      
        一次行動勝過百遍心想。有些人是“語言的巨人,行動的矮子”,所以看不到更為實際現實的事情在他身上發生;成大事者是每天都靠行動來落實自己的人生計劃的。
      
        8、善于交往——巧妙利用人力資源
      
        一個人不懂得交往,必然會推動人際關系的力量。成大事者的特點之一是:善于靠借力、借熱去營造成功的局勢,從而能把一件件難以辦成的事辦成,實現自己人生的規劃。
      
        9、重新規劃——站到更高的起點上
      
        人生是一個過程,成功也是一個過程。你如果滿足于小成功,就會推動大成功。成大事者懂得從小到大的艱辛過程,所以在實現了一個個小成功之后,能繼續拆開下一個人生的“密封袋”。
      
        可以講任何一種手段,都可以導致一種結果,但這個結果是不是最佳的結果,恐怕就很難說了。成大事者總是關于選擇最佳的手段,達到最完善的結果,這就是非一般人所能做到的。因此在成功之路上,你要想成大事,首先要解決的問題就是:你的手段對你推動成功的計劃是否立竿見影!
    12:06?| 添加評論| 閱讀評論 (1)| 固定鏈接| 引用通告 (0)| 寫入日志 2006/4/1

    空指針問題

    編碼的時候遇到的一個問題,我覺得我確實應該好好學學模式。 一個包里面的一個類向調用另一個包里面的類的成員函數~ 比如~
    //"A.class"
    package one;
    import two.B;
    public class A
    {
    ? B b;//這里不能為B b=new B();因為構造函數不能聲明為PUBLIC
    ? b.test();
    } //"B.class"
    package two;
    public class B
    {
    B(){}//構造函數 public void test()//要調用的函數
    {} } 這樣調用產生下面的空指針錯誤
    Exception in thread "main" java.lang.NullPointerException
    請問在不把兩個包合并的情況下如何解決~
    謝謝~ 網上大大的解決方法: package two;
    public class B
    {
    private B(){}//構造函數 public static B getInstance(){
    ??? return new B();
    } public void test()//要調用的函數
    {} } 在A類中用下面的方法調用以創建一個B的實例:
    B b=B.getInstance();

    13:25?| 添加評論| 閱讀評論 (1)| 固定鏈接| 引用通告 (0)| 寫入日志

    使用JDBC訪問DB2的問題

    昨天下午遇到一個問題:使用DB2自帶的驅動db2java.zip文件中的type2類型的驅動訪問DB2,總是報錯:

    java.sql.SQLException: java.lang.UnsatisfiedLinkError: no db2jdbc in java.library.path

    或者:
    java.lang.ClassNotFoundException: COM.ibm.db2.jdbc.app.DB2Driver

    要么就報:沒有合適的驅動。

    我開始查classpath,把多余的驅動都刪除了,還是報錯,后來我又把db2java.zip文件改名為db2java.jar,也還是不行。

    折騰了一個小時,我意識到訪問DB2和訪問Oracle不太一樣,于是google,結果找到這篇文章:

    http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/0402chenjunwei/0402chenjunwei.html

    原來DB2的驅動還分幾個版本,不同的DB2使用的版本的效果也不太一樣。我開始懷疑使是我的驅動的問題,于是拿MyEclipse來試,結果MyEclipse也連接不上,但用DB2的客戶端可以連接到遠程服務器,后來在同事的幫助下,MyEclipse連上了,解決辦法就是把我改成jar的后綴再改回來:o(。

    但使用程序還是訪問不了DB2,經過MyEclipse的測試,可以肯定那個驅動是沒有問題的,所以,還是使用方法或者配置不對,我又搜了很多文章,結果發現這個問題很多人問,國外的帖子也很多,但沒有詳細回答的,又郁悶:o(,后來從幾篇文章中零碎的找到一些提示,是db2jdbc.dll文件找不到,于是我試著把這個文件從DB2的bin目錄下復制到System32目錄下

    ,還是不行,我又把它復制到Java_Home/bin下面,重啟機器,OK!一定要記住:是bin下面!!!

    db2java.zip文件要改名為db2java.jar,并且放到Common/lib下。

    解決方法很簡單,問題是很多人知道了,這樣可以節省大家的時間,但沒人寫下來。

    另外還有一篇參考文章:

    http://www-912.ibm.com/s_dir/slkbase.NSF/0/3f2a44217ec5c05786256c3e007194b3?OpenDocument

    但對這個問題并沒有提出什么有意義的意見。

    13:12?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志 2006/3/28

    DB2中幾種遇到的SQL1032N出錯的解決

    在使用DB2以來,碰到了幾次出現提示SQL1032N錯誤,每次出錯時出錯信息大概如下:
    11/21/2004 22:15:33 0 0 SQL1042C 發生意外的系統錯誤。
    ?
    SQL1032N 未發出啟動數據庫管理器的命令。 SQLSTATE=57019。

    每次出現問題后,都到網上找了很多資料,也問了許多人,費了些力才搞定的。幾次出錯的原因和解決方法都不盡相同,解決后我也只做了個簡單的記錄。一直想把它們寫下來,方便方便后來也遇到同樣問題,跟我一樣到處查找的人,中間也寫了一些廢話,比如我如何查找錯誤,甚至于作了哪些無用功。
    第一種SQL1032N出錯,是某天DB2的實例突然無法啟動了,用db2start就提示大概如下的出錯信息:

    12/30/2004 11:28:39 0 0 SQL1042C 發生意外的系統錯誤。
    SQL1032N 未發出啟動數據庫管理器的命令。 SQLSTATE=57019。

    初次遇到這種問題,還以為會不會是數據庫沒起來,情急之下什么命令比如激活數據庫只類的,都拿來試了試,實例都起不來,當時運行這些命令,肯定都是不行的了。
    后來突然發現,在開啟機器的時候,提示有個服務出錯了沒啟動,由此推想應該就是在Window服務里設置為自動啟動的DB2實例服務沒有正常啟動,我在服務里面手動啟動它,提示這樣的錯誤:

    WINDOWS不能在本地計算機啟動DB2-DB2-0.有關更多信息,查閱系統事件日志.并參考特定服務代碼-8000.

    查看事件管理器,有這樣的記錄:

    DB2-DB2-0服務因4294959296服務性錯誤而停止.來源SERVICE CONTROL 事件ID:7024

    做了這么多,全都是無用功,只限于知道了服務沒起來,等于沒找。
    之后通過各方詢問,終于找到了原因:License到期了。
    在db2cmd界面下運行db2licm -l,可以很明顯的看到許可證已經過期了。
    知道原因所在了,剩下的,就是自己想辦法去解決這個問題了。

    小結:直到現在,在有些論壇中,還很經常看到有人發這種帖子來問,至少我在兩個月內就碰到了三次這種帖子。所以,如果不是可以確定已經有永久授權的情況下,發生這種情況,用db2licm -l查一下,也不算壞事。

    還有一種情況,跟前面的差不多
    也是在啟動實例的時候出現如上的SQL1032N錯誤。在windows NT服務中無法啟動DB2-DB0服務,同時提示:
    出錯1069,登陸失敗錯誤。
    這個錯誤比較簡單,是用來啟動服務的用戶名或密碼錯誤。只需要在服務的屬性中,選擇登陸選項卡,選擇用戶,并填好密碼,重新啟動服務就可以了。

    小結:這種情況,一般發生在切換用戶登陸NT系統或者更改了DB2用戶的密碼的情況下。

    第三種情況是這樣的:
    最開始,是突然DB2的客戶端連接不上server了,提示如下:

    ?C:/Documents and Settings/Administrator>db2 connect to fjdldw user install using install3211
    ?SQL30081N? 檢測到通信錯誤。正在使用的通信協議:"TCP/IP"。正在使用的通信API:
    ?"SOCKETS"。檢測到錯誤的位置:"10.142.12.1"。檢測到錯誤的通信函數:"connect"。協
    ?議特定的錯誤代碼:"10061"、"*"、"*"。? SQLSTATE=08001

    我本來還以為真是什么TCP/IP協議的問題,去查找了很多與SQL30081N錯誤相關的信息,都無法解決問題。后來到了在服務器上檢查,發現DB2實例未起來。
    用db2start命令,仍是提示:

    ?D:/Program QLLIB/BIN>db2start
    ?12/30/2004 11:28:39 0 0 SQL1042C 發生意外的系統錯誤。
    ?SQL1032N 未發出啟動數據庫管理器的命令。 SQLSTATE=57019

    ?用db2 get dbm cfg查看配置文件,因未作過其他操作,所以沒有什么異常
    查看相應實例下的db2diag.log文件,摘取真正有用的部分出錯日志:

    ?Failed to create the memory segment used for communication with fenced routines. If re-starting db2, ensure no db2fmp processes were on the instance prior to start. Otherwise, you can ajust this value through DB2_FMP_COMM_HEAPSZ db2set value, or by decreasing your ASLHEAPSZ setting.
    ?
    依據ensure no db2fmp processes were on the instance prior to start,將任務管理器里的db2fmp進程全部殺掉,然后重新啟動實例。db2start,OK!

    小結:后來查了一查,db2fmp進程用于執行受保護的存儲過程,或者自定義函數。這次出錯的原因,一直沒有弄清楚。但是,通過這次解決,可以說明一點,出了錯誤,查查db2diag.log文件,總是不會錯的。^_^
    22:42?| 添加評論| 固定鏈接| 引用通告 (0)| 寫入日志 2006/3/5

    JDBC連接數據庫經驗技巧集萃

    JDBC是如何連接數據庫的?

    jdbc.sql.Driver d =
    ??????????? (Driver)Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");???????? jdbc.sql.Connection con =
    ??????????? DriverManager.getConnection("jdbc:odbc:pubs","sa","");?

    以上的語句您已經很熟了吧 但
    ?到底是怎么連上數據庫的?:-)

    java.sql包中的 java.sql.Driver, jdbc.sql.Connection等提供給程序開發人員統一的開發接口,數據庫提供商提供相應的實現,對程序開發人員來講只要知道這些接口都有哪些方法就可以了,但我們可以深入一些 看看到底這里面都做了那些事, 同時也可以學習其中的編程模式(如Interface模式等)

    1 Class.forName(String classname) 的源碼為:
    public final
    class Class implements java.io.Serializable {
    ...
    public static Class forName(String className)
    ??? throws ClassNotFoundException {
    ?return forName0(className, true, ClassLoader.getCallerClassLoader());
    }

    ...
    }
    關于forName0 請自己查看jdk source.

    目的是把指定的Class裝載到JVM中來。(注意class的裝載、初始化過程)
    在裝載過程中將執行被裝載類的static塊(如下)

    2 sun的JdbcOdbcDriver 源碼
    public class JdbcOdbcDriver extends JdbcOdbcObject
    ??? implements JdbcOdbcDriverInterface
    {
    ? ...
    ? /**
    ?? * connect to DB
    ?? */
    ? public synchronized Connection connect(String s, Properties properties)
    ??????? throws SQLException
    ??? {
    ??????? if(JdbcOdbcObject.isTracing())
    ??????????? JdbcOdbcObject.trace("*Driver.connect (" + s + ")");
    ??????? if(!acceptsURL(s))
    ??????????? return null;
    ??????? if(hDbc != 0)
    ??????? {
    ??????????? disconnect(hDbc);
    ??????????? closeConnection(hDbc);
    ??????????? hDbc = 0;
    ??????? }
    ??????? if(!initialize())
    ??????? {
    ??????????? return null;
    ??????? }
    ??????? else
    ??????? {
    ??????????? JdbcOdbcConnection jdbcodbcconnection = new JdbcOdbcConnection(OdbcApi, hEnv, this);
    ??????????? jdbcodbcconnection.initialize(getSubName(s), properties, DriverManager.getLoginTimeout());
    ??????????? jdbcodbcconnection.setURL(s);
    ??????????? return jdbcodbcconnection;
    ??????? }
    ?? }
    ?
    ? static
    ??? {
    ??????? if(JdbcOdbcObject.isTracing())
    ??????????? JdbcOdbcObject.trace("JdbcOdbcDriver class loaded");
    ??????? JdbcOdbcDriver jdbcodbcdriver = new JdbcOdbcDriver();
    ??????? try
    ??????? {
    ??????????? DriverManager.registerDriver(jdbcodbcdriver);
    ??????? }
    ??????? catch(SQLException sqlexception)
    ??????? {
    ??????????? if(JdbcOdbcObject.isTracing())
    ??????????????? JdbcOdbcObject.trace("Unable to register driver");
    ??????? }
    ??? }
    }

    public interface JdbcOdbcDriverInterface
    ??? extends Driver
    {
    ? ...
    }

    3 連接過程
    jdbc.sql.Connection con =
    ??????????? DriverManager.getConnection("jdbc:odbc:pubs","sa","");

    public class DriverManager {
    public static synchronized Connection getConnection(String url,
    ????????????? String user, String password) throws SQLException {
    ??????? java.util.Properties info = new java.util.Properties();

    ??????? // Gets the classloader of the code that called this method, may
    ?// be null.
    ?ClassLoader callerCL = DriverManager.getCallerClassLoader();

    ?if (user != null) {
    ???? info.put("user", user);
    ?}
    ?if (password != null) {
    ???? info.put("password", password);
    ?}

    ??????? return (getConnection(url, info, callerCL));
    }
    private static synchronized Connection getConnection(
    ??????? String url,
    ??????? java.util.Properties info,
    ??????? ClassLoader callerCL) throws SQLException
    {
    ...
    Connection result = di.driver.connect(url, info);
    ...
    }?????????????
    }
    4? 結構圖:
    請點擊查看 結構圖?? http://jdeveloper.myrice.com/doc/jdbc/images/howjdbc.jpg

    JDBC連接DB2數據庫詳解??

    關于DB2數據庫的JDBC連接文章有很多,比較出名的有諸如“JDBC數據庫連接大全”和“JSP的DB2連接數據庫”,雖然都是很詳細的資料,也都說解決了前人沒有解決的問題,但還是有許多紕漏。我就這兩天的經驗給大家寫一篇關于JDBC連接數據庫的文章,以解決一部分人的疑問。
    ?????第一:JDBC是JDK的一部分(至少在
    Java?Tiger?Development?Kits中是這樣),使用JDBC直接在程序文件中寫import?java.sql.*;即可使用了。
    ?????第二:連接字符串的格式。本地連接的連接字符串格式為jdbc:product_name:database_name,遠程連接的格式為:product_name://host_name/port_number:database_name。即如果我的
    數據庫名字為rdb,則本地連接字符串為jdbc:db2:rdb(當然rdb一定是處于DB2的默認實例之中的),而遠程連接字符串為jdbc:db2://192.168.1.10/50000:rdb(這里192.168.1.10為數據庫所在服務器IP地址,而50000為DB2連接服務的端口號)。
    ?????第三:?安裝DB2
    數據庫提供的為JDBC準備的類庫(在.NET中叫Provider,在Java中怎么叫還沒研究過)。查找IBM?DB2?UDB的安裝目錄或者Java??Tiger的JDK目錄你會找到db2java.zip,把它先做一個副本以后就它最有用了。現在我們開始討論數據庫連接的程序代碼。

    ?????應用
    程序連接DB2數據庫
    先將db2
    java.zip解壓縮,把COM目錄轉移到代碼的當前目錄,然后我們開始注冊這個Provider的實例,代碼為:
    ?????Class.forName(“COM.ibm.db2.jdbc.app.DB2Driver”).newInstance();
    ?????Class.forName(“COM.ibm.db2.jdbc.net.DB2Driver”).newInstance():
    這兩句任選其一,作用稍有不同,前者是具有DB2客戶端的Provider實例,后者是無DB2客戶端的Provider實例。
    此后再寫Connection?con=DriverManager.getConnection();即可得到
    數據庫連接的實例。

    ???
    JSP中連接DB2數據庫
    ???這里以Tomcat作為Servlet容器,如果想在Tomcat中使用DB2?
    ???Provider必須把db2
    java.zip更名成db2java.jar然后復制到tomcat主目錄下c

    Class.forName("com.ibm.db2.jdbc.app.DB2Driver").newInstance();
    String url="jdbc:db2://localhost:5000/sample"; //sample為你的數據庫名
    String user="admin";
    String password="";
    Connection conn= DriverManager.getConnection(url,user,password);
    ??

    總結

    以上是生活随笔為你收集整理的技术文章的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    黑人大群体交免费视频 | 久久久久久亚洲精品a片成人 | 久久久成人毛片无码 | 国产亚洲tv在线观看 | 国产精品自产拍在线观看 | 色噜噜亚洲男人的天堂 | 少妇激情av一区二区 | 国产精品对白交换视频 | 中文亚洲成a人片在线观看 | 国产亚洲精品久久久久久 | 小鲜肉自慰网站xnxx | 99久久精品日本一区二区免费 | 性开放的女人aaa片 | 初尝人妻少妇中文字幕 | 久久精品国产一区二区三区肥胖 | 国产成人综合在线女婷五月99播放 | 内射爽无广熟女亚洲 | 亚洲の无码国产の无码步美 | 国产精品久久久久久亚洲影视内衣 | 欧美亚洲国产一区二区三区 | 亚洲天堂2017无码中文 | 蜜桃臀无码内射一区二区三区 | 亚洲小说春色综合另类 | 亚洲国产精品毛片av不卡在线 | 国产高清av在线播放 | 娇妻被黑人粗大高潮白浆 | 伊人久久大香线蕉av一区二区 | 婷婷色婷婷开心五月四房播播 | 日本一卡二卡不卡视频查询 | 日韩人妻系列无码专区 | 国产深夜福利视频在线 | 中文字幕无码免费久久9一区9 | 我要看www免费看插插视频 | 亚洲熟熟妇xxxx | 国产艳妇av在线观看果冻传媒 | 初尝人妻少妇中文字幕 | 白嫩日本少妇做爰 | 国产亚洲精品久久久ai换 | 久久99精品国产麻豆蜜芽 | 国产精品久久久久久亚洲影视内衣 | 青春草在线视频免费观看 | 久久精品无码一区二区三区 | 婷婷五月综合缴情在线视频 | 又紧又大又爽精品一区二区 | 免费播放一区二区三区 | 亚洲午夜久久久影院 | 永久免费观看美女裸体的网站 | 色 综合 欧美 亚洲 国产 | 在线精品亚洲一区二区 | 2020最新国产自产精品 | 1000部啪啪未满十八勿入下载 | 中文字幕人妻无码一区二区三区 | 亚洲国产精品一区二区第一页 | 在线精品国产一区二区三区 | 国产av人人夜夜澡人人爽麻豆 | 亚洲人成无码网www | 欧美人妻一区二区三区 | 伊人久久大香线蕉午夜 | 乱人伦人妻中文字幕无码 | 午夜福利一区二区三区在线观看 | 婷婷综合久久中文字幕蜜桃三电影 | 亚洲阿v天堂在线 | 亚洲综合另类小说色区 | 六月丁香婷婷色狠狠久久 | 人人爽人人澡人人人妻 | 日本www一道久久久免费榴莲 | 久久婷婷五月综合色国产香蕉 | 日韩在线不卡免费视频一区 | www成人国产高清内射 | 亚洲成在人网站无码天堂 | 国内揄拍国内精品少妇国语 | 无码国产色欲xxxxx视频 | 丁香花在线影院观看在线播放 | 一本色道婷婷久久欧美 | 日本高清一区免费中文视频 | 国产亚洲人成a在线v网站 | 99riav国产精品视频 | av香港经典三级级 在线 | 久久久久99精品国产片 | 老头边吃奶边弄进去呻吟 | 性欧美牲交在线视频 | 国产亚洲美女精品久久久2020 | 未满小14洗澡无码视频网站 | 无遮挡啪啪摇乳动态图 | 免费观看激色视频网站 | 日韩精品无码免费一区二区三区 | 亚洲精品国产精品乱码不卡 | 少妇高潮一区二区三区99 | 久久国产精品二国产精品 | 午夜精品一区二区三区的区别 | 无遮无挡爽爽免费视频 | 亚无码乱人伦一区二区 | 久久精品无码一区二区三区 | 国产极品视觉盛宴 | 国产艳妇av在线观看果冻传媒 | 精品久久久久久人妻无码中文字幕 | 亚洲国产精品美女久久久久 | 国产午夜无码精品免费看 | 天天躁夜夜躁狠狠是什么心态 | 一本一道久久综合久久 | 亚洲午夜久久久影院 | 日本精品人妻无码免费大全 | 国产精品无码一区二区三区不卡 | 国产人妻人伦精品1国产丝袜 | 日本高清一区免费中文视频 | 国产莉萝无码av在线播放 | 男女性色大片免费网站 | √天堂资源地址中文在线 | 久久综合九色综合欧美狠狠 | 野狼第一精品社区 | 国产人妻人伦精品1国产丝袜 | 真人与拘做受免费视频一 | 性色av无码免费一区二区三区 | 国产成人精品三级麻豆 | av小次郎收藏 | 色婷婷综合中文久久一本 | 色五月丁香五月综合五月 | 亚洲熟妇色xxxxx欧美老妇y | 欧美喷潮久久久xxxxx | 免费人成在线视频无码 | 人人爽人人澡人人高潮 | 国产国产精品人在线视 | 无套内射视频囯产 | 老司机亚洲精品影院 | 亚洲成a人片在线观看日本 | 国产午夜精品一区二区三区嫩草 | 色综合久久久无码中文字幕 | 麻豆md0077饥渴少妇 | 55夜色66夜色国产精品视频 | 99riav国产精品视频 | 综合人妻久久一区二区精品 | 国产真实夫妇视频 | 熟妇人妻无码xxx视频 | 一本久久a久久精品vr综合 | 麻豆精品国产精华精华液好用吗 | 乌克兰少妇性做爰 | 国产成人精品必看 | 亚洲啪av永久无码精品放毛片 | 国产精品亚洲五月天高清 | 国产精品va在线观看无码 | 精品乱码久久久久久久 | 欧美阿v高清资源不卡在线播放 | 亚洲 a v无 码免 费 成 人 a v | 久久99精品久久久久婷婷 | 伊人久久大香线焦av综合影院 | 又粗又大又硬毛片免费看 | 嫩b人妻精品一区二区三区 | 无码精品国产va在线观看dvd | 丰满人妻翻云覆雨呻吟视频 | 在线a亚洲视频播放在线观看 | 日产国产精品亚洲系列 | 精品国产一区二区三区四区 | 沈阳熟女露脸对白视频 | 免费无码肉片在线观看 | 国产亚洲欧美在线专区 | 四虎4hu永久免费 | 国产成人午夜福利在线播放 | 久久久精品成人免费观看 | 精品午夜福利在线观看 | 无码国产激情在线观看 | 女人高潮内射99精品 | 日本在线高清不卡免费播放 | 欧美性生交活xxxxxdddd | 亚洲精品一区二区三区在线 | 国产一区二区不卡老阿姨 | 波多野结衣 黑人 | 亚洲啪av永久无码精品放毛片 | 内射爽无广熟女亚洲 | 国产成人午夜福利在线播放 | 久久午夜夜伦鲁鲁片无码免费 | 99riav国产精品视频 | 国产sm调教视频在线观看 | 国产日产欧产精品精品app | 精品国产一区av天美传媒 | 日日躁夜夜躁狠狠躁 | aⅴ亚洲 日韩 色 图网站 播放 | 国产片av国语在线观看 | 麻豆av传媒蜜桃天美传媒 | 国产精品成人av在线观看 | 中文字幕色婷婷在线视频 | 精品亚洲成av人在线观看 | 少妇被黑人到高潮喷出白浆 | 国精品人妻无码一区二区三区蜜柚 | 任你躁国产自任一区二区三区 | 成年女人永久免费看片 | 人人妻人人澡人人爽欧美精品 | 疯狂三人交性欧美 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 人人妻人人澡人人爽人人精品 | 国产精品亚洲五月天高清 | 国产成人精品视频ⅴa片软件竹菊 | 久久无码中文字幕免费影院蜜桃 | 国产精品va在线观看无码 | 亚洲色成人中文字幕网站 | 好男人www社区 | 午夜福利试看120秒体验区 | 欧美黑人性暴力猛交喷水 | 亚洲成a人片在线观看无码3d | 欧美喷潮久久久xxxxx | 强伦人妻一区二区三区视频18 | 疯狂三人交性欧美 | 国产精品高潮呻吟av久久4虎 | 国产在线一区二区三区四区五区 | 中文字幕无线码免费人妻 | 少妇人妻av毛片在线看 | 久9re热视频这里只有精品 | 欧美xxxx黑人又粗又长 | 在教室伦流澡到高潮hnp视频 | 国产精品怡红院永久免费 | 一本久久伊人热热精品中文字幕 | 一二三四在线观看免费视频 | 无码中文字幕色专区 | 国产精品对白交换视频 | 动漫av网站免费观看 | a在线亚洲男人的天堂 | 亚洲国产精华液网站w | 中文字幕av日韩精品一区二区 | 国内精品久久久久久中文字幕 | 无码一区二区三区在线 | 熟妇人妻无码xxx视频 | 亚洲男人av香蕉爽爽爽爽 | 精品无码国产一区二区三区av | 亚洲欧美综合区丁香五月小说 | 亚洲精品一区二区三区婷婷月 | 人妻与老人中文字幕 | 欧美 丝袜 自拍 制服 另类 | 亚洲国产日韩a在线播放 | 色窝窝无码一区二区三区色欲 | 成人精品天堂一区二区三区 | 国产在线精品一区二区高清不卡 | 精品水蜜桃久久久久久久 | 女高中生第一次破苞av | 久久婷婷五月综合色国产香蕉 | 中文字幕无码日韩欧毛 | 久久99国产综合精品 | 日本大香伊一区二区三区 | 国产亚洲欧美日韩亚洲中文色 | 亚洲国产精品毛片av不卡在线 | 一个人免费观看的www视频 | 亚洲精品国产第一综合99久久 | 欧美日韩色另类综合 | 色综合视频一区二区三区 | 国产sm调教视频在线观看 | 日韩av无码一区二区三区不卡 | 久久精品中文字幕一区 | 国产精品亚洲а∨无码播放麻豆 | 国产精品视频免费播放 | 日本一卡二卡不卡视频查询 | 国产精品爱久久久久久久 | 国产精品亚洲综合色区韩国 | 久久亚洲中文字幕无码 | 97精品人妻一区二区三区香蕉 | 亚洲成a人一区二区三区 | a国产一区二区免费入口 | 偷窥村妇洗澡毛毛多 | 日本免费一区二区三区最新 | 无码人妻精品一区二区三区下载 | 久久人妻内射无码一区三区 | 国产午夜手机精彩视频 | 欧美野外疯狂做受xxxx高潮 | 欧美肥老太牲交大战 | 中国女人内谢69xxxx | 久久久久免费精品国产 | 中文字幕无码av波多野吉衣 | 精品国产一区av天美传媒 | 无码精品人妻一区二区三区av | 久久人人爽人人人人片 | 狠狠噜狠狠狠狠丁香五月 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 亚洲狠狠色丁香婷婷综合 | 婷婷六月久久综合丁香 | 性生交大片免费看女人按摩摩 | 风流少妇按摩来高潮 | 中文字幕精品av一区二区五区 | 四虎4hu永久免费 | 秋霞成人午夜鲁丝一区二区三区 | 国产又爽又黄又刺激的视频 | 色综合久久网 | 国内丰满熟女出轨videos | 88国产精品欧美一区二区三区 | 国产精品久久久午夜夜伦鲁鲁 | 精品亚洲韩国一区二区三区 | 婷婷丁香五月天综合东京热 | 亚洲狠狠婷婷综合久久 | 亚洲精品欧美二区三区中文字幕 | 国产凸凹视频一区二区 | 国产麻豆精品精东影业av网站 | 中文字幕色婷婷在线视频 | 毛片内射-百度 | 国产亚洲精品精品国产亚洲综合 | 欧美亚洲国产一区二区三区 | 4hu四虎永久在线观看 | 好男人www社区 | 欧美人与禽猛交狂配 | 波多野结衣一区二区三区av免费 | 牲欲强的熟妇农村老妇女 | 影音先锋中文字幕无码 | 国产精品成人av在线观看 | 久久国产自偷自偷免费一区调 | 亚洲国产精品无码一区二区三区 | 欧洲极品少妇 | 精品国产乱码久久久久乱码 | 久久亚洲国产成人精品性色 | 国产在线精品一区二区三区直播 | 亚洲欧美日韩国产精品一区二区 | 国产免费久久精品国产传媒 | 亚无码乱人伦一区二区 | 久久无码专区国产精品s | 夜夜高潮次次欢爽av女 | 99久久精品午夜一区二区 | 成人影院yy111111在线观看 | 丰满人妻一区二区三区免费视频 | 97资源共享在线视频 | 又湿又紧又大又爽a视频国产 | 婷婷丁香六月激情综合啪 | 狠狠综合久久久久综合网 | 少妇的肉体aa片免费 | 亚洲熟妇色xxxxx亚洲 | 日本乱偷人妻中文字幕 | 亚洲の无码国产の无码步美 | 国产成人无码av在线影院 | 亚洲人成影院在线观看 | 亚洲国产欧美日韩精品一区二区三区 | 露脸叫床粗话东北少妇 | 午夜无码人妻av大片色欲 | 国产精品久久久久久无码 | 欧美性生交活xxxxxdddd | 亚洲小说春色综合另类 | 国产超碰人人爽人人做人人添 | 99精品国产综合久久久久五月天 | 中国大陆精品视频xxxx | 亚洲精品国产第一综合99久久 | 久久综合九色综合欧美狠狠 | 欧美成人高清在线播放 | 欧美野外疯狂做受xxxx高潮 | 国产又粗又硬又大爽黄老大爷视 | 亚洲中文字幕在线观看 | 国产极品视觉盛宴 | 亚洲色无码一区二区三区 | 国精产品一区二区三区 | а√天堂www在线天堂小说 | 国产精品鲁鲁鲁 | 大乳丰满人妻中文字幕日本 | a国产一区二区免费入口 | 精品国产青草久久久久福利 | 久久精品无码一区二区三区 | 国产乡下妇女做爰 | 国产黄在线观看免费观看不卡 | 国产精品久久久久7777 | 亚洲成a人片在线观看日本 | 人人爽人人澡人人人妻 | 啦啦啦www在线观看免费视频 | 久久亚洲中文字幕无码 | 日本饥渴人妻欲求不满 | 中文字幕无线码免费人妻 | 亚洲成av人综合在线观看 | 国产免费无码一区二区视频 | 无套内谢的新婚少妇国语播放 | 亚洲成色在线综合网站 | 欧美自拍另类欧美综合图片区 | 日日夜夜撸啊撸 | 丰满岳乱妇在线观看中字无码 | 中文精品无码中文字幕无码专区 | 无码人妻精品一区二区三区不卡 | 香蕉久久久久久av成人 | 国产av一区二区三区最新精品 | 一区二区传媒有限公司 | 中文字幕无码日韩专区 | 中文字幕乱码人妻无码久久 | 亚洲熟妇色xxxxx亚洲 | 欧美丰满熟妇xxxx | 波多野结衣乳巨码无在线观看 | 亚洲熟妇自偷自拍另类 | 久久精品国产99精品亚洲 | 在线播放亚洲第一字幕 | 国产猛烈高潮尖叫视频免费 | 亚洲日韩一区二区三区 | 欧美国产日韩久久mv | 97人妻精品一区二区三区 | 99久久精品无码一区二区毛片 | 99riav国产精品视频 | av人摸人人人澡人人超碰下载 | 一本无码人妻在中文字幕免费 | 成熟女人特级毛片www免费 | 色婷婷av一区二区三区之红樱桃 | 欧美成人家庭影院 | 亚洲国产成人av在线观看 | 久久久精品欧美一区二区免费 | 亚洲午夜久久久影院 | 国产乱人偷精品人妻a片 | 精品人妻人人做人人爽 | 亚洲成av人在线观看网址 | 午夜熟女插插xx免费视频 | 国产人妻人伦精品 | 中文字幕人成乱码熟女app | 亚洲熟熟妇xxxx | 国产9 9在线 | 中文 | a国产一区二区免费入口 | 真人与拘做受免费视频一 | 国产精品久久久久久久影院 | 色狠狠av一区二区三区 | 97夜夜澡人人爽人人喊中国片 | 丰满人妻翻云覆雨呻吟视频 | 中文字幕中文有码在线 | 麻豆国产丝袜白领秘书在线观看 | 亚洲高清偷拍一区二区三区 | 无码人妻出轨黑人中文字幕 | 亚洲成av人影院在线观看 | 青青草原综合久久大伊人精品 | 亚洲中文字幕av在天堂 | 久久亚洲中文字幕精品一区 | 久久精品视频在线看15 | 欧美性生交活xxxxxdddd | 蜜臀av无码人妻精品 | 无码人妻久久一区二区三区不卡 | 人人妻人人澡人人爽精品欧美 | 国产网红无码精品视频 | 亚洲大尺度无码无码专区 | 色综合久久久无码中文字幕 | 在线播放免费人成毛片乱码 | 兔费看少妇性l交大片免费 | 久久久久av无码免费网 | 亚洲乱码日产精品bd | 欧美老熟妇乱xxxxx | 日韩精品一区二区av在线 | 色诱久久久久综合网ywww | 亚洲春色在线视频 | 欧美日本精品一区二区三区 | 精品一区二区三区无码免费视频 | 国产成人一区二区三区在线观看 | 国产精品无码一区二区桃花视频 | 色欲综合久久中文字幕网 | 少妇一晚三次一区二区三区 | 欧美日韩亚洲国产精品 | 四虎4hu永久免费 | 秋霞成人午夜鲁丝一区二区三区 | 中文字幕av无码一区二区三区电影 | 久久国产精品精品国产色婷婷 | 久久久av男人的天堂 | 宝宝好涨水快流出来免费视频 | 中文字幕乱码中文乱码51精品 | 国产午夜无码精品免费看 | 国产精品igao视频网 | 久久 国产 尿 小便 嘘嘘 | 黑人巨大精品欧美黑寡妇 | 人人澡人人妻人人爽人人蜜桃 | 色婷婷欧美在线播放内射 | 久久久久成人片免费观看蜜芽 | 狠狠色噜噜狠狠狠狠7777米奇 | 婷婷六月久久综合丁香 | 国产黑色丝袜在线播放 | 国产欧美熟妇另类久久久 | 亚洲日本在线电影 | 久久久无码中文字幕久... | 久久天天躁狠狠躁夜夜免费观看 | 亚洲 日韩 欧美 成人 在线观看 | 永久免费精品精品永久-夜色 | 7777奇米四色成人眼影 | 亚洲一区二区三区香蕉 | 亚洲小说春色综合另类 | 免费中文字幕日韩欧美 | 日本熟妇人妻xxxxx人hd | 无码国产激情在线观看 | 小鲜肉自慰网站xnxx | 3d动漫精品啪啪一区二区中 | 精品无码成人片一区二区98 | 国产成人精品久久亚洲高清不卡 | 中文字幕乱码亚洲无线三区 | 国产成人无码专区 | 曰韩少妇内射免费播放 | 激情内射亚州一区二区三区爱妻 | 亚洲狠狠色丁香婷婷综合 | 露脸叫床粗话东北少妇 | v一区无码内射国产 | 国产乱子伦视频在线播放 | 中文毛片无遮挡高清免费 | 人人妻人人澡人人爽人人精品 | 国产欧美精品一区二区三区 | 日本免费一区二区三区最新 | 亚洲欧美日韩成人高清在线一区 | 欧美放荡的少妇 | 99久久无码一区人妻 | 爆乳一区二区三区无码 | 天海翼激烈高潮到腰振不止 | 国产成人无码午夜视频在线观看 | 东北女人啪啪对白 | 色欲综合久久中文字幕网 | 亚洲色www成人永久网址 | 疯狂三人交性欧美 | 亚洲国产综合无码一区 | 久激情内射婷内射蜜桃人妖 | 日本高清一区免费中文视频 | 久久国产精品二国产精品 | 日本在线高清不卡免费播放 | 国产绳艺sm调教室论坛 | 亚洲日韩一区二区 | 成人毛片一区二区 | 牲欲强的熟妇农村老妇女视频 | 国产成人无码av在线影院 | 国产在线精品一区二区三区直播 | 亚洲日本一区二区三区在线 | 亚洲综合伊人久久大杳蕉 | 99麻豆久久久国产精品免费 | 秋霞成人午夜鲁丝一区二区三区 | 久久午夜夜伦鲁鲁片无码免费 | 亚洲综合在线一区二区三区 | 婷婷丁香六月激情综合啪 | 久久精品视频在线看15 | 噜噜噜亚洲色成人网站 | 红桃av一区二区三区在线无码av | 2020久久超碰国产精品最新 | 亚洲另类伦春色综合小说 | 欧美老妇交乱视频在线观看 | 日本一本二本三区免费 | 大肉大捧一进一出视频出来呀 | 性色欲情网站iwww九文堂 | 午夜性刺激在线视频免费 | 麻豆精品国产精华精华液好用吗 | 日本va欧美va欧美va精品 | 色诱久久久久综合网ywww | 中文字幕人妻丝袜二区 | 色婷婷综合中文久久一本 | 在线а√天堂中文官网 | 国产精品久久国产三级国 | 国产无遮挡又黄又爽免费视频 | 国产乱人偷精品人妻a片 | 午夜理论片yy44880影院 | 国产suv精品一区二区五 | 欧美激情一区二区三区成人 | 性啪啪chinese东北女人 | 亚洲一区二区三区 | 女人被男人爽到呻吟的视频 | 妺妺窝人体色www婷婷 | 久久久精品国产sm最大网站 | 亚洲午夜福利在线观看 | 亚洲の无码国产の无码影院 | 国产欧美精品一区二区三区 | 精品国产精品久久一区免费式 | 亚洲日本一区二区三区在线 | 狠狠色噜噜狠狠狠狠7777米奇 | 欧美自拍另类欧美综合图片区 | 欧美三级a做爰在线观看 | 欧美三级不卡在线观看 | 欧美国产亚洲日韩在线二区 | 亚洲中文字幕在线无码一区二区 | 中文字幕无码免费久久9一区9 | 精品人妻人人做人人爽夜夜爽 | 久久久久久国产精品无码下载 | 国产精品久久久久久亚洲影视内衣 | 亚洲人成影院在线观看 | 国产疯狂伦交大片 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 国产av无码专区亚洲a∨毛片 | 亚洲中文字幕av在天堂 | 成年美女黄网站色大免费全看 | 国产精品无套呻吟在线 | 亚洲国产欧美国产综合一区 | 麻豆精品国产精华精华液好用吗 | 未满小14洗澡无码视频网站 | 精品国精品国产自在久国产87 | 亚洲精品久久久久中文第一幕 | 婷婷五月综合激情中文字幕 | 久久熟妇人妻午夜寂寞影院 | 荫蒂添的好舒服视频囗交 | 国产免费观看黄av片 | 樱花草在线播放免费中文 | 日本一本二本三区免费 | 国产精品无码一区二区三区不卡 | 久久亚洲精品中文字幕无男同 | 伊人久久婷婷五月综合97色 | 人妻aⅴ无码一区二区三区 | 精品国产av色一区二区深夜久久 | 国产精品久久久一区二区三区 | 最新国产乱人伦偷精品免费网站 | 国产精品va在线播放 | 夜夜高潮次次欢爽av女 | 国产三级精品三级男人的天堂 | 色欲久久久天天天综合网精品 | 国产区女主播在线观看 | 波多野结衣一区二区三区av免费 | 日本一区二区更新不卡 | 精品一区二区不卡无码av | 久久久中文久久久无码 | 99久久久国产精品无码免费 | 狂野欧美性猛xxxx乱大交 | 久久综合九色综合97网 | 麻豆人妻少妇精品无码专区 | 性欧美疯狂xxxxbbbb | 国产又爽又猛又粗的视频a片 | 亚洲精品国产a久久久久久 | 精品久久久中文字幕人妻 | 国产三级精品三级男人的天堂 | 女高中生第一次破苞av | 亚洲成色在线综合网站 | 在线播放亚洲第一字幕 | 亚洲中文字幕va福利 | 国产9 9在线 | 中文 | 国产sm调教视频在线观看 | 日日鲁鲁鲁夜夜爽爽狠狠 | 国产无遮挡吃胸膜奶免费看 | 人妻插b视频一区二区三区 | 清纯唯美经典一区二区 | 丰满肥臀大屁股熟妇激情视频 | 日日橹狠狠爱欧美视频 | 18无码粉嫩小泬无套在线观看 | 亚洲 另类 在线 欧美 制服 | 久久国产精品偷任你爽任你 | 欧美野外疯狂做受xxxx高潮 | 亚拍精品一区二区三区探花 | 国产舌乚八伦偷品w中 | 人人妻人人澡人人爽精品欧美 | 国产午夜无码视频在线观看 | 久久天天躁夜夜躁狠狠 | 国产麻豆精品一区二区三区v视界 | 日本成熟视频免费视频 | 波多野结衣高清一区二区三区 | 日韩亚洲欧美中文高清在线 | 一个人看的视频www在线 | 国产亚洲欧美日韩亚洲中文色 | 成人无码精品一区二区三区 | 西西人体www44rt大胆高清 | 亚洲乱亚洲乱妇50p | 亚洲中文字幕在线无码一区二区 | 久久精品国产亚洲精品 | 麻豆人妻少妇精品无码专区 | 四虎影视成人永久免费观看视频 | 日本成熟视频免费视频 | 欧美 日韩 亚洲 在线 | 四虎国产精品免费久久 | 亚洲无人区一区二区三区 | 欧美日韩一区二区免费视频 | 国产综合久久久久鬼色 | 狂野欧美激情性xxxx | 日韩成人一区二区三区在线观看 | 日韩精品无码一区二区中文字幕 | 日产精品高潮呻吟av久久 | 国产午夜福利亚洲第一 | 亚洲乱码国产乱码精品精 | 欧美xxxx黑人又粗又长 | 老熟女重囗味hdxx69 | 亚洲阿v天堂在线 | 久久伊人色av天堂九九小黄鸭 | 蜜桃av抽搐高潮一区二区 | 99久久人妻精品免费一区 | 欧美 日韩 人妻 高清 中文 | 国产午夜福利亚洲第一 | 国产一区二区三区四区五区加勒比 | 天天综合网天天综合色 | 秋霞成人午夜鲁丝一区二区三区 | 久久久久亚洲精品中文字幕 | 人妻少妇精品视频专区 | 熟妇人妻中文av无码 | 亚洲精品中文字幕乱码 | 色一情一乱一伦一视频免费看 | 日日鲁鲁鲁夜夜爽爽狠狠 | 伊人久久婷婷五月综合97色 | 蜜桃臀无码内射一区二区三区 | 国产情侣作爱视频免费观看 | 亚洲 激情 小说 另类 欧美 | 丰满人妻被黑人猛烈进入 | 西西人体www44rt大胆高清 | 最新国产麻豆aⅴ精品无码 | 老熟妇仑乱视频一区二区 | 一本久久a久久精品亚洲 | 一本色道久久综合亚洲精品不卡 | 亲嘴扒胸摸屁股激烈网站 | 无码吃奶揉捏奶头高潮视频 | 97资源共享在线视频 | 欧美精品在线观看 | 亚拍精品一区二区三区探花 | 国产精品.xx视频.xxtv | 精品国产国产综合精品 | 国产人妻大战黑人第1集 | 欧美性猛交内射兽交老熟妇 | 精品国产福利一区二区 | 久久国产精品精品国产色婷婷 | 久久精品女人天堂av免费观看 | 亚洲精品成人福利网站 | 在线观看免费人成视频 | 亚洲中文字幕无码中字 | 中文字幕无码免费久久9一区9 | 亚洲熟悉妇女xxx妇女av | 欧美 日韩 人妻 高清 中文 | 国产香蕉97碰碰久久人人 | 欧美激情综合亚洲一二区 | 日本www一道久久久免费榴莲 | 日本熟妇人妻xxxxx人hd | 日欧一片内射va在线影院 | 丁香啪啪综合成人亚洲 | 国产成人精品一区二区在线小狼 | 国产一区二区三区四区五区加勒比 | 国产精品怡红院永久免费 | 亚洲成a人片在线观看无码3d | 无码中文字幕色专区 | 欧美成人免费全部网站 | 99久久亚洲精品无码毛片 | 窝窝午夜理论片影院 | 纯爱无遮挡h肉动漫在线播放 | 中文字幕无码热在线视频 | 2020久久香蕉国产线看观看 | 麻花豆传媒剧国产免费mv在线 | 日韩精品久久久肉伦网站 | yw尤物av无码国产在线观看 | 欧美 日韩 人妻 高清 中文 | 精品午夜福利在线观看 | 高潮喷水的毛片 | 老太婆性杂交欧美肥老太 | 色综合视频一区二区三区 | 欧美国产日韩亚洲中文 | 成人综合网亚洲伊人 | 在线欧美精品一区二区三区 | 综合人妻久久一区二区精品 | 少妇无码一区二区二三区 | 人人妻人人澡人人爽人人精品 | 在线观看免费人成视频 | 日本丰满护士爆乳xxxx | 精品国精品国产自在久国产87 | 亚洲成a人片在线观看无码 | 国产亚av手机在线观看 | 人妻少妇精品无码专区二区 | 综合网日日天干夜夜久久 | 久久午夜无码鲁丝片 | 欧美国产日韩亚洲中文 | 中文字幕+乱码+中文字幕一区 | ass日本丰满熟妇pics | 国产成人午夜福利在线播放 | 国产超级va在线观看视频 | 精品国产一区二区三区av 性色 | 成人一在线视频日韩国产 | 久久精品国产一区二区三区肥胖 | 狠狠亚洲超碰狼人久久 | 亚洲一区二区三区无码久久 | 大屁股大乳丰满人妻 | 亚拍精品一区二区三区探花 | 久久午夜无码鲁丝片午夜精品 | 国产亚洲欧美在线专区 | 狠狠综合久久久久综合网 | 综合激情五月综合激情五月激情1 | 久久人人爽人人人人片 | 精品国精品国产自在久国产87 | 水蜜桃色314在线观看 | 亚洲乱亚洲乱妇50p | 成熟妇人a片免费看网站 | 欧美成人家庭影院 | 中文字幕 亚洲精品 第1页 | 女人被爽到呻吟gif动态图视看 | 国产免费久久精品国产传媒 | 日日夜夜撸啊撸 | 全黄性性激高免费视频 | 亚洲一区二区三区播放 | 国产成人精品必看 | 欧美喷潮久久久xxxxx | 在线观看国产一区二区三区 | 国产成人精品无码播放 | 久久精品国产一区二区三区 | 亚洲综合伊人久久大杳蕉 | 午夜精品久久久久久久久 | 99久久久国产精品无码免费 | √天堂资源地址中文在线 | 男女下面进入的视频免费午夜 | 无码一区二区三区在线 | 日本精品高清一区二区 | 激情内射日本一区二区三区 | 国产片av国语在线观看 | 日本xxxx色视频在线观看免费 | 水蜜桃色314在线观看 | 久久精品中文字幕一区 | 国内揄拍国内精品少妇国语 | 熟妇人妻无乱码中文字幕 | yw尤物av无码国产在线观看 | 国产精品无套呻吟在线 | 日韩av激情在线观看 | 成人片黄网站色大片免费观看 | 人人澡人人透人人爽 | 领导边摸边吃奶边做爽在线观看 | 久久五月精品中文字幕 | 人人妻人人澡人人爽欧美精品 | 国产美女极度色诱视频www | 国产午夜福利100集发布 | 亚洲天堂2017无码 | 黄网在线观看免费网站 | 又大又紧又粉嫩18p少妇 | 一二三四在线观看免费视频 | 亚洲人成影院在线观看 | 国产亚洲欧美在线专区 | 亚洲精品成人av在线 | 国产精品亚洲а∨无码播放麻豆 | 久久精品国产精品国产精品污 | 骚片av蜜桃精品一区 | 亚洲日韩精品欧美一区二区 | 波多野结衣高清一区二区三区 | 国产精品亚洲五月天高清 | 国产高清不卡无码视频 | 国产精品18久久久久久麻辣 | 天天躁日日躁狠狠躁免费麻豆 | 欧美变态另类xxxx | 老熟妇仑乱视频一区二区 | 国产精品18久久久久久麻辣 | 久久久久国色av免费观看性色 | 97精品国产97久久久久久免费 | 亚洲中文字幕在线观看 | 丰满少妇高潮惨叫视频 | 国产女主播喷水视频在线观看 | 高清不卡一区二区三区 | 人妻人人添人妻人人爱 | 国产精品毛片一区二区 | 国产精品多人p群无码 | 一本大道久久东京热无码av | 性啪啪chinese东北女人 | 中文字幕+乱码+中文字幕一区 | 久久99久久99精品中文字幕 | 蜜臀aⅴ国产精品久久久国产老师 | 国产三级精品三级男人的天堂 | 国产精品99爱免费视频 | 国产精品久久久 | 玩弄人妻少妇500系列视频 | 国产精品久久久久久久影院 | 美女扒开屁股让男人桶 | 伊人久久婷婷五月综合97色 | 亚洲成av人影院在线观看 | 成人三级无码视频在线观看 | 国产精品二区一区二区aⅴ污介绍 | 亚洲一区二区三区四区 | 人人爽人人爽人人片av亚洲 | 色窝窝无码一区二区三区色欲 | 国产激情无码一区二区 | 99re在线播放 | 国产乱人伦偷精品视频 | 久久人人97超碰a片精品 | 亚洲成在人网站无码天堂 | 高潮毛片无遮挡高清免费视频 | 伊人久久婷婷五月综合97色 | 久久久国产一区二区三区 | 极品尤物被啪到呻吟喷水 | 少妇无套内谢久久久久 | 色偷偷人人澡人人爽人人模 | 亚洲 a v无 码免 费 成 人 a v | 国产乱子伦视频在线播放 | 狂野欧美激情性xxxx | 国产综合色产在线精品 | 噜噜噜亚洲色成人网站 | 国产精品高潮呻吟av久久4虎 | 国产精品亚洲а∨无码播放麻豆 | 亚洲成a人一区二区三区 | 国产福利视频一区二区 | 亚洲第一网站男人都懂 | 特黄特色大片免费播放器图片 | 男女下面进入的视频免费午夜 | 精品久久久久久人妻无码中文字幕 | 色情久久久av熟女人妻网站 | 国产乱人无码伦av在线a | 国产精品手机免费 | 好男人社区资源 | 精品一二三区久久aaa片 | 三级4级全黄60分钟 | 日本精品少妇一区二区三区 | 小鲜肉自慰网站xnxx | 爱做久久久久久 | 亚洲色偷偷偷综合网 | 99国产欧美久久久精品 | 六十路熟妇乱子伦 | 久久精品中文闷骚内射 | 久久久久久九九精品久 | 成人一在线视频日韩国产 | 免费观看又污又黄的网站 | 桃花色综合影院 | 国产精品久久久一区二区三区 | 中国女人内谢69xxxx | 夜夜影院未满十八勿进 | 国内综合精品午夜久久资源 | 丰满人妻翻云覆雨呻吟视频 | 亚洲熟妇色xxxxx欧美老妇 | 夜夜夜高潮夜夜爽夜夜爰爰 | 午夜精品久久久内射近拍高清 | 亚洲日韩精品欧美一区二区 | 亚洲精品一区二区三区在线 | 老头边吃奶边弄进去呻吟 | 亚洲精品一区三区三区在线观看 | 东京一本一道一二三区 | 国产精品久久久久久亚洲影视内衣 | 蜜臀aⅴ国产精品久久久国产老师 | 国产美女精品一区二区三区 | 东京热一精品无码av | 欧美肥老太牲交大战 | 国产色在线 | 国产 | 久久zyz资源站无码中文动漫 | 无套内射视频囯产 | 久久国产精品精品国产色婷婷 | 国产女主播喷水视频在线观看 | 国产又爽又黄又刺激的视频 | 岛国片人妻三上悠亚 | 国产精品视频免费播放 | 夜精品a片一区二区三区无码白浆 | 国产成人无码午夜视频在线观看 | 宝宝好涨水快流出来免费视频 | 一本久道久久综合狠狠爱 | 亚洲精品一区二区三区婷婷月 | 亚洲日本在线电影 | 国产激情精品一区二区三区 | 日韩精品一区二区av在线 | 日韩精品乱码av一区二区 | 亚洲精品一区二区三区婷婷月 | 男人的天堂av网站 | 四虎永久在线精品免费网址 | 亚洲中文字幕在线观看 | 国产在线精品一区二区高清不卡 | 日本精品高清一区二区 | 国产精品久久久久7777 | 久久久久久国产精品无码下载 | 欧美真人作爱免费视频 | 午夜丰满少妇性开放视频 | 亚洲精品久久久久avwww潮水 | 熟女少妇人妻中文字幕 | 免费人成网站视频在线观看 | 亚洲日本一区二区三区在线 | 国产精品成人av在线观看 | 成人精品天堂一区二区三区 | 欧美 日韩 人妻 高清 中文 | 国产绳艺sm调教室论坛 | 又湿又紧又大又爽a视频国产 | 亚洲性无码av中文字幕 | 丝袜 中出 制服 人妻 美腿 | 少妇被粗大的猛进出69影院 | 日韩精品乱码av一区二区 | 在线欧美精品一区二区三区 | 欧美性猛交内射兽交老熟妇 | 久久久成人毛片无码 | 在线观看免费人成视频 | 最近中文2019字幕第二页 | 国产女主播喷水视频在线观看 | 亚洲中文字幕在线观看 | 性色av无码免费一区二区三区 | 国产艳妇av在线观看果冻传媒 | 成人无码精品一区二区三区 | 4hu四虎永久在线观看 | 丰满少妇高潮惨叫视频 | 亚洲日本在线电影 | 97无码免费人妻超级碰碰夜夜 | 无码人妻少妇伦在线电影 | 日韩精品乱码av一区二区 | 男女作爱免费网站 | 一个人看的视频www在线 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 免费播放一区二区三区 | 欧美高清在线精品一区 | 久久久久久久人妻无码中文字幕爆 | 西西人体www44rt大胆高清 | 麻豆精品国产精华精华液好用吗 | 人妻有码中文字幕在线 | 露脸叫床粗话东北少妇 | 奇米影视7777久久精品人人爽 | 国产国产精品人在线视 | 亚洲欧美国产精品专区久久 | 国产精品a成v人在线播放 | 精品人妻av区 | 九九综合va免费看 | 国产午夜视频在线观看 | av无码久久久久不卡免费网站 | 久久国产精品_国产精品 | 97无码免费人妻超级碰碰夜夜 | 一本大道伊人av久久综合 | 色综合久久久久综合一本到桃花网 | 午夜免费福利小电影 | 亚洲精品久久久久久久久久久 | 国产艳妇av在线观看果冻传媒 | 久久国语露脸国产精品电影 | 天天拍夜夜添久久精品大 | 国产精品久久精品三级 | 野外少妇愉情中文字幕 | 国产亚洲精品久久久久久 | 又黄又爽又色的视频 | 国产精品99爱免费视频 | 大色综合色综合网站 | 亚洲人成网站在线播放942 | 奇米影视888欧美在线观看 | 老司机亚洲精品影院无码 | 少妇一晚三次一区二区三区 | 成人无码视频免费播放 | 思思久久99热只有频精品66 | 亚洲成在人网站无码天堂 | 欧美精品国产综合久久 | 国产精品无码成人午夜电影 | 日韩av无码一区二区三区 | 伊人久久婷婷五月综合97色 | 在线观看国产午夜福利片 | 人妻无码αv中文字幕久久琪琪布 | 久久久久久久女国产乱让韩 | 国产精品美女久久久久av爽李琼 | 亚洲高清偷拍一区二区三区 | 亚洲欧洲日本无在线码 | 成人精品视频一区二区 | 亚洲一区二区三区香蕉 | 久久这里只有精品视频9 | 黑人巨大精品欧美黑寡妇 | 国产亚洲视频中文字幕97精品 | 欧美色就是色 | 国产午夜亚洲精品不卡下载 | 午夜精品久久久久久久久 | 捆绑白丝粉色jk震动捧喷白浆 | 国产美女精品一区二区三区 | 国产免费久久精品国产传媒 | 国内丰满熟女出轨videos | 精品久久久久香蕉网 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 亚洲熟妇色xxxxx欧美老妇 | 丰满妇女强制高潮18xxxx | 国产精品18久久久久久麻辣 | 内射后入在线观看一区 | 久久久久se色偷偷亚洲精品av | 亚洲熟妇色xxxxx亚洲 | 国产精品久久精品三级 | 欧美人与禽猛交狂配 | 久久久中文久久久无码 | 对白脏话肉麻粗话av | 免费无码av一区二区 | 久久综合色之久久综合 | 中国女人内谢69xxxxxa片 | 日本xxxx色视频在线观看免费 | 噜噜噜亚洲色成人网站 | 日韩av激情在线观看 | 亚洲中文字幕av在天堂 | 日韩人妻少妇一区二区三区 | 精品一区二区三区波多野结衣 | 妺妺窝人体色www婷婷 | 亚洲春色在线视频 | 人妻少妇被猛烈进入中文字幕 | а√资源新版在线天堂 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 老熟妇仑乱视频一区二区 | 国产精品久久久久9999小说 | 欧美日韩亚洲国产精品 | 久久久久亚洲精品男人的天堂 | 亚洲最大成人网站 | 久久久久亚洲精品男人的天堂 | 亚洲人成影院在线无码按摩店 | 对白脏话肉麻粗话av | 无码av最新清无码专区吞精 | 一本一道久久综合久久 | 日韩欧美中文字幕在线三区 | 成人一区二区免费视频 | 久久精品女人天堂av免费观看 | 一本色道久久综合亚洲精品不卡 | 特级做a爰片毛片免费69 | 美女张开腿让人桶 | 精品厕所偷拍各类美女tp嘘嘘 | 青青青手机频在线观看 | 51国偷自产一区二区三区 | 九九久久精品国产免费看小说 | 丰满少妇高潮惨叫视频 | 在线精品国产一区二区三区 | 九一九色国产 | 激情内射亚州一区二区三区爱妻 | 欧美freesex黑人又粗又大 | 欧洲美熟女乱又伦 | 99久久婷婷国产综合精品青草免费 | 国产精品久久久av久久久 | 老头边吃奶边弄进去呻吟 | 欧美精品一区二区精品久久 | 天天做天天爱天天爽综合网 | 3d动漫精品啪啪一区二区中 | 久久精品中文字幕一区 | 精品欧美一区二区三区久久久 | 日本xxxx色视频在线观看免费 | 国产成人一区二区三区在线观看 | 无码人妻丰满熟妇区五十路百度 | 亚洲国产欧美国产综合一区 | 日韩精品a片一区二区三区妖精 | 天天综合网天天综合色 | 久久精品一区二区三区四区 | 国产成人精品无码播放 | 欧美日韩在线亚洲综合国产人 | 中文字幕乱码人妻无码久久 | 国产农村乱对白刺激视频 | 久久久久国色av免费观看性色 | 国产成人综合在线女婷五月99播放 | 国产av无码专区亚洲awww | 久久这里只有精品视频9 | 国产麻豆精品一区二区三区v视界 | 日韩少妇白浆无码系列 | 欧美xxxx黑人又粗又长 | 国产成人精品无码播放 | 又湿又紧又大又爽a视频国产 | 国产精品.xx视频.xxtv | 精品无码成人片一区二区98 | 国产一区二区三区四区五区加勒比 | 成人精品一区二区三区中文字幕 | 国产色精品久久人妻 | 日本免费一区二区三区最新 | 色综合天天综合狠狠爱 | 亚洲欧美中文字幕5发布 | 亚无码乱人伦一区二区 | 无码国产色欲xxxxx视频 | 牲欲强的熟妇农村老妇女 | 内射巨臀欧美在线视频 | 久久国产精品二国产精品 | 国产无av码在线观看 | 玩弄人妻少妇500系列视频 | 欧洲欧美人成视频在线 | 久久综合给久久狠狠97色 | 九九在线中文字幕无码 | 久久精品中文字幕一区 | 一区二区传媒有限公司 | 老子影院午夜精品无码 | 久久久国产一区二区三区 | 377p欧洲日本亚洲大胆 | 7777奇米四色成人眼影 | 国产精品成人av在线观看 | 婷婷丁香六月激情综合啪 | 久久久久亚洲精品中文字幕 | 亚洲国产综合无码一区 | 亚洲呦女专区 | 在线播放无码字幕亚洲 | 成人免费视频在线观看 | 色欲久久久天天天综合网精品 | 欧美zoozzooz性欧美 | 欧美一区二区三区视频在线观看 | 欧美丰满老熟妇xxxxx性 | 亚洲精品国产品国语在线观看 | 成人三级无码视频在线观看 | 好男人社区资源 | 婷婷五月综合激情中文字幕 | 亚洲乱码中文字幕在线 | 亚洲色欲色欲天天天www | 欧洲精品码一区二区三区免费看 | 亲嘴扒胸摸屁股激烈网站 | 欧美日韩一区二区三区自拍 | 精品熟女少妇av免费观看 | 国产亚洲精品久久久久久国模美 | 蜜桃臀无码内射一区二区三区 | 成人无码精品1区2区3区免费看 | 国产精品福利视频导航 | 国产精品二区一区二区aⅴ污介绍 | 国产亚洲精品久久久久久国模美 | 国产三级精品三级男人的天堂 | 色婷婷综合中文久久一本 | 国产乡下妇女做爰 | 欧美人与牲动交xxxx | 在线播放无码字幕亚洲 | 欧美激情内射喷水高潮 | 人妻尝试又大又粗久久 | 精品夜夜澡人妻无码av蜜桃 | 高潮毛片无遮挡高清免费视频 | 亚洲熟悉妇女xxx妇女av | 久久天天躁夜夜躁狠狠 | a国产一区二区免费入口 | 国产成人精品视频ⅴa片软件竹菊 | 亚洲欧美精品伊人久久 | 中文字幕中文有码在线 | 午夜时刻免费入口 | 日本一区二区更新不卡 | av在线亚洲欧洲日产一区二区 | 中文无码伦av中文字幕 | 国产精品久久精品三级 | 久在线观看福利视频 | 国产亚洲人成在线播放 | 精品国产国产综合精品 | 欧美自拍另类欧美综合图片区 | 欧美高清在线精品一区 | 国产精品丝袜黑色高跟鞋 | 永久免费精品精品永久-夜色 | 成人精品一区二区三区中文字幕 | 国産精品久久久久久久 | 亚洲 欧美 激情 小说 另类 | 日韩在线不卡免费视频一区 | 天天摸天天碰天天添 | 亚洲精品美女久久久久久久 | 亚洲天堂2017无码中文 | 欧美性猛交内射兽交老熟妇 | 中文字幕无线码 | 国产精品资源一区二区 | 久久久久久av无码免费看大片 | 亚洲中文字幕无码一久久区 | 日本一卡二卡不卡视频查询 | 久久婷婷五月综合色国产香蕉 | 国产熟妇另类久久久久 | 人人爽人人澡人人高潮 | 无码人妻久久一区二区三区不卡 | 国产成人精品视频ⅴa片软件竹菊 | 亚洲精品一区二区三区婷婷月 | 日本va欧美va欧美va精品 | 日韩精品成人一区二区三区 | 夜夜影院未满十八勿进 | 色妞www精品免费视频 | 在教室伦流澡到高潮hnp视频 | 久久人人爽人人人人片 | 无码av岛国片在线播放 | 51国偷自产一区二区三区 | 欧美老人巨大xxxx做受 | 成人免费视频一区二区 | 熟妇人妻激情偷爽文 | 特级做a爰片毛片免费69 | 亚洲经典千人经典日产 | 国色天香社区在线视频 | 嫩b人妻精品一区二区三区 | 午夜肉伦伦影院 | 麻豆蜜桃av蜜臀av色欲av | 一区二区传媒有限公司 | 国产亚洲tv在线观看 | 日本丰满护士爆乳xxxx | 中文字幕无码免费久久99 | 精品久久久无码人妻字幂 | 综合网日日天干夜夜久久 | 人人爽人人爽人人片av亚洲 | 久久精品人妻少妇一区二区三区 | 国产精品无码成人午夜电影 | 久久久久se色偷偷亚洲精品av | 爱做久久久久久 | 中文精品久久久久人妻不卡 | 999久久久国产精品消防器材 | 国产人妻人伦精品1国产丝袜 | 免费人成网站视频在线观看 | 在线天堂新版最新版在线8 | 亚洲成av人影院在线观看 | 内射后入在线观看一区 | 丰满岳乱妇在线观看中字无码 | 国产精品理论片在线观看 | 无码人妻少妇伦在线电影 | 中文字幕无码视频专区 | 精品无码一区二区三区爱欲 | 无套内射视频囯产 | 国内揄拍国内精品少妇国语 | 最近的中文字幕在线看视频 | 一本大道伊人av久久综合 | 熟妇人妻无乱码中文字幕 | 午夜男女很黄的视频 | 国产在线精品一区二区三区直播 | 精品国产一区av天美传媒 | 国产成人久久精品流白浆 | 乱人伦人妻中文字幕无码久久网 | 乌克兰少妇xxxx做受 | 熟女俱乐部五十路六十路av | 女人被男人躁得好爽免费视频 | 丝袜人妻一区二区三区 | 人妻与老人中文字幕 | 久久亚洲中文字幕精品一区 | 日本肉体xxxx裸交 | 四十如虎的丰满熟妇啪啪 | 欧美阿v高清资源不卡在线播放 | 午夜无码人妻av大片色欲 | 亚洲国产综合无码一区 | 67194成是人免费无码 | 日本精品人妻无码免费大全 | 熟女少妇在线视频播放 | av无码不卡在线观看免费 | 黑人巨大精品欧美黑寡妇 | 樱花草在线播放免费中文 | aa片在线观看视频在线播放 | 曰本女人与公拘交酡免费视频 | 内射后入在线观看一区 | 亚洲精品www久久久 | 88国产精品欧美一区二区三区 | 国产无套内射久久久国产 | 奇米影视888欧美在线观看 | 亚洲中文字幕久久无码 | 2019nv天堂香蕉在线观看 | 四虎影视成人永久免费观看视频 | 无码任你躁久久久久久久 | 老子影院午夜伦不卡 | 熟妇激情内射com | 乱码午夜-极国产极内射 | 色老头在线一区二区三区 | 国产免费观看黄av片 | 亚洲国产精品一区二区第一页 | 久久久久久九九精品久 | 国产超级va在线观看视频 | 国产精品99爱免费视频 | 亚洲色欲色欲欲www在线 | 双乳奶水饱满少妇呻吟 | 特级做a爰片毛片免费69 | 精品厕所偷拍各类美女tp嘘嘘 | 国产三级久久久精品麻豆三级 | 成年美女黄网站色大免费全看 | 日本护士xxxxhd少妇 | 少妇高潮一区二区三区99 | 999久久久国产精品消防器材 | 久久久精品国产sm最大网站 | 欧美变态另类xxxx | 一二三四在线观看免费视频 | 又大又紧又粉嫩18p少妇 | 国产区女主播在线观看 | 久久99国产综合精品 | 波多野结衣aⅴ在线 | 久久久久人妻一区精品色欧美 | 久久婷婷五月综合色国产香蕉 | 日韩视频 中文字幕 视频一区 | 一区二区三区高清视频一 | 青青久在线视频免费观看 | 精品人妻中文字幕有码在线 | 国产精品香蕉在线观看 | 精品 日韩 国产 欧美 视频 | 大屁股大乳丰满人妻 | 久久 国产 尿 小便 嘘嘘 | 俺去俺来也在线www色官网 | 久久久久久亚洲精品a片成人 | 精品久久久久久亚洲精品 | 女人色极品影院 | 国产精品自产拍在线观看 | 欧美日韩一区二区免费视频 | 国产亚洲精品久久久久久久 | 国产无遮挡又黄又爽免费视频 | 女人和拘做爰正片视频 | 丝袜人妻一区二区三区 | 国产精品第一国产精品 | 国产精品亚洲lv粉色 | 午夜精品久久久内射近拍高清 | 国产熟妇高潮叫床视频播放 | 精品国产一区二区三区av 性色 | 亚洲色欲色欲天天天www | 亚洲色无码一区二区三区 | 一本久道高清无码视频 | 成年美女黄网站色大免费全看 | 55夜色66夜色国产精品视频 | 四虎永久在线精品免费网址 | 精品夜夜澡人妻无码av蜜桃 | 永久免费精品精品永久-夜色 | 国色天香社区在线视频 | 欧美三级a做爰在线观看 | 亚洲一区二区三区无码久久 | 亚洲aⅴ无码成人网站国产app | 欧美日韩久久久精品a片 | 国产超碰人人爽人人做人人添 | 久久精品女人天堂av免费观看 | 天堂一区人妻无码 | 亚洲精品一区国产 | 无码成人精品区在线观看 | 中文字幕无码av波多野吉衣 | 狠狠噜狠狠狠狠丁香五月 | 国产精品无码永久免费888 | 丰满人妻一区二区三区免费视频 | 亚洲一区二区观看播放 | 动漫av网站免费观看 | 成人性做爰aaa片免费看不忠 | 亚洲国产成人av在线观看 | 亚洲爆乳精品无码一区二区三区 | 成人精品一区二区三区中文字幕 | 久久国产精品萌白酱免费 | 99视频精品全部免费免费观看 | 熟妇人妻无码xxx视频 | 日韩 欧美 动漫 国产 制服 | 夜先锋av资源网站 | 中文字幕av无码一区二区三区电影 | 亚洲国产精品无码一区二区三区 | 一个人免费观看的www视频 | 国产亚洲精品久久久久久久 | www一区二区www免费 | 四虎国产精品一区二区 | 99re在线播放 | 久久精品国产一区二区三区肥胖 | 欧美大屁股xxxxhd黑色 | 免费无码肉片在线观看 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲中文字幕乱码av波多ji | 欧美第一黄网免费网站 | √天堂资源地址中文在线 | 亚洲色无码一区二区三区 | 色综合久久久无码网中文 | 好男人社区资源 | 红桃av一区二区三区在线无码av | 一本久久a久久精品亚洲 | 日本护士毛茸茸高潮 | 国产 精品 自在自线 | 欧美成人家庭影院 | 亚洲乱码日产精品bd | 最新国产乱人伦偷精品免费网站 | 狠狠cao日日穞夜夜穞av | 激情亚洲一区国产精品 | 国产成人无码专区 | 国产精品久久精品三级 | 一本久久a久久精品vr综合 | 久久精品女人天堂av免费观看 | 欧美人与物videos另类 | 又紧又大又爽精品一区二区 | 真人与拘做受免费视频 | 性生交大片免费看女人按摩摩 | 精品一区二区三区无码免费视频 | 天天躁夜夜躁狠狠是什么心态 | 中文字幕无码av波多野吉衣 | 装睡被陌生人摸出水好爽 | 精品国产麻豆免费人成网站 | 无码国模国产在线观看 | 国产人妻久久精品二区三区老狼 | 亚洲乱码中文字幕在线 | 色 综合 欧美 亚洲 国产 | 99精品无人区乱码1区2区3区 | 日本精品久久久久中文字幕 | 国产午夜视频在线观看 | 欧洲美熟女乱又伦 | 无码吃奶揉捏奶头高潮视频 | 国产高清av在线播放 | 久久婷婷五月综合色国产香蕉 | 国产精品亚洲五月天高清 | 无码任你躁久久久久久久 | 国产精品无码mv在线观看 | 国产精品办公室沙发 | 一本久久a久久精品vr综合 | 奇米影视888欧美在线观看 | 嫩b人妻精品一区二区三区 | 午夜精品久久久久久久久 | 波多野结衣 黑人 | 99精品国产综合久久久久五月天 | 欧美丰满少妇xxxx性 | 久久zyz资源站无码中文动漫 | 国内精品人妻无码久久久影院 | 两性色午夜视频免费播放 | 国产小呦泬泬99精品 | 国产精品国产自线拍免费软件 | 亚洲精品成a人在线观看 | 人人妻人人澡人人爽人人精品浪潮 | 无码人妻av免费一区二区三区 | 国产人妻人伦精品 | 国产精品人人爽人人做我的可爱 | 亚洲一区二区三区四区 | 中文字幕无码av激情不卡 | 亚洲国产高清在线观看视频 | 日韩精品无码一区二区中文字幕 | 欧美一区二区三区 | 伊人久久婷婷五月综合97色 | 国产一区二区三区日韩精品 | а√资源新版在线天堂 | 乱人伦人妻中文字幕无码久久网 | 正在播放老肥熟妇露脸 | 天天拍夜夜添久久精品 | 成人无码影片精品久久久 | 理论片87福利理论电影 | 国产人妻精品一区二区三区 | 波多野结衣一区二区三区av免费 | 男女下面进入的视频免费午夜 | 久久这里只有精品视频9 | 亚洲中文字幕成人无码 | 蜜桃臀无码内射一区二区三区 | 色妞www精品免费视频 | 国产亚洲精品久久久久久 | 少妇被黑人到高潮喷出白浆 | 精品人妻人人做人人爽夜夜爽 | 夫妻免费无码v看片 | 亚洲综合另类小说色区 | √天堂中文官网8在线 | 在线观看国产午夜福利片 | 国产精品多人p群无码 | 久久亚洲精品成人无码 | 中文无码精品a∨在线观看不卡 | 国产一区二区不卡老阿姨 | 国产激情精品一区二区三区 | 成人亚洲精品久久久久软件 | √天堂中文官网8在线 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 人人超人人超碰超国产 | 国产精品亚洲专区无码不卡 | 天堂а√在线中文在线 | 欧美精品在线观看 | 中国女人内谢69xxxxxa片 | 天天躁夜夜躁狠狠是什么心态 | 欧洲熟妇色 欧美 | 人妻尝试又大又粗久久 | 亚洲国产精品毛片av不卡在线 | 亚洲人成网站在线播放942 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 内射后入在线观看一区 | 亚洲欧美综合区丁香五月小说 | 日日摸夜夜摸狠狠摸婷婷 | 强伦人妻一区二区三区视频18 | a片免费视频在线观看 | 国产成人精品必看 | 荫蒂添的好舒服视频囗交 | 欧美日韩亚洲国产精品 | 免费网站看v片在线18禁无码 | 男人和女人高潮免费网站 | 亚洲色欲久久久综合网东京热 | 国产xxx69麻豆国语对白 | 又湿又紧又大又爽a视频国产 | 性欧美熟妇videofreesex | 免费观看激色视频网站 | 国产在线精品一区二区高清不卡 | 日韩在线不卡免费视频一区 | 六月丁香婷婷色狠狠久久 | 日韩亚洲欧美精品综合 | 中文无码精品a∨在线观看不卡 | 亚洲色欲久久久综合网东京热 | 国产人成高清在线视频99最全资源 | 亚洲精品成a人在线观看 | 成人动漫在线观看 | 国产精品亚洲专区无码不卡 | 久久综合九色综合97网 | 国产色精品久久人妻 | 波多野42部无码喷潮在线 | 久久综合九色综合97网 | 日韩在线不卡免费视频一区 | 国产农村乱对白刺激视频 | 亚洲精品综合一区二区三区在线 | 最新国产麻豆aⅴ精品无码 | 久久国产精品二国产精品 | 久久久亚洲欧洲日产国码αv | 精品久久综合1区2区3区激情 | 亚洲一区二区三区含羞草 | 初尝人妻少妇中文字幕 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 风流少妇按摩来高潮 | 日本肉体xxxx裸交 | 老太婆性杂交欧美肥老太 | 中文久久乱码一区二区 | 国产在线无码精品电影网 | 水蜜桃av无码 | 18黄暴禁片在线观看 | 狠狠cao日日穞夜夜穞av | 亚洲精品一区二区三区大桥未久 | 国产精品第一国产精品 | 国产两女互慰高潮视频在线观看 | 精品国产精品久久一区免费式 | 色噜噜亚洲男人的天堂 | 少妇无套内谢久久久久 | 亚洲欧洲中文日韩av乱码 | 国产精品久久久久9999小说 | 波多野结衣av一区二区全免费观看 | 无码福利日韩神码福利片 | 久久精品99久久香蕉国产色戒 | 亚洲天堂2017无码 | 国产高潮视频在线观看 | 久久久久人妻一区精品色欧美 | 99久久久国产精品无码免费 | 亚洲а∨天堂久久精品2021 | 亚洲精品一区二区三区在线 | 香港三级日本三级妇三级 | 极品嫩模高潮叫床 | 成人欧美一区二区三区黑人免费 | 永久黄网站色视频免费直播 | 国产精品-区区久久久狼 | 久久综合狠狠综合久久综合88 | 人人妻人人澡人人爽欧美一区九九 | 日韩欧美成人免费观看 | 无码av最新清无码专区吞精 | 国产极品美女高潮无套在线观看 | 人妻aⅴ无码一区二区三区 | 日韩无套无码精品 | 亚洲色欲久久久综合网东京热 | 夜夜夜高潮夜夜爽夜夜爰爰 | 捆绑白丝粉色jk震动捧喷白浆 | 国产精品久久久久久亚洲影视内衣 | 东北女人啪啪对白 | 精品久久久无码人妻字幂 | 亚洲aⅴ无码成人网站国产app | 鲁大师影院在线观看 | 色综合视频一区二区三区 | 九月婷婷人人澡人人添人人爽 | 日韩精品无码一区二区中文字幕 | 中文字幕乱码人妻二区三区 | 无码人妻精品一区二区三区下载 | 狠狠色丁香久久婷婷综合五月 | 久久久久国色av免费观看性色 | 一本久久伊人热热精品中文字幕 | 天天躁日日躁狠狠躁免费麻豆 | 成人无码影片精品久久久 | 欧美变态另类xxxx | 久久www免费人成人片 | 亚洲色欲色欲欲www在线 | 精品国产一区av天美传媒 | 久久久精品人妻久久影视 | 狠狠噜狠狠狠狠丁香五月 | 久青草影院在线观看国产 | 欧美第一黄网免费网站 | 日产精品高潮呻吟av久久 | 又粗又大又硬毛片免费看 | 成人性做爰aaa片免费看不忠 | 国产高清不卡无码视频 | 东京热男人av天堂 | 无码国模国产在线观看 | 欧洲美熟女乱又伦 | 色欲综合久久中文字幕网 | 国产熟妇另类久久久久 | 欧美国产日韩亚洲中文 | 欧美 丝袜 自拍 制服 另类 | 国产精品a成v人在线播放 | 亚洲 欧美 激情 小说 另类 | 在线天堂新版最新版在线8 | 国产sm调教视频在线观看 | 人妻人人添人妻人人爱 | 国产精品福利视频导航 | 又大又黄又粗又爽的免费视频 | 国产一区二区三区影院 | 无人区乱码一区二区三区 | 日韩少妇白浆无码系列 | 国产午夜精品一区二区三区嫩草 | 欧美日本精品一区二区三区 | 国产成人综合在线女婷五月99播放 | 日产精品高潮呻吟av久久 | 夜夜夜高潮夜夜爽夜夜爰爰 | 亚洲综合精品香蕉久久网 | 亚洲小说春色综合另类 | 在线播放亚洲第一字幕 | 中文字幕人妻无码一区二区三区 | 色综合久久久无码网中文 | 水蜜桃av无码 | 国内揄拍国内精品少妇国语 | 成人精品视频一区二区 | 成人一区二区免费视频 | 3d动漫精品啪啪一区二区中 | 日本乱人伦片中文三区 | 丰满少妇弄高潮了www | 国产av一区二区三区最新精品 | 国产特级毛片aaaaaaa高清 | 亚洲一区二区三区在线观看网站 | 欧美国产日韩久久mv | 精品水蜜桃久久久久久久 | 日日摸日日碰夜夜爽av | 久久精品国产日本波多野结衣 | 岛国片人妻三上悠亚 | 成人动漫在线观看 | 鲁一鲁av2019在线 | 激情亚洲一区国产精品 | 天干天干啦夜天干天2017 | 中文字幕av无码一区二区三区电影 | 成人欧美一区二区三区黑人免费 | 免费无码一区二区三区蜜桃大 | 天天拍夜夜添久久精品大 | 日韩精品无码一区二区中文字幕 | 东京热无码av男人的天堂 | 无码av中文字幕免费放 | 免费视频欧美无人区码 | 久久久久国色av免费观看性色 | 国产精品无码成人午夜电影 | 夫妻免费无码v看片 | 中文字幕+乱码+中文字幕一区 | 麻花豆传媒剧国产免费mv在线 | 荫蒂被男人添的好舒服爽免费视频 | 99精品无人区乱码1区2区3区 | 97se亚洲精品一区 | 巨爆乳无码视频在线观看 | 亚洲经典千人经典日产 | 国产在线aaa片一区二区99 | 国产口爆吞精在线视频 | 午夜男女很黄的视频 | 日日麻批免费40分钟无码 | 亚洲精品一区二区三区在线观看 | 免费看少妇作爱视频 | 国产乱人伦av在线无码 | 国产黄在线观看免费观看不卡 | 亚洲精品国产精品乱码不卡 | 色综合久久久久综合一本到桃花网 | 日本熟妇乱子伦xxxx | 国产疯狂伦交大片 | 色婷婷av一区二区三区之红樱桃 | 水蜜桃亚洲一二三四在线 | 老司机亚洲精品影院 | 97久久精品无码一区二区 | 国产无遮挡吃胸膜奶免费看 | 国产精品高潮呻吟av久久4虎 | 亚洲国产精品久久人人爱 | 亚洲乱亚洲乱妇50p | 欧美肥老太牲交大战 | 日韩精品无码免费一区二区三区 | 色综合久久88色综合天天 | 中文字幕中文有码在线 | 成人性做爰aaa片免费看不忠 | 中文字幕+乱码+中文字幕一区 | 欧洲vodafone精品性 | 麻豆国产人妻欲求不满 | 国产精品怡红院永久免费 | 亚洲成av人在线观看网址 | 日韩av无码中文无码电影 | 性色av无码免费一区二区三区 | 欧美丰满老熟妇xxxxx性 | 亚洲欧美国产精品专区久久 | 国产成人综合美国十次 | 免费人成在线视频无码 | 又粗又大又硬毛片免费看 | 欧美丰满熟妇xxxx性ppx人交 | 欧美熟妇另类久久久久久多毛 | 青青青手机频在线观看 | 久久精品99久久香蕉国产色戒 | 国产亚洲精品久久久久久久久动漫 | 女人被爽到呻吟gif动态图视看 | 国产情侣作爱视频免费观看 | 国产精品久久久久无码av色戒 | 欧美zoozzooz性欧美 | 久久99精品久久久久婷婷 | 少妇愉情理伦片bd | 玩弄人妻少妇500系列视频 |