int DIBWidth, DIBHeight;int DIBPitch; double roll, pitch, yaw;double currentspeed; point_t currentpos; double fieldofview, xcenter, ycenter;double xscreenscale, yscreenscale, maxscale; int numobjects; double speedscale = 1.0; plane_t frustumplanes[NUM_FRUSTUM_PLANES]; double mroll[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; double mpitch[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; double myaw[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; point_t vpn, vright, vup; point_t xaxis = {1, 0, 0}; point_t zaxis = {0, 0, 1}; convexobject_t objecthead = {NULL, {0,0,0}, -999999.0}; // Projette les points du polygone de l'espace de visualisation en coordonnées. // d'écran. Notez que l'axe y monte dans l'espace monde et l'espace de // visualisation, mais descend dans l'espace écran. void ProjectPolygon (polygon_t *ppoly, polygon2D_t *ppoly2D) { int i; double zrecip; for (i=0 ; inumverts ; i++) { zrecip = 1.0 / ppoly->verts[i].v[2]; ppoly2D->verts[i].x = ppoly->verts[i].v[0] * zrecip * maxscale + xcenter; ppoly2D->verts[i].y = DIBHeight - (ppoly->verts[i].v[1] * zrecip * maxscale + ycenter); } ppoly2D->color = ppoly->color; ppoly2D->numverts = ppoly->numverts; } // Trie les objets en fonction de la distance z depuis le point de vue. void ZSortObjects(void) { int i, j; double vdist; convexobject_t *pobject; point_t dist; objecthead.pnext = &objecthead; for (i=0 ; ipnext->vdist) pobject = pobject->pnext; objects[i].pnext = pobject->pnext; pobject->pnext = &objects[i]; }} // Déplace la position de l'observateur et paramètre la transformation monde->observateur. void UpdateViewPos() { int i; point_t motionvec; double s, c, mtemp1[3][3], mtemp2[3][3]; // Se déplace dans le sens de l'observateur, en traversant le plan x-y, motionvec.v[0] = DotProduct(&vpn, &xaxis); motionvec.v[1] = 0.0; motionvec.v[2] = DotProduct(&vpn, &zaxis); for (i=0 ; i<3 ; i++) { currentpos.v[i] += motionvec.v[i] * currentspeed; if (currentpos.v[i] > MAX_COORD) currentpos.v[i] = MAX_COORD; if (currentpos.v[i] < -MAX_COORD) currentpos.v[i] = -MAX_COORD; } // Configure la rotation monde/visualisation. // Note: la majeure partie du travail est faite en concaténant ces matrices // se fait à l'extérieur, car elle ne contribue pas au résultat // final; multiplions les trois matrices pour // générer un minimum d'équation pour les 9 éléments finaux s = sin(roll); c = cos(roll); mroll[0][0] = c; mroll[0][1] = s; mroll[1][0] = -s; mroll[1][1] = c; s = sin(pitch); c = cos(pitch); mpitch[1][1] = c; mpitch[1][2] = s; mpitch[2][1] = -s; mpitch[2][2] = c; s = sin(yaw); c = cos(yaw); myaw[0][0] = c; myaw[0][2] = -s; myaw[2][0] = s; myaw[2][2] = c; MConcat(mroll, myaw, mtemp1); MConcat(mpitch, mtemp1, mtemp2); // Divise la matrice de rotation en vright, vup, et vpn. // Nous pourrions travailler directement avec la matrice; la diviser // en 3 vecteurs nous permet de clarifier l'opération for (i=0 ; i<3 ; i++) { vright.v[i] = mtemp2[0][i]; vup.v[i] = mtemp2[1][i]; vpn.v[i] = mtemp2[2][i]; } // Simule une friction simplifiée if (currentspeed > (MOVEMENT_SPEED * speedscale / 2.0)) currentspeed -= MOVEMENT_SPEED * speedscale / 2.0; else if (currentspeed < -(MOVEMENT_SPEED * speedscale / 2.0)) currentspeed += MOVEMENT_SPEED * speedscale / 2.0; else currentspeed = 0.0;} // Effectue la rotation d'un vecteur de l'espace de visualisation à l'espace monde. void BackRotateVector(point_t *pin, point_t *pout){ int i; // Effectue une rotation for (i=0 ; i<3 ; i++) pout->v[i] = pin->v[0] * vright.v[i] + pin->v[1] * vup.v[i] + pin->v[2] * vpn.v[i];} // Transforme un point de l'espace monde à l'espace de visualisation. void TransformPoint(point_t *pin, point_t *pout){ int i; point_t tvert; // Convertit en coordonnées relatives au point de vue for (i=0 ; i<3 ; i++) tvert.v[i] = pin->v[i] - currentpos.v[i]; // Effectue une rotation pout->v[0] = DotProduct(&tvert, &vright); pout->v[1] = DotProduct(&tvert, &vup); pout->v[2] = DotProduct(&tvert, &vpn);} // Transforme un polygone de l'espace monde à l'espace de visualisation. void TransformPolygon(polygon_t *pinpoly, polygon_t *poutpoly){ int i; for (i=0 ; inumverts ; i++) TransformPoint(&pinpoly->verts[i], &poutpoly->verts[i]); poutpoly->color = pinpoly->color; poutpoly->numverts = pinpoly->numverts;} // Retourne true si le polygone est face au point de vue, en supposant que les points // suivent le sens des aiguilles d'une montre, vu de dessus. int PolyFacesViewer(polygon_t *ppoly){ int i; point_t viewvec, edge1, edge2, normal; for (i=0 ; i<3 ; i++) { viewvec.v[i] = ppoly->verts[0].v[i] - currentpos.v[i]; edge1.v[i] = ppoly->verts[0].v[i] - ppoly->verts[1].v[i]; edge2.v[i] = ppoly->verts[2].v[i] - ppoly->verts[1].v[i]; } CrossProduct(&edge1, &edge2, &normal); if (DotProduct(&viewvec, &normal) > 0) return 1; else return 0;} // Configure un plan de clipping avec la normale spécifiée. void SetWorldspaceClipPlane(point_t *normal, plane_t *plane){ // Effectue la rotation de la normale du plan dans l'espace monde BackRotateVector(normal, &plane->normal); plane->distance = DotProduct(¤tpos, &plane->normal) + CLIP_PLANE_EPSILON;} // Configure les plans de la pyramide en coordonnées de l'espace monde. void SetUpFrustum(void){ double angle, s, c; point_t normal; angle = atan(2.0 / fieldofview * maxscale / xscreenscale); s = sin(angle); c = cos(angle); // Plan de clipping gauche normal.v[0] = s; normal.v[1] = 0; normal.v[2] = c; SetWorldspaceClipPlane(&normal, &frustumplanes[0]); // Plan de clipping droit normal.v[0] = -s; SetWorldspaceClipPlane(&normal, &frustumplanes[1]); angle = atan(2.0 / fieldofview * maxscale / yscreenscale); s = sin(angle); c = cos(angle); // Plan de clipping du bas normal.v[0] = 0; normal.v[1] = s; normal.v[2] = c; SetWorldspaceClipPlane(&normal, &frustumplanes[2]); // Plan de clipping du haut normal.v[1] = -s; SetWorldspaceClipPlane(&normal, &frustumplanes[3]);} // Clippe un polygone de la pyramide. int ClipToFrustum(polygon_t *pin, polygon_t *pout){ int i, curpoly; polygon_t tpoly[2], *ppoly; curpoly = 0; ppoly = pin; for (i=0 ; i<(NUM_FRUSTUM_PLANES-1); i++) { if (!ClipToPlane(ppoly, &frustumplanes[i], &tpoly[curpoly])) return 0; ppoly = &tpoly[curpoly]; curpoly ^= 1; } return ClipToPlane(ppoly, &frustumplanes[NUM_FRUSTUM_PLANES-1], pout); } // Rendu de l'état courant du monde à l'écran. void UpdateWorld(){ HPALETTE holdpal; HDC hdcScreen, hdcDIBSection; HBITMAP holdbitmap; polygon2D_t screenpoly; polygon_t *ppoly, tpoly0, tpoly1, tpoly2; convexobject_t *pobject; int i, j, k; UpdateViewPos(); memset(pDIBBase, 0, DIBWidth*DIBHeight); // clear frame SetUpFrustum(); ZSortObjects(); // affiche toutes les faces visibles dans tous les objets pobject = objecthead.pnext; while (pobject != &objecthead) { ppoly = pobject->ppoly; for (i=0 ; inumpolys ; i++) { // Déplace le polygone au centre de l'objet tpoly0.color = ppoly->color; tpoly0.numverts = ppoly->numverts; for (j=0 ; jverts[j].v[k] + pobject->center.v[k]; } if (PolyFacesViewer(&tpoly0)) { if (ClipToFrustum(&tpoly0, &tpoly1)) { TransformPolygon (&tpoly1, &tpoly2); ProjectPolygon (&tpoly2, &screenpoly); FillPolygon2D (&screenpoly); } } ppoly++; } pobject = pobject->pnext; } // Nous avons affiché la trame; copions-la à l'écran hdcScreen = GetDC(hwndOutput); holdpal = SelectPalette(hdcScreen, hpalDIB, FALSE); RealizePalette(hdcScreen); hdcDIBSection = CreateCompatibleDC(hdcScreen); holdbitmap = SelectObject(hdcDIBSection, hDIBSection); BitBlt(hdcScreen, 0, 0, DIBWidth, DIBHeight, hdcDIBSection, 0, 0, SRCCOPY); SelectPalette(hdcScreen, holdpal, FALSE); ReleaseDC(hwndOutput, hdcScreen); SelectObject(hdcDIBSection, holdbitmap); ReleaseDC(hwndOutput, hdcDIBSection); }