// ----------------------------------------------------------- // NGCK - Exemple de 3D // On defini un monde 3D, avec des triangles et une camera. // Les triangles sont projettés dans le repere de la camera, puis sur l'ecran. // Et ensuite on affiche les triangles avec la technique du "scanline" (ligne par ligne, point par point) // // Le reparer choix est X (positif a droite) et Z (positif vers nous) pour le sol. Y pour l'altitude (vers le haut). // ----------------------------------------------------------- #include "../Ngck_moteur/Ngck.h" #include "math.h" // On utilise une structure pour les points 3D struct Vertex { float x; float y; float z; int yceiled; // For fill algorithm }; // On utilise un buffer local, pour pouvoir controler l'affichage des points hors de l'ecran. // Pour afficher un point, on appelle la fonction "DrawPoint" (elle testera si le point est sur l'écran ou pas). // Et on affiche tout d'un coup sur le vrai ecran. // Chaque point a le code couleur. 0=rien 1 a x ... le code couleur // On garde aussi en memoire la profondeur du point car on affiche toutes les faces et dans le desordre. // Il faut donc trier a un moment donné, on le fait au moment de stocker le point avec un "z buffer". unsigned char buffer[30][20]; float bufferz[30][20]; // 0 (near) to -10000 (far) // -- Position camera float camerax, cameray, cameraz; // Angle camera (only rotate around vertical axe). Here, it is Z. float cameraangle; // -- On utilise ces deux variables pour afficher plusieurs fois le meme cube, en le decalant en X et Z. float translatex; float translatez; int currentcolor; // 0 1 2 3 // -- Input Vertices Vertex AllVerts[8]; // Point de departs Vertex AllVertsRot[8]; // Points avec rotation Vertex AllVertsProj[8]; // Points avec rotation et projection (point ecran). void DrawTriangle(Vertex p1, Vertex p2, Vertex p3); // ----------------------------------------------------------- // Nom : InitialiseGame // ----------------------------------------------------------- void InitialiseGame() { camerax=0; cameray=0; cameraz=5.0f; translatex=0; translatez=0; cameraangle=180; // Degres // Defini les 8 points du cube // Le cube a une taille de 20 unités de large. AllVerts[0].x=-10.0f; AllVerts[0].y=10.0f; AllVerts[0].z=10.0f; AllVerts[1].x=-10.0f; AllVerts[1].y=-10.0f; AllVerts[1].z=10.0f; AllVerts[2].x=10.0f; AllVerts[2].y=-10.0f; AllVerts[2].z=10.0f; AllVerts[3].x=10.0f; AllVerts[3].y=10.0f; AllVerts[3].z=10.0f; AllVerts[4].x=-10.0f; AllVerts[4].y=10.0f; AllVerts[4].z=-10.0f; AllVerts[5].x=-10.0f; AllVerts[5].y=-10.0f; AllVerts[5].z=-10.0f; AllVerts[6].x=10.0f; AllVerts[6].y=-10.0f; AllVerts[6].z=-10.0f; AllVerts[7].x=10.0f; AllVerts[7].y=10.0f; AllVerts[7].z=-10.0f; } // ----------------------------------------------------------- // Nom : RotateVertices // Rotation d'un ensemble de points autours de l'axe Y (vertical). // On pourrait faire une rotation autours de 3 axes ici par exemple. // ----------------------------------------------------------- void RotateVertices(Vertex* AllVertsRot, Vertex* AllVerts, int nbvert) { int i; for (i=0; i 0 ) return; } // -- Affiche les triangles. currentcolor=1; DrawTriangle(AllVertsProj[0],AllVertsProj[2],AllVertsProj[1]); DrawTriangle(AllVertsProj[0],AllVertsProj[3],AllVertsProj[2]); currentcolor=2; DrawTriangle(AllVertsProj[3],AllVertsProj[6],AllVertsProj[2]); DrawTriangle(AllVertsProj[3],AllVertsProj[7],AllVertsProj[6]); currentcolor=3; DrawTriangle(AllVertsProj[7],AllVertsProj[5],AllVertsProj[6]); DrawTriangle(AllVertsProj[7],AllVertsProj[4],AllVertsProj[5]); currentcolor=4; DrawTriangle(AllVertsProj[4],AllVertsProj[1],AllVertsProj[5]); DrawTriangle(AllVertsProj[4],AllVertsProj[0],AllVertsProj[1]); } // ----------------------------------------------------------- // Nom : GameLoop // ----------------------------------------------------------- void GameLoop() { int i,j; // - Efface notre buffer écran. (et notre Z buffer) for (i=0; i<30; i++) { for (j=0; j<20; j++) { buffer[i][j]=0; bufferz[i][j]=-10000; } } // -- La camera tourne a chaque oucle. cameraangle+=5.0f; if ( cameraangle > 360.0f ) cameraangle -= 360.0f; // -- On modifie aussi la distance de la camera, pour donner un effet oscillant. float cameradistance; cameradistance = 45.0f + 30.0f*cosf(cameraangle*3.14f/180.0f); // -- A partir de l'angle, on calcul la position de la camera. // Ici notre camera tourne autours de notre scene. camerax = cameradistance*sinf(cameraangle*3.14f/180.0f); cameraz = cameradistance*cosf(cameraangle*3.14f/180.0f); // On calcule et affiche tous les cubes. // Cube 1 translatex=0; translatez=0; FillCube(); // Cube 2 translatex=30; translatez=30; FillCube(); // Cube 3 translatex=-50; translatez=0; FillCube(); // Cube 4 translatex=50; translatez=50; FillCube(); // Cube 5 translatex=100; translatez=-20; FillCube(); // Cube 6 translatex=-500; translatez=500; FillCube(); // - Affiche notre buffer qui est maintenant rempli for (i=0; i<30; i++) { for (j=0; j<20; j++) { if ( buffer[i][j]!=0) { if ( buffer[i][j]==1) KSetDisplayColor(WHITE); if ( buffer[i][j]==2) KSetDisplayColor(RED); if ( buffer[i][j]==3) KSetDisplayColor(GREEN); if ( buffer[i][j]==4) KSetDisplayColor(BLUE); KPrintSquare(i,j); } } } } // ----------------------------------------------------------- // Nom : DrawPoint // On "affiche" un point dans notre buffer // La fonction teste si le point est dans l'écran // Et si il n'y a pas deja un point devant. // ----------------------------------------------------------- void DrawPoint(int x,int y,float currentz) { if ( x<0 || x>29) return; if ( y<0 || y>19) return; if ( currentz >= bufferz[x][y] ) { buffer[x][y]=currentcolor; bufferz[x][y]=currentz; } } // ----------------------------------------------------------- // Nom : DrawTriangle // Affiche un triangle 2D (en coordonnées écran). // Avec la technique du "scanline". // ----------------------------------------------------------- void DrawTriangle(Vertex p1, Vertex p2, Vertex p3) { float xLeft; float xRight; float dxdy_left; float dxdy_right; if ( p1.x < -15 || p1.x > 30+15 || p1.y < -10 || p1.y > 20+10 ) return; if ( p2.x < -15 || p2.x > 30+15 || p2.y < -10 || p2.y > 20+10 ) return; if ( p3.x < -15 || p3.x > 30+15 || p3.y < -10 || p3.y > 20+10 ) return; float currentz; currentz = (p1.z+p2.z+p3.z)/3.0f; Vertex vtx[3]; vtx[0].x = p1.x; vtx[0].y = p1.y; vtx[1].x = p2.x; vtx[1].y = p2.y; vtx[2].x = p3.x; vtx[2].y = p3.y; int top = 0; int bottom = 0; int left = 1; int right = 2; // -- On echange la position des points pour avoir les points triés du haut vers le bas. if (vtx[1].y < vtx[0].y) { if (vtx[2].y < vtx[1].y) { top = 2; left = 0; right = 1; } else { top = 1; left = 2; right = 0; } } else { if (vtx[2].y < vtx[0].y) { top = 2; left = 0; right = 1; } else { top = 0; left = 1; right = 2; } } if (vtx[1].y > vtx[bottom].y) bottom = 1; if (vtx[2].y > vtx[bottom].y) bottom = 2; vtx[0].yceiled = ceilf(vtx[0].y); vtx[1].yceiled = ceilf(vtx[1].y); vtx[2].yceiled = ceilf(vtx[2].y); if (vtx[top].yceiled == vtx[bottom].yceiled) return; float height; float frac; float a, b; height = vtx[left].y - vtx[top].y; if (height != 0) { frac = ((float)vtx[top].yceiled) - vtx[top].y; a = vtx[left].x - vtx[top].x; b = height; dxdy_left = a / b; xLeft = vtx[top].x + dxdy_left*frac; } height = vtx[right].y - vtx[top].y; if (height != 0) { frac = ((float)vtx[top].yceiled) - vtx[top].y; a = vtx[right].x - vtx[top].x; b = height; dxdy_right = a / b; xRight = vtx[top].x + dxdy_right*frac; } int middle = vtx[left].yceiled; if (middle > vtx[right].yceiled) middle = vtx[right].yceiled; // -- Affiche la partie haute du triangle // Ligne par ligne (= "scanline") for (int y=vtx[top].yceiled; y