#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"

#undef XDC_LOCKBITMAP

// Variables statiques au module
static WPARAM wModifier;								// Mémorisé sur le LBUTTONDOWN : SHIFT / CTRL / ...
static HBRUSH hbrDkBlueSk, hbrLtBlueSk;						// Brosses de remplissage
static HPEN hpenWhite, hpenGrey, hpenCyan, hpenYellow, hpenPink, hpenDkBlueSk, hpenRed, hpenLtBlueSk, hpenGrid;	// Crayons
static HFONT hFont;
static HBITMAP hbTBuf;    // Bitmap du triple buffer (unique pour les 3 fenêtres)
static DC dcTBuf, *hdcTBuf = &dcTBuf;

// Forcer le redessin des 3 vues 2D (WM_PAINT)
// Le paramètre sert à indiquer s'il faut tout redessiner (y/c triangles & sommets) -> TRUE
// ou s'il ne faut redessiner que les curseurs, caméra & target
void vForce2DRefresh(BOOL bMode)
{
	PostMessage(hWndTop,    WM_PAINT, 0, (LPARAM) bMode);
	PostMessage(hWndFace,   WM_PAINT, 0, (LPARAM) bMode);
	PostMessage(hWndRight,  WM_PAINT, 0, (LPARAM) bMode);
}

// Callback management fenêtres vues 2D
LRESULT CALLBACK lrPlanarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{                                            
	// Variables à usage général
	HDC hdcWin = GetDC(hWnd), hdcDoubleBuf;     // Récupérer notre identifiant (vue dessus, face, droite) et mémoriser dans une globale pour que les autres fonctions sachent quelle fenêtre 2D est active
    POINT pClick;
    WinThings *pWT;									    // Pointeur sur structure étendue fenêtre (double buffer, gadgets etc.)
	char cCoords[256];
	char cAxis;
	D3DVECTOR vVect1, vVect2, vVect3;
    RECT rExtRect, rIntRect;							// Zones client externe (avec gadgets) et interne (zone de dessin uniquement)
    register int iX, iY, iZ, iT, iWindowXMin, iWindowXMax, iWindowYMin, iWindowYMax;
	float fX, fY, fZ, fT, fXs, fYs, fZs, f3D2DXCoef, f3D2DYCoef;

    W3D_Point sP;
    W3D_Line sL; sL.st_enable = FALSE; sL.linewidth = 1.0;
    W3D_Vertex sV[4];
    W3D_Triangles sTs; sTs.vertexcount = 4; sTs.v = sV;
    W3D_Lines sLs; sLs.vertexcount = 4; sLs.v = sV; sLs.st_enable = FALSE; sLs.linewidth = 1.0;
    W3D_Color sC = {0., 0., 0., 1.};
    float fTopEdge, fLeftEdge, fU, fV;
    long lError;
#ifdef XDC_LOCKBITMAP
    APTR hBM;
#endif

	// Récupérer le rectangle complet de la zone client de la fenêtre
	GetClientRect(hWnd, &rExtRect);

	// Cloner ce rectangle et le réduire pour laisser la place aux gadgets
	CopyRect(&rIntRect, &rExtRect);
	InflateRect(&rIntRect, -XDC_G_LARG, -XDC_G_HAUT);

    if (hWnd == hWndTop) lWActive = XDC_WID_TOP;
    if (hWnd == hWndFace) lWActive = XDC_WID_FACE;
    if (hWnd == hWndRight) lWActive = XDC_WID_SIDE;

	// Précalculer les coefficients de pente de conversion 3D/2D et déréférencer les coordonnées de la fenêtre
	iWindowXMin = rIntRect.left; iWindowXMax = rIntRect.right;
	iWindowYMax = rIntRect.top; iWindowYMin = rIntRect.bottom;
	switch(lWActive)
	{
		case XDC_WID_TOP : // X et Z
			f3D2DXCoef = (float) (iWindowXMax - iWindowXMin) / (fXmax - fXmin);
			f3D2DYCoef = (float) (iWindowYMax - iWindowYMin) / (fZmax - fZmin);
			break;

		case XDC_WID_FACE : // X et Y
			f3D2DXCoef = (float) (iWindowXMax - iWindowXMin) / (fXmax - fXmin);
			f3D2DYCoef = (float) (iWindowYMax - iWindowYMin) / (fYmax - fYmin);
			break;
				
		case XDC_WID_SIDE : // Z et Y
			f3D2DXCoef = (float) (iWindowXMax - iWindowXMin) / (fZmax - fZmin);
			f3D2DYCoef = (float) (iWindowYMax - iWindowYMin) / (fYmax - fYmin);
			break;
	}

	// Récupérer le pointeur sur la structure de données étendue de la fenêtre (double & triple buffer, gadgets etc.)
    pWT = (WinThings *) hWnd -> UserData;

    switch( uMsg )
    {
		case WM_CREATE:	//************************* C R E A T E ************************
			// Allouer les objets GDI (globaux à toutes les fenêtres 2D d'où if ...)
			if (!hpenWhite)		hpenWhite = CreatePen(PS_SOLID, 1, XDC_COL_WHITE);
			if (!hpenRed)		hpenRed = CreatePen(PS_SOLID, 1, XDC_COL_RED);
			if (!hpenYellow)	hpenYellow = CreatePen(PS_SOLID, 1, XDC_COL_YELLOW);
			if (!hpenCyan)		hpenCyan = CreatePen(PS_SOLID, 1, XDC_COL_CYAN);
			if (!hpenPink)		hpenPink = CreatePen(PS_SOLID, 1, XDC_COL_PINK);
			if (!hpenGrey)		hpenGrey = CreatePen(PS_SOLID, 1, XDC_COL_GREY);
			if (!hpenDkBlueSk)	hpenDkBlueSk = CreatePen(PS_SOLID, 1, XDC_COL_DKBLUESK);
			if (!hpenLtBlueSk)	hpenLtBlueSk = CreatePen(PS_SOLID, 1, XDC_COL_LTBLUESK);
			if (!hpenGrid)      hpenGrid = CreatePen(PS_SOLID, 1, XDC_COL_GRID);

#ifndef _AMIGA_
		    if (!hbrDkBlueSk)   hbrDkBlueSk = CreateSolidBrush(XDC_COL_DKBLUESK);
		    if (!hbrLtBlueSk)   hbrLtBlueSk = CreateSolidBrush(XDC_COL_LTBLUESK);

			// Créer la fonte pour indiquer l'outil en cours à coté du curseur
			if (!hFont)			hFont = CreateFont( -12,
													0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE,
													ANSI_CHARSET,
													OUT_DEFAULT_PRECIS,
													CLIP_DEFAULT_PRECIS,
													DEFAULT_QUALITY,
													VARIABLE_PITCH,
													"Arial" );
#endif

			// Créer la structure de données étendue et l'attacher à la fenêtre
			if (!(pWT = (WinThings *) malloc(sizeof(WinThings))))
			{
	            // Si fail, sortir de l'application
				PostQuitMessage(0);
				break;
		    }

            // Attacher la structure de données étendue à la fenêtre
            hWnd -> UserData = (UBYTE *) pWT;

			// Initialiser les pointeurs de gadgets sur l'imagerie gadgets (commune à toutes les fenêtres)
			pWT -> Widgets[XDC_GID_AddPoint].Gad = &gAddPoint;
			pWT -> Widgets[XDC_GID_Center].Gad = &gCenter;
			pWT -> Widgets[XDC_GID_GoDown].Gad = &gGoDown;
			pWT -> Widgets[XDC_GID_GoLeft].Gad = &gGoLeft;
			pWT -> Widgets[XDC_GID_GoUp].Gad = &gGoUp;
			pWT -> Widgets[XDC_GID_GoRight].Gad = &gGoRight;
			pWT -> Widgets[XDC_GID_Grab].Gad = &gGrab;
			pWT -> Widgets[XDC_GID_MakeTri].Gad = &gMakeTri;
			pWT -> Widgets[XDC_GID_Reverse].Gad = &gReverse;
			pWT -> Widgets[XDC_GID_RotAnti].Gad = &gRotAnti;
			pWT -> Widgets[XDC_GID_RotClock].Gad = &gRotClock;
			pWT -> Widgets[XDC_GID_ZoomIn].Gad = &gZoomIn;
			pWT -> Widgets[XDC_GID_ZoomOut].Gad = &gZoomOut;

            // Mémoriser qu'on doit mettre à jour la zone de clipping fenêtre
            pWT -> hClip = (struct Region *) ~NULL;

            // Créer le double buffer et l'attacher à la structure de données étendue
            pWT -> hbDBuf = NULL; // Sera alloué par le premier WM_SIZE
            pWT -> hdcDBuf = &(pWT -> dcDBuf); InitRastPort(pWT -> hdcDBuf);

			// Se rappeler que le prochain WM_PAINT sera le premier
		    pWT -> bFirstPaint = TRUE;

			break;

		case WM_USER + 1: //*********************** Rafraichir le point d'indice wParam dans le double buffer
						  // (-> refresh 2D mode partiel à suivre)
            // Si pas de contexte 3D ou pas de double buffer alors ne rien faire
            if ((!hW3DContext) || !(pWT -> hbDBuf))
            {
                 vTrace("*** E0070 : [%s] / msg %d : contexte @%08lX, bmdBuf %08lX)", hWnd -> Title, uMsg, hW3DContext, pWT -> hbDBuf);
                 break;
            }
			
            // Déréférencer le point à rafraîchir
            vVect1 = Vertices[wParam].vPoint;

			switch(lWActive)
			{
				case XDC_WID_TOP : // X et Z
					fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
					fY = XDM_to2D(vVect1.z, fZmin, iWindowYMin, f3D2DYCoef);
					break;

				case XDC_WID_FACE : // X et Y
					fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
					fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
					break;
				
				case XDC_WID_SIDE : // Z et Y
					fX = XDM_to2D(vVect1.z, fZmin, iWindowXMin, f3D2DXCoef);
					fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
					break;
		    }

        	// Set Drawing region sur le double buffer
            fLeftEdge = fTopEdge = 0.;
        	sScissor.left   = (int) fLeftEdge + iWindowXMin + 1;
            sScissor.top    = (int) fTopEdge + iWindowYMax + 1;
            sScissor.width  = iWindowXMax - iWindowXMin - 1;
            sScissor.height = iWindowYMin - iWindowYMax - 1;
            W3D_SetDrawRegion(hW3DContext, pWT -> hbDBuf, 0, &sScissor);

#ifdef XDC_LOCKBITMAP
            // Verrouiller le bitmap en VRAM
            lError = TAG_DONE;
            if (!(hBM = LockBitMapTagList(pWT -> hbDBuf,(struct TagItem *) &lError)))
            {
                 vTrace("*** E0021 : [%s] / msg %d : lock bitmap (contexte @%08lX, code %ld)", hWnd -> Title, uMsg, hW3DContext, lError);
                 break;
            }
#endif

            // Si on ne peut pas verrouiller le hardware 3D on ne fait rien
            if (W3D_SUCCESS != (lError = W3D_LockHardware(hW3DContext)))
            {
#ifdef XDC_LOCKBITMAP
                 // Déverrouiller le bitmap
                 UnLockBitMap(hBM);
#endif

                 vTrace("*** E0071 : [%s] / msg %d : lock hardware (contexte @%08lX, code %ld)", hWnd -> Title, uMsg, hW3DContext, lError);
                 break;
            }

			// Tracer le sommet (petite croix)
			WSetPen(hW3DContext, Vertices[wParam].bSelected ? XDC_COL_YELLOW : XDC_COL_DKBLUESK);
			WPixel(hW3DContext, fX, fY);
			WPixel(hW3DContext, fX - 1, fY);
			WPixel(hW3DContext, fX + 1, fY);
			WPixel(hW3DContext, fX, fY - 1);
			WPixel(hW3DContext, fX, fY + 1);

            // Déverrouiller le hardware 3D
            W3D_UnLockHardware(hW3DContext);

#ifdef XDC_LOCKBITMAP
            // Déverrouiller le bitmap
            UnLockBitMap(hBM);
#endif
			break;

		case WM_PAINT: //************************** P A I N T ***********************
			{
                // Si pas de contexte 3D ou pas de double buffer, on ne fait rien
                if ((!hW3DContext) || !(pWT -> hbDBuf))
                {
                     vTrace("*** E0072 : [%s] / msg %d : contexte @%08lX, bmdBuf %08lX)", hWnd -> Title, uMsg, hW3DContext, pWT -> hbDBuf);
                     break;
                }

                // déréférencer le HDC du double buffer pour aller plus vite
				hdcDoubleBuf = pWT -> hdcDBuf;

				// On redessine tout seulement si on nous le demande ou si c'est le premier WM_PAINT
	            if (((BOOL) lParam || pWT -> bFirstPaint) == XDC_MODE_PARTIEL) goto _skip;

            	// Set Drawing region sur le double buffer
                fLeftEdge = fTopEdge = 0.;
            	sScissor.left   = (int) fLeftEdge + iWindowXMin + 1;
                sScissor.top    = (int) fTopEdge + iWindowYMax + 1;
                sScissor.width  = iWindowXMax - iWindowXMin - 1;
                sScissor.height = iWindowYMin - iWindowYMax - 1;
                W3D_SetDrawRegion(hW3DContext, pWT -> hbDBuf, 0, &sScissor);

#ifdef XDC_LOCKBITMAP
                // Verrouiller le bitmap en VRAM
                lError = TAG_DONE;
                if (!(hBM = LockBitMapTagList(pWT -> hbDBuf,(struct TagItem *) &lError)))
                {
                     vTrace("*** E0020 : [%s] / msg %d : lock bitmap (contexte @%08lX, code %ld)", hWnd -> Title, uMsg, hW3DContext, lError);
                     break;
                }
#endif

                // Si on ne peut pas verrouiller le hardware 3D on ne fait rien
                if (W3D_SUCCESS != (lError = W3D_LockHardware(hW3DContext)))
                {
#ifdef XDC_LOCKBITMAP
                     // Déverrouiller le bitmap
                     UnLockBitMap(hBM);
#endif

                     vTrace("*** E0073 : [%s] : lock hardware (contexte @%08lX, code %ld)", hWnd -> Title, hW3DContext, lError);
                     break;
                }

    			// Se rappeller que les WM_PAINT à venir ne sont pas les premiers
			    pWT -> bFirstPaint = FALSE;

			    // Remplir le grand rectangle en bleu foncé :           déplacé dans WM_SIZE
                // Séparer les zones int/ext avec tour blanc :          déplacé dans WM_SIZE
				// Ajouter tous les gadgets Sculpt3D :                  déplacé dans WM_SIZE
                // Tracer les noms des axes :                           déplacé dans WM_SIZE
                // Définir la zone de clipping : rectangle interne :    déplacé dans WM_SIZE

				// Remplir le petit rectangle en bleu clair
                WSetPen(hW3DContext, XDC_COL_LTBLUESK);
                WRectFill(hW3DContext, iWindowXMin + 1, iWindowYMax + 1, iWindowXMax - 1, iWindowYMin - 1);

                // Tracer la grille (si elle est activée)
                if (bGrid)
				{
				    WSetPen(hW3DContext, XDC_COL_GRID);
                    switch(lWActive)
					{
					case XDC_WID_TOP : // Axes X (gauche > droite) et Z (bas > haut)
						for (fXs = XDM_toGrid(fXmin) ; fXs <= XDM_toGrid(fXmax) ; fXs += fGridSize)
							for (fZs = XDM_toGrid(fZmin) ; fZs <= XDM_toGrid(fZmax) ; fZs += fGridSize)
								WPixel(hW3DContext, XDM_to2D(fXs, fXmin, iWindowXMin, f3D2DXCoef),
									   XDM_to2D(fZs, fZmin, iWindowYMin, f3D2DYCoef));
						break;

					case XDC_WID_FACE : // Axes X (gauche > droite) et Y (bas > haut)
						for (fXs = XDM_toGrid(fXmin) ; fXs <= XDM_toGrid(fXmax) ; fXs += fGridSize)
							for (fYs = XDM_toGrid(fYmin) ; fYs <= XDM_toGrid(fYmax) ; fYs += fGridSize)
								WPixel(hW3DContext, XDM_to2D(fXs, fXmin, iWindowXMin, f3D2DXCoef),
									   XDM_to2D(fYs, fYmin, iWindowYMin, f3D2DYCoef));
						break;

					case XDC_WID_SIDE : // Axes Z (gauche > droite) et Y (bas > haut)
						for (fZs = XDM_toGrid(fZmin) ; fZs <= XDM_toGrid(fZmax) ; fZs += fGridSize)
							for (fYs = XDM_toGrid(fYmin) ; fYs <= XDM_toGrid(fYmax) ; fYs += fGridSize)
								WPixel(hW3DContext, XDM_to2D(fZs, fZmin, iWindowXMin, f3D2DXCoef),
									   XDM_to2D(fYs, fYmin, iWindowYMin, f3D2DYCoef));
						break;
					}
				}

				// Tracer les axes du repère, en gris
                WSetPen(hW3DContext, XDC_COL_GREY);

				switch(lWActive)
				{
    				case XDC_WID_TOP : // Axes X (gauche > droite) et Z (bas > haut)
    					fY = XDM_to2D(0, fZmin, iWindowYMin, f3D2DYCoef);
    					WDraw(hW3DContext, iWindowXMax, fY, iWindowXMin, fY);

    					fX = XDM_to2D(0, fXmin, iWindowXMin, f3D2DXCoef);
    					WDraw(hW3DContext, fX, iWindowYMin, fX, iWindowYMax);
    					break;

    				case XDC_WID_FACE : // Axes X (gauche > droite) et Y (bas > haut)
    					fY = XDM_to2D(0, fYmin, iWindowYMin, f3D2DYCoef);
    					WDraw(hW3DContext, iWindowXMax, fY, iWindowXMin, fY);

    					fX = XDM_to2D(0, fXmin, iWindowXMin, f3D2DXCoef);
    					WDraw(hW3DContext, fX, iWindowYMin, fX, iWindowYMax);
    					break;

    				case XDC_WID_SIDE : // Axes Z (gauche > droite) et Y (bas > haut)
    					fY = XDM_to2D(0, fYmin, iWindowYMin, f3D2DYCoef);
    					WDraw(hW3DContext, iWindowXMax, fY, iWindowXMin, fY);

    					fX = XDM_to2D(0, fZmin, iWindowXMin, f3D2DXCoef);
    					WDraw(hW3DContext, fX, iWindowYMin, fX, iWindowYMax);
    					break;
				}

				// Tracer les objets géométriques
				{
      				    // 1 - Arêtes
        				// Choisir le crayon rouge
                        WSetPen(hW3DContext, XDC_COL_RED);
    					for (int iEdge = 0 ; iEdge <= iEdgeLastUsed ; iEdge++)
    					{
    						// Ignorer les arêtes inexistants
    						if (Edges[iEdge].bEnabled == FALSE) continue;

    						// Ignorer les arêtes cachés
    						if (Edges[iEdge].bHidden == TRUE) continue;

    						vVect1 = Vertices[Edges[iEdge].iSommets[0]].vPoint;
    						vVect2 = Vertices[Edges[iEdge].iSommets[1]].vPoint;

    						switch(lWActive)
    						{
    							case XDC_WID_TOP : // X et Z
    								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fY = XDM_to2D(vVect1.z, fZmin, iWindowYMin, f3D2DYCoef);
    								fZ = XDM_to2D(vVect2.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fT = XDM_to2D(vVect2.z, fZmin, iWindowYMin, f3D2DYCoef);
    								break;

    							case XDC_WID_FACE : // X et Y
    								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
    								fZ = XDM_to2D(vVect2.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fT = XDM_to2D(vVect2.y, fYmin, iWindowYMin, f3D2DYCoef);
    								break;

    							case XDC_WID_SIDE : // Z et Y
    								fX = XDM_to2D(vVect1.z, fZmin, iWindowXMin, f3D2DXCoef);
    								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
    								fZ = XDM_to2D(vVect2.z, fZmin, iWindowXMin, f3D2DXCoef);
    								fT = XDM_to2D(vVect2.y, fYmin, iWindowYMin, f3D2DYCoef);
    								break;
    						}

    						// Tracer l'arête
    						WDraw(hW3DContext, fX, fY, fZ, fT);
    					}

					// On ne dessine les triangles que si on n'est pas en dragging
                    if (!bTracking)
                    {
    					// 2 - Triangles
    					// Choisir le crayon rose
    					WSetPen(hW3DContext, XDC_COL_PINK);
    					for (int iTriangle = 0 ; iTriangle <= iTriaLastUsed ; iTriangle++)
    					{
    						// Ignorer les triangles inexistants
    						if (Triangles[iTriangle].bEnabled == FALSE) continue;

    						// Ignorer les triangles cachés
    						if (Triangles[iTriangle].bHidden == TRUE) continue;

    						vVect1 = Vertices[Triangles[iTriangle].iSommets[0]].vPoint;
    						vVect2 = Vertices[Triangles[iTriangle].iSommets[1]].vPoint;
    						vVect3 = Vertices[Triangles[iTriangle].iSommets[2]].vPoint;

    						switch(lWActive)
    						{
    							case XDC_WID_TOP : // X et Z
    								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fY = XDM_to2D(vVect1.z, fZmin, iWindowYMin, f3D2DYCoef);
    								fZ = XDM_to2D(vVect2.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fT = XDM_to2D(vVect2.z, fZmin, iWindowYMin, f3D2DYCoef);
    								fU = XDM_to2D(vVect3.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fV = XDM_to2D(vVect3.z, fZmin, iWindowYMin, f3D2DYCoef);
    								break;

    							case XDC_WID_FACE : // X et Y
    								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
    								fZ = XDM_to2D(vVect2.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fT = XDM_to2D(vVect2.y, fYmin, iWindowYMin, f3D2DYCoef);
    								fU = XDM_to2D(vVect3.x, fXmin, iWindowXMin, f3D2DXCoef);
    								fV = XDM_to2D(vVect3.y, fYmin, iWindowYMin, f3D2DYCoef);
    								break;

    							case XDC_WID_SIDE : // Z et Y
    								fX = XDM_to2D(vVect1.z, fZmin, iWindowXMin, f3D2DXCoef);
    								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
    								fZ = XDM_to2D(vVect2.z, fZmin, iWindowXMin, f3D2DXCoef);
    								fT = XDM_to2D(vVect2.y, fYmin, iWindowYMin, f3D2DYCoef);
    								fU = XDM_to2D(vVect3.z, fZmin, iWindowXMin, f3D2DXCoef);
    								fV = XDM_to2D(vVect3.y, fYmin, iWindowYMin, f3D2DYCoef);
    								break;
    						}

    						// Tracer l'arête du triangle
    						WTriEdge(hW3DContext, fX, fY, fZ, fT, fU, fV);
    					}
                    }

					// 3.1 - Points
					for (int iVertex = 0 ; iVertex <= iVertLastUsed ; iVertex++)
					{
                        if (PeekSignal(l2DWinSigMask))
                        {
                            W3D_UnLockHardware(hW3DContext);
                            goto _skip;
                        }

						// Ignorer les points inexistants
						if (Vertices[iVertex].bEnabled == FALSE) continue;

						// Ignorer les points cachés
						if (Vertices[iVertex].bHidden == TRUE) continue;

						vVect1 = Vertices[iVertex].vPoint;

						switch(lWActive)
						{
							case XDC_WID_TOP : // X et Z
								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
								fY = XDM_to2D(vVect1.z, fZmin, iWindowYMin, f3D2DYCoef);
								break;

							case XDC_WID_FACE : // X et Y
								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
								break;

							case XDC_WID_SIDE : // Z et Y
								fX = XDM_to2D(vVect1.z, fZmin, iWindowXMin, f3D2DXCoef);
								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
								break;
						}

						// Tracer le sommet du triangle (petite croix)
                        WSetPen(hW3DContext, Vertices[iVertex].bSelected ? XDC_COL_YELLOW : XDC_COL_DKBLUESK);
						WPixel(hW3DContext, fX, fY);
                        if (!bTracking)
                        {
     						WPixel(hW3DContext, fX - 1, fY);
     						WPixel(hW3DContext, fX + 1, fY);
     						WPixel(hW3DContext, fX, fY - 1);
     						WPixel(hW3DContext, fX, fY + 1);
                        }
			        }

					// 4 - Lampes
					for (int iLamp = 0 ; iLamp <= iLampLastUsed ; iLamp++)
					{
						// Ignorer les lampes inexistantes
						if (Lampes[iLamp].bEnabled == FALSE) continue;

						WSetPen(hW3DContext, Lampes[iLamp].bSelected ? XDC_COL_YELLOW : XDC_COL_RED);

						vVect1 = Lampes[iLamp].lLamp.dvPosition;

						switch(lWActive)
						{
							case XDC_WID_TOP : // X et Z
								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
								fY = XDM_to2D(vVect1.z, fZmin, iWindowYMin, f3D2DYCoef);
								break;

							case XDC_WID_FACE : // X et Y
								fX = XDM_to2D(vVect1.x, fXmin, iWindowXMin, f3D2DXCoef);
								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
								break;

							case XDC_WID_SIDE : // Z et Y
								fX = XDM_to2D(vVect1.z, fZmin, iWindowXMin, f3D2DXCoef);
								fY = XDM_to2D(vVect1.y, fYmin, iWindowYMin, f3D2DYCoef);
								break;
					    }
                        WDraw(hW3DContext, fX, fY, fX + 3, fY - 3);
                        WDraw(hW3DContext, fX, fY, fX + 3, fY + 3);
					}
			    }

#ifdef XDC_LOCKBITMAP
                // Déverrouiller le bitmap
                UnLockBitMap(hBM);
#endif

                // Déverrouiller le hardware 3D
                W3D_UnLockHardware(hW3DContext);
_skip:
            	// Set Drawing region sur la fenêtre ou sur le triple buffer s'il existe
                sScissor.width  = iWindowXMax - iWindowXMin - 1;
                sScissor.height = iWindowYMin - iWindowYMax - 1;

                if (hbTBuf) // triple buffer, on va continuer à dessiner dedans
                {
                    fLeftEdge = fTopEdge = 0.;
                	sScissor.left   = (int) fLeftEdge + iWindowXMin + 1;
                    sScissor.top    = (int) fTopEdge + iWindowYMax + 1;
                    W3D_SetDrawRegion(hW3DContext, hbTBuf, 0, &sScissor);

	    			// BitBlitter le double buffer dans le triple
                    ClipBlit(hdcDoubleBuf, hWnd -> BorderLeft, hWnd -> BorderTop,
                             hdcTBuf, hWnd -> BorderLeft, hWnd -> BorderTop,
                             rExtRect.right, rExtRect.bottom,
                             0xc0);
                }
                else // Pas de triple buffer, on va continuer à dessiner dans la fenêtre
                {
                    fLeftEdge = hWnd -> LeftEdge; fTopEdge = hWnd -> TopEdge;
                	sScissor.left   = (int) fLeftEdge + iWindowXMin + 1;
                    sScissor.top    = (int) fTopEdge + iWindowYMax + 1;
                    W3D_SetDrawRegion(hW3DContext, hdcWin -> BitMap, 0, &sScissor);

	    			// BitBlitter le double buffer dans la fenêtre
                    ClipBlit(hdcDoubleBuf, hWnd -> BorderLeft, hWnd -> BorderTop,
                             hdcWin, hWnd -> BorderLeft, hWnd -> BorderTop,
                             rExtRect.right, rExtRect.bottom,
                             0xc0);
                }

#ifdef XDC_LOCKBITMAP
                // Verrouiller le bitmap en VRAM
                lError = TAG_DONE;
                if (!(hBM = LockBitMapTagList(hbTBuf ? hbTBuf : hdcWin -> BitMap, (struct TagItem *) &lError)))
                {
                     vTrace("*** E0033 : [%s] / msg %d : lock bitmap (contexte @%08lX, code %ld)", hWnd -> Title, uMsg, hW3DContext, lError);
                     break;
                }
#endif

                // Si on ne peut pas verrouiller le hardware 3D on ne fait rien
                if (W3D_SUCCESS != (lError = W3D_LockHardware(hW3DContext)))
                {
#ifdef XDC_LOCKBITMAP
                     // Déverrouiller le bitmap
                     UnLockBitMap(hBM);
#endif

                     vTrace("*** E0074 : [%s] : lock hardware (contexte @%08lX, code %ld)", hWnd -> Title, hW3DContext, lError);
                     break;
                }

				// 5 - Tracer le curseur principal et la zone de tolérance
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						iZ = (int) (fX = XDM_to2D(Cursor1.x, fXmin, iWindowXMin, f3D2DXCoef));
						iT = (int) (fY = XDM_to2D(Cursor1.z, fZmin, iWindowYMin, f3D2DYCoef));
						fZ = XDM_to2D(Cursor1.x - (fXmax - fXmin) / XDC_ZONE, fXmin, iWindowXMin, f3D2DXCoef);
						fT = XDM_to2D(Cursor1.z - (fXmax - fXmin) / XDC_ZONE, fZmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_FACE : // X et Y
						iZ = (int) (fX = XDM_to2D(Cursor1.x, fXmin, iWindowXMin, f3D2DXCoef));
						iT = (int) (fY = XDM_to2D(Cursor1.y, fYmin, iWindowYMin, f3D2DYCoef));
						fZ = XDM_to2D(Cursor1.x - (fXmax - fXmin) / XDC_ZONE, fXmin, iWindowXMin, f3D2DXCoef);
						fT = XDM_to2D(Cursor1.y - (fXmax - fXmin) / XDC_ZONE, fYmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_SIDE : // Z et Y
						iZ = (int) (fX = XDM_to2D(Cursor1.z, fZmin, iWindowXMin, f3D2DXCoef));
						iT = (int) (fY = XDM_to2D(Cursor1.y, fYmin, iWindowYMin, f3D2DYCoef));
						fZ = XDM_to2D(Cursor1.z - (fXmax - fXmin) / XDC_ZONE, fZmin, iWindowXMin, f3D2DXCoef);
						fT = XDM_to2D(Cursor1.y - (fXmax - fXmin) / XDC_ZONE, fYmin, iWindowYMin, f3D2DYCoef);
						break;
				}

				// Tracer le curseur 1
				WSetPen(hW3DContext, XDC_COL_CYAN);
				WDraw(hW3DContext, fX - 9, fY, fX + 10, fY);
				WDraw(hW3DContext, fX, fY - 9, fX, fY + 10);

				// Tracer la zone de tolérance
				WSetPen(hW3DContext, (cTool == XDC_TOOL_UNSELECT) ? XDC_COL_DKBLUESK : XDC_COL_YELLOW);
				WDraw(hW3DContext, fZ, fT, fZ + 2 * (fX - fZ), fT);
				WDraw(hW3DContext, fZ, fT, fZ, fT + 2 * (fY - fT));

				if ((cTool == XDC_TOOL_SELECT) || (cTool == XDC_TOOL_UNSELECT))
				{
					WDraw(hW3DContext, fZ, fT + 2 * (fY - fT), fZ + 2 * (fX - fZ), fT + 2 * (fY - fT));
					WDraw(hW3DContext, fZ + 2 * (fX - fZ), fT, fZ + 2 * (fX - fZ), fT + 2 * (fY - fT));
				}

				// 6 - Tracer l'observateur
				WSetPen(hW3DContext, XDC_COL_WHITE);
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						fX = XDM_to2D(Observer.x, fXmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Observer.z, fZmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_FACE : // X et Y
						fX = XDM_to2D(Observer.x, fXmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Observer.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_SIDE : // Z et Y
						fX = XDM_to2D(Observer.z, fZmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Observer.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;
				}
                WDraw(hW3DContext, fX, fY, fX + 3, fY - 3);
                WDraw(hW3DContext, fX, fY, fX + 3, fY + 3);

				// 7 - Tracer le segment caméra / cible
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						fZ = XDM_to2D(Target.x, fXmin, iWindowXMin, f3D2DXCoef);
						fT = XDM_to2D(Target.z, fZmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_FACE : // X et Y
						fZ = XDM_to2D(Target.x, fXmin, iWindowXMin, f3D2DXCoef);
						fT = XDM_to2D(Target.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_SIDE : // Z et Y
						fZ = XDM_to2D(Target.z, fZmin, iWindowXMin, f3D2DXCoef);
						fT = XDM_to2D(Target.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;
				}
				WDraw(hW3DContext, fX, fY, fZ, fT);
if (bTracking) goto _fastrefresh;

                // 8 - Tracer la cible
                WDraw(hW3DContext, fZ - 2, fT - 2, fZ + 3, fT + 3);
				WDraw(hW3DContext, fZ - 2, fT + 2, fZ + 3, fT - 3);

				// 9 - Tracer le curseur 2
                WSetPen(hW3DContext, XDC_COL_CYAN);
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						fX = XDM_to2D(Cursor2.x, fXmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Cursor2.z, fZmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_FACE : // X et Y
						fX = XDM_to2D(Cursor2.x, fXmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Cursor2.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_SIDE : // Z et Y
						fX = XDM_to2D(Cursor2.z, fZmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Cursor2.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;
                }
				WDraw(hW3DContext, fX - 4, fY, fX + 5, fY);
				WDraw(hW3DContext, fX, fY - 4, fX, fY + 5);

				// 10 - Tracer le curseur 3
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						fX = XDM_to2D(Cursor3.x, fXmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Cursor3.z, fZmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_FACE : // X et Y
						fX = XDM_to2D(Cursor3.x, fXmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Cursor3.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;

					case XDC_WID_SIDE : // Z et Y
						fX = XDM_to2D(Cursor3.z, fZmin, iWindowXMin, f3D2DXCoef);
						fY = XDM_to2D(Cursor3.y, fYmin, iWindowYMin, f3D2DYCoef);
						break;
				}
				WDraw(hW3DContext, fX - 4, fY - 4, fX + 5, fY + 5);
				WDraw(hW3DContext, fX - 4, fY + 4, fX + 5, fY - 5);

_fastrefresh:
#ifdef XDC_LOCKBITMAP
                // Déverrouiller le bitmap
                UnLockBitMap(hBM);
#endif

                // Déverrouiller le hardware 3D
                W3D_UnLockHardware(hW3DContext);

                // Si triple buffer, blitter dans la fenêtre
                if (hbTBuf)
                    ClipBlit(hdcTBuf, hWnd -> BorderLeft, hWnd -> BorderTop,
                             hdcWin, hWnd -> BorderLeft, hWnd -> BorderTop,
                             rExtRect.right, rExtRect.bottom,
                             0xc0);

				// Rappeller l'outil courant à coté du curseur
		        if (cTool != XDC_TOOL_NONE)
			        ExtTextOut(hdcWin, iZ + 2, iT  - 13, 0, (RECT *) NULL, &cTool, 1, (int *) NULL);

				// 10 - Coordonnées courantes
				if (bCoords)
				{
					sprintf(cCoords, "(%012.5f, %012.5f, %012.5f)", vTracker.x, vTracker.y, vTracker.z);
					ExtTextOut(hdcWin, 18, 10, 0, (RECT *) NULL, cCoords, strlen(cCoords), (int *) NULL);
				}

                // Libérer le clipping de la fenêtre : déporté dans WM_SIZE et WM_DESTROY
				break;
			}

		case WM_LBUTTONDOWN : //**************** C L I C K  G A U C H E *******************
#ifndef _AMIGA_
	        RECT rClipCurseur;
#endif

			// Si on est déjà en tracking curseur alors ne rien faire
			if (bTracking) break;

			// Noter le qualifier (touches enfoncées au moment du click)
			wModifier = wParam;

			// Précalculer les offsets (déplacement, zoom, etc.) en fonction du modifieur
			fXs = (fXmax - fXmin) / ((wModifier & IEQUALIFIER_LSHIFT) ? 4.f : 8.f),
			fYs = (fYmax - fYmin) / ((wModifier & IEQUALIFIER_LSHIFT) ? 4.f : 8.f),
			fZs = (fZmax - fZmin) / ((wModifier & IEQUALIFIER_LSHIFT) ? 4.f : 8.f);
			
			// Vérifier si le click n'est pas dans les boutons Sculpt.
			pClick.x = LOWORD(lParam);
			pClick.y = HIWORD(lParam);

			if (XDM_BoutonClic(XDC_GID_MakeTri, pClick)) //********* Bouton MakeTri
			{
				int iPoint[3], iPoints = 0;

				// Compter et mémoriser les points sélectionnés (jusqu'à 3)
				for (int iVertex = 0 ; iVertex <= iVertLastUsed ; iVertex++)
					if (bIsVertexSelected(iVertex))
					{
						if (iPoints < 3) iPoint[iPoints] = iVertex;
						iPoints++;
					}

				// - soit il existe exactement 2 points sélectionnés et on fait une arête avec
				//   si elle n'existe pas déjà
				//   (on ne transforme pas 3 points reliés par 3 arêtes en triangle ***???)
				if (iPoints == 2)
				{
					vTrace("Deux points sélectionnés -> création arête");

					if (-1 == iFindEdge(iPoint[0], iPoint[1]))
					{
						iMakeEdge(iPoint[0], iPoint[1]);
						vForce2DRefresh(XDC_MODE_COMPLET);
						vForce3DRefresh(XDC_MODE_COMPLET);
					}
					else
						vTrace("*** E0001 : L'arête existe déjà");
					break;
				}

				// - soit il existe exactement 3 points sélectionnés et on fait un triangle avec
				//   (en supprimant les arêtes éventuelles existant entre ces 3 points) s'il n'existe pas déjà
				if (iPoints == 3)
				{
					vTrace("Trois points sélectionnés -> création facette");

					if (-1 == iFindTriangle(iPoint[0], iPoint[1], iPoint[2]))
					{
						iMakeTriangle(iPoint[0], iPoint[1], iPoint[2], 0);
						vForce2DRefresh(XDC_MODE_COMPLET);
						vForce3DRefresh(XDC_MODE_COMPLET);
					}
					else
						vTrace("*** E0002 : Le triangle existe déjà");
					break;
				}

				// - soit il existe exactement 0 points sélectionnés et on fait un triangle entre
				//   les 3 curseurs s'il n'existe pas déjà
				if (iPoints == 0)
				{
					vTrace("Zéro points sélectionnés -> création facette avec les 3 curseurs");
					iPoint[0] = iMakeVertex(Cursor2, XDC_ALLOWSAME),
					iPoint[1] = iMakeVertex(Cursor1, XDC_ALLOWSAME),
					iPoint[2] = iMakeVertex(Cursor3, XDC_ALLOWSAME);

					if (-1 == iFindTriangle(iPoint[0], iPoint[1], iPoint[2]))
					{
						iMakeTriangle(iPoint[0], iPoint[1], iPoint[2], 0);
						vForce2DRefresh(XDC_MODE_COMPLET);
						vForce3DRefresh(XDC_MODE_COMPLET);
					}
					else
						vTrace("*** E0003 : Le triangle existe déjà");
					break;
				}

				vTrace("*** E0004 : Nombre de points sélectionnés incorrect (doit être 0, 2 ou 3)");
				break;
			}

			if (XDM_BoutonClic(XDC_GID_ZoomOut, pClick)) //********* Bouton Zoom Out
			{
				fXmin -= fXs; fXmax += fXs;
				fYmin -= fYs; fYmax += fYs;
				fZmin -= fZs; fZmax += fZs;

				vForce2DRefresh(XDC_MODE_COMPLET);
				break;
			}

			if (XDM_BoutonClic(XDC_GID_ZoomIn, pClick)) //********* Bouton Zoom In
			{
				fXmin += fXs; fXmax -= fXs;
				fYmin += fYs; fYmax -= fYs;
				fZmin += fZs; fZmax -= fZs;

				vForce2DRefresh(XDC_MODE_COMPLET);
				break;
			}

			if (	(XDM_BoutonClic(XDC_GID_RotClock, pClick)) //********* Bouton Rotation clockwise
				 || (XDM_BoutonClic(XDC_GID_RotAnti, pClick))  //********* Bouton Rotation anti-clockwise
			   )
			{
				D3DMATRIX mRot;
				float fAngle = g_PI / (wModifier & MK_SHIFT ? 10.f : 20.f);

				if (XDM_BoutonClic(XDC_GID_RotClock, pClick)) fAngle *= -1.f;

				// Préparer la matrice de rotation
				switch(lWActive)
				{
				case XDC_WID_TOP : // X et Z
					D3DUtil_SetRotateYMatrix(mRot, -fAngle);
					break;

				case XDC_WID_FACE : // X et Y
					D3DUtil_SetRotateZMatrix(mRot, fAngle);
					break;
				
				case XDC_WID_SIDE : // Z et Y
					D3DUtil_SetRotateXMatrix(mRot, -fAngle);
					break;
				}

				// Appliquer la matrice aux sommets sélectionnés
				for (int iVertex = 0 ; iVertex <= iVertLastUsed ; iVertex++)
					if (bIsVertexSelected(iVertex))
					{
						// Changer de repère => Cursor1
						D3DVECTOR vTrans = Vertices[iVertex].vPoint - Cursor1;
					
						// Appliquer la rotation au point translaté
						D3DMath_VectorMatrixMultiply(vTrans,
													 vTrans,
													 mRot);

						// Remettre dans le repère d'origine
						Vertices[iVertex].vPoint = vTrans + Cursor1;
					}

				// Rafraichir les vues 2D et 3D
				vForce2DRefresh(XDC_MODE_COMPLET);
				vForce3DRefresh(XDC_MODE_COMPLET);
				break;
			}

			if (XDM_BoutonClic(XDC_GID_Reverse, pClick)) // ********* Bouton reverse (?)
			{
                break;
			}

			if (XDM_BoutonClic(XDC_GID_Grab, pClick)) // ********* Bouton Grabber (toggle)
			{
				cTool = (cTool == XDC_TOOL_GRAB ? XDC_TOOL_NONE : XDC_TOOL_GRAB);
				vForce2DRefresh(XDC_MODE_PARTIEL);
				vUpdateMenu();
				break;
			}

			if (XDM_BoutonClic(XDC_GID_GoRight, pClick)) //********* Bouton Go Right
			{
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
					case XDC_WID_FACE : // X et Y
						fXmin += fXs; fXmax += fXs;
						break;
					case XDC_WID_SIDE : // Z et Y
						fZmin += fZs; fZmax += fZs;
						break;
				}

				vForce2DRefresh(XDC_MODE_COMPLET);
				break;
			}

			if (XDM_BoutonClic(XDC_GID_GoUp, pClick)) //********* Bouton Go up
			{
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						fZmin += fZs; fZmax += fZs;
						break;
					case XDC_WID_FACE : // X et Y
					case XDC_WID_SIDE : // Z et Y
						fYmin += fYs; fYmax += fYs;
						break;
				}

				vForce2DRefresh(XDC_MODE_COMPLET);
				break;
			}

			if (XDM_BoutonClic(XDC_GID_GoLeft, pClick)) //********* Bouton Go left
			{
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
					case XDC_WID_FACE : // X et Y
						fXmin -= fXs; fXmax -= fXs;
						break;
					case XDC_WID_SIDE : // Z et Y
						fZmin -= fZs; fZmax -= fZs;
						break;
				}

				vForce2DRefresh(XDC_MODE_COMPLET);
				break;
			}

			if (XDM_BoutonClic(XDC_GID_GoDown, pClick)) //********* Bouton Go down
			{
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						fZmin -= fZs; fZmax -= fZs;
						break;
					case XDC_WID_FACE : // X et Y
					case XDC_WID_SIDE : // Z et Y
						fYmin -= fYs; fYmax -= fYs;
						break;
				}

				vForce2DRefresh(XDC_MODE_COMPLET);
				break;
			}

			if (XDM_BoutonClic(XDC_GID_AddPoint, pClick)) //********* Bouton Add Point
			{
				PostMessage(hWnd, WM_CHAR, 'p', 0);
				break;
			}

			if (XDM_BoutonClic(XDC_GID_Center, pClick)) //********* Bouton Center
			{
				float	fXs = (fXmax - fXmin) / 2.f,
						fYs = (fYmax - fYmin) / 2.f,
						fZs = (fZmax - fZmin) / 2.f;
				
				fXmin = Cursor1.x - fXs; fXmax = Cursor1.x + fXs;
				fYmin = Cursor1.y - fYs; fYmax = Cursor1.y + fYs;
				fZmin = Cursor1.z - fZs; fZmax = Cursor1.z + fZs;

				vForce2DRefresh(XDC_MODE_COMPLET);
				break;
			}

			// Si click dans le rectangle interne, alors :
			// - déclencher le tracking (curseur, observer ou target en fonction du qualifier
			// - n'autoriser le déplacement que dans le subrect client (clipcursor)
			// - initialiser le tracking (MOUSEMOVE)
		    if (PtInRect(&rIntRect, pClick))
			{
#ifndef _AMIGA_ // Pas de fonction permettant le clipping curseur
				CopyRect(&rClipCurseur, &rIntRect);
				ClientToScreen(hWnd, (POINT *) &rClipCurseur);
				ClientToScreen(hWnd, (POINT *) &rClipCurseur + 1);
				ClipCursor(&rClipCurseur);
#endif
				bTracking = TRUE;
                ReportMouse(TRUE, hWnd);
				PostMessage(hWnd, WM_MOUSEMOVE, wParam, lParam);
				break;

		    }

			break;

		case WM_LBUTTONDBLCLK:
			pClick.x = LOWORD(lParam);
			pClick.y = HIWORD(lParam);

			// Si le double click est dans le rectangle interne alors toggler la sélection du noeud indiqué
			// en envoyant le message ID_EDITION_SLECTIONNER_NOEUDINDIQU à la fenêtre menu avec lParam = XDC_TOGGLE
			// Sinon, traiter le message comme un simple click
			if (PtInRect(&rIntRect, pClick))
				PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_SLECTIONNER_NOEUDINDIQU, XDC_SINGLETOGGLE);
			else
				PostMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam);
			break;

		case WM_LBUTTONUP : //******************** R E L A C H E  B O U T O N  G A U C H E ******
/*
			// (ou envoyer les coordonnées à la dlg d'acquisition des trous des formes à remplir si elle est active).
			if (hHoleDlgActive)
			{
				switch(lWActive)
				{
					case XDC_WID_TOP : // X et Z
						dHoles[iHoles * 2 + 0] = XDM_to3D(LOWORD(lParam), fXmin, iWindowXMin, f3D2DXCoef);
						dHoles[iHoles * 2 + 1] = XDM_to3D(HIWORD(lParam), fZmin, iWindowYMin, f3D2DYCoef);
						break;
					case XDC_WID_FACE : // X et Y
						dHoles[iHoles * 2 + 0] = XDM_to3D(LOWORD(lParam), fXmin, iWindowXMin, f3D2DXCoef);
						dHoles[iHoles * 2 + 1] = XDM_to3D(HIWORD(lParam), fYmin, iWindowYMin, f3D2DYCoef);
						break;
					case XDC_WID_SIDE : // Z et Y
						dHoles[iHoles * 2 + 0] = XDM_to3D(LOWORD(lParam), fZmin, iWindowXMin, f3D2DXCoef);
						dHoles[iHoles * 2 + 1] = XDM_to3D(HIWORD(lParam), fYmin, iWindowYMin, f3D2DYCoef);
						break;
				}
				iHoles++;
				SendMessage(hHoleDlgActive, WM_USER+1, 0, 0);
			}
*/
			// Si on n'est pas en mode tracking, ne rien faire
			if (!bTracking)
				break;

			// Si on est en mode tracking, arrêter le mode tracking et supprimer le clipping.
			bTracking = FALSE;

            // Arrêter le reporting des mouvements souris sur la fenêtre
            ReportMouse(FALSE, hWnd);

            // Forcer le redessin des fenêtres 2D
            vForce2DRefresh(XDC_MODE_COMPLET);

//			  ClipCursor(NULL);
			break;

		case WM_MOUSEMOVE : //****************** M O U V E M E N T  S O U R I S *************
			D3DVECTOR *pObject;

			// Si on n'est pas en tracking alors on ne fait rien.
			if (!bTracking) break;

			// Déterminer l'objet à déplacer en fonction des qualifiers du WM_LBUTTONDOWN (par défaut, sans qualifiers, c'est le curseur principal)
			pObject = &Cursor1;
			if (wModifier & IEQUALIFIER_RALT)       pObject = &Target;
			if (wModifier & IEQUALIFIER_RCOMMAND)   pObject = &Observer;
			if (wModifier & IEQUALIFIER_CONTROL)    pObject = &Cursor2;
			if (wModifier & IEQUALIFIER_LSHIFT)     pObject = &Cursor3;

			// Mémoriser l'ancien vecteur
			vVect1 = *pObject;

			// Déterminer les nouvelles coordonnées 2D de l'objet à déplacer
			iX = LOWORD(lParam);  // horizontal position 
			iY = HIWORD(lParam);  // vertical position

			// Mettre à jour les coordonnées 3D de l'objet à déplacer
			switch(lWActive)
			{
				case XDC_WID_TOP : // X et Z
					pObject -> x = XDM_to3D(iX, fXmin, iWindowXMin, f3D2DXCoef);
					pObject -> z = XDM_to3D(iY, fZmin, iWindowYMin, f3D2DYCoef);
					break;
				case XDC_WID_FACE : // X et Y
					pObject -> x = XDM_to3D(iX, fXmin, iWindowXMin, f3D2DXCoef);
					pObject -> y = XDM_to3D(iY, fYmin, iWindowYMin, f3D2DYCoef);
					break;
				case XDC_WID_SIDE : // Z et Y
					pObject -> z = XDM_to3D(iX, fZmin, iWindowXMin, f3D2DXCoef);
					pObject -> y = XDM_to3D(iY, fYmin, iWindowYMin, f3D2DYCoef);
					break;
			}

			// Si la grille est activée, snapper l'objet à la grille (en x/y/z)
			if (bGrid)
			{
				pObject -> x = XDM_toGrid(pObject -> x);
				pObject -> y = XDM_toGrid(pObject -> y);
				pObject -> z = XDM_toGrid(pObject -> z);
			}

			// Mémoriser les coordonnées pour l'affichage des coordonnées
			if (bCoords) vTracker = *pObject;

			// Si on a déplacé l'observer ou le target, mettre à jour la VIEW MATRIX
			// (la vue 3D se met automatiquement à jour par timer dans 3dwndproc)
			if ((pObject == &Target) || (pObject == &Observer))
			{
				D3DUtil_SetViewMatrix(matView,
						  Observer,	// From
                          Target,	// To
						  D3DVECTOR(0.f, 0.f, 0.f));

				// Mettre à jour les variables d'état du pipe D3D
				vSetD3DState();

				// Recalculer la scène
				vForce3DRefresh(XDC_MODE_COMPLET);
			}

			// Si on a un outil sélectionné, appliquer l'outil à la sélection
			if ((pObject == &Cursor1) && (cTool != XDC_TOOL_NONE))
			{
				switch(cTool)
				{
					case XDC_TOOL_SELECT:
						PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_SLECTIONNER_NOEUDINDIQU, XDC_SELECTALL);
						break;

					case XDC_TOOL_UNSELECT:
						PostMessage(hWndMenu, WM_COMMAND, ID_EDITION_SLECTIONNER_NOEUDINDIQU, XDC_DESELECTALL);
						break;

					case XDC_TOOL_MAGNET:
						break;

					case XDC_TOOL_CURVE:
						break;

					case XDC_TOOL_EXTRUDE:
						break;

					case XDC_TOOL_EDGE:
						break;

					case XDC_TOOL_GRAB:
				 		// Faire suivre le mouvement du curseur à tous les vertices sélectionnés
				 		for (int iVertex = 0 ; iVertex <= iVertLastUsed ; iVertex++)
				 			if (bIsVertexSelected(iVertex))
				 				Vertices[iVertex].vPoint += (Cursor1 - vVect1);

						// Faire suivre le mouvement du curseur à toutes les lampes sélectionnées
						for (int iLamp = 0 ; iLamp <= iLampLastUsed ; iLamp++)
						{
							if (Lampes[iLamp].bEnabled == FALSE) continue;
							if (Lampes[iLamp].bSelected == FALSE) continue;

							Lampes[iLamp].lLamp.dvPosition += (Cursor1 - vVect1);
							bUpdateLamp(iLamp);
					    }

						vForce2DRefresh(XDC_MODE_COMPLET);
					    vForce3DRefresh(XDC_MODE_COMPLET);
						goto _break;
				}
			}

			// Forcer le redessin des fenêtres 2D pour prendre en compte le déplacement de l'objet
			vForce2DRefresh(XDC_MODE_PARTIEL);
_break:
			break;

        case WM_SIZE: //**************** T A I L L E  F E N E T R E ****************
            // Si le contexte Warp3D n'est pas initialisé, on s'arrête là
            if (!hW3DContext) return 0;

			// Détruire et recréer le double buffer avec les nouvelles dimensions de la fenêtre
			iX = LOWORD(lParam);  // width of client area 
			iY = HIWORD(lParam);  // height of client area

            // Supprimer le bitmap offscreen du double buffer s'il est alloué
            if (pWT -> hbDBuf) FreeBitMap(pWT -> hbDBuf);

            // Créer le bitmap offscreen du double buffer
            if (!(pWT -> hbDBuf = AllocBitMap(hWnd -> Width, hWnd -> Height, 8, BMF_MINPLANES | BMF_DISPLAYABLE, hWnd -> RPort -> BitMap)))
            {
                vTrace("*** E0075 : allocation double buffer %s", hWnd -> Title);
                return 0;
            }
            pWT -> hdcDBuf -> BitMap = pWT -> hbDBuf;

            // Libérer le clipping de la fenêtre s'il est activé
            if (pWT -> hClip != (struct Region *) ~NULL)
            {
                UnclipWindow(hWnd);
                pWT -> hClip = (struct Region *) ~NULL;
            }

   		    // Remplir l'ensemble de la zone cliente en bleu foncé
            rExtRect.left   = hWnd -> BorderLeft;
            rExtRect.right  = iX - hWnd -> BorderRight - 1;
            rExtRect.top    = hWnd -> BorderTop;
            rExtRect.bottom = iY - hWnd -> BorderBottom - 1;

            SetAPen(hdcWin, hpenDkBlueSk);
            RectFill(hdcWin, rExtRect.left, rExtRect.top, rExtRect.right, rExtRect.bottom);

            // Séparer les zones int/ext avec tour blanc
        	CopyRect(&rIntRect, &rExtRect);
        	InflateRect(&rIntRect, -XDC_G_LARG, -XDC_G_HAUT);

            SetAPen(hdcWin, hpenWhite);
            {
                short rRect[10];
                rRect[0] = rIntRect.left; rRect[1] = rIntRect.bottom;
                rRect[2] = rIntRect.right; rRect[3] = rIntRect.bottom;
                rRect[4] = rIntRect.right; rRect[5] = rIntRect.top;
                rRect[6] = rIntRect.left; rRect[7] = rIntRect.top;
                rRect[8] = rIntRect.left; rRect[9] = rIntRect.bottom;
                Move(hdcWin, iWindowXMin, iWindowYMin);
                PolyDraw(hdcWin, 5, &rRect[0]);
            }

			// Tracer les noms des axes
            switch(lWActive)
			{
				case XDC_WID_TOP : // Axes X (gauche > droite) et Z (bas > haut)
					cAxis = 'z'; ExtTextOut(hdcWin, (rExtRect.right - rExtRect.left) / 2 + 13, -3, 0, (RECT *) NULL, &cAxis, 1, (int *) NULL);
					cAxis = 'x'; ExtTextOut(hdcWin, rExtRect.right - XDC_G_LARG + 5, (rExtRect.bottom - rExtRect.top) / 2 + 4, 0, (RECT *) NULL, &cAxis, 1, (int *) NULL);
					break;

				case XDC_WID_FACE : // Axes X (gauche > droite) et Y (bas > haut)
					cAxis = 'y'; ExtTextOut(hdcWin, (rExtRect.right - rExtRect.left) / 2 + 13, -3, 0, (RECT *) NULL, &cAxis, 1, (int *) NULL);
					cAxis = 'x'; ExtTextOut(hdcWin, rExtRect.right - XDC_G_LARG + 5, (rExtRect.bottom - rExtRect.top) / 2 + 4, 0, (RECT *) NULL, &cAxis, 1, (int *) NULL);
					break;

				case XDC_WID_SIDE : // Axes Z (gauche > droite) et Y (bas > haut)
					cAxis = 'y'; ExtTextOut(hdcWin, (rExtRect.right - rExtRect.left) / 2 + 13, -3, 0, (RECT *) NULL, &cAxis, 1, (int *) NULL);
					cAxis = 'z'; ExtTextOut(hdcWin, rExtRect.right - XDC_G_LARG + 5, (rExtRect.bottom - rExtRect.top) / 2 + 4, 0, (RECT *) NULL, &cAxis, 1, (int *) NULL);
					break;
		    }

			// Ajouter tous les gadgets Sculpt
            XDM_Bouton(pWT -> Widgets[XDC_GID_AddPoint], rExtRect.left, rExtRect.top + 11);
			XDM_Bouton(pWT -> Widgets[XDC_GID_Center], XDC_G_LARG + 6, rExtRect.bottom - XDC_G_HAUT + 1);
			XDM_Bouton(pWT -> Widgets[XDC_GID_GoDown], (rExtRect.right - rExtRect.left) / 2 - 4, rExtRect.bottom - XDC_G_HAUT + 1);
			XDM_Bouton(pWT -> Widgets[XDC_GID_GoLeft], rExtRect.left, (rExtRect.bottom - rExtRect.top) / 2 + 6);
			XDM_Bouton(pWT -> Widgets[XDC_GID_GoUp], (rExtRect.right - rExtRect.left) / 2 - 4, rExtRect.top);
			XDM_Bouton(pWT -> Widgets[XDC_GID_GoRight], rExtRect.right - XDC_G_LARG + 1, (rExtRect.bottom - rExtRect.top) / 2 + 6);
			XDM_Bouton(pWT -> Widgets[XDC_GID_Grab], rExtRect.left, rExtRect.bottom - XDC_G_HAUT + 1);
			XDM_Bouton(pWT -> Widgets[XDC_GID_MakeTri], rExtRect.left, rExtRect.bottom - 19);
			XDM_Bouton(pWT -> Widgets[XDC_GID_Reverse], rExtRect.right - XDC_G_LARG + 1, 26);
			XDM_Bouton(pWT -> Widgets[XDC_GID_RotAnti], rExtRect.left, rExtRect.top);
			XDM_Bouton(pWT -> Widgets[XDC_GID_RotClock], XDC_G_LARG + 6, rExtRect.top);
			XDM_Bouton(pWT -> Widgets[XDC_GID_ZoomIn], rExtRect.right - 36, rExtRect.bottom - XDC_G_HAUT + 1);
			XDM_Bouton(pWT -> Widgets[XDC_GID_ZoomOut], rExtRect.right - XDC_G_LARG + 1, rExtRect.bottom - 20);

            // Définir la zone de clipping : rectangle interne
            pWT -> hClip = ClipWindow(hWnd,
                rExtRect.left,
                rExtRect.top,
                rExtRect.right,
                rExtRect.bottom);

            // Blitter la fenêtre dans le double buffer
            ClipBlit(hdcWin, hWnd -> BorderLeft, hWnd -> BorderTop,
                     pWT -> hdcDBuf, hWnd -> BorderLeft, hWnd -> BorderTop,
                     iX - hWnd -> BorderRight - 1, iY - hWnd -> BorderBottom - 1,
                     0xC0);

            // Déterminer s'il faut réallouer le triple buffer :
            // - s'il n'est pas déjà alloué ou
            // - s'il est plus petit que la fenêtre redimensionnée ou
            // - s'il est plus grand que les trois fenêtres
            {
                int iXMax = 0, iYMax = 0;
                if (iXMax < hWndTop -> Width) iXMax = hWndTop -> Width;
                if (iXMax < hWndFace -> Width) iXMax = hWndFace -> Width;
                if (iXMax < hWndRight -> Width) iXMax = hWndRight -> Width;
                if (iYMax < hWndTop -> Height) iYMax = hWndTop -> Height;
                if (iYMax < hWndFace -> Height) iYMax = hWndFace -> Height;
                if (iYMax < hWndRight -> Height) iYMax = hWndRight -> Height;
                iX = GetBitMapAttr(hbTBuf, BMA_WIDTH);
                iY = GetBitMapAttr(hbTBuf, BMA_HEIGHT);

                if (!hbTBuf || iX != iXMax || iY != iYMax)
                {
                    InitRastPort(hdcTBuf);
                    if (hbTBuf) FreeBitMap(hbTBuf);

                    // Créer le bitmap offscreen du triple buffer
                    if (!(hbTBuf = AllocBitMap(iXMax, iYMax, 8, BMF_MINPLANES | BMF_DISPLAYABLE, hWnd -> RPort -> BitMap)))
                    {
                        vTrace("*** E0076 : allocation triple buffer %d x %d", iXMax, iYMax);
                        break;
                    }
                    hdcTBuf -> BitMap = hbTBuf;
                    vTrace("%s : Redim. triple buffer %d x %d ok", hWnd -> Title, iXMax, iYMax);
                }
            }

			// Forcer le redessin complet de la fenêtre
			PostMessage(hWnd, WM_PAINT, 0, (LPARAM) XDC_MODE_COMPLET);

            break;

		case WM_COMMAND: // Rediriger les frappes clavier et chois menus vers la fenêtre menus
		case WM_CHAR :
			PostMessage(hWndMenu, uMsg, wParam, lParam );
			break;

        case WM_DESTROY:
			// Libérer les objets GDI
			if (hpenWhite)		{ DeleteObject(hInst -> ViewPort.ColorMap, hpenWhite);		hpenWhite = NULL; }
			if (hpenRed)		{ DeleteObject(hInst -> ViewPort.ColorMap, hpenRed);		hpenRed = NULL; }
			if (hpenYellow)		{ DeleteObject(hInst -> ViewPort.ColorMap, hpenYellow);		hpenYellow = NULL; }
			if (hpenCyan)		{ DeleteObject(hInst -> ViewPort.ColorMap, hpenCyan);		hpenCyan = NULL; }
			if (hpenPink)		{ DeleteObject(hInst -> ViewPort.ColorMap, hpenPink);		hpenPink = NULL; }
			if (hpenGrey)		{ DeleteObject(hInst -> ViewPort.ColorMap, hpenGrey);		hpenGrey = NULL; }
			if (hpenLtBlueSk)	{ DeleteObject(hInst -> ViewPort.ColorMap, hbrLtBlueSk);	hbrLtBlueSk = NULL; }
			if (hpenDkBlueSk)	{ DeleteObject(hInst -> ViewPort.ColorMap, hpenDkBlueSk);	hpenDkBlueSk = NULL; }

            // Libérer le clipping de la fenêtre s'il est activé
            if (pWT -> hClip != (struct Region *) ~NULL)
            {
                UnclipWindow(hWnd);
                pWT -> hClip = (struct Region *) ~NULL;
            }

            // Supprimer le bitmap offscreen du double buffer s'il est alloué
            if (pWT -> hbDBuf) FreeBitMap(pWT -> hbDBuf);

			// Détruire la structure de données attachée à la fenêtre
			free(pWT);

            // Supprimer le bitmap offscreen du triple buffer s'il est alloué
            if (hbTBuf)
            {
                FreeBitMap(hbTBuf);
                hbTBuf = NULL;
            }

            PostQuitMessage(0);
            return 0L;
    }
    return 0;
}

