Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

BLUE FIRE (ANIMATION DE FLAMMES - EXEMPLE DIRECTDRAW PLEIN ECRAN)


Information sur la source

Catégorie :Graphique Niveau : Expert Date de création : 11/03/2002 Date de mise à jour : 14/03/2002 23:23:58 Vu / téléchargé: 9 022 / 319

Note :
Aucune note

Commentaire sur cette source (4)
Ajouter un commentaire et/ou une note

Description

  Algorythme d'effet fraphique 2D de flammes, utilisant l'interface DirectDraw en plein ecran.
 

Source

  • //Compilé avec Borland C++ Builder 4
  • /*----------------------------------------------------------------------------*
  • | |
  • | Dessiner des Flammes, Unité principale (Feu.cpp) |
  • | |
  • | programmé par Blustuff |
  • | |
  • *----------------------------------------------------------------------------*/
  • /*----------------------------------------------------------------------------*
  • | Includes |
  • *----------------------------------------------------------------------------*/
  • #include <windows.h>
  • #include "affichage.cpp"
  • //Uniquement pour Borland (Les lignes suivantes permette la liaision des librairies DirectDraw) :
  • #include <condefs.h>
  • USELIB("..\mssdk\lib\borland\dxguid.lib");
  • USELIB("..\mssdk\lib\borland\ddraw.lib");
  • /*----------------------------------------------------------------------------*
  • | Variables Globales |
  • *----------------------------------------------------------------------------*/
  • bool FinDeProgramme = false;
  • /*----------------------------------------------------------------------------*
  • | Fonction WindowProc |
  • *----------------------------------------------------------------------------*/
  • #pragma argsused //La fonction suivante n'utilise pas tous ses parametres
  • LRESULT CALLBACK WindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
  • {
  • switch (uMsg)
  • {
  • case WM_KEYDOWN :
  • if (wParam == VK_ESCAPE)
  • FinDeProgramme = true; //Quitter le programme lors de l'appui sur escape
  • break;
  • default : return DefWindowProc(hWnd, uMsg, wParam, lParam);
  • }
  • return 0;
  • }
  • /*----------------------------------------------------------------------------*
  • | Initialisation de la fenêtre |
  • *----------------------------------------------------------------------------*/
  • bool InitialisationFenetre(HINSTANCE hInstance, HWND* hWnd)
  • {
  • *hWnd = 0;
  • WNDCLASS wc;
  • wc.style = CS_HREDRAW | CS_VREDRAW;
  • wc.lpfnWndProc = (WNDPROC) WindowProc;
  • wc.cbClsExtra = 0;
  • wc.cbWndExtra = sizeof(DWORD);
  • wc.hInstance = hInstance;
  • wc.hIcon = NULL;
  • wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  • wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
  • wc.lpszMenuName = NULL;
  • wc.lpszClassName = "XmplHr17Class";
  • if (!RegisterClass(&wc))
  • return false;
  • if (!(*hWnd = CreateWindow("XmplHr17Class", "Line Race", WS_VISIBLE|WS_POPUP, 0, 0, 0, 0, NULL, NULL, hInstance, NULL)))
  • return false;
  • ShowCursor(false); //Cacher le curseur de la souris
  • return true;
  • }
  • /*----------------------------------------------------------------------------*
  • | Fonction WinMain |
  • *----------------------------------------------------------------------------*/
  • #pragma argsused //La fonction suivante n'utilise pas tous ses parametres
  • WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
  • {
  • HWND hWnd;
  • MSG msg;
  • if (!InitialisationFenetre(hInstance, &hWnd))
  • return 0;
  • if (!InitialiseAffichage(hWnd))
  • FinDeProgramme = true;
  • AfficherTexte(SurfaceTexte, "Blustuff Blue Fire", 0, 0, false, 0x00ff0000); //Afficher le texte sur la surface de texte
  • while (!FinDeProgramme)
  • {
  • if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  • {
  • TranslateMessage(&msg);
  • DispatchMessage(&msg);
  • }
  • else
  • {
  • DessinerFlammes(); //Dessiner les flammes
  • RECT Destination = {0,0,200,50};
  • SurfaceSecondaire->BltFast(40, 130, SurfaceTexte, &Destination, DDBLTFAST_SRCCOLORKEY);//Ecire le texte
  • SurfacePrimaire->Flip(0,DDFLIP_DONOTWAIT); //Afficher la surface
  • }
  • }
  • DesallocationAffichage();
  • return 0;
  • }
  • /*----------------------------------------------------------------------------*
  • | |
  • | Dessiner des Flammes, Unité d'affichage V.1.0 (Affichage.cpp) |
  • | |
  • | programmé par Blustuff |
  • | |
  • *----------------------------------------------------------------------------*/
  • /*
  • Note du programmeur :
  • - Ce programme utilise DirectDraw pour initialiser l'affichage, mais accede
  • par ses propres fonctions a la mémore vidéo pour la modifier. La connaissance
  • de DirectDraw n'est pas importante, la fonction DessinerFlamme et la seulle
  • qui puisse etre interessante ici.
  • - Ce programme n'est pas entièrement optimisé en rapidité. Cela pour diverse
  • raison (j'ai la flemme), dont la lisibilité du code qui doit rester a peu près
  • acceptable
  • - Note lexicale : Une interface est une structure de base de DirectX, dont
  • chaque autre élément,DirectDraw, DirectSound, DirectInput etc. hérite. Elle
  • possède notament la fonction Release() qui permet de désallouer la mémoire qui
  • lui était réservée.
  • Une Surface est un emplacement en mémoire contenant des
  • données susceptible d'etre affichée, a l'ecran ou sur une autre surface.
  • - J'ai horreur des termes en anglais dans une programme, parce que j'aime bien
  • rapeller que les francais ont réalisé les plus beaux programme jusqu'a
  • maintenant. Si j'ai laissé des noms de variables en anglais, veuillez m'en
  • excuser. (Celles de DX j'y suis pour rien)
  • - Je n'ai pas tout inventé tout seul. J'ai repris cet algorythme de Franck
  • Bauquier (defcon1@caramail.com) sur son site http://defcon1.free.fr/ . J'ai
  • juste personalisé les flammes et adapté le programme pour DirectDraw
  • - Les flammes sont bleues. Ca simplifie vraiment le programme. Si vous voulez
  • faire des flammes avec autre chose, il faut revoir une bonne partie du code.
  • Les flammes doivent etre traitées couleur par couleur. Si vous voulez faire des
  • flammes violettes, vous devez traiter le rouge et le bleu séparement. Mon
  • programme ne s'y adapte pas (Et puis j'aime bien le bleu).
  • Pour me contacter :
  • blustuff@wanadoo.fr
  • http://perso.wanadoo.fr/blustuff/
  • */
  • /*----------------------------------------------------------------------------*
  • | Fichier d'en tête |
  • *----------------------------------------------------------------------------*/
  • #include "affichage.h"
  • /*----------------------------------------------------------------------------*
  • | Initialisation |
  • *----------------------------------------------------------------------------*/
  • /*
  • N'essayez pas trop de vous lancer dans la comprehension du code de
  • l'initialisation graphique de DirectDraw si vous n'avez jamais programmé avec
  • DirectX. J'appelle ici de nombreuse fonctions que je n'expliquerai pas
  • */
  • bool InitialiseAffichage(HWND hWnd)
  • {
  • DDSURFACEDESC2 ddsd;
  • DDSCAPS2 ddscaps;
  • //Creation de l'interface DirectDraw
  • if (DirectDrawCreateEx(NULL, (void**)&InterfaceDirectDraw, IID_IDirectDraw7, NULL) != DD_OK)
  • return false;
  • //Selection du niveau cooperatif
  • if (InterfaceDirectDraw->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT) != DD_OK)
  • return false;
  • //Choix du mode d'affichage
  • if (InterfaceDirectDraw->SetDisplayMode(LargeurEcran, HauteurEcran, 32, 0, 0) != DD_OK)
  • return false;
  • //Creation des surfaces primaires et secondaires
  • ZeroMemory(&ddsd, sizeof(ddsd));
  • ZeroMemory(&ddscaps, sizeof(ddscaps));
  • ddsd.dwSize = sizeof( ddsd );
  • ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  • ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
  • ddsd.dwBackBufferCount = 1;
  • ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
  • if (InterfaceDirectDraw->CreateSurface(&ddsd, &SurfacePrimaire, NULL) != DD_OK)
  • return false;
  • //Association du back Buffer
  • if (SurfacePrimaire->GetAttachedSurface(&ddscaps, &SurfaceSecondaire) != DD_OK)
  • return false;
  • //Effacement des surfaces
  • EffacerSurface(SurfacePrimaire);
  • EffacerSurface(SurfaceSecondaire);
  • //Création et effacement d'une surface réservée au texte
  • ZeroMemory(&ddsd,sizeof(ddsd));
  • ddsd.dwSize = sizeof(DDSURFACEDESC2);
  • ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;
  • ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
  • ddsd.dwWidth = 200;
  • ddsd.dwHeight = 50;
  • InterfaceDirectDraw->CreateSurface(&ddsd, &SurfaceTexte, NULL);
  • DDCOLORKEY key;
  • key.dwColorSpaceLowValue = 0x00000000;
  • key.dwColorSpaceHighValue = 0x00000000;
  • SurfaceTexte->SetColorKey(DDCKEY_SRCBLT, &key);
  • EffacerSurface(SurfaceTexte);
  • //Stockage dans un tableau des valeurs en octet pour le débur de chaque ligne de l'ecran
  • SurfacePrimaire->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); //Obtention des propriétés de la surface
  • SurfacePrimaire->Unlock(NULL);
  • AlignY = (int*) malloc(ddsd.dwHeight * sizeof(int));
  • for (unsigned int y = 0 ; y < ddsd.dwHeight ; y++)
  • AlignY[y] = ddsd.lPitch * y / 4;
  • //La fonction retourne true si l'initialisation réussit
  • return true;
  • }
  • /*----------------------------------------------------------------------------*
  • | Desallocation Affichage |
  • *----------------------------------------------------------------------------*/
  • /*
  • Desallocation de l'affichage : Cette fonction libère l'espace alloué par
  • DirectDraw. Il faut vérifier que ces interface aient été allouées d'où
  • l'importance d'initialiser les pointeurs vers elle à NULL. Lorsque les
  • interfaces sont initialisée, le pointeur prend donc leur adresse, et on sait
  • donc que l'on peut les désallouer. Voici donc l'interet de verifier la
  • non-nullité des pointeurs vers les interfaces avant d'appeler Release()
  • */
  • void DesallocationAffichage()
  • {
  • free(AlignY);
  • if (SurfaceTexte)
  • SurfaceTexte->Release();
  • if (SurfaceSecondaire)
  • SurfaceSecondaire->Release();
  • if (SurfacePrimaire)
  • SurfacePrimaire->Release();
  • if (InterfaceDirectDraw)
  • InterfaceDirectDraw->Release();
  • }
  • /*----------------------------------------------------------------------------*
  • | Affichage de texte (Fonction GDI) |
  • *----------------------------------------------------------------------------*/
  • /*
  • Cette fonction fait appel a une fonction GDI de Windows pour afficher du
  • texte, cela grace a la compatibilité de DirectX, qui fournit un handle context
  • (hdc) pour permetre a la fonction TextOut d'ecrire sur cette surface. Ici on ne
  • voit pas très bien le fonctionement interne de l'affichage de texte. Il est en
  • réalité plus compliqué puisque DirectX doit informer la fonction GDI de la
  • taille de la surface, de son format de pixels, etc. Ca n'a ici aucune
  • importance. Cette fonction est lente, mais l'affichage de texte ne recquiert pas
  • une grande rapidité. J'ai donc préféré la simplicité a la rapidité. (J'espère
  • que vous ne m'en voulez pas trop :) )
  • */
  • void AfficherTexte(LPDIRECTDRAWSURFACE7 Surface, char* Text, int X, int Y, bool Center, DWORD TextColor, DWORD BackColor)
  • {
  • if (InterfaceDirectDraw) //La fonction s'eecute que si l'affichage est initialisé
  • {
  • HDC hdc;
  • if (Surface->GetDC(&hdc) == DD_OK) //Creer un contexte de dessin et bloquer la surface pour cet usage
  • {
  • if (Center)
  • SetTextAlign(hdc, TA_CENTER); //Centrer le texte sur (x,y) si Center est spécifié
  • else
  • SetTextAlign(hdc, TA_LEFT | TA_TOP); //Sinon (x,y) corespond au coin haut droit du cadre de texte
  • SetTextColor(hdc,TextColor); //Selection de la couleur du texte
  • SetBkColor(hdc,BackColor); //Et du fond
  • TextOut(hdc, X, Y, Text, strlen(Text)); //Ecrire le texte
  • Surface->ReleaseDC(hdc); //Débloquer la surface pour qu'elle puisse etre réutilisée normalement
  • }
  • }
  • }
  • /*----------------------------------------------------------------------------*
  • | Effacer une surface |
  • *----------------------------------------------------------------------------*/
  • /*
  • Cette fonction efface une surface quatre octets par quatre octets. Elle est
  • optimisée
  • */
  • void EffacerSurface(LPDIRECTDRAWSURFACE7 Surface)
  • {
  • if (InterfaceDirectDraw) //La fonction s'eecute que si l'affichage est initialisé
  • {
  • DDSURFACEDESC2 ddsd;
  • ddsd.dwSize = sizeof(ddsd);
  • if (Surface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) //Bloquer la surface pour l'ecriture
  • {
  • register int* FinDeSurface = (int*)((char*)ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch); //Fin de la surface (lPitch est le nombre d'octets entre deux lignes)
  • for (register int* x = (int*)ddsd.lpSurface ; x < FinDeSurface ; x++)
  • *x = 0;
  • Surface->Unlock(NULL); //Debloquer la surface pour usage posterieur
  • }
  • }
  • }
  • /*----------------------------------------------------------------------------*
  • | Dessiner les Flammes |
  • *----------------------------------------------------------------------------*/
  • /*
  • Voici la fonction interessante : Elle va dessiner les flammes. J'ai tout
  • d'abord défini des macros pour obtenir la valeur d'un pixel et une pour changer
  • la valeur d'un pixel. Elle sont optimisés pour le mode d'affichage en couleur
  • 32 bits , mais ne vérifient jamais si le pixel est hors ecran, ce qui est
  • dangereux si on controle mal son usage.
  • Ensuite viens la fonction proprement dite. Elle suit l'algorythme suivant :
  • On trace d'abord une ligne de pixels aléatoirement (appelée foyer). Dans cet
  • exemple elle n'est pas réelement aléatoire. J'ai choisi de faire suivre aux
  • couleurs une fonction sinusoidale irégulière. C'est a dire que l'angle
  • n'évolue pas uniformenent. Cela donerait si on voulait tracer la courbe
  • représentative de la fonction, des formes courbes mais sans direction apparente
  • ni periode.
  • L'étape suivante consiste a fiare partir les flammes du foyer. Ici j'ai
  • choisit d'attribuer a chaque pixel la valeur moyenne des pixels inferieurs (bas
  • gauche, bas milieu, bas droit). Cela permet d'avoir une continuité dans les
  • flammes. J'ai mis un plus fort coeficient devant le pixel juste en dessous, de
  • telle sorte que les flammes soient plus fines. J'ai ajouté a cette moyenne la
  • couleur du pixel précédent a la même place. Cela donne encore une continuité
  • mais cette fois-ci dans le temps.
  • L'effet d'ondulation est effectué par un décalage (quie j'ai appelé z) de
  • chaque ligne suivant la fonction sinus. Pour que L'ondulation ne soit pas
  • régulière, j'ai décalé l'angle de départ de la fonction sinus a chaque affichage
  • d'une valeur aléatoire (c).
  • Il faut que la pointe des flammes soit plus faible. Avant l'affichage de
  • chaque pixel, on décrémente donc sa valeur de 1. De cette manière le foyer est
  • nécessairement le plus lumineux.
  • Il y a bien sur d'autre algorythmes, et d'autre effet pour dessiner des
  • flammes.
  • */
  • #define GetLastPixel(X, Y) (*((int*)ddsd1.lpSurface + AlignY[(Y)] + (X))) //Obtention de la couleur du pixel sur la Surface Primaire (Couleur précédente)
  • #define GetCurrentPixel(X, Y) (*((int*)ddsd2.lpSurface + AlignY[(Y)] + (X))) //Obtention de la couleur du pixel sur la Surface Secondaire (Couleur courante)
  • #define SetPixel(X, Y, Couleur) (*((int*)ddsd2.lpSurface + AlignY[(Y)] + (X)) = (int)(Couleur)) //Définition de la couleur d'un pixel sur la surface secondaire
  • void DessinerFlammes()
  • {
  • if (InterfaceDirectDraw)
  • {
  • DDSURFACEDESC2 ddsd1, ddsd2;
  • ddsd1.dwSize = ddsd2.dwSize = sizeof(DDSURFACEDESC2);
  • //Blocage des surfaces pour pouvoir y dessiner
  • if (SurfacePrimaire->Lock(NULL, &ddsd1, DDLOCK_WAIT, NULL) == DD_OK &&
  • SurfaceSecondaire->Lock(NULL, &ddsd2, DDLOCK_WAIT, NULL) == DD_OK)
  • {
  • float c;
  • //Tracé du foyer
  • for (int x = 10 ; x < LargeurEcran-12 ; x++, c += (float(random(100)-50)/140)) //Changer 140 par une valeur plus grande pour avoir des flammes au foyer plus fin
  • SetPixel(x, 180, (sin(c)+1)/2*(ddsd1.ddpfPixelFormat.dwBBitMask)); //ddsd1.ddpfPixelFormat.dwBBitMask est la couleur bleue. Par ailleur : 0 < sin(c)+1)/2 < 1
  • c += random(100); //Décalage d'angle pour la fonction sinus
  • for (register int y = 179 ; y > 60 ; y--) //Tracé du corps de la flamme
  • {
  • int z = sin((float)y/5+c)*2; //Décalage de chaque ligne pour l'ondulation
  • for (register int x = 1 ; x < LargeurEcran-2 ; x++) //Tracé de la ligne de flamme
  • {
  • int NouvelleCouleur;
  • //Calcul de la couleur moyenne
  • if (NouvelleCouleur = (GetCurrentPixel(x-1+z, y+1) + GetCurrentPixel(x+z, y+1)*16 + GetCurrentPixel(x+1+z, y+1) + GetLastPixel(x,y)*2)/20)
  • NouvelleCouleur--; //Attenuation de la flamme
  • SetPixel(x, y, NouvelleCouleur); //Tracé du pixel
  • }
  • }
  • }
  • SurfacePrimaire->Unlock(NULL);
  • SurfaceSecondaire->Unlock(NULL);
  • }
  • }
  • //----------------------------------------------------------------------------
  • /*----------------------------------------------------------------------------*
  • | |
  • | Dessiner des Flammes, Unité d'affichage V.1.0 (Affichage.h) |
  • | |
  • | programmé par Blustuff |
  • | |
  • *----------------------------------------------------------------------------*/
  • /*----------------------------------------------------------------------------*
  • | Includes |
  • *----------------------------------------------------------------------------*/
  • #include <ddraw.h> //Pour l'interface IDirectDraw
  • #include <stdlib.h> //Pour random()
  • #include <math.h> //Pour sin()
  • /*----------------------------------------------------------------------------*
  • | Propriétés de l'Affichage |
  • *----------------------------------------------------------------------------*/
  • /*
  • Les valeurs suivantes doivent corespondre a un mode d'ecran existant et supporté
  • sinon l'initialisation ne fonctionera pas
  • */
  • #define LargeurEcran 320
  • #define HauteurEcran 200
  • /*----------------------------------------------------------------------------*
  • | Variables Globales |
  • *----------------------------------------------------------------------------*/
  • LPDIRECTDRAW7 InterfaceDirectDraw = NULL;
  • LPDIRECTDRAWSURFACE7 SurfacePrimaire = NULL; //Primary Buffer. En clair, interface représentant la mémore vidéo directement reliée à l'ecran
  • LPDIRECTDRAWSURFACE7 SurfaceSecondaire = NULL; //Back Buffer. Surface sur laquelle on dessine avant de proceder au bliting (Je n'epliquerai pas le bliting ici)
  • LPDIRECTDRAWSURFACE7 SurfaceTexte = NULL; //Surface pour afficher le texte
  • int* AlignY; //Tableau de valeurs indiquant la taille en octet jusqu'a une ligne de l'ecran
  • /*----------------------------------------------------------------------------*
  • | Fonctions |
  • *----------------------------------------------------------------------------*/
  • bool InitialiseAffichage(HWND hWnd);
  • void DesallocationAffichage();
  • void AfficherTexte(LPDIRECTDRAWSURFACE7 Surface, char* Text, int X, int Y, bool Center = true, DWORD TextColor = 0x00ffffff, DWORD BackColor = 0x00000000);
  • void EffacerSurface(LPDIRECTDRAWSURFACE7 Surface);
  • void DessinerFlammes();
  • //----------------------------------------------------------------------------
//Compilé avec Borland C++ Builder 4

/*----------------------------------------------------------------------------*
 |                                                                            |
 | Dessiner des Flammes, Unité principale (Feu.cpp)                           |
 |                                                                            |
 |   programmé par Blustuff                                                   |
 |                                                                            |
 *----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------*
 | Includes                                                                   |
 *----------------------------------------------------------------------------*/

#include <windows.h>
#include "affichage.cpp"

//Uniquement pour Borland (Les lignes suivantes permette la liaision des librairies DirectDraw) :
#include <condefs.h>
USELIB("..\mssdk\lib\borland\dxguid.lib");
USELIB("..\mssdk\lib\borland\ddraw.lib");

/*----------------------------------------------------------------------------*
 | Variables Globales                                                         |
 *----------------------------------------------------------------------------*/

bool FinDeProgramme = false;


/*----------------------------------------------------------------------------*
 | Fonction WindowProc                                                        |
 *----------------------------------------------------------------------------*/

#pragma argsused                //La fonction suivante n'utilise pas tous ses parametres
LRESULT CALLBACK WindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
    case WM_KEYDOWN :
      if (wParam == VK_ESCAPE)
        FinDeProgramme = true;                  //Quitter le programme lors de l'appui sur escape
      break;

    default : return DefWindowProc(hWnd, uMsg, wParam, lParam);
  }

  return 0;
}


/*----------------------------------------------------------------------------*
 | Initialisation de la fenêtre                                               |
 *----------------------------------------------------------------------------*/

bool InitialisationFenetre(HINSTANCE hInstance, HWND* hWnd)
{
  *hWnd = 0;

  WNDCLASS wc;
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = (WNDPROC) WindowProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = sizeof(DWORD);
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = "XmplHr17Class";

  if (!RegisterClass(&wc))
    return false;

  if (!(*hWnd = CreateWindow("XmplHr17Class", "Line Race", WS_VISIBLE|WS_POPUP, 0, 0, 0, 0, NULL, NULL, hInstance, NULL)))
    return false;

  ShowCursor(false);   //Cacher le curseur de la souris

  return true;
}


/*----------------------------------------------------------------------------*
 | Fonction WinMain                                                           |
 *----------------------------------------------------------------------------*/

#pragma argsused                //La fonction suivante n'utilise pas tous ses parametres
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
  HWND hWnd;
  MSG msg;

 if (!InitialisationFenetre(hInstance, &hWnd))
   return 0;

 if (!InitialiseAffichage(hWnd))
   FinDeProgramme = true;

 AfficherTexte(SurfaceTexte, "Blustuff Blue Fire", 0, 0, false, 0x00ff0000); //Afficher le texte sur la surface de texte

  while (!FinDeProgramme)               
  {
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    else
    {
      DessinerFlammes();                                                                     //Dessiner les flammes
      RECT Destination = {0,0,200,50};
      SurfaceSecondaire->BltFast(40, 130, SurfaceTexte, &Destination, DDBLTFAST_SRCCOLORKEY);//Ecire le texte
      SurfacePrimaire->Flip(0,DDFLIP_DONOTWAIT);                                             //Afficher la surface
    }
  }

  DesallocationAffichage();
  return 0;
}



/*----------------------------------------------------------------------------*
 |                                                                            |
 | Dessiner des Flammes, Unité d'affichage V.1.0 (Affichage.cpp)              |
 |                                                                            |
 |   programmé par Blustuff                                                   |
 |                                                                            |
 *----------------------------------------------------------------------------*/

/*
Note du programmeur :

  - Ce programme utilise DirectDraw pour initialiser l'affichage, mais accede
par ses propres fonctions a la mémore vidéo pour la modifier. La connaissance
de DirectDraw n'est pas importante, la fonction DessinerFlamme et la seulle
qui puisse etre interessante ici.

  - Ce programme n'est pas entièrement optimisé en rapidité. Cela pour diverse
raison (j'ai la flemme), dont la lisibilité du code qui doit rester a peu près
acceptable

  - Note lexicale : Une interface est une structure de base de DirectX, dont
chaque autre élément,DirectDraw, DirectSound, DirectInput etc. hérite. Elle
possède notament la fonction Release() qui permet de désallouer la mémoire qui
lui était réservée.
                    Une Surface est un emplacement en mémoire contenant des
données susceptible d'etre affichée, a l'ecran ou sur une autre surface.

  - J'ai horreur des termes en anglais dans une programme, parce que j'aime bien
rapeller que les francais ont réalisé les plus beaux programme jusqu'a
maintenant. Si j'ai laissé des noms de variables en anglais, veuillez m'en
excuser. (Celles de DX j'y suis pour rien)

  - Je n'ai pas tout inventé tout seul. J'ai repris cet algorythme de Franck
Bauquier (defcon1@caramail.com) sur son site http://defcon1.free.fr/ . J'ai
juste personalisé les flammes et adapté le programme pour DirectDraw

  - Les flammes sont bleues. Ca simplifie vraiment le programme. Si vous voulez
faire des flammes avec autre chose, il faut revoir une bonne partie du code.
Les flammes doivent etre traitées couleur par couleur. Si vous voulez faire des
flammes violettes, vous devez traiter le rouge et le bleu séparement. Mon
programme ne s'y adapte pas (Et puis j'aime bien le bleu).


Pour me contacter :

      blustuff@wanadoo.fr
      http://perso.wanadoo.fr/blustuff/

*/


/*----------------------------------------------------------------------------*
 | Fichier d'en tête                                                          |
 *----------------------------------------------------------------------------*/

#include "affichage.h"


/*----------------------------------------------------------------------------*
 | Initialisation                                                             |
 *----------------------------------------------------------------------------*/

/*
N'essayez pas trop de vous lancer dans la comprehension du code de
l'initialisation graphique de DirectDraw si vous n'avez jamais programmé avec
DirectX. J'appelle ici de nombreuse fonctions que je n'expliquerai pas
*/

bool InitialiseAffichage(HWND hWnd)
{
  DDSURFACEDESC2 ddsd;
  DDSCAPS2 ddscaps;

  //Creation de l'interface DirectDraw
  if (DirectDrawCreateEx(NULL, (void**)&InterfaceDirectDraw, IID_IDirectDraw7, NULL) != DD_OK)
    return false;

  //Selection du niveau cooperatif
  if (InterfaceDirectDraw->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT) != DD_OK)
    return false;

  //Choix du mode d'affichage
  if (InterfaceDirectDraw->SetDisplayMode(LargeurEcran, HauteurEcran, 32, 0, 0) != DD_OK)
    return false;

  //Creation des surfaces primaires et secondaires
  ZeroMemory(&ddsd, sizeof(ddsd));
  ZeroMemory(&ddscaps, sizeof(ddscaps));
  ddsd.dwSize = sizeof( ddsd );
  ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
  ddsd.dwBackBufferCount = 1;
  ddscaps.dwCaps = DDSCAPS_BACKBUFFER;

  if (InterfaceDirectDraw->CreateSurface(&ddsd, &SurfacePrimaire, NULL) != DD_OK)
    return false;

  //Association du back Buffer
  if (SurfacePrimaire->GetAttachedSurface(&ddscaps, &SurfaceSecondaire) != DD_OK)
    return false;

  //Effacement des surfaces
  EffacerSurface(SurfacePrimaire);
  EffacerSurface(SurfaceSecondaire);

  //Création et effacement d'une surface réservée au texte
  ZeroMemory(&ddsd,sizeof(ddsd));
  ddsd.dwSize = sizeof(DDSURFACEDESC2);
  ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;
  ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
  ddsd.dwWidth = 200;
  ddsd.dwHeight = 50;

  InterfaceDirectDraw->CreateSurface(&ddsd, &SurfaceTexte, NULL);

  DDCOLORKEY key;
  key.dwColorSpaceLowValue = 0x00000000;
  key.dwColorSpaceHighValue = 0x00000000;
  SurfaceTexte->SetColorKey(DDCKEY_SRCBLT, &key);
  EffacerSurface(SurfaceTexte);


  //Stockage dans un tableau des valeurs en octet pour le débur de chaque ligne de l'ecran
  SurfacePrimaire->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);        //Obtention des propriétés de la surface
  SurfacePrimaire->Unlock(NULL);

  AlignY = (int*) malloc(ddsd.dwHeight * sizeof(int));
  for (unsigned int y = 0 ; y < ddsd.dwHeight ; y++)
    AlignY[y] = ddsd.lPitch * y / 4;


  //La fonction retourne true si l'initialisation réussit

  return true;
}


/*----------------------------------------------------------------------------*
 | Desallocation Affichage                                                    |
 *----------------------------------------------------------------------------*/

/*
Desallocation de l'affichage : Cette fonction libère l'espace alloué par
DirectDraw. Il faut vérifier que ces interface aient été allouées d'où
l'importance d'initialiser les pointeurs vers elle à NULL. Lorsque les
interfaces sont initialisée, le pointeur prend donc leur adresse, et on sait
donc que l'on peut les désallouer. Voici donc l'interet de verifier la
non-nullité des pointeurs vers les interfaces avant d'appeler Release()
*/

void DesallocationAffichage()
{
  free(AlignY);

  if (SurfaceTexte)
    SurfaceTexte->Release();
  if (SurfaceSecondaire)
    SurfaceSecondaire->Release();
  if (SurfacePrimaire)
    SurfacePrimaire->Release();
  if (InterfaceDirectDraw)
    InterfaceDirectDraw->Release();
}


/*----------------------------------------------------------------------------*
 | Affichage de texte (Fonction GDI)                                          |
 *----------------------------------------------------------------------------*/

/*
  Cette fonction fait appel a une fonction GDI de Windows pour afficher du
texte, cela grace a la compatibilité de DirectX, qui fournit un handle context
(hdc) pour permetre a la fonction TextOut d'ecrire sur cette surface. Ici on ne
voit pas très bien le fonctionement interne de l'affichage de texte. Il est en
réalité plus compliqué puisque DirectX doit informer la fonction GDI de la
taille de la surface, de son format de pixels, etc. Ca n'a ici aucune
importance. Cette fonction est lente, mais l'affichage de texte ne recquiert pas
une grande rapidité. J'ai donc préféré la simplicité a la rapidité. (J'espère
que vous ne m'en voulez pas trop :) )
*/


void AfficherTexte(LPDIRECTDRAWSURFACE7 Surface, char* Text, int X, int Y, bool Center, DWORD TextColor, DWORD BackColor)
{
  if (InterfaceDirectDraw)                                //La fonction s'eecute que si l'affichage est initialisé
  {
    HDC hdc;
    if (Surface->GetDC(&hdc) == DD_OK)                    //Creer un contexte de dessin et bloquer la surface pour cet usage
    {
      if (Center)
        SetTextAlign(hdc, TA_CENTER);                     //Centrer le texte sur (x,y) si Center est spécifié
      else
        SetTextAlign(hdc, TA_LEFT | TA_TOP);              //Sinon (x,y) corespond au coin haut droit du cadre de texte

      SetTextColor(hdc,TextColor);                        //Selection de la couleur du texte
      SetBkColor(hdc,BackColor);                          //Et du fond
      TextOut(hdc, X, Y, Text, strlen(Text));             //Ecrire le texte
      Surface->ReleaseDC(hdc);                            //Débloquer la surface pour qu'elle puisse etre réutilisée normalement
    }
  }
}


/*----------------------------------------------------------------------------*
 | Effacer une surface                                                        |
 *----------------------------------------------------------------------------*/

/*
Cette fonction efface une surface quatre octets par quatre octets. Elle est
optimisée
*/

void EffacerSurface(LPDIRECTDRAWSURFACE7 Surface)
{
  if (InterfaceDirectDraw)                                                                              //La fonction s'eecute que si l'affichage est initialisé
  {
    DDSURFACEDESC2 ddsd;
    ddsd.dwSize = sizeof(ddsd);

    if (Surface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK)                                         //Bloquer la surface pour l'ecriture
    {
      register int* FinDeSurface = (int*)((char*)ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch);         //Fin de la surface (lPitch est le nombre d'octets entre deux lignes)
      for (register int* x = (int*)ddsd.lpSurface ; x < FinDeSurface ; x++)
        *x = 0;

      Surface->Unlock(NULL);                                                                            //Debloquer la surface pour usage posterieur
    }
  }
}


/*----------------------------------------------------------------------------*
 | Dessiner les Flammes                                                       |
 *----------------------------------------------------------------------------*/

/*
  Voici la fonction interessante : Elle va dessiner les flammes. J'ai tout
d'abord défini des macros pour obtenir la valeur d'un pixel et une pour changer
la valeur d'un pixel. Elle sont optimisés pour le mode d'affichage en couleur
32 bits , mais ne vérifient jamais si le pixel est hors ecran, ce qui est
dangereux si on controle mal son usage.

  Ensuite viens la fonction proprement dite. Elle suit l'algorythme suivant :
On trace d'abord une ligne de pixels aléatoirement (appelée foyer). Dans cet
exemple elle n'est pas réelement aléatoire. J'ai choisi de faire suivre aux
couleurs une fonction sinusoidale irégulière. C'est a dire que l'angle
n'évolue pas uniformenent. Cela donerait si on voulait tracer la courbe
représentative de la fonction, des formes courbes mais sans direction apparente
ni periode.

  L'étape suivante consiste a fiare partir les flammes du foyer. Ici j'ai
choisit d'attribuer a chaque pixel la valeur moyenne des pixels inferieurs (bas
gauche, bas milieu, bas droit). Cela permet d'avoir une continuité dans les
flammes. J'ai mis un plus fort coeficient devant le pixel juste en dessous, de
telle sorte que les flammes soient plus fines. J'ai ajouté a cette moyenne la
couleur du pixel précédent a la même place. Cela donne encore une continuité
mais cette fois-ci dans le temps.

  L'effet d'ondulation est effectué par un décalage (quie j'ai appelé z) de
chaque ligne suivant la fonction sinus. Pour que L'ondulation ne soit pas
régulière, j'ai décalé l'angle de départ de la fonction sinus a chaque affichage
d'une valeur aléatoire (c).

  Il faut que la pointe des flammes soit plus faible. Avant l'affichage de
chaque pixel, on décrémente donc sa valeur de 1. De cette manière le foyer est
nécessairement le plus lumineux.
  Il y a bien sur d'autre algorythmes, et d'autre effet pour dessiner des
flammes.
*/


#define GetLastPixel(X, Y)       (*((int*)ddsd1.lpSurface + AlignY[(Y)] + (X)))                         //Obtention de la couleur du pixel sur la Surface Primaire (Couleur précédente)
#define GetCurrentPixel(X, Y)    (*((int*)ddsd2.lpSurface + AlignY[(Y)] + (X)))                         //Obtention de la couleur du pixel sur la Surface Secondaire (Couleur courante)
#define SetPixel(X, Y, Couleur)  (*((int*)ddsd2.lpSurface + AlignY[(Y)] + (X)) = (int)(Couleur))        //Définition de la couleur d'un pixel sur la surface secondaire

void DessinerFlammes()
{
  if (InterfaceDirectDraw)
  {
    DDSURFACEDESC2 ddsd1, ddsd2;
    ddsd1.dwSize = ddsd2.dwSize = sizeof(DDSURFACEDESC2);

    //Blocage des surfaces pour pouvoir y dessiner
    if (SurfacePrimaire->Lock(NULL, &ddsd1, DDLOCK_WAIT, NULL) == DD_OK &&
        SurfaceSecondaire->Lock(NULL, &ddsd2, DDLOCK_WAIT, NULL) == DD_OK)
    {
      float c;

      //Tracé du foyer
      for (int x = 10 ; x < LargeurEcran-12 ; x++, c += (float(random(100)-50)/140))                       //Changer 140 par une valeur plus grande pour avoir des flammes au foyer plus fin
        SetPixel(x, 180, (sin(c)+1)/2*(ddsd1.ddpfPixelFormat.dwBBitMask));                                 //ddsd1.ddpfPixelFormat.dwBBitMask est la couleur bleue. Par ailleur : 0 < sin(c)+1)/2 < 1

      c += random(100);                                                                                    //Décalage d'angle pour la fonction sinus

      for (register int y = 179 ; y > 60 ; y--)                                                            //Tracé du corps de la flamme
      {
        int z = sin((float)y/5+c)*2;                                                                       //Décalage de chaque ligne pour l'ondulation
        for (register int x = 1 ; x < LargeurEcran-2 ; x++)                                                //Tracé de la ligne de flamme
        {
          int NouvelleCouleur;

          //Calcul de la couleur moyenne
          if (NouvelleCouleur = (GetCurrentPixel(x-1+z, y+1) + GetCurrentPixel(x+z, y+1)*16 + GetCurrentPixel(x+1+z, y+1) + GetLastPixel(x,y)*2)/20)
            NouvelleCouleur--;                                                                             //Attenuation de la flamme
          SetPixel(x, y, NouvelleCouleur);                                                                 //Tracé du pixel
        }
      }
    }

    SurfacePrimaire->Unlock(NULL);
    SurfaceSecondaire->Unlock(NULL);
  }
}


//----------------------------------------------------------------------------


/*----------------------------------------------------------------------------*
 |                                                                            |
 | Dessiner des Flammes, Unité d'affichage V.1.0 (Affichage.h)                |
 |                                                                            |
 |   programmé par Blustuff                                                   |
 |                                                                            |
 *----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*
 | Includes                                                                   |
 *----------------------------------------------------------------------------*/

#include <ddraw.h>   //Pour l'interface IDirectDraw
#include <stdlib.h>  //Pour random()
#include <math.h>    //Pour sin()


/*----------------------------------------------------------------------------*
 | Propriétés de l'Affichage                                                  |
 *----------------------------------------------------------------------------*/

/*
Les valeurs suivantes doivent corespondre a un mode d'ecran existant et supporté
sinon l'initialisation ne fonctionera pas
*/
#define LargeurEcran  320
#define HauteurEcran  200



/*----------------------------------------------------------------------------*
 | Variables Globales                                                         |
 *----------------------------------------------------------------------------*/

LPDIRECTDRAW7        InterfaceDirectDraw = NULL;
LPDIRECTDRAWSURFACE7 SurfacePrimaire = NULL;            //Primary Buffer. En clair, interface représentant la mémore vidéo directement reliée à l'ecran
LPDIRECTDRAWSURFACE7 SurfaceSecondaire = NULL;          //Back Buffer. Surface sur laquelle on dessine avant de proceder au bliting (Je n'epliquerai pas le bliting ici)
LPDIRECTDRAWSURFACE7 SurfaceTexte = NULL;               //Surface pour afficher le texte

int* AlignY; //Tableau de valeurs indiquant la taille en octet jusqu'a une ligne de l'ecran


/*----------------------------------------------------------------------------*
 | Fonctions                                                                  |
 *----------------------------------------------------------------------------*/

bool InitialiseAffichage(HWND hWnd);
void DesallocationAffichage();
void AfficherTexte(LPDIRECTDRAWSURFACE7 Surface, char* Text, int X, int Y, bool Center = true, DWORD TextColor = 0x00ffffff, DWORD BackColor = 0x00000000);
void EffacerSurface(LPDIRECTDRAWSURFACE7 Surface);
void DessinerFlammes();


//----------------------------------------------------------------------------


 

Conclusion

Peut ramer sur certains PC mais pas sur mon 200 MHz (Je suis ébloui devant certeines choses inexpliquables)  
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Commentaires et avis

signaler à un administrateur
Commentaire de GoldenEye le 11/03/2002 22:05:18

Bravo !
Tu m'a précédé de quelques jours pour le dépôt d'un source: je travaille aussi sur les effets de feu (merci M. Bauquier...) mais avec Allegro et DJGPP. Ce qui est sympa avec le feu, c'est que une fois qu'on a compris le truc, c'est super facile
@+

signaler à un administrateur
Commentaire de Pompei2 le 06/04/2002 10:54:11

et c'est quoi le truc?

signaler à un administrateur
Commentaire de Kirua le 14/06/2004 19:28:34

le truc c'est le particle engine, cherche ça sur google ;-)

signaler à un administrateur
Commentaire de ensi2005 le 08/09/2005 13:35:46

comment dessiner des vagues ou des flammes en langagr c

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Septembre 2008
LMMJVSD
1234567
891011121314
15161718192021
22232425262728
2930     

Consulter la suite du CalendriCode



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel BAÏSE, Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,23 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.