#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>

// Includes utilitaires D3D
#include "d3dmath.h"
#include "d3dutil.h"
#include "D3DEnum.h"

#include <d3dx.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"

#define XDC_CLIPPINGDIST    (4.f)

static W3D_Context *hW3DC_3D;                               // Contexte Warp3D associé à la fenêtre 3D
static HBITMAP hbDBuf_3D;                                   // Bitmap offscreen du double buffer
static DC dcDBuf_3D, *hdcDBuf_3D = &dcDBuf_3D;              // RastPort du double buffer
static BOOL bZBuf = FALSE;                                  // TRUE si on est arrivé à allouer le ZBuf dans le contexte W3D, FALSE sinon
static int iWidth, iHeight;                                 // Mémoire largeur / hauteur pour ne rien faire si NEWSIZE avec mêmes dimensions
static long lError;
static BOOL bImage;                                         // TRUE si une image est OK dans le double buffer, FALSE sinon
static W3D_Triangle sTriangle;
static W3D_Double dZdepth = 1.;
static W3D_Double fLeftEdge, fTopEdge, fW, fH, fXMed, fYMed;
static int iLeftEdge, iTopEdge, iW, iH;
static W3D_Vertex sV[4];
static W3D_Triangles sTs; 
static W3D_Color sC = {0., 0., 0., 1.};
static W3D_Lines sLs;
static W3D_Point sP;

static inline void vShow3DFrame(void)
{
    if (!bImage) return;

    ClipBlit(hdcDBuf_3D, iLeftEdge, iTopEdge, hWndPersp -> RPort, iLeftEdge, iTopEdge, iW, iH, 0xc0);
}

// Versions plus rapides que celle de D3Dmath

static inline void vFastVectorMatrixMultiply(D3DVECTOR *vDest, D3DVECTOR *vSrc, D3DMATRIX *mat) // !!! vDest != vSrc !!!
{
    FLOAT w = vSrc -> x * mat -> _m._14 + vSrc -> y * mat -> _m._24 + vSrc -> z *  mat -> _m._34 + mat -> _m._44;

    vDest -> x = (vSrc -> x * mat -> _m._11 + vSrc -> y * mat -> _m._21 + vSrc -> z *  mat -> _m._31 + mat -> _m._41) / w;
    vDest -> y = (vSrc -> x * mat -> _m._12 + vSrc -> y * mat -> _m._22 + vSrc -> z *  mat -> _m._32 + mat -> _m._42) / w;
    vDest -> z = (vSrc -> x * mat -> _m._13 + vSrc -> y * mat -> _m._23 + vSrc -> z *  mat -> _m._33 + mat -> _m._43) / w;
}

static inline void vFastNormalize(register D3DVECTOR *vDest, register D3DVECTOR *vSrc)
{
   register FLOAT fNorm = sqrt(vSrc -> x * vSrc -> x + vSrc -> y * vSrc -> y + vSrc -> z * vSrc -> z);
   vDest -> x = vSrc -> x / fNorm;
   vDest -> y = vSrc -> y / fNorm;
   vDest -> z = vSrc -> z / fNorm;
}

static void vRender3DEnvironment(void)
{
    bImage = FALSE;

    // Verrouiller le hardware 3D
    if (!hW3DC_3D || !hbDBuf_3D || W3D_SUCCESS != (lError = W3D_LockHardware(hW3DC_3D)))
    {
         vTrace(GetCatalogStr(hCat, MSG_0330, "*** E0067 : vue 3D : lock hardware (contexte @%08lX, code %ld, bm %08lX)"), hW3DC_3D, lError, hbDBuf_3D);
         return;
    }

    // Clearer le zbuf
    if (dZBuf == D3DZB_TRUE && bZBuf) W3D_ClearZBuffer(hW3DC_3D, &dZdepth);

	// Set Drawing region
	sScissor.left   = (int) fLeftEdge;
    sScissor.top    = (int) fTopEdge;
    sScissor.width  = (int) fW;
    sScissor.height = (int) fH;
    W3D_SetDrawRegion(hW3DC_3D, NULL, 0, &sScissor);

    // Calculer le produit des matrices view / proj
    D3DMath_MatrixMultiply(matWorld, matView, matProj);

    // Gouraud shading & zbuffer off
    W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_DISABLE);
    W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_DISABLE);

	// Remplir le fond
    WSetPen(hW3DC_3D, cBack);
    WRectFill(hW3DC_3D, 0, 0, fW - 1, fH - 1);

    // Gouraud shading & zBuffer on
    W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_ENABLE);
    if (dZBuf == D3DZB_TRUE && bZBuf) W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_ENABLE);

    // Calculer le vecteur normalisé observateur / cible (pour le lighting simpliste)
    D3DVECTOR vObs = Normalize(Observer - Target);

    // Dessiner les triangles
    for (register int iTriangle = 0 ; iTriangle <= iTriaLastUsed ; iTriangle++)
    {
        // Déréférencer le triangle
        register gTri *hTri = &Triangles[iTriangle];

        // Si le triangle n'est pas enabled ou est caché alors ne rien faire
        if (!hTri -> bEnabled) continue;
        if (hTri -> bHidden)   continue;

        D3DVECTOR
            // Déréférencer les 3 sommets du triangle
            v0_UnT = Vertices[hTri -> iSommets[0]].vPoint,
            v1_UnT = Vertices[hTri -> iSommets[1]].vPoint,
            v2_UnT = Vertices[hTri -> iSommets[2]].vPoint,
            // Transformés en coordonnées homogènes des 3 vecteurs ci-dessus
            v0, v1, v2;

        // Sommets copiés pour dessiner l'outline du triangle s'il est en cours de texturage
        W3D_Vertex vl0, vl1, vl2;

        // Déréférencer le material du triangle
        register gMtrl *hMat = &(Materials[hTri -> iMtrl]);
        D3DMATERIAL7 *hMtrl = &(hMat -> mtrl);

        // CLIPPING, step 1 : éliminer les triangles dont au moins un des sommets est trop proche de la caméra
        if (    SquareMagnitude(v0_UnT - Observer) < XDC_CLIPPINGDIST
             || SquareMagnitude(v1_UnT - Observer) < XDC_CLIPPINGDIST
             || SquareMagnitude(v2_UnT - Observer) < XDC_CLIPPINGDIST
           ) continue;

        // TRANSFORM, step 1 : Transformation coordonnées 3D vers [-0.5..+0.5] en utilisant la concaténation des matrices View et Proj
        vFastVectorMatrixMultiply(&v0, &v0_UnT, &matWorld);
        vFastVectorMatrixMultiply(&v1, &v1_UnT, &matWorld);
        vFastVectorMatrixMultiply(&v2, &v2_UnT, &matWorld);

        // CLIPPING, step 2 : élimination des triangles en dehors du range de zbuf qu'on veut dessiner
        if (    v0.z < 0.f || v0.z > 1.f
             || v1.z < 0.f || v1.z > 1.f
             || v2.z < 0.f || v2.z > 1.f
           ) continue;

        // CLIPPING, step 3 : élimination des triangles complètement hors champ
        // (le reste est laissé à Warp3D, ce qui n'est pas forcément génial)
        if (    (v0.x < -0.5f && v1.x < -0.5f && v2.x < -0.5f) // complètement à gauche
             || (v0.x >  0.5f && v1.x >  0.5f && v2.x >  0.5f) // complètement à droite
             || (v0.y < -0.5f && v1.y < -0.5f && v2.y < -0.5f) // complètement en haut
             || (v0.y >  0.5f && v1.y >  0.5f && v2.y >  0.5f) // complètement en bas
           ) continue;

        // TRANSFORM, step 2 : ajustement du span X/Y dans fenêtre 3D
        sTriangle.v1.x = fXMed + v0.x * fW;
        sTriangle.v1.y = fYMed - v0.y * fH;
        sTriangle.v2.x = fXMed + v1.x * fW;
        sTriangle.v2.y = fYMed - v1.y * fH;
        sTriangle.v3.x = fXMed + v2.x * fW;
        sTriangle.v3.y = fYMed - v2.y * fH;
        sTriangle.v1.z = v0.z;
        sTriangle.v2.z = v1.z;
        sTriangle.v3.z = v2.z;

        // LIGHT, step 1 : calculer les vecteurs normaux sur les trois sommets
        D3DVECTOR
			v01 = v1_UnT - v0_UnT,
			v02 = v2_UnT - v0_UnT,
			v12 = v2_UnT - v1_UnT,
            vn0, vn1, vn2;

		    vn0 = CrossProduct(v01, v02),
            vn1 = CrossProduct(v01, v12),
            vn2 = CrossProduct(v02, v12);
            vFastNormalize(&vn0, &vn0),
            vFastNormalize(&vn1, &vn1),
            vFastNormalize(&vn2, &vn2);

        // Déréférencer les 3 couleurs à calculer
        W3D_Color *c1 = &sTriangle.v1.color,
                  *c2 = &sTriangle.v2.color,
                  *c3 = &sTriangle.v3.color;

        // Pour l'Alpha, seule la diffuse est prise en compte, et c'est la même pour les 3 sommets
        c1 -> a =
        c2 -> a =
        c3 -> a = hMtrl -> diffuse.a;

        // On a 2 modes de lighting :
        //  A - simpliste : 1 seule lampe, à la place de la caméra ; produit avec la seule composante diffuse du matériau
        if (!bLightFull)
        {
            // LIGHT, step 2a : Calculer les facteurs de pondération pour les 3 sommets : cos(angle vecteur regard / normale)
            FLOAT
                f0 = fabs(DotProduct(vObs, vn0)),
                f1 = fabs(DotProduct(vObs, vn1)),
                f2 = fabs(DotProduct(vObs, vn2));

            // LIGHT, step 3a : moduler la couleur en fonction de la pondération calculée
            FLOAT r = hMtrl -> diffuse.r,
                  g = hMtrl -> diffuse.g,
                  b = hMtrl -> diffuse.b;

            c1 -> r = r * f0; c1 -> g = g * f0; c1 -> b = b * f0;
            c2 -> r = r * f1; c2 -> g = g * f1; c2 -> b = b * f1;
            c3 -> r = r * f2; c3 -> g = g * f2; c3 -> b = b * f2;
        }
        else
        //  B - complet : prise en compte de toutes les lampes et de toutes les composantes lampe / material
        {
            // LIGHT, step 2b : pré accumuler dans les RGB des 3 sommets la somme (material emissive + material ambient * ambient light)
            FLOAT r0 = hMtrl -> emissive.r + hMtrl -> ambient.r  * WL_RED(cAmbient),        r1 = r0, r2 = r0,
                  g0 = hMtrl -> emissive.g + hMtrl -> ambient.g  * WL_GREEN(cAmbient),      g1 = g0, g2 = g0,
                  b0 = hMtrl -> emissive.b + hMtrl -> ambient.b  * WL_BLUE(cAmbient),       b1 = b0, b2 = b0;

            // LIGHT, step 3b : ajouter la contribution de chaque lampe ...
            for (int iLamp = 0 ; iLamp <= iLampLastUsed ; iLamp++)
            {
                // Déférérencer la lampe
                D3DLIGHT7 *hLamp = &(Lampes[iLamp].lLamp);

                // Si la lampe n'est pas enabled ni allumée, ne rien faire
                if (!Lampes[iLamp].bEnabled) continue;
                if (!Lampes[iLamp].bLit) continue;

                FLOAT
                    // LIGHTING, step 4b : calculer les 3 pondérations : cos(lampe/sommet, normale au sommet)
                    f0 = fabs(DotProduct(Normalize(v0_UnT - hLamp -> dvPosition), vn0)),
                    f1 = fabs(DotProduct(Normalize(v1_UnT - hLamp -> dvPosition), vn1)),
                    f2 = fabs(DotProduct(Normalize(v2_UnT - hLamp -> dvPosition), vn2)),

                    // LIGHTING, step 5b : calculer les 3 distances lampe / sommet
                    d0 = Magnitude(v0_UnT - hLamp -> dvPosition),
                    d1 = Magnitude(v1_UnT - hLamp -> dvPosition),
                    d2 = Magnitude(v2_UnT - hLamp -> dvPosition),

                    // LIGHTING, step 6b : calculer les 3 facteurs d'atténuation polynomiale (0, 1, 2)
                    a0 = 1. / (hLamp -> dvAttenuation0 + d0 * hLamp -> dvAttenuation1 + d0 * d0 * hLamp -> dvAttenuation2),
                    a1 = 1. / (hLamp -> dvAttenuation0 + d1 * hLamp -> dvAttenuation1 + d1 * d1 * hLamp -> dvAttenuation2),
                    a2 = 1. / (hLamp -> dvAttenuation0 + d2 * hLamp -> dvAttenuation1 + d2 * d2 * hLamp -> dvAttenuation2),

                    // LIGHTING, step 7b : calculer les contributions RGB diffuse de la lampe (lampe * material) non atténuées
                    ldr = hMtrl -> diffuse.r * hLamp -> dcvDiffuse.r,
                    ldg = hMtrl -> diffuse.g * hLamp -> dcvDiffuse.g,
                    ldb = hMtrl -> diffuse.b * hLamp -> dcvDiffuse.b,

                    // LIGHTING, step 8b : calculer les contributions RGB ambient de la lampe (lampe * material) non atténuées
                    lar = hMtrl -> ambient.r * hLamp -> dcvAmbient.r,
                    lag = hMtrl -> ambient.g * hLamp -> dcvAmbient.g,
                    lab = hMtrl -> ambient.b * hLamp -> dcvAmbient.b;

                // LIGHTING, steb 9b : accumuler les contributions diffuse et ambient
                r0 += (ldr * f0 + lar) * a0;
                r1 += (ldr * f1 + lar) * a1;
                r2 += (ldr * f2 + lar) * a2;

                g0 += (ldg * f0 + lag) * a0;
                g1 += (ldg * f1 + lag) * a1;
                g2 += (ldg * f2 + lag) * a2;

                b0 += (ldb * f0 + lab) * a0;
                b1 += (ldb * f1 + lab) * a1;
                b2 += (ldb * f2 + lab) * a2;
            }

            // LIGHTING, step 10b : caper les couleurs avec 1. (devrait être 1 mais sur Permedia 2 on retombe à noir au dessus de 0.998)
            c1 -> r = min(0.998f, r0); c1 -> g = min(0.998f, g0); c1 -> b = min(0.998f, b0);
            c2 -> r = min(0.998f, r1); c2 -> g = min(0.998f, g1); c2 -> b = min(0.998f, b1);
            c3 -> r = min(0.998f, r2); c3 -> g = min(0.998f, g2); c3 -> b = min(0.998f, b2);
        }

        // Si le triangle est celui en cours de texturage, mémoriser les sommets pour en dessiner ensuite le contour (après avoir dessiné le triangle)
        if (iTriangle == iTriaHilit)
        {
            vl0 = sTriangle.v1;
            vl1 = sTriangle.v2;
            vl2 = sTriangle.v3;
        }

        // Si le matériau ext texturé et que la texture est valide, régler U/V et mettre le texmapping ON
        if (hMat -> bTextured && (sTriangle.tex = Textures[hMat -> iTexture].hTexture))
        {
        	// Set Texture mapping on
        	W3D_SetState(hW3DC_3D, W3D_TEXMAPPING, W3D_ENABLE);

            // Affecter les u & v
            sTriangle.v1.u = hTri -> u[0];  sTriangle.v1.v = hTri -> v[0];
            sTriangle.v2.u = hTri -> u[1];  sTriangle.v2.v = hTri -> v[1];
            sTriangle.v3.u = hTri -> u[2];  sTriangle.v3.v = hTri -> v[2];

            // Calculer les w pour la correction de perspective
            sTriangle.v1.w = 1. - v0.z;
            sTriangle.v2.w = 1. - v1.z;
            sTriangle.v3.w = 1. - v2.z;
        }

        switch(dFillMode)
        {
            case D3DFILL_SOLID :        // Facettes (gouraud ou texture)
               W3D_DrawTriangle(hW3DC_3D, &sTriangle);
               break;

            case D3DFILL_WIREFRAME :    // Fil de fer
                sLs.tex = sTriangle.tex;
                sV[0] = sV[3] = sTriangle.v1;
                sV[1] = sTriangle.v2;
                sV[2] = sTriangle.v3;
                W3D_DrawLineStrip(hW3DC_3D, &sLs);
                break;

            case D3DFILL_POINT :        // Points
                sP.tex = sTriangle.tex;
                sP.v1 = sTriangle.v1; W3D_DrawPoint(hW3DC_3D, &sP);
                sP.v1 = sTriangle.v2; W3D_DrawPoint(hW3DC_3D, &sP);
                sP.v1 = sTriangle.v3; W3D_DrawPoint(hW3DC_3D, &sP);
                break;
        }

        // Set Texture mapping off
        if (hMat -> bTextured && sTriangle.tex)	W3D_SetState(hW3DC_3D, W3D_TEXMAPPING, W3D_DISABLE);

        // Si le triangle est celui en cours d'édition, dessiner un contour jaune
        if (iTriangle == iTriaHilit)
        {
            // Arrêter le zbuf et le gouraud
            W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_DISABLE);
            W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_DISABLE);

            // Définir le jaune comme couleur flat
            WSetPen(hW3DC_3D, XDC_COL_GREEN);

            // Dessiner le contour du triangle
            sV[0] = sV[3] = vl0;
            sV[1] = vl1;
            sV[2] = vl2;
            W3D_DrawLineStrip(hW3DC_3D, &sLs);

            // Remettre le gouraud et le zbuf
            W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_ENABLE);
            if (dZBuf == D3DZB_TRUE && bZBuf) W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_ENABLE);
        }
    }

    // Déverrouiller le hardware 3D
    W3D_UnLockHardware(hW3DC_3D);

    // Mémoriser qu'on a bien créé une image dans le double buffer
    bImage = TRUE;

    // Blitter le double dans la fenêtre
    vShow3DFrame();
}

inline void vForce3DRefresh(BOOL bFull)
{
    if (!bActive) return;

	if (bFull == XDC_MODE_COMPLET)
		vRender3DEnvironment();
	else
		vShow3DFrame();
}

void vSetHints(void)
{
    W3D_Hint(hW3DC_3D, W3D_H_TEXMAPPING,        uHint[0]);
    W3D_Hint(hW3DC_3D, W3D_H_MIPMAPPING,        uHint[1]);
    W3D_Hint(hW3DC_3D, W3D_H_BILINEARFILTER,    uHint[2]);
    W3D_Hint(hW3DC_3D, W3D_H_MMFILTER,          uHint[3]);
    W3D_Hint(hW3DC_3D, W3D_H_PERSPECTIVE,       uHint[4]);
    W3D_Hint(hW3DC_3D, W3D_H_BLENDING,          uHint[5]);
    W3D_Hint(hW3DC_3D, W3D_H_FOGGING,           uHint[6]);
    W3D_Hint(hW3DC_3D, W3D_H_ANTIALIASING,      uHint[7]);
    W3D_Hint(hW3DC_3D, W3D_H_DITHERING,         uHint[8]);
    W3D_Hint(hW3DC_3D, W3D_H_ZBUFFER,           uHint[9]);
}

LRESULT CALLBACK lrPerspWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch( uMsg )
    {
        case WM_PAINT:
			// If we get WM_PAINT messages, it usually means our window was
			// covered up, so we need to refresh it by re-showing the contents
			// of the current frame.
			vForce3DRefresh(XDC_MODE_PARTIEL);
            break;

        case WM_CREATE:
            InitRastPort(hdcDBuf_3D);
            sTs.vertexcount = 4;
            sTs.v = sV;
            sLs.vertexcount = 4; sLs.v = sV; sLs.st_enable = FALSE; sLs.linewidth = 3.;
            sP.tex = NULL;
            sP.pointsize = 3.;

        case WM_SIZE:
            vTrace(GetCatalogStr(hCat, MSG_0331, "### Vue 3D : allocation double buffer / contexte Warp3D / z-buffer..."));
			{
				bReady = FALSE;
                // Si la taille n'a pas changé, ne rien faire
                if (iWidth == hWnd -> Width && iHeight == hWnd -> Height)
                    break;
                iWidth = hWnd -> Width;
                iHeight = hWnd -> Height;

                iLeftEdge = (int) (fLeftEdge = hWndPersp -> BorderLeft);
                iTopEdge = (int) (fTopEdge = hWndPersp -> BorderTop);
                iW = (int) (fW = hWndPersp -> Width - fLeftEdge - hWndPersp -> BorderRight);
                iH = (int) (fH = hWndPersp -> Height - fTopEdge - hWndPersp -> BorderBottom);
                fXMed = fLeftEdge + fW / 2.;
                fYMed = fTopEdge + fH / 2.;

                // Supprimer le contexte 3D s'il est déjà alloué
                if (hW3DC_3D)
                {
                    if (bZBuf)
                    {
                        W3D_FreeZBuffer(hW3DC_3D);
                        bZBuf = FALSE;
                    }
                    vCloseTextures(hW3DC_3D);
                    W3D_DestroyContext(hW3DC_3D);
                    hW3DC_3D = NULL;
                }

                // Supprimer le bitmap offscreen s'il est déjà alloué
                if (hbDBuf_3D)
                    FreeBitMap(hbDBuf_3D);

                // Créer le bitmap offscreen
                if (!(hbDBuf_3D = AllocBitMap(hWnd -> Width, hWnd -> Height, 8, BMF_MINPLANES | BMF_DISPLAYABLE, hWnd -> RPort -> BitMap)))
                {
                    vTrace(GetCatalogStr(hCat, MSG_0332, "*** E0068 : vue 3D : allocation double buffer"));
                    break;
                }

                // Attacher le bitmap au rastport
                dcDBuf_3D.BitMap = hbDBuf_3D;

            	// Créer le contexte W3D
                extern ULONG lModeID, lError;
                TagItem sTagList[] =
                {
                    { W3D_CC_MODEID,      lModeID },
                	{ W3D_CC_BITMAP,      (ULONG) hbDBuf_3D },
                	{ W3D_CC_YOFFSET,     0 },
                	{ W3D_CC_DRIVERTYPE,  W3D_DRIVER_BEST },
                	{ W3D_CC_FAST,        TRUE },
                	{ TAG_DONE,             TAG_DONE }
                };

                if (!(hW3DC_3D = W3D_CreateContext(&lError,sTagList)))
                {
                    vTrace(GetCatalogStr(hCat, MSG_0333, "*** E0069 : vue 3D : création contexte Warp3D (code %ld)"), lError);
                    FreeBitMap(hbDBuf_3D); hbDBuf_3D = NULL;
                    break;
                }

                // Charger les textures
                GenTextures(hW3DC_3D);

                // Set alpha blending mode
//                W3D_SetBlendMode(hW3DC_3D, W3D_SRC_ALPHA, W3D_ONE_MINUS_SRC_ALPHA);

                // Hints
                vSetHints();

            	// Set states pour le contexte 3D
            	W3D_SetState(hW3DC_3D, W3D_DITHERING,           W3D_ENABLE);
                W3D_SetState(hW3DC_3D, W3D_SCISSOR,             W3D_ENABLE);
                W3D_SetState(hW3DC_3D, W3D_PERSPECTIVE,         W3D_ENABLE);
                W3D_SetState(hW3DC_3D, W3D_AUTOTEXMANAGEMENT,   W3D_ENABLE);

                W3D_SetState(hW3DC_3D, W3D_BLENDING,            W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_SYNCHRON,            W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_INDIRECT,            W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_TEXMAPPING,          W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_FOGGING,             W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_LOGICOP,             W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_STENCILBUFFER,       W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_DOUBLEHEIGHT,        W3D_DISABLE);

                // Les 2 suivants n'ont pas besoin d'être réglés ici : gérés dans Render3D...()
#if 0
                W3D_SetState(hW3DC_3D, W3D_GOURAUD,             W3D_DISABLE);
                W3D_SetState(hW3DC_3D, W3D_ZBUFFER,             W3D_DISABLE);
#endif

            	// Allouer le ZBuffer
           	    if (W3D_AllocZBuffer(hW3DC_3D))
            	{
                    W3D_SetState(hW3DC_3D, W3D_ZBUFFERUPDATE, W3D_DISABLE);
                	vTrace(GetCatalogStr(hCat, MSG_0334, "*** E0047 : vue 3D : allocation ZBuffer -> pas de suppression faces cachées"));
                }
                else
                {
                    W3D_SetZCompareMode(hW3DC_3D, W3D_Z_LESS);
                    W3D_SetState(hW3DC_3D, W3D_ZBUFFERUPDATE, W3D_ENABLE);
                    bZBuf = TRUE;
                    vTrace(GetCatalogStr(hCat, MSG_0335, "### Vue 3D : allocation double buffer / contexte Warp3D / z-buffer OK"));
                }

              
				// Redessiner la 3D dans le double buffer, Intuition va de de toute façon nous envoyer un REFRESH qui nous donnera un PAINT qui nous fera blitter le double buffer dans la fenêtre
                vRender3DEnvironment();

				bReady = TRUE;
			}
            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:
            // Supprimer le contexte 3D
            if (hW3DC_3D)
            {
                if (bZBuf)
                {
                    W3D_FreeZBuffer(hW3DC_3D);
                    bZBuf = FALSE;
                }
                vCloseTextures(hW3DC_3D);
                W3D_DestroyContext(hW3DC_3D);
                hW3DC_3D = NULL;
            }

            // Supprimer le bitmap offscreen du double buffer
            if (hbDBuf_3D) FreeBitMap(hbDBuf_3D);

            PostQuitMessage(0);
            return 0L;
    }
#ifndef _AMIGA_
    return DefWindowProc( hWnd, uMsg, wParam, lParam );
#else
    return 0;
#endif
}

