討論區快速選單
知識庫快速選單
軟體開發過程中有哪些資安漏洞? 掌握Salesforce雲端管理秘訣 政府補助!學嵌入式+物聯網
[ 回上頁 ] [ 討論區發言規則 ]
glut 教學 - 點選立體模型
更改我的閱讀文章字型大小
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/24 上午 01:30:11
使用立體模型去作媒體, 比起的平面貼圖, 除了圖像變化更細緻, 更重要的, 就是 立體模型 和 人手控制 之間的互動性, 在三維空間中選取三維空間的東西, 更能直覺地讓用家使用.

這次, 我要介紹的就是如何使用滑鼠選取立體模型.

網友們, 如果你曾經嘗試自己做 平面貼圖 的 滑鼠點選, 相信也應該明白當中的難處. 三維空間的立體模型, 呈現在平面顯示時, 是多麼複雜的不規則圖形, 這麼... 點選立體模型豈不是更複雜嗎? 但... 很諷刺地, OpenGL 為我們提供了 Selection Mode 的機制, 有效地把這些想來複雜的步驟, 簡單的完成了.

好, 讓我們去寫程式點選立體模型, 並且順道觀摩一下 OpenGL 設計者的心思.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/24 上午 01:37:39
/////////////////////////
// glutTest11.cpp
//
// Created by Gary Ho, ma_hty@hotmail.com, 2006
//


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "glut.h"
#include "glm.h"

void display();
void keyboard( unsigned char key, int x, int y );
void mouse( int button, int state, int x, int y );
void motion( int x, int y );
void timer( int value );

void process_pick( float x, float y );
void pick_func( float x, float y );
void pick_change( GLMtriangle *t0, GLMtriangle *t1 );

GLuint list_plane;
GLuint list_ground;

GLMmodel *glm_ground;
GLMtriangle *pathface[1024];
int n_pathface = 0;

int drag_state = -1;
int flying = false;
float plane_pos;
float plane_vec[3] = { 0, 0, 0 };



void main()
{
  glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB );
  glutInitWindowSize( 640, 640 );
  glutCreateWindow( "glutTest11" );

  glutDisplayFunc(display);
  glutMouseFunc( mouse );
  glutMotionFunc( motion );

  {
     glm_ground = glmReadOBJ( "world_curved.obj" );
     glmUnitize( glm_ground );
     glmScale( glm_ground, 1.2 );
     list_ground = glmList( glm_ground, GLM_SMOOTH );
  }

  {
    GLMmodel *glm_model;
     glm_model= glmReadOBJ( "soccerball.obj" );
     glmUnitize( glm_model );
     glmScale( glm_model, .05 );
     list_plane = glmList( glm_model, GLM_MATERIAL | GLM_SMOOTH );
     glmDelete( glm_model );
  }

  glEnable( GL_LIGHTING );
  glEnable( GL_LIGHT0 );
  glEnable( GL_DEPTH_TEST );
  
  glutMainLoop();
}

void display()
{
  glClearColor( .2, .3, .4, 1 );
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  
  GLint viewport[4];
    glGetIntegerv( GL_VIEWPORT, viewport );

  glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective( 45, double(viewport[2])/viewport[3], 0.1, 100 );
  
  glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 0,0,3, 0,0,0, 0,1,0 );

    glDisable( GL_LIGHTING );
    
    int i;

    if( drag_state!=-1 )
    for( i=0; i<n_pathface; i++ )
    {
     glColor3f( 0, (i+1.0)/n_pathface, 0 );
     glBegin(GL_TRIANGLES);
     glVertex3fv(&glm_ground->vertices[3 * pathface[i]->vindices[0]]);
     glVertex3fv(&glm_ground->vertices[3 * pathface[i]->vindices[1]]);
     glVertex3fv(&glm_ground->vertices[3 * pathface[i]->vindices[2]]);
     glEnd();
    }

    glEnable( GL_LIGHTING );
    glCallList( list_ground );

    if( flying )
    {
     flying = false;
     float *v0, *v1;
     float d, cd, r;

     cd = 0;
     for( i=0; i<n_pathface-1; i++ )
     {
     v0 = &glm_ground->vertices[3 * pathface[i]->vindices[0]];
     v1 = &glm_ground->vertices[3 * pathface[i+1]->vindices[0]];

     d = sqrtf(
     (v0[0]-v1[0])*(v0[0]-v1[0]) +
     (v0[1]-v1[1])*(v0[1]-v1[1]) +
     (v0[2]-v1[2])*(v0[2]-v1[2]) );

     if( cd+d > plane_pos )
     {
     r = (plane_pos-cd) / d;
     plane_vec[0] = v0[0]*(1-r)+v1[0]*r;
     plane_vec[1] = v0[1]*(1-r)+v1[1]*r;
     plane_vec[2] = v0[2]*(1-r)+v1[2]*r;
     plane_pos += .02;
     flying = true;
     glutTimerFunc( 30, timer, 0 );
     break;
     }
     cd += d;
     }
    }

    glTranslatef( plane_vec[0], plane_vec[1], plane_vec[2] );
    glCallList( list_plane );

  glutSwapBuffers();
}

void timer( int value )
{
  glutPostRedisplay();
}

void pick_change( GLMtriangle *t0, GLMtriangle *t1 )
{
  if( t1 )
  {
    pathface[ n_pathface ] = t1;
    n_pathface++;
  }
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/24 上午 01:38:24
#define BUFSIZE 1024
typedef struct _GSelect
{
  unsigned int n_name;
  unsigned int d0, d1;
  unsigned int name[1];
}GSelect;
GLMtriangle *prev_face = NULL;
GLMtriangle *active_face = NULL;

void process_pick( float x, float y )
{
  GLuint selectBuf[BUFSIZE];
  unsigned int hit;
  unsigned int i;

  glSelectBuffer( BUFSIZE, selectBuf );
  glInitNames();
  glRenderMode( GL_SELECT );
  pick_func( x, y );
  hit = glRenderMode( GL_RENDER );

  active_face = NULL;
  if( hit )
  {
    GLuint *ptr;
    GLuint dmin = -1;

    ptr = selectBuf;
    for( i=0; i<hit; i++ )
    {
     GSelect &sel = *((GSelect*)ptr);
     if( sel.d0 < dmin )
     {
     dmin = sel.d0;
     active_face = (GLMtriangle*)sel.name[0];
     }
     ptr += 3 + sel.n_name;
    }
  }

  if( active_face != prev_face )
  {
    pick_change( prev_face, active_face );
    prev_face = active_face;
  }
}
void pick_func( float x, float y )
{
  unsigned int i;
  GLMmodel* model;
  GLMgroup* group;
  GLMtriangle* triangle;

  GLint viewport[4];
    glGetIntegerv( GL_VIEWPORT, viewport );

  glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPickMatrix( x*viewport[2], y*viewport[3], .1,.1, viewport );
    gluPerspective( 45, double(viewport[2])/viewport[3], 0.1, 100 );

  glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 0,0,3, 0,0,0, 0,1,0 );

    model = glm_ground;
    group = model->groups;
    while( group )
    {
     for( i=0; i<group->numtriangles; i++ )
     {
     triangle = &model->triangles[group->triangles[i]];
     glPushName( (unsigned int) triangle );
     glBegin(GL_TRIANGLES);
     glVertex3fv(&model->vertices[3 * triangle->vindices[0]]);
     glVertex3fv(&model->vertices[3 * triangle->vindices[1]]);
     glVertex3fv(&model->vertices[3 * triangle->vindices[2]]);
     glEnd();
     glPopName();
     }
     group = group->next;
    }
}

void mouse( int button, int state, int x, int y )
{
  if( flying )
    return;

  int w,h;
    w = glutGet( GLUT_WINDOW_WIDTH );
    h = glutGet( GLUT_WINDOW_HEIGHT );

  if( button == GLUT_LEFT_BUTTON )
  {
    if( state == GLUT_DOWN )
    {
     drag_state = GLUT_LEFT_BUTTON;
     n_pathface = 0;
     process_pick(float(x)/w,float(h-y)/h);
     glutPostRedisplay();
    }

    if( state == GLUT_UP && drag_state == GLUT_LEFT_BUTTON )
    {
     drag_state = -1;
     process_pick(float(x)/w,float(h-y)/h);

     plane_pos = 0;
     flying = true;

     glutPostRedisplay();
    }
  }
}

void motion( int x, int y )
{
  if( flying )
    return;

  int w,h;
    w = glutGet( GLUT_WINDOW_WIDTH );
    h = glutGet( GLUT_WINDOW_HEIGHT );

  if( drag_state == GLUT_LEFT_BUTTON )
  {
    process_pick(float(x)/w,float(h-y)/h);
    glutPostRedisplay();
  }
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/24 上午 01:48:38
這一個範例, 會用到 "glut 教學 - 使用 OBJ 立體模型" 的 glm.h 和 glm.cpp, 請依照下列連結指示, 下載它們.

http://www.programmer-club.com/pc2020v5/forum/ShowSameTitleN.asp?URL=N&board_pc2020=opengl&id=1144
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/24 下午 10:42:56
這個程式, 會由你的專案資料夾讀取兩個 OBJ 檔, 即是
world_curved.obj world_curved.mtl 和
soccerball.obj soccerball.mtl,
這些檔案, 請依照 "glut 教學 - 使用 OBJ 立體模型" 下列連結指示, 下載它們.
http://www.programmer-club.com/pc2020v5/forum/ShowSameTitleN.asp?URL=N&board_pc2020=opengl&id=1144

這個範例, 有兩件立體模型, 一個是地面 和 一個足球. 當你使用滑鼠右鍵在地面上拖曳, 地面上就會出現你的拖曳路徑, 當你完成拖曳 並放開右鍵, 足球就會依據你的拖曳路徑在地面上移動. 注意, 這裡的地面, 是一個任意的立體模型, 它可以是一個平面, 也可以是一個地球, 甚至... 是一隻牛 ^^".

以一個範例來說, 這個程式實在有點長, 因為, 這一課範例除了要示範點選立體模型之外, 也希望一併展示如何 整合好些小技巧 成為較實質的功能.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/25 下午 01:06:36
要點選在畫面上的立體模型, 即是說, 你要先把滑鼠座標轉換成於立體模型的位置, 但是, 這個是有執行上的困難的, 或是說, 一個平面你可以用兩個變數把它參數化, 一個球體 你也可以把它參數化, 但是, 任意的立體模型, 並沒有簡單的幾何關係去讓你把它參數化, 這麼... 我們應該如何去索引 立體模型的位置 呢?

這個問題, 其實有一個很簡單直接的答案, 就是, 直接用立體模型的三角形當成作索引值. 這麼, 我們的目標, 就換化成 把滑鼠座標轉換成於立體模型的三角形索引, 只要把所有三角形也轉換成視窗座標, 然後與滑鼠座標作比較, 就可以得出需要的三角形索引 ... ...

因著類同的需求, OpenGL 早已準備好一個 Selection Mode 的機制, 在 OpenGL, 繪圖模式有三種, 分別是 Render Mode, Selection Mode 和 Feedback Mode. 一般繪圖時, 我們會用 Render Mode, 當我們想要取得某位置的三角形時, 我們就會用 Selection Mode.

在 Selection Mode 下, 我們也好像一般情況的繪畫立體模型, 主要的分別, 就是 view frustum 要盡量縮小成一點, 還有, 繪畫時要一併指示索引值 ( 即 name ). 當你在 Selection Mode 完成繪畫立體模型之後, 你的 select buffer 就會存放著所有掉進 view frustum 的索引值.

void process_pick( float x, float y )
{
  //...

  // 指是 select buffer 為 selectBuf
  glSelectBuffer( BUFSIZE, selectBuf );

  // 初始化 name stack
  glInitNames();

  // 進入 Selection Mode
  glRenderMode( GL_SELECT );

  // 繪畫立體模型
  pick_func( x, y );

  // 還原成 Render Mode, 並擷取總共有多是 name 掉進了 view frustum
  hit = glRenderMode( GL_RENDER );

  //...
}

作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/27 上午 11:42:24
void pick_func( float x, float y )
{
  //...

  glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    // 把 view frustum 盡量限制成一小點
    gluPickMatrix( x*viewport[2], y*viewport[3], .1,.1, viewport );

    gluPerspective( 45, double(viewport[2])/viewport[3], 0.1, 100 );

    // 對於每三角形
     {
     triangle = &model->triangles[group->triangles[i]];
     // 推 某三角形相應的索引值 進入 name stack
     glPushName( (unsigned int) triangle );

     // 繪畫三角形
     glBegin(GL_TRIANGLES);
     //...
     glEnd();

     // 由 name stack 移除 索引值
     glPopName();
    }
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/27 下午 09:17:53
經過上述步驟, 你的 select buffer 就會記錄著所有掉進 view frustum 的 hit, 請註意!!! hit 並不包含 深度測試 (depth test), 即是說, 不論你的物件遠近, 只要掉進 view frustum, 也包括在 select buffer 之內. 就是這個原因, 每個 hit 也會同時提供 最近 和 最遠 的深度值, 用來辨別深度的. 除此之外, hit 也提供了階層索引架構, 當然, 這個範例用了一層 階層 (即 三角形索引 ).

hit 的資料結構如下:
typedef struct _GSelect
{
  unsigned int n_name; // 索引值的階層總數
  unsigned int d0, d1; // 最近 和 最遠 的值
  unsigned int name[1]; // 索引值資料陣列
}GSelect;

說到這部份, 實質上已經離開了 OpenGL 的範圍, 如下的部份, 只是 解釋 和 應用 資料.

void process_pick( float x, float y )
{
  //...

    // 對於每個 hit
    for( i=0; i<hit; i++ )
    {
     GSelect &sel = *((GSelect*)ptr);
     // 如果最近視點
     if( sel.d0 < dmin )
     {
     // 更新被選取三角形
     dmin = sel.d0;
     active_face = (GLMtriangle*)sel.name[0];
     }
     ptr += 3 + sel.n_name;
    }
  }

  // 如果被選取三角形有改變, 呼叫 pick_change
  if( active_face != prev_face )
  {
    pick_change( prev_face, active_face );
    prev_face = active_face;
  }
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/27 下午 09:39:14
當我們把 process_pick 和 pick_func 都寫好, 尚差的工作就是當接數到 滑鼠輸入 時, 呼叫
process_pick(). 如果 被選取三角形有改變, pick_change() 就會再被呼叫. 對於這個範例, 被選取三角形有改變時, 新三角形索引值會被順序記錄, 而成為路徑. 繪畫時, 再按需要使用路徑... ...

就這樣... 範例完成了.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/27 下午 09:56:53
順帶一提, 這範例應用了 glutTimerFunc 來做出 鎖定更新率 的功能, 用意是做出 動畫效果 的, 並且 鎖定更新率 為一秒三十次, 如果只是 重複呼叫 display() 或是 重複呼叫glutPostRedisplay() 來達到 動畫效果, 會出現 因更新率過高 所致的 CPU 使用率偏高情況. 反正動畫一秒三十幅已很足夠, 鎖定更新率 為一秒三十次, 皆大歡喜...

void display()
{
    // ...

    // 30 微秒之後呼叫 timer()
     glutTimerFunc( 30, timer, 0 );

    // ...
}

void timer( int value )
{
  // 重畫視窗
  glutPostRedisplay();
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/11/27 下午 10:17:00
經過了漫長的講述之後... ...

白老鼠環顧四周, 然後說道 : "甚麼!!! 你還支撐著啊, 好~ 好~ 好~, 能撐到尾的, 都有獎品"

網友睡眼惺忪的說道 : "唉... 白老鼠 的獎品, 會有什麼好東西呢... 又不過是功課了吧... "

白老鼠汗顏中接著說下去 :

"這一次, 也做一點功課吧, 地面的立體模型 ( 即 glm_ground ), 現在是一個立體世界地圖, 但是, 這個範例並沒限制你只能使用它, 自行換上其它 立體模型 試試看呀.

最後... 如果你完成了這個功課, 就在這簽個名吧, 謝謝."
作者 : bjk(Up2u) 貼文超過1000則人氣指數超過50000點
[ 貼文 1047 | 人氣 64249 | 評價 730 | 評價/貼文 0.7 | 送出評價 196 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2016/2/4 下午 01:29:50
https://mega.nz/#!9l8RSRhB

我把這個範例包裝成VC放在網路上
希望對後面的人有幫助
作者 : bjk(Up2u) 貼文超過1000則人氣指數超過50000點
[ 貼文 1047 | 人氣 64249 | 評價 730 | 評價/貼文 0.7 | 送出評價 196 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2016/2/4 下午 01:31:38
https://mega.nz/#!9l8RSRhB!gPMfkyuAJO7IAGJpTRLYBqC43ni5h_-YJdGXfsuRsyc

抱歉,這個網址才對
作者 : bjk(Up2u) 貼文超過1000則人氣指數超過50000點
[ 貼文 1047 | 人氣 64249 | 評價 730 | 評價/貼文 0.7 | 送出評價 196 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2016/2/4 下午 03:57:28
更新一下版本

https://mega.nz/#!FhlGiLAD!9co851sRd2eyUF79yg1GArg9HpxiLkqciMJwWHSFiwY
 板主 : 白老鼠(Gary)
 > OpenGL - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - OpenGL - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
OpenGL
1 白老鼠(Gary) 2720 
2 CROMAYEN2000 1530 
3 aming 500 
4 東昇 380 
5 PLAYER 120 
6 富伯 110 
7 qq 100 
8 NDark 80 
9 ozzy 60 
10 simula 60 
OpenGL
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2018 程式設計俱樂部 http://www.programmer-club.com.tw/
0.0625