Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Win32 OpenGL vraiment "from scratch"

Bonjour !!

À vous petits curieux qui me lisez, je tiens à vous dire que ceci est pour moi un rappel de mes galères sur Vscode ainsi que l'apprentissage des bases d'OpenGL et la Win32 api. Cette clarification faite, nous pouvons commencer.

j'ai commencé par me renseigner sur quel IDE pouvais accueillir le développement de mes projets et mes test C++. Je lis et écoute diverses personnes parler de leur IDE et je finis par me dire  "tien VScode est beaucoup utiliser semble t'il". Vscode n'est pas livré avec un compilateur, il faut donc ajouter. Je pars à la recherche de l'émulateur idéal !!!! Et beaucoup parlent d'un certain Clang fessant partis d'une suite du nom de LLVM. Je décide donc de le tester ... Aile il y a un bug dessus et qui m'empêche d'ouvrir un terminal externe. Et celui-ci est connu depuis 2020 !!! Ce bug a 3 ans !!! Ce bug ne sera pas résolu de ci tôt... Bon pour utiliser clang de la meilleure façon, il faut 3 fichiers que voici :

un fichier build.bat :

```batch
@echo off
if not exist "./bin" mkdir "./bin"  
if not exist "./bin/debug" mkdir "./bin/debug"  
if not exist "./bin/release" mkdir "./bin/release"  
clang --version  
clang++ -std=c++20 -Wall -Wextra -pedantic -g main.cpp -o /bin/debug/test.exe -lgdi32 -lopengl32 -luser32
clang++ -std=c++20 -Wall -Wextra -pedantic    main.cpp -o /bin/release/test.exe -lgdi32 -lopengl32 -luser32
//pause  
@echo on
```

(-lgdi32 -lopengl32 pour OpenGL et -luser32 pour Win32)

Puis un fichier tasks.json dans un dossier .vscode :

```json
{      
    "version": "2.0.0",
    "tasks": 
    [
        {
            "label": "Build",
            "type": "shell",
            "windows":
            {
                "command": "Start-Process -FilePath \"build.bat\" -NoNewWindow 2>&1"
            },
            "group":
            {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}
```

Et un fichier launch.json dans un dossier .vscode 

```json
{
    "version": "0.2.0",
    "configurations": 
    [ 
        {
            "name": "Windows Launch(lldb)",
            "type": "lldb",
            "request": "launch",
            "program": "${workspaceFolder}/test.exe",
            "args": [],
            "cwd": "${workspaceFolder}",
            "preLaunchTask": "Build",
            //"console": "externalTerminal"
        }
    ]
}
```

Oui, je laisse l'option pour que la console se lance en externe en commentaire, j'ai espoir. Mais bon s'il a moyen de compiler, on pourrait se dire que ce n'est pas grave et se passer de la console externe... Il y a un autre problème ... Et celui-ci bien plus grave.

j'ai compilé plusieurs mêmes fichiers avec GCC et Clang. j'ai donc pu comparer le temps de compilation ainsi que le poids en octet des deux versions de ces mêmes applications. Clang est connu pour être plus rapide... Pourtant je n'ai vu aucune différence ... Par contre, je passe d'une application entre 50 et 59 ko avec GCC a une application de 600 a 1 000 ko. Et ces applications étaient très simples. Je me suis demandé en voyant ça combien supportait les disquettes de mon enfance ... 1,44 Mo... Soit 2 applications faites avec Clang... Ça ne fait pas beaucoup ... Donc va pour GCC. Et ce coup si je prévois le cas où je ferais du Linux 

./build.bat

```batch 
@echo off
if not exist "./bin" mkdir "./bin"   
if not exist "./bin/debug" mkdir "./bin/debug"   
if not exist "./bin/release" mkdir "./bin/release"   
g++ --version   
g++ -g -Wall -o ./bin/debug/test.exe ./main.cpp -lgdi32 -lopengl32  
g++    -Wall -o ./bin/release/test.exe ./main.cpp -lgdi32  -lopengl32 
//pause   
@echo on ```

( -lgdi32 -lopengl32 pour la suite avec OpenGL)

./.vscode/tasks.json 

```json 
{
     "version": "2.0.0",
     "tasks": [
         {
             "label": "Build",
             "type": "shell",
             "windows":
             {                 
                "command": "Start-Process -FilePath \"build.bat\" -NoNewWindow 2>&1"
              },
             "linux":
             {
                 "command": "./build.sh",
             },
             "group":
              {
                 "kind": "build",
                 "isDefault": true
             }
         }
     ]
}
```

./.vscode/launch.json 

```json  
{
     "version": "0.2.0",
     "configurations": [
       {
         "name": "Linux build & Launch(gcc)",
         "type": "cppdbg",
         "request": "launch",
         "program": "${workspaceFolder}/bin/release/test",
         "args": [],
          "cwd": "${workspaceFolder}",
          "stopAtEntry": false,
          "environment": [],
          "externalConsole": true,
          "preLaunchTask": "Build",
       },
       {
          "name": "Linux  Launch(gcc)",
          "type": "cppdbg",
          "request": "launch",
          "program": "${workspaceFolder}/bin/release/test",
          "args": [],
           "cwd": "${workspaceFolder}",
           "stopAtEntry": false,
           "environment": [],
           "externalConsole": true, 
        },
       {
         "name": "Windows build & Launch(gcc)",
         "type": "cppdbg",
         "request": "launch",
         "program": "${workspaceFolder}/bin/debug/test.exe",
         "args": [],
         "cwd": "${workspaceFolder}",
         "stopAtEntry": false,
         "environment": [],
         "externalConsole": true,
         "preLaunchTask": "Build",
       },
       {
         "name": "Windows Launch(gcc)",
         "type": "cppdbg",
         "request": "launch",
         "program": "${workspaceFolder}/bin/release/test.exe",
         "args": [],
         "cwd": "${workspaceFolder}",
         "stopAtEntry": false,
         "environment": [],
         "externalConsole": true,
       }
     ]
}
```

Bon, on va enfin vraiment coder. 

Pour commencer, on pourrait commencer par une fenêtre non ? pour cela Windows propose une fonction d'entrée particulier mais pas les autres OS alors on va s'en passé donc tout commence par :

```c++
int main() 
{    
    return 0;
}
```

Un petit hello world ?

```c++
#include <iostream>
int main() 
{
     std::cout << "Hello World! "<<" \n";
     system("pause");
     return 0;
}
```

Voilà ça dit hello world dans la console, mais c'est un peu nul une console ? Pour cela, il nous faut le HINSTANCE de notre application pour cela, il nous faut utiliser cette fonction ::

```c++ 
HINSTANCE hInstance = GetModuleHandle(NULL);
```

Ne pas oublier de rajouter le petit include :

```c++  
#include <windows.h>
 ```

Jusque-là rien de bien compliqué maintenant, il va nous falloir créer le style de la fenêtre (il parle de winclass dans l'api Win32) pour ce faire, je vais avoir besoin de donne de données textuelles qui change de format suivant si je suis en Unicode ou pas et donc je vais unifier cela.

```c++  
void StrFreeA(char * s) {
     delete[] s;
} 
void StrFreeW(wchar_t * s) {
     delete[] s;
}   
//Surcharge C++, plus simple. 
inline void StrFree( char   * s) { return StrFreeA(s); }
 inline void StrFree(wchar_t * s) { return StrFreeW(s); }
//Fonctions de conversion 
wchar_t * char_to_wchar(char const *inString) 
{     
     size_t const cchLenA = strlen(inString);
     size_t const cchLenW = mbstowcs(NULL, inString, cchLenA+1);
     wchar_t * outString = new wchar_t[cchLenW+1];
     mbstowcs(outString, inString, cchLenA+1);
      return outString;
}
 char * wchar_to_char(wchar_t const *inString) 
{     
    size_t const cchLenW = wcslen(inString);     
    size_t const cchLenA = wcstombs(NULL, inString, cchLenW+1);     
    char * outString = new char[cchLenA+1];     
    wcstombs(outString, inString, cchLenW+1);      
    return outString;
}
#ifdef UNICODE
     #define TXT_Win(t) char_to_wchar(t)     
#else
     #define TXT_Win(t) t     
#endif
```

Voilà cela unifie un peu tout cela ensuite créons la winClass, c'est ce qui donne un peu le style de la fenêtre :

```c++  
WNDCLASSEX  wc = {0};
wc.cbSize        = sizeof(WNDCLASSEX);              //taille de la class
wc.style         = 0;                               //pas de style parent
wc.lpfnWndProc   = WndProc;                         //fonction de message de la fenêtre a définir
wc.cbClsExtra    = 0;                               //pas de taille extra pour la fenêtre
wc.cbWndExtra    = 0;                               //pas de taille extra pour la class
wc.hInstance     = hInstance;                      //le HINSTANCE que nous avons récupérer
wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);//pas d'icone spécifique    
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    //pas de curseur spécifique
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);       //pas de font de fenêtre
wc.lpszMenuName  = NULL;                           //pas de menu
wc.lpszClassName = TXT_Win("CLASS_NAME");          //le nom de la class c'est CLASS_NAME
wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);//pas d'icone spécifique versions petite
RegisterClassEx(&wc);                              // penser en enregistre la class
```

Mais qu'est que cette fonction WndProc ? Une simple fonction qui gère des message envoyer à la fenêtre comme celle-ci :

```c++  
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{
     switch(msg)
     {
         case WM_CREATE:                                    //si la fenetre est créer
         break;
         case WM_CLOSE:                                     //si on appuye sur la croix
             DestroyWindow(hwnd);                           //détruit la fenetre
         break;
         case WM_DESTROY:                                   //évènement quand la fenêtre est détruite
             PostQuitMessage(0);                            //message pour quiter l'application
         break;
         default:
             return DefWindowProc(hwnd, msg, wParam, lParam);//procedure par default
     }
     return 0;
 }
```

Bien maintenant, nous pouvons ouvrir une fenêtre en utilisant cette winClass :

```c++
HWND hwnd;                                      //identifiant de la fenetre
hwnd = CreateWindowEx(         
         WS_EX_CLIENTEDGE,                      //La fenêtre a une bordure avec un bord enfoncé.
         TXT_Win("CLASS_NAME"),                 //le nom de la winclass que nous avons choisi
         TXT_Win("The title window"),           //le titre de la fenetre sera : "The title window"
         WS_OVERLAPPEDWINDOW,                   //une fenetre classic de windows
         CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,//position par défaut taille 800x600
         NULL,                                  //pas de parent
         NULL,                                  //pas de menu
         hInstance,                             // le fameux HINSTANCE
         NULL);                                 //user pointer
```

Et là, rien ne s'affiche !! Normal, il faut dire de montrer la fenêtre.

```c++
ShowWindow(hwnd, SW_SHOW);  //on montre la fenêtre
UpdateWindow(hwnd);         //première mise a jour de la fenêtre
```

Bon, la fenêtre s'affiche, mais elle reste figée.

C'est normal, les événements de la fenêtre ne sont pas lus :

```c++
while(GetMessage(&Msg, NULL, 0, 0) > 0)//récupère le message ou attend le prochain
{
         TranslateMessage(&Msg);       //traduit le message
         DispatchMessage(&Msg);        //transfere le message a la fonction de fenetre(WndProc)
}
```

Bien, nous avons une fenêtre qui répond normalement. Que faire de plus ? Déjà un récap ?

```c++
#include <iostream>
#include <windows.h>
void StrFreeA(char * s) {      delete[] s; }  
void StrFreeW(wchar_t * s) {      delete[] s; }    
//Surcharge C++, plus simple.  
inline void StrFree( char   * s) { return StrFreeA(s); }  
inline void StrFree(wchar_t * s) { return StrFreeW(s); } 
//Fonctions de conversion  
wchar_t * char_to_wchar(char const *inString)  
{
    size_t const cchLenA = strlen(inString);
    size_t const cchLenW = mbstowcs(NULL, inString, cchLenA+1);
    wchar_t * outString = new wchar_t[cchLenW+1];
    mbstowcs(outString, inString, cchLenA+1);
    return outString; 
}  
char * wchar_to_char(wchar_t const *inString)  
{          
    size_t const cchLenW = wcslen(inString);         
    size_t const cchLenA = wcstombs(NULL, inString, cchLenW+1);          
    char * outString = new char[cchLenA+1];          
    wcstombs(outString, inString, cchLenW+1);           
    return outString; 
} 
#ifdef UNICODE
      #define TXT_Win(t) char_to_wchar(t)      
#else
      #define TXT_Win(t) t      
#endif
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)  
{
      switch(msg)
      {
          case WM_CREATE:                                    //si la fenetre est créer
          break;
          case WM_CLOSE:                                     //si on appuye sur la croix
              DestroyWindow(hwnd);                           //détruit la fenetre
          break;
          case WM_DESTROY:                                   //évènement quand la fenêtre est détruite
              PostQuitMessage(0);                            //message pour quiter l'application
          break;
          default:
              return DefWindowProc(hwnd, msg, wParam, lParam);//procedure par default
      }
      return 0;
}
 int main()
{
    HINSTANCE hInstance = GetModuleHandle(NULL);
    WNDCLASSEX  wc = {0};
    wc.cbSize        = sizeof(WNDCLASSEX);              //taille de la class 
    wc.style         = 0;                               //pas de style parent 
    wc.lpfnWndProc   = WndProc;                         //fonction de message de la fenêtre a définir 
    wc.cbClsExtra    = 0;                               //pas de taille extra pour la fenêtre 
    wc.cbWndExtra    = 0;                               //pas de taille extra pour la class 
    wc.hInstance     = hInstance;                      //le HINSTANCE que nous avons récupérer 
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);//pas d'icone spécifique     
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    //pas de curseur spécifique 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);       //pas de font de fenêtre 
    wc.lpszMenuName  = NULL;                           //pas de menu 
    wc.lpszClassName = TXT_Win("CLASS_NAME");          //le nom de la class c'est CLASS_NAME 
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);//pas d'icone spécifique versions petite 
 
    RegisterClassEx(&wc);                              // penser en enregistre la class
    HWND hwnd;                                   //identifiant de la fenetre 
    hwnd = CreateWindowEx(
          WS_EX_CLIENTEDGE,                      //La fenêtre a une bordure avec un bord enfoncé.
          TXT_Win("CLASS_NAME"),                 //le nom de la winclass que nous avons choisi
          TXT_Win("The title window"),           //le titre de la fenetre sera : "The title window"
          WS_OVERLAPPEDWINDOW,                   //une fenetre classic de windows
          CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,//position par défaut taille 800x600
          NULL,                                  //pas de parent
          NULL,                                  //pas de menu
          hInstance,                             // le fameux HINSTANCE
          NULL);                                 //user pointer
    ShowWindow(hwnd, SW_SHOW);  //on montre la fenêtre
    UpdateWindow(hwnd);         //première mise a jour de la fenêtre
    while(GetMessage(&Msg, NULL, 0, 0) > 0)//récupère le message ou attend le prochain
    {
          TranslateMessage(&Msg);       //traduit le message
          DispatchMessage(&Msg);        //transfere le message a la fonction de fenetre(WndProc)
    }
    return 0;
}
```

Maintenant, la chose intéressante serait d'afficher quelque chose dans cette fenêtre : pour cela je test OpenGL et... problème, partout, on me parle de glu, glaux ou glew... j'en veux pas !!!!

Déjà ajouter un include

```c++ 
#include <gl/gl.h>
```

Bon après de dures recherches, je découvre qu'il faut commencer par créer un contexte de rendu. Et on commence par récupérer le contexte de la zone client... En gros, un identifiant de zone a dessiné qui est utiliser pour l'api graphique GDI de Windows.

```c++
HDC gldc = GetDC(hwnd); //utiliser pour connaitre la zone de dessin
```

Super, maintenant, nous devons définir le format de pixel utilisé pour OpenGL

```c++ 
PIXELFORMATDESCRIPTOR pfd =
{
             sizeof(PIXELFORMATDESCRIPTOR), // taille du PIXELFORMATDESCRIPTOR
             1,                             // versiom
             PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,    //Flags
             //PFD_DRAW_TO_WINDOW = peut dessiner la fenetre
             //PFD_SUPPORT_OPENGL = supporte OpenGL
             //PFD_DOUBLEBUFFER = dessine dans une mémoire avant d'envoyer a la fenêtre
             PFD_TYPE_RGBA,        // utilise le rouge vert bleu et transparence
             32,                   // bits du framebuffer.
             0, 0,                 //plan et décalage rouge
             0, 0,                 //plan et décalage vert
             0, 0,                 //plan et décalage bleu
             0, 0,                 //plan et décalage de transparence
             0,                    //plan total accumulation buffer
             0, 0, 0, 0,           //plan r,g,b,a de accumulation buffer
             24,                   // nombre de bits du depthbuffer
             8,                    // nombre de bits du stencilbuffer
             0,                    // nombre de Aux buffers du framebuffer.
             PFD_MAIN_PLANE,       // ceci est la couche pricipal
             0,                    // reserved
             0, 0, 0               // couche ignorer
};
int  PixelFormat;
PixelFormat = ChoosePixelFormat(gldc, &pfd);  //trouve un pixel format qui correspond        
SetPixelFormat(gldc,PixelFormat, &pfd);       //sélection le pixel format
```

Maintenant, il nous faut la fonction spécifique de Windows pour obtenir le contexte.

```c++
HGLRC gl_context = wglCreateContext(gldc);//créer le context
wglMakeCurrent(gldc, gl_context);         //utilise ce context
```

Voilà, je peux maintenant jouer avec OpenGL !!!! On test ?

```c++
while(GetMessage(&Msg, NULL, 0, 0) > 0)//récupère le message ou attend le prochain
{
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
    //nettoye l'écran avec un noire immaculer
    glClearColor(0f, 0f, 0f, 1.0f);         
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //couleur de dessin blanche
    glColor3f(1.0f, 1.0f, 1.0f);
    //dessine un triangle
    glBegin(GL_TRIANGLES);
        glVertex3f(-0.25f, -0.25f, 0.0f); // V0
        glVertex3f( 0.0f, 0.25f, 0.0f); // V1
        glVertex3f( 0.25f, -0.25f, 0.0f); // V2
    glEnd();
    SwapBuffers(gldc); //mise a jour du dessin
}
```

Bon deux problème OpenGL dessine que quand des event arrive à la fenêtre. On peut donc changer ça dans la boucle.  

```c++
while (true)     
{         
    while(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))//récupère le message ou attend le prochain
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    if(Msg.message == WM_QUIT)
        break;
    //nettoye l'écran avec un noire immaculer
    glClearColor(0f, 0f, 0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    //couleur de dessin blanche
    glColor3f(1.0f, 1.0f, 1.0f); 
    //dessine un triangle 
    glBegin(GL_TRIANGLES); 
        glVertex3f(-0.25f, -0.25f, 0.0f); // V0 
        glVertex3f( 0.0f, 0.25f, 0.0f); // V1 
        glVertex3f( 0.25f, -0.25f, 0.0f); // V2 
    glEnd();
    SwapBuffers(gldc); //mise a jour du dessin
}
```

Maintenant, rajoutons de la couleur pour cela, je vais adapter des fonction que j'ai utilisé dans : https://dimesto.itch.io/circleloop

```c++ 
typedef struct RGB {   float r, g, b; } ;
typedef struct HSV {   float h, s, v; } ;
RGB hsl_to_rgb( HSV hsv) 
{     
    RGB rgb = {};     
    if(hsv.h > 360)         
        hsv.h -= 360.0 * ((int)(hsv.h / 360));
    if(hsv.h < 0)
         hsv.h += 360.0 * ((int)(hsv.h / 360));
    float v = hsv.v;
    int t1 = (int)(hsv.h/60.0f) % 6;
    float f = hsv.h/60.0f - t1;
    float l = hsv.v * (1-hsv.s);
    float m = hsv.v * (1-f*hsv.s);
    float n = hsv.v * (1-(1-f)*hsv.s);
    
    float r,g,b;
    switch (t1)
    {
        case 0:
            r = v; g = n; b=l;
        break;
        case 1:
            r = m; g = v; b=l;
        break;
        case 2:
            r = l; g = v; b=n;
        break;
        case 3:
            r = l; g = m; b=v;
        break;
        case 4:
            r = n; g = l; b=v;
        break;
        case 5:
            r = v; g = l; b=m;
        break;
        default:
            break;
    }
    rgb.r = r;
    rgb.g = g;
    rgb.b = b;    
    
    return rgb;
}
HSV rgb_to_hsv(RGB rgb)
{
    HSV hsv = {};
    float max = 0.0f;
    float min = 0.0f;
    int maxI = 0;
    if(rgb.r > max)
    {
        max = rgb.r;
        maxI = 1;
    }
    if(rgb.g > max)
    {
        max = rgb.g;
        maxI = 2;
    }
    if(rgb.b > max)
    {
        max = rgb.b;
        maxI = 3;
    }
    
    if(rgb.r < min)min = rgb.r;
    if(rgb.g < min)min = rgb.g;
    if(rgb.b < min)min = rgb.b;
    hsv.v = max;
    if(max == 0) hsv.s = 0;
    else hsv.s = 1-(min/max);
    
    if     (max == min)hsv.h = 0;
    else if(maxI== 1) hsv.h = (360 + 60 * (rgb.g - rgb.b)/(max-min));
    else if(maxI== 2) hsv.h = (120 + 60 * (rgb.b - rgb.r)/(max-min));
    else if(maxI== 3) hsv.h = (240 + 60 * (rgb.r - rgb.g)/(max-min));
    hsv.h -= 360.0 * ((int)(hsv.h / 360));
    return hsv; 
}
 ```

Et donc récap ....

``` c++
#include <iostream>
#include <windows.h>
#include <gl/gl.h>
void StrFreeA(char * s) {      delete[] s; }
void StrFreeW(wchar_t * s) {      delete[] s; }
//Surcharge C++, plus simple.   
inline void StrFree( char   * s) { return StrFreeA(s); }   
inline void StrFree(wchar_t * s) { return StrFreeW(s); }
  
//Fonctions de conversion   
wchar_t * char_to_wchar(char const *inString)   
{
     size_t const cchLenA = strlen(inString);
     size_t const cchLenW = mbstowcs(NULL, inString, cchLenA+1);
     wchar_t * outString = new wchar_t[cchLenW+1];
     mbstowcs(outString, inString, cchLenA+1);
     return outString;  
}
char * wchar_to_char(wchar_t const *inString)   
{
    size_t const cchLenW = wcslen(inString);
    size_t const cchLenA = wcstombs(NULL, inString, cchLenW+1);
    char * outString = new char[cchLenA+1];
    wcstombs(outString, inString, cchLenW+1);
    return outString;
}
#ifdef UNICODE
    #define TXT_Win(t) char_to_wchar(t)
#else
    #define TXT_Win(t) t
#endif LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_CREATE:                                    //si la fenetre est créer
        break;
        case WM_CLOSE:                                     //si on appuye sur la croix
            DestroyWindow(hwnd);                           //détruit la fenetre
        break;
        case WM_DESTROY:                                   //évènement quand la fenêtre est détruite
            PostQuitMessage(0);                            //message pour quiter l'application
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);//procedure par default
    }
    return 0;
}
typedef struct RGB {   float r, g, b; } ;
typedef struct HSV {   float h, s, v; } ;
RGB hsl_to_rgb( HSV hsv)  
{
    RGB rgb = {};
    if(hsv.h > 360)
        hsv.h -= 360.0 * ((int)(hsv.h / 360));
    if(hsv.h < 0)
        hsv.h += 360.0 * ((int)(hsv.h / 360));
    float v = hsv.v;
    int t1 = (int)(hsv.h/60.0f) % 6;
    float f = hsv.h/60.0f - t1;
    float l = hsv.v * (1-hsv.s);
    float m = hsv.v * (1-f*hsv.s);
    float n = hsv.v * (1-(1-f)*hsv.s);
    
    float r,g,b;
    switch (t1)
    {
        case 0:
            r = v; g = n; b=l;
        break;
        case 1:
            r = m; g = v; b=l;
        break;
        case 2:
            r = l; g = v; b=n;
        break;
        case 3:
            r = l; g = m; b=v;
        break;
        case 4:
            r = n; g = l; b=v;
        break;
        case 5:
            r = v; g = l; b=m;
        break;
        default:
            break;
    }
    rgb.r = r;
    rgb.g = g;
    rgb.b = b;
    return rgb;
} 
HSV rgb_to_hsv(RGB rgb) 
{
    HSV hsv = {};
    float max = 0.0f;
    float min = 0.0f;
    int maxI = 0;
    
    if(rgb.r > max)
    {
        max = rgb.r;
        maxI = 1;
    }
    if(rgb.g > max)
    {
        max = rgb.g;
        maxI = 2;
    }
    if(rgb.b > max)
    {
        max = rgb.b;
        maxI = 3;
    }
    if(rgb.r < min)min = rgb.r;
    if(rgb.g < min)min = rgb.g;
    if(rgb.b < min)min = rgb.b;
    
    hsv.v = max;
    if(max == 0) hsv.s = 0;
    else hsv.s = 1-(min/max);
    if     (max == min)hsv.h = 0;
    else if(maxI== 1) hsv.h = (360 + 60 * (rgb.g - rgb.b)/(max-min));
    else if(maxI== 2) hsv.h = (120 + 60 * (rgb.b - rgb.r)/(max-min));
    else if(maxI== 3) hsv.h = (240 + 60 * (rgb.r - rgb.g)/(max-min));
    
    hsv.h -= 360.0 * ((int)(hsv.h / 360));
    return hsv;  
}
int main() 
{
    HINSTANCE hInstance = GetModuleHandle(NULL);
    WNDCLASSEX  wc = {0};
    wc.cbSize        = sizeof(WNDCLASSEX);              //taille de la class
    wc.style         = 0;                               //pas de style parent
    wc.lpfnWndProc   = WndProc;                         //fonction de message de la fenêtre a définir
    wc.cbClsExtra    = 0;                               //pas de taille extra pour la fenêtre
    wc.cbWndExtra    = 0;                               //pas de taille extra pour la class
    wc.hInstance     = hInstance;                      //le HINSTANCE que nous avons récupérer
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);//pas d'icone spécifique
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    //pas de curseur spécifique
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);       //pas de font de fenêtre
    wc.lpszMenuName  = NULL;                           //pas de menu
    wc.lpszClassName = TXT_Win("CLASS_NAME");          //le nom de la class c'est CLASS_NAME
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);//pas d'icone spécifique versions petite
    RegisterClassEx(&wc);                              // penser en enregistre la class
    HWND hwnd;                                   //identifiant de la fenetre
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,                      //La fenêtre a une bordure avec un bord enfoncé.
        TXT_Win("CLASS_NAME"),                 //le nom de la winclass que nous avons choisi
        TXT_Win("The title window"),           //le titre de la fenetre sera : "The title window"
        WS_OVERLAPPEDWINDOW,                   //une fenetre classic de windows
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,//position par défaut taille 800x600
        NULL,                                  //pas de parent
        NULL,                                  //pas de menu
        hInstance,                             // le fameux HINSTANCE
        NULL);                                 //user pointer
    
    ShowWindow(hwnd, SW_SHOW);  //on montre la fenêtre
    UpdateWindow(hwnd);         //première mise a jour de la fenêtre 
    HDC gldc = GetDC(hwnd); //utiliser pour connaitre la zone de dessin
    PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR), // taille du PIXELFORMATDESCRIPTOR
        1,                             // versiom
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,    //Flags
        //PFD_DRAW_TO_WINDOW = peut dessiner la fenetre
        //PFD_SUPPORT_OPENGL = supporte OpenGL
        //PFD_DOUBLEBUFFER = dessine dans une mémoire avant d'envoyer a la fenêtre
        PFD_TYPE_RGBA,        // utilise le rouge vert bleu et transparence
        32,                   // bits du framebuffer.
        0, 0,                 //plan et décalage rouge
        0, 0,                 //plan et décalage vert
        0, 0,                 //plan et décalage bleu
        0, 0,                 //plan et décalage de transparence
        0,                    //plan total accumulation buffer
        0, 0, 0, 0,           //plan r,g,b,a de accumulation buffer
        24,                   // nombre de bits du depthbuffer
        8,                    // nombre de bits du stencilbuffer
        0,                    // nombre de Aux buffers du framebuffer.
        PFD_MAIN_PLANE,       // ceci est la couche pricipal
        0,                    // reserved
        0, 0, 0               // couche ignorer 
        }; 
    int  PixelFormat;
    PixelFormat = ChoosePixelFormat(gldc, &pfd);  //trouve un pixel format qui correspond
    SetPixelFormat(gldc,PixelFormat, &pfd);       //sélection le pixel format
    HGLRC gl_context = wglCreateContext(gldc);//créer le context 
    wglMakeCurrent(gldc, gl_context);         //utilise ce context
    RGB color ={};
    color.r = 1.0;
    color.g = 0.0;
    color.b = 0.0;
    while(true)
    {
        while(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))//récupère le message ou attend le prochain
        { 
            TranslateMessage(&Msg); //traduit le message
            DispatchMessage(&Msg);  //transfere le message a la fonction de fenetre(WndProc)
        } 
        if(Msg.message == WM_QUIT) 
            break;
        HSV hcolor =rgb_to_hsv(color);
        hcolor.h +=0.01f;
        color = hsl_to_rgb(hcolor);
        glClearColor(color.r, color.g, color.b, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glColor3f(1.0f - color.r, 1.0f - color.g, 1.0f - color.b);
        glBegin(GL_TRIANGLES);
            glVertex3f(-0.25f, -0.25f, 0.0f); // V0
            glVertex3f( 0.0f, 0.25f, 0.0f); // V1
            glVertex3f( 0.25f, -0.25f, 0.0f); // V2
        glEnd();
        
        SwapBuffers(gldc);//mise a jour du dessin
    }
    return 0;
}
```


Support this post

Did you like this post? Tell us

In this post

Leave a comment

Log in with your itch.io account to leave a comment.

C'est le fameux triangle, mais pas n'importe comment mossieu ! C'est un triangle en HSV ! Et ça, ça, c'est la classe.

Merci, ça permet de mieux voir si le programme freeze, ça aide avec des api plus compliqué.

Mentioned in this post

This is a little rhythm game
Action
Play in browser