#define STRICT

// Includes standard Windows
#include <windows.h>
#include <windowsx.h>
#include <time.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <stdio.h>

// Includes D3D
#define  D3D_OVERLOADS
#include <ddraw.h>
#include <d3d.h>
#include <d3dx.h>

// Includes utilitaires D3D
#include "d3dmath.h"
#include "d3dutil.h"
#include "D3DEnum.h"

// Ids Resources
#include "resource.h"

// Constantes
#include "const.h"

// Types
#include "types.h"

// Variables globales projet
#include "vars.h"

// Prototypes fonctions autres modules
#include "proto.h"

// Macros
#include "macros.h"

// Sortir un résumé de la scène dans la fenêtre de trace
void vDescribe(void)
{
    int iCnt,
        iVertices = 0,
        iSelectedVertices = 0,
        iHiddenVertices = 0,
        iTriangles = 0,
        iHiddenTriangles = 0,
        iEdges = 0,
        iHiddenEdges = 0,
        iLamps = 0,
        iSelectedLamps = 0,
        iLitLamps = 0,
        iObjects = 0,
        iSelectedObjects = 0,
        iMaterials = 0,
        iHiddenObjects = 0;

    vTrace("*** Résumé scène courante :");

    // Compter les points
    for (iCnt = 0 ; iCnt <= iVertLastUsed ; iCnt++)
        if (Vertices[iCnt].bEnabled)
        {
            iVertices++;
            if (bIsVertexSelected(iCnt)) iSelectedVertices++;
            if (Vertices[iCnt].bHidden) iHiddenVertices++;
        }

    // Compter les lampes
    for (iCnt = 0 ; iCnt <= iLampLastUsed ; iCnt++)
        if (Lampes[iCnt].bEnabled)
        {
            iLamps++;
            if (Lampes[iCnt].bSelected) iSelectedLamps++;
            if (Lampes[iCnt].bLit) iLitLamps++;
        }

    // Compter les objets
    for (iCnt = 0 ; iCnt <= iObjtLastUsed ; iCnt++)
        if (Objects[iCnt].bEnabled)
        {
            iObjects++;
            if (Objects[iCnt].bSelected) iSelectedObjects++;
            if (Objects[iCnt].bHidden) iHiddenObjects++;
        }

    // Compter les arêtes
    for (iCnt = 0 ; iCnt <= iEdgeLastUsed ; iCnt++)
        if (Edges[iCnt].bEnabled)
        {
            iEdges++;
            if (Edges[iCnt].bHidden) iHiddenEdges++;
        }

    // Compter les triangles
    for (iCnt = 0 ; iCnt <= iTriaLastUsed ; iCnt++)
        if (Triangles[iCnt].bEnabled)
        {
            iTriangles++;
            if (Triangles[iCnt].bHidden) iHiddenTriangles++;
        }

    // Compter les matériaux
    for (iCnt = 0 ; iCnt <= iMtrlLastUsed ; iCnt++)
        if (Materials[iCnt].bEnabled)
            iMaterials++;

    vTrace("Taille buffers en Ko : sommets %d, arêtes %d, triangles %d, objets %d, matériaux %d, total %d.", sizeof(Vertices) / 1024, sizeof(Edges) / 1024, sizeof(Triangles) / 1024, sizeof(Objects) / 1024, sizeof(Materials) / 1024, (sizeof(Vertices) + sizeof(Edges) + sizeof(Triangles) + sizeof(Objects) + sizeof(Materials)) / 1024);
    if (iVertices) vTrace("Points : %d (%d sélectionnés, %d cachés). Dernier utilisé : %d, premier dispo : %d (frag : %d%%)", iVertices, iSelectedVertices, iHiddenVertices, iVertLastUsed, iVertFirstAvailable, 100 * ((iVertLastUsed + 1) - iVertices) / iVertices);
    if (iEdges) vTrace("Arêtes : %d (%d cachées). Dernier utilisé : %d, premier dispo : %d (frag : %d%%)", iEdges, iHiddenEdges, iEdgeLastUsed, iEdgeFirstAvailable, 100 * ((iEdgeLastUsed + 1) - iEdges) / iEdges);
    if (iTriangles) vTrace("Triangles : %d (%d cachés). Dernier utilisé : %d, premier dispo : %d (frag : %d%%)", iTriangles, iHiddenTriangles, iTriaLastUsed, iTriaFirstAvailable, 100 * ((iTriaLastUsed + 1) - iTriangles) / iTriangles);
    if (iObjects) vTrace("Objets : %d (%d sélectionnés, %d cachés). Dernier utilisé : %d, premier dispo : %d (frag : %d%%)", iObjects, iSelectedObjects, iHiddenObjects, iObjtLastUsed, iObjtFirstAvailable, 100 * ((iObjtLastUsed + 1) - iObjects) / iObjects);
    if (iLamps) vTrace("Lampes : %d (%d sélectionnées, %d allumées). Dernière utilisée : %d, premier dispo : %d (frag : %d%%)", iLamps, iSelectedLamps, iLitLamps, iLampLastUsed, iLampFirstAvailable, 100 * ((iLampLastUsed + 1) - iLamps) / iLamps);
    if (iMaterials) vTrace("Matériaux : %d. Dernier utilisé : %d, premier dispo : %d (frag : %d%%)", iMaterials, iMtrlLastUsed, iMtrlFirstAvailable, 100 * ((iMtrlLastUsed + 1) - iMaterials) / iMaterials);
}

// Redimensionner la triview
void vRemakeTriview(LPARAM lParam)
{
    int iScreenWidth, iScreenHeight;
#ifndef _AMIGA_
    HDC hdcScreen;

    if (!lParam)
    {
        hdcScreen = GetDC(NULL);
        iScreenWidth = GetDeviceCaps(hdcScreen, HORZRES);
        iScreenHeight = GetDeviceCaps(hdcScreen, VERTRES);
    }
    else
    {
        iScreenWidth = LOWORD(lParam);
        iScreenHeight = HIWORD(lParam);
    }

    vTrace("Réorganisation des fenêtres (écran %d x %d)", iScreenWidth, iScreenHeight);

    MoveWindow(hWndMenu, 0, 0, iScreenWidth, iScreenHeight / 9, TRUE);
    MoveWindow(hWndPersp, iScreenWidth/2, iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9, TRUE);
    MoveWindow(hWndTop, 0, iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9, TRUE);
    MoveWindow(hWndFace, 0, 5 * iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9, TRUE);
    MoveWindow(hWndRight, iScreenWidth/2, 5 * iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9, TRUE);

    if (!lParam) ReleaseDC(NULL, hdcScreen);
#else
    iScreenWidth = hInst -> Width;
    iScreenHeight = hInst -> Height;

    vTrace("Réorganisation des fenêtres (écran %d x %d)", iScreenWidth, iScreenHeight);

    ChangeWindowBox(hWndMenu, 0, 0, iScreenWidth, iScreenHeight / 9);
    ChangeWindowBox(hWndPersp, iScreenWidth/2, iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9);
    ChangeWindowBox(hWndTop, 0, iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9);
    ChangeWindowBox(hWndFace, 0, 5 * iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9);
    ChangeWindowBox(hWndRight, iScreenWidth/2, 5 * iScreenHeight / 9, iScreenWidth/2, 4 * iScreenHeight / 9);

    // Laisser à Intuition le temps de flusher ses buffers de rendering
    Delay(5); // * 20 ms soit 100 ms
#endif
}

// Mettre à jour les menus en fonction des variables de mode courant
void vUpdateMenu(void)
{
/*
    HMENU hMenu = GetMenu(hWndMenu);
    CheckMenuItem(hMenu, ID_EDITION_GRILLE, MF_BYCOMMAND | (bGrid ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hMenu, ID_EDITION_COORDONNES, MF_BYCOMMAND | (bCoords ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hMenu, ID_OBSERVATEUR_MODE_POINT, MF_BYCOMMAND | (dFillMode == D3DFILL_POINT ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hMenu, ID_OBSERVATEUR_MODE_FILDEFERF, MF_BYCOMMAND | (dFillMode == D3DFILL_WIREFRAME ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hMenu, ID_OBSERVATEUR_MODE_FACETTES, MF_BYCOMMAND | (dFillMode == D3DFILL_SOLID ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hMenu, ID_OBSERVATEUR_MODE_ZBUFFER, (dZBuf == D3DZB_TRUE ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hMenu, ID_OBSERVATEUR_MODE_TRANSPARENCEALPHABLENDING, (bAlpha ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hMenu, ID_OBSERVATEUR_MODE_ECLAIRAGESPCULAIRE, (bSpecular ? MF_CHECKED : MF_UNCHECKED));

    CheckMenuItem(hMenu, ID_OUTILS_AUCUN, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_OUTILS_SLECTEUR, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_OUTILS_DSLECTEUR, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_OUTILS_AIMANT, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_OUTILS_COURBE, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_OUTILS_EXTRUDEUR, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_OUTILS_ARTES, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_OUTILS_PINCE, MF_BYCOMMAND | MF_UNCHECKED);

    CheckMenuItem(hMenu, ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_NONE, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_CW, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_CCW, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(hMenu, ID_SPCIFICITSDX7D3DIM_RENDERSTATE_ANTIALIAS, (bAntialias ? MF_CHECKED : MF_UNCHECKED));

    switch(dCull)
    {
        case D3DCULL_NONE:
            CheckMenuItem(hMenu, ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_NONE, MF_BYCOMMAND | MF_CHECKED);
            break;

        case D3DCULL_CW:
            CheckMenuItem(hMenu, ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_CW, MF_BYCOMMAND | MF_CHECKED);
            break;

        case D3DCULL_CCW:
            CheckMenuItem(hMenu, ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_CCW, MF_BYCOMMAND | MF_CHECKED);
            break;
    }

    switch(cTool)
    {
        case XDC_TOOL_NONE:
            CheckMenuItem(hMenu, ID_OUTILS_AUCUN, MF_BYCOMMAND | MF_CHECKED);
            break;

        case XDC_TOOL_SELECT:
            CheckMenuItem(hMenu, ID_OUTILS_SLECTEUR, MF_BYCOMMAND | MF_CHECKED);
            break;

        case XDC_TOOL_UNSELECT:
            CheckMenuItem(hMenu, ID_OUTILS_DSLECTEUR, MF_BYCOMMAND | MF_CHECKED);
            break;

        case XDC_TOOL_MAGNET:
            CheckMenuItem(hMenu, ID_OUTILS_AIMANT, MF_BYCOMMAND | MF_CHECKED);
            break;

        case XDC_TOOL_CURVE:
            CheckMenuItem(hMenu, ID_OUTILS_COURBE, MF_BYCOMMAND | MF_CHECKED);
            break;

        case XDC_TOOL_EXTRUDE:
            CheckMenuItem(hMenu, ID_OUTILS_EXTRUDEUR, MF_BYCOMMAND | MF_CHECKED);
            break;

        case XDC_TOOL_EDGE:
            CheckMenuItem(hMenu, ID_OUTILS_ARTES, MF_BYCOMMAND | MF_CHECKED);
            break;

        case XDC_TOOL_GRAB:
            CheckMenuItem(hMenu, ID_OUTILS_PINCE, MF_BYCOMMAND | MF_CHECKED);
            break;
    }
*/
}


// Gérer les messages de la fenêtre menu / trace
LRESULT CALLBACK lrMenuWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static int iCurveStartVertex, iCurveLastVertex;
    int wmId, wmEvent, iCnt, jCnt;
    float fDist;
    SMALLBOOL bShouldRedraw, bSelectState;
    char cKey;
    DWORD dwTime;
    D3DCOLORVALUE   dcvDiffuse  = {1.f, 1.f, 1.f, 0.f},
                    dcvSpecular = {1.f, 1.f, 1.f, 0.f},
                    dcvAmbient  = {1.f, 1.f, 1.f, 0.f};

    // vTrace("Message fenêtre menu type %d, wP %d, lP %d", uMsg, wParam, lParam);

    switch( uMsg )
    {
/*
    case WM_CREATE :
        // Mettre à jour les toggle flags des menus en fonction de l'état des variables de mode
        vUpdateMenu();
        break;
*/
    case WM_COMMAND :
         wmId    = LOWORD(wParam);
         wmEvent = HIWORD(wParam);

         switch (wmId) {

            //////////////////////////////////// M E N U  P R O J E T ///////////////
            case ID_PROJET_CHARGER_SCNESCULPT :
                // Charger la scène Sculpt
                vLoadSculpt();

                // Mettre à jour les variables d'état du pipe D3D et tout redessiner
                vSetD3DState();
                goto _RedrawAll;

            case ID_PROJET_CHARGER_SCNESKULPTD3DW3D :
                // Charger la scène
                vLoadBin();

                // Mettre à jour les variables d'état du pipe D3D et tout redessiner
                vSetD3DState();
                goto _RedrawAll;

            case ID_PROJET_CHARGER_SCNEDIRECTX :
                // Charger la scène
                vLoadDirectX();

                // Mettre à jour les variables d'état du pipe D3D et tout redessiner
                vSetD3DState();
                goto _RedrawAll;

            case ID_PROJET_SAUVER_SCNESCULPT :
                // Sauver la scène Sculpt
                vSaveSculpt();
                break;

            case ID_PROJET_SAUVER_SCNESKULPTD3DW3D :
                // Sauver la scène
                vSaveBin();
                break;

            case ID_PROJET_SAUVER_SCNEDIRECTX :
                // Sauver la scène
                vSaveDirectX();
                break;

            case ID_PROJET_APROPOS:
                vLogo();
                break;

            case ID_PROJET_RSUM :
                vDescribe();
                break;

            case ID_PROJET_QUITTER:
               DestroyWindow (hWnd);
               break;

            //////////////////////////////////// M E N U  E D I T I O N ///////////////
            case ID_EDITION_SLECTIONNER_TOUT:
            case ID_EDITION_DSLECTIONNER_TOUT:
                // (Dé)Sélectionner les sommets
                for (iCnt = 0 ; iCnt <= iVertLastUsed ; iCnt++)
                {
                    if (Vertices[iCnt].bEnabled == FALSE) continue;
                    if (Vertices[iCnt].bHidden == TRUE) continue;

                    Vertices[iCnt].bSelected = (wmId == ID_EDITION_SLECTIONNER_TOUT ? TRUE : FALSE);
                }

                // (Dé)Sélectionner les objets D3D
                for (iCnt = 0 ; iCnt <= iObjtLastUsed ; iCnt++)
                {
                    if (Objects[iCnt].bEnabled == FALSE) continue;
                    if (Objects[iCnt].bHidden == TRUE) continue;

                    Objects[iCnt].bSelected = (wmId == ID_EDITION_SLECTIONNER_TOUT ? TRUE : FALSE);
                }

                // Ne pas sélectionner les lampes
                if (wmId == ID_EDITION_SLECTIONNER_TOUT) goto _SelectDone;

                // Désélectionner les lampes
                for (iCnt = 0 ; iCnt <= iLampLastUsed ; iCnt++)
                {
                    if (Lampes[iCnt].bEnabled == FALSE) continue;

                    Lampes[iCnt].bSelected = (wmId == ID_EDITION_SLECTIONNER_TOUT ? TRUE : FALSE);
                }
_SelectDone:
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_SLECTIONNER_NOEUDSCONNEXES:
                vCollect();
                vPropagateSelection();
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_SLECTIONNER_NOEUDINDIQU:
            case ID_EDITION_DSLECTIONNER_NOEUDINDIQU:
                // Attention : en mode menu, ce traitement sélectionne le noeud indiqué.
                // Il peut aussi être appelé par 2nwndproc.c en mode dé/sélecteur, avec lParam != 0.
                // Il faut alors dé/sélectionner tous les points dans la zone de tolérance et pas seulement le
                // premier

                // Chercher le premier point dans la zone curseur
                iCnt = iFindVertex(Cursor1, -1);
                bShouldRedraw = FALSE; // Si true à l'issue du traitement alors il faut redessiner la 2D car des points ont changé d'état de sélection

                // Si on n'a pas trouvé alors on cherche une lampe
                if (iCnt == -1)
                {
                    PostMessage(hWnd, WM_COMMAND, ID_EDITION_SLECTIONNER_LAMPEINDIQUE, lParam);
                    break;
                }

                switch(lParam)
                {
                case 0: bSelectState = (wmId == ID_EDITION_SLECTIONNER_NOEUDINDIQU ? TRUE : FALSE); break;
                case XDC_SINGLETOGGLE: bSelectState = !Vertices[iCnt].bSelected; break;
                case XDC_SELECTALL: bSelectState = TRUE; break;
                case XDC_DESELECTALL: bSelectState = FALSE; break;
                }

                while (iCnt != -1)
                {
                    if (Vertices[iCnt].bSelected != bSelectState)
                    {
                        Vertices[iCnt].bSelected = bSelectState;
                        PostMessage(hWndTop, WM_USER+1, iCnt, 0);
                        PostMessage(hWndFace, WM_USER+1, iCnt, 0);
                        PostMessage(hWndRight, WM_USER+1, iCnt, 0);
                        bShouldRedraw = TRUE;
                    }

                    // Trouver le point suivant si on est en mode outil sélecteur (lParam == XDC_SELECTALL)
                    if ((lParam == XDC_SELECTALL) || (lParam == XDC_DESELECTALL))
                        iCnt = iFindVertex(Cursor1, iCnt);
                    else
                        iCnt = -1;
                }

                // Redessiner partiellement les vues 2D si des points ont été traités
                if (bShouldRedraw) vForce2DRefresh(XDC_MODE_PARTIEL);
                break;

            case ID_EDITION_SLECTIONNER_LAMPEINDIQUE:
            case ID_EDITION_DSLECTIONNER_LAMPEINDIQUE:
                // Chercher la lampe dans la zone curseur
                iCnt = iFindLamp(Cursor1, -1);
                bShouldRedraw = FALSE; // Si true à l'issue du traitement alors il faut redessiner la 2D car des points ont changé d'état de sélection

                // Si on n'a pas trouvé alors on cherche un objet D3D
                if (iCnt == -1)
                {
                    PostMessage(hWnd, WM_COMMAND, ID_EDITION_SLECTIONNER_OBJETD3DINDIQU, lParam);
                    break;
                }

                switch(lParam)
                {
                case 0: bSelectState = (wmId == ID_EDITION_SLECTIONNER_LAMPEINDIQUE ? TRUE : FALSE); break;
                case XDC_SINGLETOGGLE: bSelectState = !Lampes[iCnt].bSelected; break;
                case XDC_SELECTALL: bSelectState = TRUE; break;
                case XDC_DESELECTALL: bSelectState = FALSE; break;
                }

                while (iCnt != -1)
                {
                    if (Lampes[iCnt].bSelected != bSelectState)
                    {
                        Lampes[iCnt].bSelected = bSelectState;
                        bShouldRedraw = TRUE;
                    }
                    if ((lParam == XDC_SELECTALL) || (lParam == XDC_DESELECTALL))
                        iCnt = iFindLamp(Cursor1, iCnt);
                    else
                        iCnt = -1;
                }
                if (bShouldRedraw) vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_SLECTIONNER_TOUTESLESLAMPES:
            case ID_EDITION_DSLECTIONNER_TOUTESLESLAMPES:
                for (iCnt = 0 ; iCnt <= iLampLastUsed ; iCnt++)
                    if (Lampes[iCnt].bEnabled)
                        Lampes[iCnt].bSelected = (wmId == ID_EDITION_SLECTIONNER_TOUTESLESLAMPES ? TRUE : FALSE);
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_SLECTIONNER_OBJETD3DINDIQU:
            case ID_EDITION_DSLECTIONNER_OBJETD3DINDIQU:
                // Chercher le premier point dans la zone curseur
                iCnt = iFindD3DObject(Cursor1, -1);
                bShouldRedraw = FALSE; // Si true à l'issue du traitement alors il faut redessiner la 2D car des points ont changé d'état de sélection

                if (iCnt == -1) break;

                switch(lParam)
                {
                case 0: bSelectState = (wmId == ID_EDITION_SLECTIONNER_OBJETD3DINDIQU ? TRUE : FALSE); break;
                case XDC_SINGLETOGGLE: bSelectState = !Objects[iCnt].bSelected; break;
                case XDC_SELECTALL: bSelectState = TRUE; break;
                case XDC_DESELECTALL: bSelectState = FALSE; break;
                }

                while (iCnt != -1)
                {
                    if (Objects[iCnt].bSelected != bSelectState)
                    {
                        Objects[iCnt].bSelected = bSelectState;
                        bShouldRedraw = TRUE;
                    }
                    if ((lParam == XDC_SELECTALL) || (lParam == XDC_DESELECTALL))
                        iCnt = iFindD3DObject(Cursor1, iCnt);
                    else
                        iCnt = -1;
                }
                if (bShouldRedraw) vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_SLECTIONNER_INVERSERLASLECTION:
                for (iCnt = 0 ; iCnt <= iVertLastUsed ; iCnt++)
                    if (Vertices[iCnt].bEnabled)
                        Vertices[iCnt].bSelected = (Vertices[iCnt].bSelected ? FALSE : TRUE);
                for (iCnt = 0 ; iCnt <= iObjtLastUsed ; iCnt++)
                    if (Objects[iCnt].bEnabled)
                        Objects[iCnt].bSelected = (Objects[iCnt].bSelected ? FALSE : TRUE);
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_EFFACER_TOUT :
                vDeleteObjects();
                goto _RedrawAll;

            case ID_EDITION_EFFACER_NOEUDINDIQU:
                iCnt = iFindVertex(Cursor1, -1);
                if (iCnt != -1)
                {
                    if (bDeleteVertex(iCnt))
                        goto _RedrawAll;
                }
                else
                    vTrace("*** E0010 : Pas de noeud à proximité du curseur principal");
                break;

            case ID_EDITION_EFFACER_NOAUDSSLECTIONNS:
                bShouldRedraw = FALSE;

                for (iCnt = 0 ; iCnt <= iVertLastUsed ; iCnt++)
                    if (bIsVertexSelected(iCnt))
                        bShouldRedraw |= bDeleteVertex(iCnt);
                
                if (bShouldRedraw)
                    goto _RedrawAll;
                break;

            case ID_EDITION_EFFACER_ARTESSLECTIONNES:
                bShouldRedraw = FALSE;
                for (iCnt = 0 ; iCnt <= iEdgeLastUsed ; iCnt++)
                {
                    if (Edges[iCnt].bEnabled == FALSE) continue;

                    if (
                            (Vertices[Edges[iCnt].iSommets[0]].bSelected)
                         && (Vertices[Edges[iCnt].iSommets[1]].bSelected)
                       )
                    {
                       bDeleteEdge(iCnt);
                       bShouldRedraw = TRUE;
                    }
                }

                if (bShouldRedraw) vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_EFFACER_TRIANGLESSLECTIONNS:
                bShouldRedraw = FALSE;
                for (iCnt = 0 ; iCnt <= iTriaLastUsed ; iCnt++)
                {
                    if (Triangles[iCnt].bEnabled == FALSE) continue;

                    if (bIsTriangleSelected(iCnt))
                    {
                       bDeleteTriangle(iCnt);
                       bShouldRedraw = TRUE;
                    }
                }

                if (bShouldRedraw) goto _RedrawAll;
                break;

            case ID_EDITION_EFFACER_LAMPEINDIQUE:
                iCnt = iFindLamp(Cursor1, -1);
                if (iCnt != -1)
                {
                    if (bDeleteLamp(iCnt))
                        goto _RedrawAll;
                }
                else
                    vTrace("*** E0011 : Pas de lampe à proximité du curseur principal");
                break;

            case ID_EDITION_EFFACER_LAMPESSLECTIONNES:
                bShouldRedraw = FALSE;
                for (iCnt = 0 ; iCnt <= iLampLastUsed ; iCnt++)
                {
                    if (Lampes[iCnt].bEnabled == FALSE) continue;

                    if (Lampes[iCnt].bSelected)
                    {
                        bDeleteLamp(iCnt);
                        bShouldRedraw = TRUE;
                    }
                }

                if (bShouldRedraw) goto _RedrawAll;
                break;

            case ID_EDITION_EFFACER_TOUTESLESLAMPES:
                bShouldRedraw = FALSE;
                for (iCnt = 0 ; iCnt <= iLampLastUsed ; iCnt++)
                {
                    if (Lampes[iCnt].bEnabled == FALSE) continue;
                    bDeleteLamp(iCnt);
                    bShouldRedraw = TRUE;
                }

                if (bShouldRedraw) goto _RedrawAll;
                break;

            case ID_EDITION_MODIFIER_FACETTES :
                DialogBox(hInst, MAKEINTRESOURCE(IDD_FACE), hWnd, bFaceDlgProc);
                break;

            case ID_EDITION_MODIFIER_LAMPES :
                DialogBox(hInst, MAKEINTRESOURCE(IDD_LAMP), hWnd, NULL);
                break;

            case ID_EDITION_TRANSFORMER_AGRANDIRRDUIRE:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_EXPAND), hWnd, bExpandDlgProc);
                break;

            case ID_EDITION_TRANSFORMER_REMPLIR:
                fill();
                goto _RedrawAll;

            case ID_EDITION_TRANSFORMER_SUBDIVISER:
                {
                    int iTriNum = iTriaLastUsed;
                    SMALLBOOL *bSubdiv = (SMALLBOOL *) malloc((iTriNum + 1) * sizeof(SMALLBOOL));
                    if (!bSubdiv) break;
                    ZeroMemory(bSubdiv, (iTriNum + 1) * sizeof(SMALLBOOL));

                    for (iCnt = 0 ; iCnt <= iTriNum ; iCnt++)
                    {
                        if (Triangles[iCnt].bEnabled == FALSE) continue;
                        bSubdiv[iCnt] = bIsTriangleSelected(iCnt);
                    }

                    bShouldRedraw = FALSE;
                    for (iCnt = 0 ; iCnt <= iTriNum ; iCnt++)
                        if (bSubdiv[iCnt])
                        {
                            bSubdivideTriangle(iCnt);
                            bShouldRedraw = TRUE;
                        }

                    free(bSubdiv);

                    if (bShouldRedraw) goto _RedrawAll;
                }
                break;

            case ID_EDITION_TRANSFORMER_RPLIQUERSURHLICE:
                iCnt = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_HELIX), hWnd, bHelixDlgProc, (LPARAM) 20 << 16 | 20);
                break;

#ifndef _AMIGA_
            case ID_EDITION_TRANSFORMER_LISSERLESNOEUDSSLECTIONNS:
            case ID_EDITION_TRANSFORMER_DLISSERLESNOEUDSSLECTIONNS:
                bSelectState = (wmId == ID_EDITION_TRANSFORMER_LISSERLESNOEUDSSLECTIONNS);
                for (iCnt = 0 ; iCnt <= iVertLastUsed ; iCnt++)
                {
                    if (Vertices[iCnt].bEnabled == FALSE) continue;
                    if (Vertices[iCnt].bSelected == FALSE) continue;
                    if (Vertices[iCnt].bSmooth == bSelectState) continue;

                    Vertices[iCnt].bSmooth = bSelectState;
                }
                for (iCnt = 0 ; iCnt <= iTriaLastUsed ; iCnt++)
                    if (
                            (Vertices[Triangles[iCnt].iSommets[0]].bSelected)
                         || (Vertices[Triangles[iCnt].iSommets[1]].bSelected)
                         || (Vertices[Triangles[iCnt].iSommets[2]].bSelected)
                       )
                        bUpdateD3DTri(iCnt);
                goto _RedrawAll;
#endif

            case ID_EDITION_TRANSFORMER_CACHERLESNOEUDSSLECTIONNS:
                vHideSelection();
                goto _RedrawAll;

            case ID_EDITION_TRANSFORMER_RVLERLESNOEUDSCACHS:
                vRevealHidden();
_RedrawAll:
                // Tout retracer
                vForce2DRefresh(XDC_MODE_COMPLET);
                vForce3DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_TRANSFORMER_MULTIVIEW:
                vRemakeTriview(NULL);
                break;

            case ID_EDITION_AJOUTER_UNCLNE:
                cTool = XDC_TOOL_GRAB;
                bCloneSelection(D3DVECTOR(0.f, 0.f, 0.f), XDC_MODE_CLONE);
                vUpdateMenu();
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;
            
            case ID_EDITION_AJOUTER_POINT:
                iCnt = iMakeVertex(Cursor1, XDC_ALLOWSAME);
                if (cTool == XDC_TOOL_CURVE)
                {
                    // Si le vertex n'est pas le premier de la courbe, tracer l'arête et redessiner en mode complet
                    if (iCurveLastVertex != -1)
                    {
                        iMakeEdge(iCurveLastVertex, iCnt);
                        vForce2DRefresh(XDC_MODE_COMPLET);
                    }
                    else // Sinon redessiner en mode partiel après avoir forcé le dessin du point, et mémoriser l'indice du point pour pouvoir refermer la courbe sur 'f'
                    {
                        iCurveStartVertex = iCnt;
                        PostMessage(hWndTop, WM_USER+1, iCnt, 0);
                        PostMessage(hWndFace, WM_USER+1, iCnt, 0);
                        PostMessage(hWndRight, WM_USER+1, iCnt, 0);
                        vForce2DRefresh(XDC_MODE_PARTIEL);
                    }

                    iCurveLastVertex = iCnt;
                }
                else
                {
                    // Forcer le dessin du point et rafraichir 2D en mode partiel
                    PostMessage(hWndTop, WM_USER+1, iCnt, 0);
                    PostMessage(hWndFace, WM_USER+1, iCnt, 0);
                    PostMessage(hWndRight, WM_USER+1, iCnt, 0);
                    vForce2DRefresh(XDC_MODE_PARTIEL);
                }
                break;

            case ID_EDITION_AJOUTER_ARTEOUTRIANGLE:
                break;

            case ID_EDITION_AJOUTER_SPHRE:
            case ID_EDITION_AJOUTER_HMISPHRE:
                iCnt = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SPHERE), hWnd, bSphereDlgProc, (LPARAM) 20 << 16 | 20);
                if (iCnt == -1) break;
                
                // Récupérer le nombre de subdivisions horizontales & verticales
                // dans le hiword et le loword du code de retour
                jCnt = HIWORD(iCnt);
                iCnt = LOWORD(iCnt);

                // Ajouter la sphère
                vAddSphere(Cursor1, 6.f, iCnt, jCnt, (wmId == ID_EDITION_AJOUTER_HMISPHRE));
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_TORE:
                iCnt = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SPHERE), hWnd, bSphereDlgProc, (LPARAM) 20 << 16 | 20);
                if (iCnt == -1) break;
                
                // Récupérer le nombre de subdivisions horizontales & verticales
                // dans le hiword et le loword du code de retour
                jCnt = HIWORD(iCnt);
                iCnt = LOWORD(iCnt);

                // Ajouter le tore
                vAddTorus(Cursor1, 7., 6., iCnt, jCnt);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_BOINGBALL:
                // Ajouter la boing ball
                vAddBoing(Cursor1, 6.f);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_DISQUE:
                iCnt = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SUBDIV), hWnd, bSphereDlgProc, (LPARAM) 20 << 16 | 20);
                if (iCnt == -1) break;

                // Ajouter le cercle
                vAddDisk(Cursor1, 6., LOWORD(iCnt));
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_CERCLE:
                iCnt = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SUBDIV), hWnd, bSphereDlgProc, (LPARAM) 20 << 16 | 20);
                if (iCnt == -1) break;

                // Ajouter le cercle
                vAddCircle(Cursor1, 6., LOWORD(iCnt));
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_AJOUTER_LAMPE:
                iMakeLamp(
                        D3DLIGHT_POINT,
                        dcvDiffuse,
                        dcvSpecular,
                        dcvAmbient,
                        Cursor1,
                        D3DVECTOR(0.f, 0.f, 0.f),
                        D3DLIGHT_RANGE_MAX,
                        0.f,
                        0.f,
                        0.1f,
                        0.f,
                        0.f,
                        0.f);
                goto _RedrawAll;
#ifndef NO3D
            case ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_CUBE:
                if (-1 == (iCnt = iMakeD3DObj())) break;
                D3DXCreateBox(lpd3dDevice,  5.f, 5.F, 5.f, D3DX_DEFAULT, &Objects[iCnt].pShape);
                bMoveD3DObj(iCnt, Cursor1);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_CYLINDRE:
                if (-1 == (iCnt = iMakeD3DObj())) break;
                D3DXCreateCylinder(lpd3dDevice,  3.f, 3.f, 7.f, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, &Objects[iCnt].pShape);
                bMoveD3DObj(iCnt, Cursor1);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_CNE:
                if (-1 == (iCnt = iMakeD3DObj())) break;
                D3DXCreateCylinder(lpd3dDevice,  0.f, 4.f, 7.f, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, &Objects[iCnt].pShape);
                bMoveD3DObj(iCnt, Cursor1);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_DONUT:
                if (-1 == (iCnt = iMakeD3DObj())) break;
                D3DXCreateTorus(lpd3dDevice,  3.f, 7.f, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, &Objects[iCnt].pShape);
                bMoveD3DObj(iCnt, Cursor1);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_POLYGONE:
                if (-1 == (iCnt = iMakeD3DObj())) break;
                D3DXCreatePolygon(lpd3dDevice,  1.f, 20, D3DX_DEFAULT, &Objects[iCnt].pShape);
                bMoveD3DObj(iCnt, Cursor1);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_SPHRE:
                if (-1 == (iCnt = iMakeD3DObj())) break;
                D3DXCreateSphere(lpd3dDevice,  6.f, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, &Objects[iCnt].pShape);
                bMoveD3DObj(iCnt, Cursor1);
                goto _RedrawAll;

            case ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_TEAPOT:
                if (-1 == (iCnt = iMakeD3DObj())) break;
                D3DXCreateTeapot(lpd3dDevice,  D3DX_DEFAULT, &Objects[iCnt].pShape);
                bMoveD3DObj(iCnt, Cursor1);
                if (lParam) // Patch pour ne pas tout redessiner si on est en Timedemo
                    break;
                goto _RedrawAll;
#endif

            case ID_EDITION_AJOUTER_CHANEDECARACTRES:
                vAddTextOutline();
                goto _RedrawAll;

            case ID_EDITION_COLLER_CURSEURSURNOEUD:
                jCnt = -1;
                fDist = 1.0e10f;

                for (iCnt = 0 ; iCnt <= iVertLastUsed ; iCnt++)
                    if ((Vertices[iCnt].bEnabled) && (fDist > SquareMagnitude(Cursor1 - Vertices[iCnt].vPoint)))
                    {
                        jCnt = iCnt;
                        fDist = SquareMagnitude(Cursor1 - Vertices[iCnt].vPoint);
                    }

                if (jCnt > -1)
                {
                    Cursor1 = Vertices[jCnt].vPoint;
                    vForce2DRefresh(XDC_MODE_PARTIEL);
                }
                else
                    vTrace("*** E0012 : Aucun noeud");
                break;

            case ID_EDITION_COLLER_CURSEURSURCENTRE:
                Cursor1 = vCenter();
                vForce2DRefresh(XDC_MODE_PARTIEL);
                break;

            case ID_EDITION_GRILLE :
                bGrid = bGrid ? FALSE : TRUE;
                vUpdateMenu();
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_GRIDSTEP :
                iCnt = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_GRIDSTEP), hWnd, bSphereDlgProc, (LPARAM) (int) (100.f * fGridSize) << 16);
                if (iCnt == -1) break;

                // Régler pas grille
                fGridSize = LOWORD(iCnt) / 100.f;
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_EDITION_COORDONNES :
                bCoords = bCoords ? FALSE : TRUE;
                vUpdateMenu();
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            //////////////////////////////////// M E N U  O U T I L ///////////////////////////
            case ID_OUTILS_AUCUN:
                cTool = XDC_TOOL_NONE;
                goto _ToolDone;

            case ID_OUTILS_SLECTEUR:
                cTool = XDC_TOOL_SELECT;
                goto _ToolDone;

            case ID_OUTILS_DSLECTEUR:
                cTool = XDC_TOOL_UNSELECT;
                goto _ToolDone;

            case ID_OUTILS_AIMANT:
                cTool = XDC_TOOL_MAGNET;
                goto _ToolDone;

            case ID_OUTILS_COURBE:
                cTool = XDC_TOOL_CURVE;
                iCurveLastVertex = -1;
                goto _ToolDone;

            case ID_OUTILS_COURBE_FINIRF:
                if (cTool == XDC_TOOL_CURVE)
                {
                    // Si la courbe a au moins 2 sommets alors la refermer
                    if (iCurveLastVertex != -1 && iCurveLastVertex != iCurveStartVertex)
                    {
                        if (-1 == iFindEdge(iCurveLastVertex, iCurveStartVertex))
                        {
                            iMakeEdge(iCurveLastVertex, iCurveStartVertex);
                            vForce2DRefresh(XDC_MODE_COMPLET);
                            iCurveLastVertex = -1;
                        }
                        else
                            vTrace("*** E0013 : L'arête de fermeture existe déjà");
                    }
                    else
                        vTrace("*** E0014 : La courbe ne peut être fermée, elle n'a pas au moins 2 points");
                }
                goto _ToolDone;

            case ID_OUTILS_EXTRUDEUR:
                cTool = XDC_TOOL_GRAB;
                bCloneSelection(D3DVECTOR(0.f, 0.0f, 0.f), XDC_MODE_EXTRUDE);
                vUpdateMenu();
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_OUTILS_ARTES:
                cTool = XDC_TOOL_EDGE;
                goto _ToolDone;

            case ID_OUTILS_PINCE:
                cTool = XDC_TOOL_GRAB;
_ToolDone:
                vUpdateMenu();
//                vForce2DRefresh(XDC_MODE_PARTIEL);
                vForce2DRefresh(XDC_MODE_COMPLET);
                break;

            //////////////////////////////////// M E N U  O B S E R V A T E U R ///////////////
            case ID_OBSERVATEUR_DFINIROBSERVATEUR:
                Observer = Cursor1;
                D3DUtil_SetViewMatrix(matView,
                      Observer, // From
                      Target,   // To
                      D3DVECTOR(0.f, 0.f, 0.f));

                vForce2DRefresh(XDC_MODE_PARTIEL);
                goto _ModeDone;

            case ID_OBSERVATEUR_DFINIRVISE:
                Target = Cursor1;
                D3DUtil_SetViewMatrix(matView,
                      Observer, // From
                      Target,   // To
                      D3DVECTOR(0.f, 0.f, 0.f));

                vForce2DRefresh(XDC_MODE_PARTIEL);
                goto _ModeDone;

            case ID_OBSERVATEUR_MODE_POINT:
                dFillMode = D3DFILL_POINT;
                goto _ModeDone;

            case ID_OBSERVATEUR_MODE_FILDEFERF:
                dFillMode = D3DFILL_WIREFRAME;
                goto _ModeDone;

            case ID_OBSERVATEUR_MODE_FACETTES:
                dFillMode = D3DFILL_SOLID;
                goto _ModeDone;

            case ID_OBSERVATEUR_MODE_ZBUFFER:
                dZBuf = (dZBuf == D3DZB_TRUE) ? D3DZB_FALSE : D3DZB_TRUE;
                goto _ModeDone;

            //////////////////////////////////// M E N U  M O N D E ///////////////
            case ID_MONDE_ILLUMINATIONGLOBALE :
                DialogBox(hInst, MAKEINTRESOURCE(IDD_GLOBLIGHT), hWnd, bGlobLightDlgProc);
                break;

            //////////////////////////////////// M E N U  D3D ///////////////
            case ID_SPCIFICITSDX7D3DIM_CHANGERLERENDERDEVICED3D:
                if (S_OK != D3DEnum_UserChangeDevice(&m_pDeviceInfo))
                    break;

                bReady = FALSE;

                sprintf(cTitle, "%s(%s)", XDC_S_Title3D, m_pDeviceInfo->strDesc);
                SendMessage(hWndPersp, WM_SETTEXT, 0, (LPARAM)cTitle);

                if( FAILED( hrCloseD3D(FALSE) ) )
                    DestroyWindow( hWndMenu );

                if( FAILED( hrInitD3D( hWndPersp, m_pDeviceInfo->pDeviceGUID) ) )
                    DestroyWindow( hWndMenu );

                if (FAILED(hrInitWorld( lpd3dDevice )))
                {
                    hrCloseD3D(TRUE);
                    DestroyWindow( hWndMenu );
                }

                // Mettre à jour les variables d'état du pipe D3D
                vSetD3DState();

                // Remettre en service les lampes
                for (iCnt = 0 ; iCnt <= iLampLastUsed ; iCnt++)
                    if (Lampes[iCnt].bEnabled)
                        bUpdateLamp(iCnt);

                bReady = TRUE;

                // Redessiner la scène
                vForce3DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_SPCIFICITSDX7D3DIM_TRANSPARENCEALPHABLENDING:
                bAlpha = bAlpha ? FALSE : TRUE;
                goto _ModeDone;

            case ID_SPCIFICITSDX7D3DIM_ECLAIRAGESPCULAIRE:
                bSpecular = bSpecular ? FALSE : TRUE;
_ModeDone:
                // Mettre à jour le menu pour prendre en compte le changement de mode
                vUpdateMenu();
                
                // Mettre à jour les variables d'état du pipe D3D
                vSetD3DState();
                                
                // Redessiner la 3D
                vForce3DRefresh(XDC_MODE_COMPLET);
                break;

            case ID_SPCIFICITSDX7D3DIM_RENDERSTATE_ANTIALIAS:
                bAntialias = bAntialias ? FALSE:TRUE;
                goto _ModeDone;
                
            case ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_NONE:
                dCull = D3DCULL_NONE;
                goto _ModeDone;

            case ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_CW:
                dCull = D3DCULL_CW;
                goto _ModeDone;

            case ID_SPCIFICITSDX7D3DIM_RENDERSTATE_BACKFACECULLINGMODE_CCW:
                dCull = D3DCULL_CCW;
                goto _ModeDone;

            case ID_SPCIFICITSDX7D3DIM_TIMEDEMO:
                // Tout effacer
                SendMessage(hWndMenu, WM_COMMAND, ID_EDITION_EFFACER_TOUT, 0);
                
                // Recadrer la 2D
                fXmin = -10.;                               // Espace de travail triview : X min
                fXmax = 10.;                                // Espace de travail triview : X max
                fYmin = -10.;                               // Espace de travail triview : Y min
                fYmax = 10.;                                // Espace de travail triview : Y max
                fZmin = -10.;                               // Espace de travail triview : Z min
                fZmax = 10.;                                // Espace de travail triview : Z max
                
                // Repositionner les fenêtres
                SendMessage(hWndMenu, WM_COMMAND, ID_EDITION_TRANSFORMER_MULTIVIEW, 0);
                
                // Initialiser les matrices view, proj, world
                hrInitWorld(lpd3dDevice);

                // Prépositionner l'observateur et le target
                Target = D3DVECTOR(0.f, 0.f, 0.f);
                Observer = D3DVECTOR(7.f, 7.f, 7.f);

                // Recalculer les matrices
                D3DUtil_SetViewMatrix(matView,
                          Observer, // From
                          Target,   // To
                          D3DVECTOR(0.f, 0.f, 0.f));

                // Actualiser les matrices D3D
                vSetD3DState();

                // Ajouter 3 x 3 x 3 théière
                {
                    int iX, iY, iZ;
                    for (iX = -3 ; iX < 4 ; iX += 3)
                        for (iY = -3 ; iY < 4 ; iY += 3)
                            for (iZ = -3 ; iZ < 4 ; iZ+= 3)
                            {
                                Cursor1 = D3DVECTOR((float) iX, (float) iY, (float) iZ);
                                SendMessage(hWndMenu, WM_COMMAND, ID_EDITION_AJOUTER_PRIMITIVESIMPLESHAPED3D_TEAPOT, 1);
                            }
                }
                
                // Ajouter une lampe
                Cursor1 = D3DVECTOR(8.f, 8.f, 8.f);
                SendMessage(hWndMenu, WM_COMMAND, ID_EDITION_AJOUTER_LAMPE, 0);
                
                dwTime = GetTickCount();
                for (iCnt = 0 ; iCnt < 90 ; iCnt++)
                {
                    // Repositionner l'observer
                    Observer = D3DVECTOR(   7.f * sinf(g_2_PI * (float) iCnt / 360.f),
                                            7.f * cosf(g_2_PI * (float) iCnt / 360.f),
                                            7.f * sinf(g_2_PI * (float) iCnt / 360.f));

                    // Recalculer les matrices
                    D3DUtil_SetViewMatrix(matView,
                              Observer, // From
                              Target,   // To
                              D3DVECTOR(0.f, 0.f, 0.f));

                    // Actualiser les matrices D3D
                    vSetD3DState();

                    // Retracer la 3D
                    vForce3DRefresh(XDC_MODE_COMPLET);
                }
                dwTime = GetTickCount() - dwTime;
                
                // Tout effacer
                // SendMessage(hWndMenu, WM_COMMAND, ID_EDITION_EFFACER_TOUT, 0);

                vTrace("Timedemo exécutée en %d ms", dwTime);
                break;

            default:
                vTrace("*** E0015 : Entrée menu non traitée dans cette version");
               // return (DefWindowProc(hWnd, uMsg, wParam, lParam));
         }
        break;
/*
    case WM_MOVE:
        break;

    case WM_SIZE:
        RECT rect;

        GetClientRect(hWndMenu, &rect);
        rect.left, rect.top, rect.right, rect.bottom,
        MoveWindow(hWndTrace, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); 
        break;

    case WM_GETMINMAXINFO:
        // Prevent the window from going smaller than some minimum size
        ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100;
        ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100;
        break;
*/
    case WM_CHAR:
            // Touches clavier :
            // 'o' : observer <- curseur 1
            // 't' : target <- curseur 1
            cKey = (TCHAR) wParam;    // character code 
            switch(tolower(cKey))
            {
#ifdef _DEBUG
            case 'z' : // Insérer un marqueur de trace
                vTrace("********");
                break;
#endif
            case 4 : // CTRL-D : Magic cookie !
                vTrace("Georges, t'es bien curieux !");
                vDemo();
                break;

            case 27 :   // ESC : Resizer le triview
                PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_TRANSFORMER_MULTIVIEW, 0);
                break;

            case '<' : // Désélectionner tout
                PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_DSLECTIONNER_TOUT, 0);
                break;

            case '>' : // Sélectionner tout
                PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_SLECTIONNER_TOUT, 0);
                break;

            case '!':
                vCollect();
                break;

            case ' ' : // Aucun outil
                PostMessage(hWndMenu, WM_COMMAND, ID_OUTILS_AUCUN, 0);
                break;

            case 'c' : // Outil Courbes
                PostMessage(hWndMenu, WM_COMMAND, ID_OUTILS_COURBE, 0);
                break;

            case 'f' : // Outil finir courbe
                PostMessage(hWndMenu, WM_COMMAND, ID_OUTILS_COURBE_FINIRF, 0);
                break;

            case 'g' : // Outil Grabber
                PostMessage(hWndMenu, WM_COMMAND, ID_OUTILS_PINCE, 0);
                break;

            case 'i' : // Inverser la sélection
                PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_SLECTIONNER_INVERSERLASLECTION, 0);
                break;

            case 'k' : // Sélectionner les noeuds connexes
                PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_SLECTIONNER_NOEUDSCONNEXES, 0);
                break;

            case 'o' :  // Positionner observateur
                PostMessage(hWndMenu, WM_COMMAND, ID_OBSERVATEUR_DFINIROBSERVATEUR, 0);
                break;

            case 'p' : // Ajouter point
                PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_AJOUTER_POINT, 0);
                break;

            case 'r' :  // Choisir le device de render
                PostMessage(hWndMenu, WM_COMMAND, ID_SPCIFICITSDX7D3DIM_CHANGERLERENDERDEVICED3D, 0);
                break;

            case 's' : // Outil Sélecteur
                PostMessage(hWndMenu, WM_COMMAND, ID_OUTILS_SLECTEUR, 0);
                break;

            case 't' :  // Positionner cible
                PostMessage(hWndMenu, WM_COMMAND, ID_OBSERVATEUR_DFINIRVISE, 0);
                break;

            case 'u' : // Outil Désélecteur
                PostMessage(hWndMenu, WM_COMMAND, ID_OUTILS_DSLECTEUR, 0);
                break;

            case 'x' : // Outil Extrudeur
                PostMessage(hWndMenu, WM_COMMAND, ID_OUTILS_EXTRUDEUR, 0);
                break;

            default:
                vTrace("Code touche '%c' (%d) reçu", cKey, cKey);
                break;
            }
        break;
/*
    case WM_DISPLAYCHANGE:
        vTrace("*** Changement résolution écran (%d x %d)", LOWORD(lParam), HIWORD(lParam));
        vRemakeTriview(lParam);
        break;
*/

    case WM_CLOSE:
        DestroyWindow( hWnd );
        return 0;
    
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0L;
    }
#ifndef _AMIGA_
    return DefWindowProc( hWnd, uMsg, wParam, lParam );
#else
    return 0;
#endif
}

