C#+OpenGL編程之再見小桃子(The Tao Framework)

本文基礎:編程

  C#+OpenGL編程之OpenGL 紋理載入windows

  C#+OpenGL編程之OpenGL 多重紋理數組

  

小桃子The Tao FrameworkTao提供的全部庫都是徹底開源的。其中的多數庫均可以避免費用在商業項目中,該框架較其它框架實現更簡單、容易,代碼也簡潔易讀。app

  很遺憾的是這個框架已經再也不開發了,做爲程序猿不得不想點其餘的框架了。框架

 

  下面的課程,咱們將使用另一個框架,OpenGL DotNet 官方網站:http://www.taylaninan.com/opengl-dotnetide

  做爲咱們的開發框架,比起 小桃子的後繼者OpenTK更接近C代碼風格,要知道,作什麼事都要跟隨大流。如今市面上的遊戲引擎都是C或者C++,而不少OpenGL教程也是基於C或者C++,標新立異等於在裝酷。函數

 

  首先咱們實現最先的基礎實例吧:工具

using System;  
using System.Collections.Generic;  
using System.Text;  
using OpenGLDotNet;  
  
namespace OpenGLTK  
{  
    ///OpenGLDotNet須要修改  
    ///glut32.dll ->freeglut.dll  
    ///GLUT最初是《OpenGL紅皮書(第二版)》[注2]中的示例程序。自那之後,GLUT簡單、跨平臺的特色,使其在各類實際應用中普遍應用。  
    ///目前最後版本GLUT v3.7的歷史可追溯至1998年8月,且該項目彷佛已經被廢棄。它的許可證禁止任何人發佈修改後的庫代碼。  
    ///毋庸置疑GLUT已經很老了,真的須要改善。此外,GLUT的許可證與一些軟件發行不兼容(如XFree86的)。  
    ///一個輕量級的,開源的,跨平臺的library。支持OpenGL及OpenGL ES,用來管理窗口,讀取輸入,處理事件等。由於OpenGL沒有窗口管理的功能,因此不少熱心的人寫了工具來支持這些功能,好比早期的glut,如今的freeglut等。  
    ///修改代碼位置 GLU.Functions.cs  
  
  
    ///找不到glu32.dll解決方法:  
    ///glu32.dll 改成->GLU32.dll,具體文件名大小寫能夠去 系統目錄搜索這個文件個人是server2012   
    ///修改代碼位置 GLUT.Functions.cs  
  
    ///tao->OpenGLDotNet 須要修改的地方  
    ///函數去掉glu和gl部分,例如  
    ///GL.glPopMatrix();->GL.PopMatrix();  
    ///Gl.gl->GL.  
    ///Gl.GL_->GL.GL_  
    /// Glu.glu->GLU.  
    ///固然你能夠修改源代碼private ->public  
    ///修改代碼位置 GL.CoreDelegates.cs  
  
    /// <summary>  
    /// 第二章 Opengl程序框架 C# by 大師♂羅莊  
    ///   
    /// </summary>  
    class Examplefirst : IDisposable  
    {  
         String title = "第二章 Opengl程序框架";  
        ///窗口大小  
        internal int windowWidth, windowHeight;  
    
        //當前幀  
        internal float currentTime, startTime;  
  
        //鼠標位置  
        internal int mouseX, mouseY, button,  state;  
        //鍵盤按下  
  
        internal byte key;  
        public Examplefirst()  
        {  
            GLConfig.Init(0, 0, title, 25, 25, 1024, 768);  
            GL.Init(true);  
            GLUT.KeyboardFunc(Keyboard);  
            GLUT.MouseFunc(Mouse);  
            GLUT.IdleFunc(Idle);  
            GLUT.ReshapeFunc(Reshape);  
            GLUT.MotionFunc(Motion);  
            GLUT.DisplayFunc(Display);  
         }  
  
        /// <summary>  
        /// glut鍵盤迴調函數    
        /// </summary>  
        /// <param name="key"></param>  
        /// <param name="x"></param>  
        /// <param name="y"></param>  
        public virtual void Keyboard(byte key, int x, int y)  
        {  
            this.key = key;  
        }  
  
        /// <summary>  
        ///  glut鼠標按下與釋放回調函數    
        /// </summary>  
        /// <param name="button"></param>  
        /// <param name="state"></param>  
        /// <param name="x"></param>  
        /// <param name="y"></param>  
        public virtual void Mouse(int button, int state, int x, int y)  
        {  
  
            this.button = button;  
            this.state = state;  
            this.mouseX = x;  
            this.mouseY = y;  
            return;  
        }  
  
        /// <summary>  
        /// glut空閒處理回調函數  
        /// </summary>  
        public void Idle()  
        {  
            currentTime = System.Environment.TickCount;  
            Update(currentTime - startTime);  
            startTime = currentTime;  
            return;  
        }  
  
        /// <summary>  
        ///  glut窗口重置回調函數    
        /// </summary>  
        /// <param name="width"></param>  
        /// <param name="height"></param>  
        public void Reshape(int width, int height)  
        {  
            windowWidth = width;  
            windowHeight = height;  
            //防止除零問題  
            windowHeight = windowWidth > 0 ? windowHeight : 1;  
           InitGL(windowWidth, windowHeight);  
  
        }  
  
        /// <summary>  
        /// glut鼠標移動回調函數    
        /// </summary>  
        /// <param name="x"></param>  
        /// <param name="y"></param>  
        public void Motion(int x, int y)  
        {  
            return;  
        }  
  
        /// <summary>  
        /// glut描繪回調函數    
        /// </summary>  
        public void Display()  
        {  
            //我感受用這個得從新設置下lookUP才行  
            iniView(windowWidth, windowHeight);  
            DrawGLScene();  
              
        }  
  
        /// <summary>  
        /// 入口點  
        /// </summary>  
        public void Run()  
        {  
            GLUT.MainLoop();  
        }  
  
  
        /// <summary>  
        /// 更新用  
        /// </summary>  
        public virtual void Update(float milliseconds)  
        {  
            if (key == 27)                              // Escape 按下,退出  
            {  
                this.Dispose();  
            }  
            return;  
        }  
  
        /// <summary>  
        /// 原書的初始化方法,C# by 大師♂羅莊  
        /// </summary>  
        /// <param name="windowWidth">窗口寬</param>  
        /// <param name="windowHeight">窗口高</param>  
        /// <returns></returns>  
        Boolean InitGL(int windowWidth, int windowHeight)  
        {  
            // 設置視口 viewport  
            GL.Viewport(0, 0, windowWidth, windowHeight);  
  
            //啓用陰影平滑  
            GL.ShadeModel(GL.GL_SMOOTH);  
  
            //啓用反走樣  
            GL.Hint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);  
           
            // 設置投影模式 projection matrix  
            GL.MatrixMode(GL.GL_PROJECTION);  
            GL.LoadIdentity();  
            GL.Disable(GL.GL_DITHER);  
            return true;  
        }  
        /// <summary>  
        /// 初始化視口投影,本例子沒有采用原書例子,自定義的視口  
        /// </summary>  
        public virtual void iniView(int windowWidth, int windowHeight)  
        {  
            GLU.Perspective(65, windowWidth / (double)windowHeight, 1, 100);  
            // 選擇模型觀察矩陣 modelview matrix  
            GL.MatrixMode(GL.GL_MODELVIEW);  
            //重置模型觀察矩陣  
            GL.LoadIdentity();  
            GLU.LookAt(0, 1, 0,  // 眼睛位置  
                0, 20, 0,           // 觀察點  
                0, 0, 1);           // 怎麼看  
        }  
  
        /// <summary>  
        /// 原書的繪製方法 C# by 大師♂羅莊  
        /// <param name="currentTime">當前幀</param>  
        /// </summary>  
        public virtual void DrawGLScene()  
        {  
            // 重置黑色背景  
            GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);  
            GL.Clear(GL.GL_COLOR_BUFFER_BIT);  
             // 畫三角形  
            GL.Translatef(0, 14, 0);  
            GL.Begin(GL.GL_TRIANGLES);  
            GL.Color3f(1, 0, 0);  
            GL.Vertex3f(-5, 0, -4);  
            GL.Color3f(0, 1, 0);  
            GL.Vertex3f(5, 0, -4);  
            GL.Color3f(0, 0, 1);  
            GL.Vertex3f(0, 0, 6);  
            GL.End();  
            GLUT.SwapBuffers();  
        }  
  
  
        public void Dispose()  
        {  
            GLUT.KeyboardFunc(null);  
            GLUT.MouseFunc(null);  
            GLUT.IdleFunc(null);  
            GLUT.ReshapeFunc(null);  
            GLUT.MotionFunc(null);  
            GLUT.DestroyWindow(GLUT.GetWindow());  
        }  
    }  
}  

OpenGL DotNet也非十全十美,須要咱們修改源代碼:oop

  一、首先一個問題就是使用glut32.dll,這個庫已是上個世紀的庫了,咱們須要修改GLU.Functions.cs 裏面把glut32.dll改成freeglut.dll性能

  二、找不到glu32.dll,這個要你們本身去windows目錄看文件名大小寫,在個人2012上面文件名爲GLU32.dll

  而後就能夠把桃子框架代碼移植過來了。

 

  上面的代碼就和C很類似了,使用GLUT函數實現窗口管理,代碼量從160行升至220行。

 

  下面咱們移植下多重紋理吧。

using System;  
using System.Collections.Generic;  
using System.Drawing;  
using System.IO;  
using System.Text;  
using System.Windows.Forms;  
using OpenGLDotNet;  
  
namespace OpenGLTK  
{  
    /// <summary>  
    /// 自制的紋理載入類 C# by 大師♂羅莊  
    /// </summary>  
    class TextureLoad : IDisposable  
    {  
        public uint[] ID = new uint[3];  
        Bitmap image;  
        public bool Load(String fileName)  
        {  
            ///原則上材質只應該在初始化時候載入一次,不然會影響性能  
            if (image != null)  
            {  
                return true;  
            }  
            FileInfo file = new FileInfo(fileName);  
            if (file.Exists == false)  
            {  
                MessageBox.Show("沒法載入" + fileName);  
                return false;  
            }  
            try  
            {  
                if (file.Extension.ToUpper() == ".TGA")  
                {  
                    ///http://blog.csdn.net/zgke/article/details/4667499  
                    ///C# 載入TGA 類,自行參考,這裏再也不列出  
                    ImageTGA tga = new ImageTGA(fileName);  
                    image = tga.Image;  
                }  
                else  
                {  
                    image = new Bitmap(fileName);  
                }  
  
            }  
            catch (System.ArgumentException)  
            {  
                MessageBox.Show("沒法載入" + fileName);  
                return false;  
            }  
  
            if (image != null)  
            {  
                image.RotateFlip(RotateFlipType.RotateNoneFlipY);  
                System.Drawing.Imaging.BitmapData bitmapdata;  
                Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);  
  
                ///Nearest Linear MipMapped三個紋理實現,本文暫時不考慮  
                //bitmapdata = image.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);  
                //GL.GenTextures(3, this.texture);  
  
                //// Create Nearest Filtered Texture  
                //GL.BindTexture(GL.GL_TEXTURE_2D, this.texture[0]);  
                //GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);  
                //GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);  
                //GL.TexImage2D(GL.GL_TEXTURE_2D, 0, (int)GL.GL_RGB, image.Width, image.Height, 0, GL.GL_BGR_EXT, GL.GL_UNSIGNED_BYTE, bitmapdata.Scan0);  
                //// Create Linear Filtered Texture  
                //GL.BindTexture(GL.GL_TEXTURE_2D, this.texture[1]);  
                //GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);  
                //GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);  
                //GL.TexImage2D(GL.GL_TEXTURE_2D, 0, (int)GL.GL_RGB, image.Width, image.Height, 0, GL.GL_BGR_EXT, GL.GL_UNSIGNED_BYTE, bitmapdata.Scan0);  
                //// Create MipMapped Texture  
                //GL.BindTexture(GL.GL_TEXTURE_2D, this.texture[2]);  
                //GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);  
                //GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST);  
                //GL.uBuild2DMipmaps(GL.GL_TEXTURE_2D, (int)GL.GL_RGB, image.Width, image.Height, GL.GL_BGR_EXT, GL.GL_UNSIGNED_BYTE, bitmapdata.Scan0);  
                //image.UnlockBits(bitmapdata);  
  
                /** 生成紋理對象名稱 */  
                GL.GenTextures(3, ID);  
  
                /** 建立紋理對象 */  
                GL.BindTexture(GL.GL_TEXTURE_2D, ID[0]);  
  
                /** 控制濾波 */  
                GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, (int)GL.GL_LINEAR);  
                GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, (int)GL.GL_LINEAR);  
                GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, (int)GL.GL_REPEAT);  
                GL.TexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, (int)GL.GL_REPEAT);  
  
                bitmapdata = image.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);  
                /** 建立紋理 */  
                GLU.Build2DMipmaps(GL.GL_TEXTURE_2D, (int)GL.GL_RGB, image.Width,  
                                  image.Height, GL.GL_BGR_EXT, GL.GL_UNSIGNED_BYTE,  
                                  bitmapdata.Scan0);  
                image.UnlockBits(bitmapdata);  
            }  
            return true;  
        }  
  
        public void FreeImage()  
        {  
            /** 釋放內存 */  
            if (image != null)  
            {  
                image.Dispose();  
            }  
        }  
        public void Dispose()  
        {  
            FreeImage();  
        }  
    }  
}  

 

using System;  
using System.Collections.Generic;  
using System.IO;  
using System.Text;  
using System.Windows.Forms;  
using OpenGLDotNet;  
  
namespace OpenGLTK  
{  
    /// <summary>  
    /// 第四章 OpenGl 多重紋理載入 C# by 大師♂羅莊  
    /// </summary>  
    class OpenGLMultiTexture : Examplefirst  
    {  
        TextureLoad[] m_texture = new TextureLoad[4];  
        bool multitexturing=false;  
        /** 檢查是否支持擴展 */  
        string title = "第四章 OpenGl 多重紋理載入";  
  
        public OpenGLMultiTexture()  
            : base()  
        {  
            for (int i = 0; i < 4; i++)  
            {  
                m_texture[i] = new TextureLoad();//對象數組必須初始化  
            }  
            LoadTexture();  
            GLUT.SetWindowTitle(title);  
            /** 初始化 */  
            if (!initMultiTexture())//和原來不同,GL.GetString這個函數不能放入動畫事件  
            {  
                MessageBox.Show("您的硬件和驅動不支持多重紋理");  
                return;  
            }  
  
        }  
  
        /// <summary>  
        /// 檢查多重紋理支持  
        /// </summary>  
        /// <param name="input"></param>  
        /// <returns></returns>  
        bool isExtensionSupported(string input)  
        {  
            string extension = GL.GetString(GL.GL_EXTENSIONS);  
  
            return extension.IndexOf(input)>=0;  
        }  
  
          
        bool initMultiTexture()  
        {  
            /** 檢查是否支持擴展 */  
            if (isExtensionSupported("GL_ARB_multitexture"))  
            {  
                return true;  
            }  
            else  
                return false;  
        }  
  
        /** 載入紋理數據  */  
        bool LoadTexture()  
        {  
            /// 文件名   
            String[] fileName = new String[4] { "wall.bmp", "lightmap.bmp", "bitmap.bmp", "fog.bmp" };  
  
            /// 載入四幅位圖   
            for (int i = 0; i < 4; i++)  
            {  
                    if (m_texture[i].Load(Path.Combine(Application.StartupPath, @"Image\" + fileName[i]).ToString()) == false)                         /**< 載入位圖文件 */  
                    {  
                        MessageBox.Show("沒法載入" + fileName[i]);  
                        return false;  
                    }  
                 
  
            }  
            return true;  
  
        }  
  
        /// <summary>  
        /// 初始化視口投影,恢復原書的視口  
        /// </summary>  
        public override void iniView(int windowWidth, int windowHeight)  
        {  
            GLU.Perspective(45.0f, windowWidth / windowHeight, 1.0f, 100.0f);  
            GL.MatrixMode(GL.GL_MODELVIEW);  
            GL.LoadIdentity();  
            GLU.LookAt(0, 1, 0,  // 眼睛位置  
             0, 20, 0,           // 觀察點  
             0, 0, 1);           // 怎麼看  
            
             
                        
        }  
  
        /** 用戶自定義的卸載函數 */  
        public new void Dispose()  
        {  
            base.Dispose();  
            for (int i = 0; i < 4; i++)  
            {  
                m_texture[i].FreeImage();  
                GL.DeleteTextures(1, m_texture[i].ID);  
            }  
        }  
        float wrap = 0;      /**< 用於霧的流動 */  
  
        public override void Keyboard(byte key, int x, int y)  
        {  
            if (key == 27)                              // Escape 按下,退出  
            {  
                this.Dispose();  
            }  
  
            /////** 當按下空格時,開啓多重紋理 */  
            if (key == 0x20)  
            {  
                multitexturing = true;//開啓  
            }  
            else  
            {  
                multitexturing = false;//按下其餘鍵關閉  
            }  
        }  
        /// <summary>  
        /// 重載  
        /// </summary>  
        /// <param name="currentTime"></param>  
        public override void Update(float milliseconds)  
        {  
            wrap += milliseconds /1000;                 //動畫 速度請本身調節  
            Display();  
              
        }  
        /// <summary>  
        /// 重載,使用Draw方法繪圖  
        /// </summary>  
        /// <param name="mouseX"></param>  
        /// <param name="currentTime"></param>  
        public override void DrawGLScene()  
        {  
            Draw();  
            GLUT.SwapBuffers();  
        }  
      
        /** 繪製函數 */  
        void Draw()  
        {  
            /** 用戶自定義的繪製過程 */  
            GL.Clear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);  
            GL.LoadIdentity();  
  
            GL.Translatef(0.0f, 0.0f, -10.0f);  
  
            /** 激活紋理0,並綁定紋理 */  
            GL.ActiveTextureARB(GL.GL_TEXTURE0_ARB);  
            GL.Enable(GL.GL_TEXTURE_2D);  
            GL.BindTexture(GL.GL_TEXTURE_2D, m_texture[0].ID[0]);  
  
            /** 激活紋理1,並綁定紋理 */  
            GL.ActiveTextureARB(GL.GL_TEXTURE1_ARB);  
  
            /** 若是多重紋理啓用,則啓用該紋理 */  
            if (multitexturing)  
                GL.Enable(GL.GL_TEXTURE_2D);  
            else  
                GL.Disable(GL.GL_TEXTURE_2D);  
  
            GL.BindTexture(GL.GL_TEXTURE_2D, m_texture[1].ID[0]);  
  
            /** 繪製一個四方形牆面 */  
            GL.PushMatrix();  
            GL.Translatef(-2.5f, 0f, 0f);  
            GL.Scalef(2.0f, 2.0f, 2.0f);  
            GL.Begin(GL.GL_QUADS);  
  
            /** 左上點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 0.0f, 1.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 0.0f, 1.0f);  
            GL.Vertex3f(-1, 1, 0);  
  
            /** 左下點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 0.0f, 0.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 0.0f, 0.0f);  
            GL.Vertex3f(-1, -1, 0);  
  
            /** 右下點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 1.0f, 0.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 1.0f, 0.0f);  
            GL.Vertex3f(1, -1, 0);  
  
            /** 右上點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 1.0f, 1.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 1.0f, 1.0f);  
            GL.Vertex3f(1, 1, 0);  
  
            GL.End();    /**< 繪製結束 */  
            GL.PopMatrix();  
  
  
            /** 激活紋理0,並綁定紋理 */  
            GL.ActiveTextureARB(GL.GL_TEXTURE0_ARB);  
            GL.Enable(GL.GL_TEXTURE_2D);  
            GL.BindTexture(GL.GL_TEXTURE_2D, m_texture[2].ID[0]);  
  
            /** 激活紋理1,並綁定紋理 */  
            GL.ActiveTextureARB(GL.GL_TEXTURE1_ARB);  
  
            /** 若是多重紋理啓用,則啓用該紋理 */  
            if (multitexturing)  
                GL.Enable(GL.GL_TEXTURE_2D);  
            else  
                GL.Disable(GL.GL_TEXTURE_2D);  
            GL.BindTexture(GL.GL_TEXTURE_2D, m_texture[3].ID[0]);  
  
             
  
            GL.Translatef(2.5f, 0, 0);  
            GL.Scalef(2.0f, 2.0f, 2.0f);  
            GL.Begin(GL.GL_QUADS);  
  
            /** 左上點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 0.0f, 1.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 0.0f - wrap, 1.0f);  
            GL.Vertex3f(-1, 1, 0);  
  
            /** 左下點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 0.0f, 0.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 0.0f - wrap, 0.0f);  
            GL.Vertex3f(-1, -1, 0);  
  
            /** 右下點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 1.0f, 0.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 1.0f - wrap, 0.0f);  
            GL.Vertex3f(1, -1, 0);  
  
            /** 右上點 */  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 1.0f, 1.0f);  
            GL.MultiTexCoord2fARB(GL.GL_TEXTURE1_ARB, 1.0f - wrap, 1.0f);  
            GL.Vertex3f(1, 1, 0);  
            GL.End();  
        }  
    }  
}  

  移植只要改改幾個地方就很方便了。

  這裏我再次提醒你們,由於咱們用到非託管庫,freeglut.dll。須要把這個DLL拷貝到應用程序目錄,因爲系統分32位和64位,而默認VS生成項目模板是Any CPU,也就是3二、64位自適應的EXE,而OpenGLDotNet和tao 自帶freeglut.dll都是32位。

  須要本身設置爲X86 32位EXE,或者自行下載freeglut 編譯一個64位DLL,而後作兩個版本EXE。

相關文章
相關標籤/搜索