/* Affiche une bitmap, plaquée sur polygone convexe (affiche un polygone par plaquage de texture). "Convexe" signifie que toute droite horizontale affichée dans le polygone en tout point devrait couper deux cotés actifs (ni les droites horizontales ni les cotés de longueur nulle ne sont considérés comme cotés actifs; ils sont acceptables n'importe où dans le polygone), et les cotés droit & gauche ne se coupent jamais. Les polygones Non convexes ne peuvent pas être correctement affichés.*/ #include #include #include "polygon.h" /* Décrit l'emplacement courant et l'avancée dans la source et la destination d'un coté */ typedef struct { int Direction; /* via la liste des cotés: 1 pour le coté droit (avance dans la liste des points), -1 pour le coté gauche (recule dans la liste des points) */ int RemainingScans; /* hauteur restante à parcourir dans destination */ int CurrentEnd; /* numéro de fin du point du coté courant */ Fixedpoint SourceX: /* emplacement X courant de coté dans la source */ Fixedpoint SourceY-,/* emplacement Y courant de coté dans la source */ Fixedpoint SourceStepX; /* avancée de X dans la source pour l'avancée de Y de 1 dans destination */ Fixedpoint SourceStepY; /* avancée de Y dans la source pour l'avancée de Y de 1 dans destination /* les variables utilisées pour l'avancée de X de type Bresenham dans destination, dues au placement précis des pixels pour éviter des trous */ int DestX; /* emplacement X courant de ce coté dans destination*/ int DestXIntStep; /*toute la partie de l'avancée X dans destination par l'avancée Y de ligne d'affichage */ int DestXDirection; /*-1 ou 1 pour indiquer le sens de l'avancée X (gauche/ droite) */ int DestXErrTerm; /*terme d'erreur courant de l'avancée X dans destination*/ int DestXAdjUp; /*somme à ajouter au terme d'erreur par déplacement de ligne d'affichage */ int DestXAdiDown; /* somme à soustraire au terme d'erreur quand ce dernier déborde */ }EdgeScan; int StepEdge(EdgeScan*); int SetUpEdge(EdgeScan *, int); void ScanOutLine(EdgeScan , EdgeScan*); int GetImagePixel(char *, int, int, int); /* Statics pour économiser du temps qui serait sinon dépensé dans les routines. */ static int MaxVert, NumVerts, DestY; static Point * VertexPtr; static Point * TexVertsPtr; static char * TexMapBits; static int TexMapWidth; /* Affiche un polygone par plaquage de texture en fonction d'une liste des points du polygone destination, d'une liste de points de la texture source correspondant et d'un pointeur sur le descripteur de la texture source. */ void DrawTexturedPolygon(PointListHeader * Polygon, Point TexVerts, TextureMap * TexMap) { int MinY, MaxY. MinVert, i; EdgeScan LeftEdge, RightEdge; NumVerts = Polygon->Length; VertexPtr = Polygon->PointPtr; TexVertsPtr = TexVerts; TexMapBits = TexMap->TexMapBits; TexMapWidth = TexMap->TexMapWidth; /* Rien à afficher s'il y a moins de 3 points */ if (NumVerts < 3) return; } /* Parcourt les points du polygone destination et trouve les cotés supérieur et droit, bénéficie du fait que les points avancent dans le sens des aiguilles d'une montre (autrement ce polygone ne serait pas visible en raison de la suppression de la face cachée) MinY = 32767; MaxY = -32768; for (i=0; i MaxY) MaxY = VertexPtr[i].Y; MaxVert = i; } } /*Rejette les polygones plats (dont la hauteur est 0 pixel) if (MinY >- MaxY) { return; } /*La cordonnée Y n'est pas spécifique à un coté; elle s'applique aux deux cotés, car nous avançons toujours Y de 1 */ DestY = MinY; /* Configure pour parcourir les cotés gauche et droit des polygones source et destination. Nous avançons toujours les cotés du polygone destination de un dans Y, donc nous calculons l'avancée X correspondant à la destination pour chaque coté, puis les avancées X et Y correspondant à l'image source. */ LeftEdge.Direction = -1; /* configure en premier lieu le coté gauche */ SetUpEdge(&LeftEdge, MinVert); RightEdge.Direction = 1; /* configure le coté droit SetUpEdge(&RightEdge, MinVert); /* Descend les cotés destination ligne d'affichage par ligne d'affichage. A chaque ligne d'affichage, trouve les points du coté correspondant dans l'image source. Parcourt entre les points des cotés dans la source, affichant les pixels correspondant via la ligne d'affichage dans le polygone destination (Nous connaissons le sens de l'avancée des cotés gauche et droit dans la liste de points, car les polygones visibles (sans suppression de face cachée) avancent toujours dans le sens des aiguilles d'une montre */ for ( ;;) { /* Terminé si à l'extérieur du bas du rectangle de clipping if (DestY >= ClipMaxY) { return; } /* Affiche si et seulement si à l'intérieur des limites Y du rectangle de clipping if (DestY >-= clipMinY) 1 /* Affiche la ligne d'affichage entre les deux cotés ScanOutLine(&LeftEdge, &RightEdge); } /*Avance les cotés des polygones source et destination, en terminant si nous avons tout parcouru en direction du bas du polygone if (!StepEdge(&LeftEdge)) { break; { if (!StepEdge(&RightEdge)) { break; } DestY++; } } /* Avance pour un coté d'une ligne d'affichage dans destination, et d'une distance équivalente source. Si un coté déborde, commençons un nouveau s'il y en a un. Retourne 1 en cas de réussite, ou 0 s'il n'y a plus de coté à parcourir.*/ int StepEdge(EdgeScan * Edge) { /* Décompte la ligne d'affichage que nous avons parcouru en dernier; si le coté est terminé, essayons d'en commencer un autre */ if (--Edge->RemainingScans = 0) { /* Paramètre le nouveau coté; terminé s'il n'y en a pas d'autre * : if (SetUpEdge(Edge, Edge->CurrentEnd) == 0) { return(O); /* pas d'autre coté; l'affichage du polygone est terminé */ } return(l); /* tout est paramétré pour afficher le nouveau coté */ } /* Parcourt le coté source courant Edge->SourceX += Edge->SourceStepX; Edge->SourceY += Edge->SourceStepY; /* Parcourt X destination avec les variables de style Bresenham pour obtenir un placement de pixel précis et éviter les trous */ Edge->DestX += Edge->DestXIntStep; /* avance d'un pixel en entier */ /* Traite le terme d'erreur pour traiter l'avancée fractionnaire X du pixel*/ if ((Edge->DestXErrTerm +- Edge->DestXAdjUp) > 0) { Edge->DestX += Edge->DestXDirection; Edge->DestXErrTerm -= Edge->DestXAdjDown; } return(l); } /* Paramètre un coté à parcourir; le coté commence en StartVert et avance dans le sens Edge->Direction dans la liste de points. Edge->Direction doit être paramétré avant l'appel; -1 pour parcourir le coté gauche (recule dans la liste de points), 1 pour parcourir le coté droit (avance dans la liste de points). Saute automatiquement les cotés de hauteur nulle. Retourne 1 en cas de réussite, ou 0 s'il ne reste plus de coté à parcourir. */ int SetUpEdge(EdgeScan * Edge, int StartVert) { int NextVert, DestXWidth; Fixedpoint DestYHeight: for (;;) { /* Terminé si ce coté commence au point du bas if (StartVert == MaxVert) return(O); } /* Passe au prochain point, en bouclant si nous débordons du début ou de la fin de la liste de points */ NextVert = StartVert + Edge->Direction; if (NextVert >= NumVerts) { NextVert = 0: }else if (NextVert < 0) NextVert = NumVerts - 1; } /*Calcule les variables de ce coté et terminé s'il ne s'agit pas d'un coté de hauteur nulle */ if ((Edge->RemainingScans = VertexPtr[NextYert].Y - VertexPtr[StartVertl.Y) !- 0) { DestYHeight = INT_TO_FIXED(Edge->RemainingScans); Edge->CurrentEnd = NextVert; Edge->SourceX = INT_TO_FIXED(TexVertsPtr[StartVert].X); Edge->SourceY = INT_TO_FIXED(TexVertsPtr[StartVertl.Y); Edge->SourceStepX = FixedDiv(INT_TO_FIXED(TexVertsPtr[NextVert].X)- Edge->SourceX, DestYHeight); Edge->SourceStepY = FixedD!v(INT TO FIXED(TexVertsPtr[NextVertl.Y) - Edge->SourceY, DestYHeight); /* Paramètre les variables de style Bresenham pour l'avancée X de destination */ Edge->DestX = VertexPtr[StartVert].X: if ((DestXWidth = (VertexPtr[NextVert].X - VertexPtr[StartVert].X)) < 0){ /* Configure pour afficher de droite à gauche */ Edge->DestXDirection = -1; DestXWidth = -DestXWidth; Edge->DestXErrTerm = 1 - Edge->RemainingScans; Edge->DestXIntStep = -(DestXWidth / Edge->RemainingScans); }else ( /* Configure pour afficher de gauche à droite Edge->DestXDirection = 1; Edge->DestXErrTerm = 0: Edge->DestXIntStep = DestXWidth / Edge->RemainingScans; } Edge->DestXAdjUp = DestXWidth % Edge->RemainingScans; Edge->DestXAdjDown = Edge->RemainingScans; return(l); /* réussite */ } StartVert = NextVert; /* recherche un coté dont la hauteur n'est pas */ } } /* Affiche par plaquage de texture la ligne d'affichage entre les deux cotés.*/ void ScanOutLine(EdgeScan * LeftEdge, EdgeScan * RightEdge) { Fixedpoint SourceX = LeftEdge->SourceX; Fixedpoint SourceY = LeftEdge->SourceY; int DestX = LeftEdge->DestX; int DestXMax = RightEdge->DestX; Fixedpoint DestWidth; Fixedpoint SourceXStep, SourceYStep; /*Nous n'avons rien à faire si X est complètement clippé if ((DestXMax <- ClipMinX) Il (DestX >= ClipMaxX)) } return; } if ((DestXMax = DestX) <- 0) return; /* rien à afficher } /* Largeur de la ligne d'affichage de destination pour la correspondance. Note: comme il s'agit ici d'une correspondance basée sur un entier, il peut se produire une erreur totale de quasiment un pixel. Pour une correspondance bien plus précise, maintenir un DestX en virgule fixe dans chaque coté, et employez-le pour la correspondance. Si fait, il sera également nécessaire de mettre les coordonnées de départ de la source vers la droite en quantité correspondante à la distance entre le DestX (en virgule fixe) et le premier pixel (à un entier X)à afficher */ DestWidth = INT_TO_FIXED(DestXMax = DestX); /* Calcule l'avancée de la source qui correspond à chaque avancée X destination (dans la ligne d'affichage) */ SourceXStep = FixedDiv(RightEdge->SourceX - SourceX, DestWidth); SourceYStep = FixedDiv(RightEdge->SourceY - SourceY. DestWidth); /* Clippe le coté droit si nécessaire if (DestXMax > ClipMaxX){ DestXMax = ClipMaxX; } /* Clippe le coté gauche si nécessaire if (DestX < ClipMinX) { SourceX += SourceXStep * (ClipMinX DestX); SourceY += SourceYStep * (ClipMinX DestX); DestX = ClipMinX; } /* Parcourt la ligne d'affichage destination, en mettant à jour l'emplacement de l'image conjointement */ for (; DestX