Voici un petit programme qui affiche un cube en 3D qui tourne, on peut l'afficher avec des faces pleines ou en ne montrant que les segments (le 'grillage'), on peut le bouger et zoomer avec la souris.
En gros cette source montre comment programmer :
- une rotation (désolé la j'ai directement mis les valeurs des sinus/cosinus en brut, c'est pas très clair)
- le remplissage d'un polygone convexe (faces remplies)
- tracer une ligne
- gestion de la souris
- projection orthogonale (la 3D quoi)
- enregistrement fichier PCX
- mode graphique VGA 13h, affichage de texte, palette,...
Source / Exemple :
#include <conio.h>
#include <dos.h>
#include <fcntl.h>
#include <go32.h>
#include <io.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/farptr.h>
#include <sys/stat.h>
#include <unistd.h>
#define PIXEL(x,y,c) ((((x)>=0)&&((x)<320)&&((y)>=0)&&((y)<200))?buffer[((y)<<8)+((y)<<6)+(x)]=(c):0)
typedef struct
{
double x;
double y;
double z;
} VERTEX;
int main(int argc,char **argv);
int savePCX();
void mouse();
void initGraph();
void stopGraph();
void display();
void drawCube(VERTEX* cube);
void scanLine(int x1,int y1,int x2,int y2,int* scanboard);
void fillFace(VERTEX a,VERTEX b,VERTEX c,int color);
void drawLine(VERTEX a,VERTEX b);
void writeText(char* text,int c,int posx,int posy);
char ascii[1024];
char buffer[64000];
double matrix[360][3][3];
VERTEX cube[8];
VERTEX O;
/* -------------------------- */
/* --- CORPS DU PROGRAMME --- */
/* -------------------------- */
int main(int argc,char **argv)
{
char str[23];
char key;
int i;
int param[4]={0,0,0,0};
VERTEX v;
initGraph();
do
{
/* --- AFFICHAGE --- */
for (i=63999 ; i>=0 ; i--) buffer[i]=0;
/* --- DESSINE LES FACES --- */
if (param[3]) drawCube(cube);
/* --- DESSINE LES SEGMENTS --- */
else
{
drawLine(cube[0],cube[1]);
drawLine(cube[1],cube[2]);
drawLine(cube[2],cube[3]);
drawLine(cube[3],cube[0]);
drawLine(cube[4],cube[5]);
drawLine(cube[5],cube[6]);
drawLine(cube[6],cube[7]);
drawLine(cube[7],cube[4]);
drawLine(cube[0],cube[4]);
drawLine(cube[1],cube[5]);
drawLine(cube[2],cube[6]);
drawLine(cube[3],cube[7]);
}
writeText(" appuyez sur '?' pour afficher l'aide ",1,0,24);
display();
/* --- SI UNE TOUCHE A ETE PRESSEE --- */
if (kbhit())
{
key=getch();
if (key=='1') param[2]='-';
if (key=='2') param[2]='0';
if (key=='3') param[2]='+';
if (key=='4') param[1]='-';
if (key=='5') param[1]='0';
if (key=='6') param[1]='+';
if (key=='7') param[0]='-';
if (key=='8') param[0]='0';
if (key=='9') param[0]='+';
if (key==' ') param[3]=~param[3];
if ((key=='c')||(key=='C')) savePCX();
/* --- AFFICHE L'ECRAN D'AIDE --- */
if (key=='?')
{
writeText(" ",512,6,5);
writeText(" ? aide ",512,6,6);
writeText(" mouse bouger ",512,6,7);
writeText(" space grille/faces ",512,6,8);
writeText(" 1->9 tourner ",512,6,9);
writeText(" escape quitter ",512,6,10);
writeText(" C capture d'ecran ",512,6,11);
writeText(" ",512,6,12);
for (i=0 ; i<6 ; i++)
{
sprintf(str," V%d(%4d,%4d,%4d) ",i+1,(int)cube[i].x,(int)cube[i].y,(int)cube[i].z);
writeText(str,512,6,13+i);
}
writeText(" ",512,6,19);
display();
getch();
}
}
/* --- ROTATIONS --- */
if (param[0]=='+') for (i=0 ; i<8 ; i++) { v=cube[i]; cube[i].y=v.y*0.9998476951-v.z*0.0174524064; cube[i].z=v.y*0.0174524064+v.z*0.9998476951; }
else if (param[0]=='-') for (i=0 ; i<8 ; i++) { v=cube[i]; cube[i].y=v.y*0.9998476951+v.z*0.0174524064; cube[i].z=v.z*0.9998476951-v.y*0.0174524064; }
if (param[1]=='+') for (i=0 ; i<8 ; i++) { v=cube[i]; cube[i].x=v.x*0.9998476951-v.z*0.0174524064; cube[i].z=v.z*0.9998476951+v.x*0.0174524064; }
else if (param[1]=='-') for (i=0 ; i<8 ; i++) { v=cube[i]; cube[i].x=v.x*0.9998476951+v.z*0.0174524064; cube[i].z=v.z*0.9998476951-v.x*0.0174524064; }
if (param[2]=='+') for (i=0 ; i<8 ; i++) { v=cube[i]; cube[i].x=v.x*0.9998476951-v.y*0.0174524064; cube[i].y=v.y*0.9998476951+v.x*0.0174524064; }
else if (param[2]=='-') for (i=0 ; i<8 ; i++) { v=cube[i]; cube[i].x=v.x*0.9998476951+v.y*0.0174524064; cube[i].y=v.y*0.9998476951-v.x*0.0174524064; }
mouse();
} while (key!=27);
stopGraph();
return 0;
}
/* -------------------------------- */
/* --- FAIT UNE CAPTURE D'ECRAN --- */
/* -------------------------------- */
int savePCX()
{
int file;
char header[128];
char pal[768];
char c;
char n;
unsigned int width=(320+(320&1));
unsigned long max=320*200;
unsigned long i;
/* --- CREATION DU PCX --- */
file=creat("capture.pcx",S_IRUSR|S_IWUSR);
if (file==-1)
{
close(file);
return 0;
}
/* --- EN-TETE --- */
header[0]=10; /* octet PCX */
header[1]=5; /* version de PCX */
header[2]=1; /* RLE active */
header[3]=8; /* 8 bits pour un pixel */
header[4]=0; /* Xmin */
header[5]=0; /* Xmin */
header[6]=0; /* Ymin */
header[7]=0; /* Ymin */
header[8]=(320-1)&0x00FF; /* Xmax */
header[9]=((320-1)&0xFF00)>>8; /* Xmax */
header[10]=(200-1)&0x00FF; /* Ymax */
header[11]=((200-1)&0xFF00)>>8; /* Ymax */
header[12]=72; /* resolution horizontale */
header[13]=0; /* resolution horizontale */
header[14]=72; /* resolution verticale */
header[15]=0; /* resolution verticale */
for (i=0 ; i<16 ; i++) /* palette 16 couleurs */
{
header[i*3+16]=15-i;
header[i*3+17]=15-i;
header[i*3+18]=15-i;
}
header[64]=0; /* toujours 0 */
header[65]=1; /* toujours 1 */
header[66]=width&0x00FF; /* largeur, doit etre un nombre pair */
header[67]=(width&0xFF00)>>8; /* largeur, doit etre un nombre pair */
header[68]=1; /* 1 = couleurs, 2 = noir et blanc */
for (i=69 ; i<128 ; i++) header[i]=0; /* tout le reste c'est 0 */
_write(file,header,128); /* on ecrit l'en-tete */
/* --- IMAGE --- */
i=0;
do
{
c=buffer[i];
i++;
n=1;
if (i!=max) while (buffer[i]==c)
{
if ((n<63)&&(i%320)) n++;
else break;
i++;
if (i==max) break;
}
if ((n==1)&&((c&0xC0)!=0xC0)) _write(file,&c,1);
else
{
if ((i%320==0)&&(320!=width)&&(c==(char)(255)))
{
n++;
width=0;
}
n|=0xC0;
_write(file,&n,1);
_write(file,&c,1);
}
if (!width) width=320+1;
else if ((i%320==0)&&(320!=width)) _write(file,"\xC1\xFF",2);
} while (i!=max);
/* --- PALETTE --- */
c=12;
_write(file,&c,1);
for (i=0 ; i<768 ; i++) pal[i]=0;
pal[3]=255; pal[4]=255; pal[5]=255;
pal[6]=255; pal[7]=0; pal[8]=0;
pal[9]=0; pal[10]=255; pal[11]=0;
pal[12]=0; pal[13]=0; pal[14]=255;
pal[15]=0; pal[16]=255; pal[17]=255;
pal[18]=255; pal[19]=0; pal[20]=255;
pal[21]=255; pal[22]=255; pal[23]=0;
_write(file,pal,768);
return 1;
}
/* ----------------------------- */
/* --- S'OCCUPE DE LA SOURIS --- */
/* ----------------------------- */
void mouse()
{
union REGS regs;
regs.x.ax=0x0003; // MOV AX,0003h
int86(0x33,®s,®s); // INT 33h
O.x=(regs.x.cx&0x03FF)>>1;
O.y=regs.x.dx&0x00FF;
if ((regs.x.bx&1)&&(O.z>400)) O.z-=4;
if ((regs.x.bx&2)&&(O.z<1000)) O.z+=4;
}
/* ----------------------- */
/* --- INITIALISATIONS --- */
/* ----------------------- */
void initGraph()
{
int i;
union REGS regs;
/* --- INITIALISE LE MODE GRAPHIQUE --- */
regs.x.ax=0x0013; // MOV AX,0013h
int86(0x10,®s,®s); // INT 10h
_farsetsel(_dos_ds); // pour pouvoir acceder a la memoire
outportb(0x03C8,0); outportb(0x03C9,0); // noir
outportb(0x03C9,0);
outportb(0x03C9,0);
outportb(0x03C8,1); outportb(0x03C9,63); // blanc
outportb(0x03C9,63);
outportb(0x03C9,63);
outportb(0x03C8,2); outportb(0x03C9,63); // rouge
outportb(0x03C9,0);
outportb(0x03C9,0);
outportb(0x03C8,3); outportb(0x03C9,0); // vert
outportb(0x03C9,63);
outportb(0x03C9,0);
outportb(0x03C8,4); outportb(0x03C9,0); // bleu
outportb(0x03C9,0);
outportb(0x03C9,63);
outportb(0x03C8,5); outportb(0x03C9,0); // jaune
outportb(0x03C9,63);
outportb(0x03C9,63);
outportb(0x03C8,6); outportb(0x03C9,63); // magenta
outportb(0x03C9,0);
outportb(0x03C9,63);
outportb(0x03C8,7); outportb(0x03C9,63); // cyan
outportb(0x03C9,63);
outportb(0x03C9,0);
/* --- CREE UN CUBE --- */
O.x=160.0; O.y=100.0; O.z=500.0; // definit l'origine
cube[0].x=-100.0; cube[0].y=-100.0; cube[0].z=-100.0;
cube[1].x= 100.0; cube[1].y=-100.0; cube[1].z=-100.0;
cube[2].x= 100.0; cube[2].y= 100.0; cube[2].z=-100.0;
cube[3].x=-100.0; cube[3].y= 100.0; cube[3].z=-100.0;
cube[4].x=-100.0; cube[4].y=-100.0; cube[4].z= 100.0;
cube[5].x= 100.0; cube[5].y=-100.0; cube[5].z= 100.0;
cube[6].x= 100.0; cube[6].y= 100.0; cube[6].z= 100.0;
cube[7].x=-100.0; cube[7].y= 100.0; cube[7].z= 100.0;
/* --- INITIALISE LA SOURIS --- */
regs.x.ax=0x0000; // MOV AX,0000h
int86(0x33,®s,®s); // INT 33h
regs.x.ax=0x0002; // MOV AX,0002h
int86(0x33,®s,®s); // INT 33h
/* --- VA CHERCHER LA POLICE DE CARACTERE --- */
for (i=0 ; i<1024 ; i++) ascii[i]=_farnspeekb(i+0x000FFA6E);
}
/* ---------------------------- */
/* --- RETOUR AU MODE TEXTE --- */
/* ---------------------------- */
void stopGraph()
{
union REGS regs;
regs.x.ax=0x0003; // MOV AX,03h
int86(0x10,®s,®s); // INT 10h
}
/* -------------------------- */
/* --- RAFRAICHIT L'ECRAN --- */
/* -------------------------- */
void display()
{
unsigned int i;
unsigned long i2;
i=0;
i2=0xA0000; // adresse de la memoire video
while (!(inportw(0x03DA)&8)); // synchronise avec l'ecran
while (inportw(0x03DA)&8);
while (i<64000) _farnspokeb(i2++,buffer[i++]); // affichage
}
/* -------------------------------------- */
/* --- DESSINE LE CUBE AVEC LES FACES --- */
/* -------------------------------------- */
void drawCube(VERTEX* cube)
{
int b[7];
int b2[7]={0,1,2,3,4,5,6};
int i;
int i2;
/* --- TRIE LES FACES POUR NE PAS DESSINER LES FACES ARRIERES --- */
b[0]=cube[0].z+cube[1].z+cube[2].z+cube[3].z;
b[1]=cube[4].z+cube[5].z+cube[6].z+cube[7].z;
b[2]=cube[0].z+cube[3].z+cube[4].z+cube[7].z;
b[3]=cube[1].z+cube[2].z+cube[5].z+cube[6].z;
b[4]=cube[0].z+cube[1].z+cube[4].z+cube[5].z;
b[5]=cube[2].z+cube[3].z+cube[6].z+cube[7].z;
for (i2=0 ; i2<6 ; i2++)
{
for (i=0 ; i<5 ; i++) if (b[i]>b[i+1])
{
b[7]=b[i];
b[i]=b[i+1];
b[i+1]=b[7];
b2[7]=b2[i];
b2[i]=b2[i+1];
b2[i+1]=b2[7];
}
}
/* --- DESSINE LES 3 FACES AVANT --- */
for (i=2 ; i>=0 ; i--)
{
if (b2[i]==0) { fillFace(cube[0],cube[1],cube[2],2); fillFace(cube[2],cube[3],cube[0],2); }
else if (b2[i]==1) { fillFace(cube[4],cube[5],cube[6],3); fillFace(cube[6],cube[7],cube[4],3); }
else if (b2[i]==2) { fillFace(cube[4],cube[0],cube[3],4); fillFace(cube[4],cube[7],cube[3],4); }
else if (b2[i]==3) { fillFace(cube[1],cube[2],cube[6],5); fillFace(cube[5],cube[1],cube[6],5); }
else if (b2[i]==4) { fillFace(cube[4],cube[5],cube[0],6); fillFace(cube[0],cube[5],cube[1],6); }
else if (b2[i]==5) { fillFace(cube[2],cube[3],cube[7],7); fillFace(cube[7],cube[6],cube[2],7); }
}
}
/* ------------------------ */
/* --- SCANNE UNE LIGNE --- */
/* ------------------------ */
void scanLine(int x1,int y1,int x2,int y2,int* scanboard)
{
int x;
int y;
int m;
if (y2!=y1)
{
if (y2<y1)
{
m=y1;
y1=y2;
y2=m;
m=x1;
x1=x2;
x2=m;
}
x=x1<<8;
m=((x2-x1)<<8)/(y2-y1);
x+=m;
y1++;
for (y=y1 ; y<=y2 ; y++)
{
if ((y<200)&&(y>=0))
{
if (scanboard[y]==-16000) scanboard[y]=x>>8;
else scanboard[y+200]=x>>8;
}
x+=m;
}
}
}
/* ------------------------ */
/* --- REMPLIT UNE FACE --- */
/* ------------------------ */
void fillFace(VERTEX a,VERTEX b,VERTEX c,int color)
{
int ax=(int)((a.x*256.0)/(a.z+O.z)+O.x);
int ay=(int)((a.y*256.0)/(a.z+O.z)+O.y);
int bx=(int)((b.x*256.0)/(b.z+O.z)+O.x);
int by=(int)((b.y*256.0)/(b.z+O.z)+O.y);
int cx=(int)((c.x*256.0)/(c.z+O.z)+O.x);
int cy=(int)((c.y*256.0)/(c.z+O.z)+O.y);
int x;
int y;
int scanboard[400];
for(y=0 ; y<200 ; y++) scanboard[y]=scanboard[y+200]=-16000;
scanLine(ax,ay,bx,by,scanboard);
scanLine(bx,by,cx,cy,scanboard);
scanLine(cx,cy,ax,ay,scanboard);
for(y=0;y<200;y++)
{
if(scanboard[y]>scanboard[y+200])
{
x=scanboard[y];
scanboard[y]=scanboard[y+200];
scanboard[y+200]=x;
}
if (scanboard[y]!= -16000)
{
if(scanboard[y+200]==-16000)
scanboard[y+200]=scanboard[y];
for(x=scanboard[y] ; x<scanboard[y+200] ; x++) PIXEL(x,y,color);
}
}
}
/* ----------------------- */
/* --- TRACE UNE LIGNE --- */
/* ----------------------- */
void drawLine(VERTEX a,VERTEX b)
{
int fx=(int)((a.x*256.0)/(a.z+O.z)+O.x);
int fy=(int)((a.y*256.0)/(a.z+O.z)+O.y);
int lx=(int)((b.x*256.0)/(b.z+O.z)+O.x);
int ly=(int)((b.y*256.0)/(b.z+O.z)+O.y);
int x1;
int y1;
int x2;
int y2;
int dx;
int dy;
int sub;
int remain;
int error;
int inc1;
int inc2;
if (fx>lx)
{
x1=lx;
x2=fx;
y1=ly;
y2=fy;
}
else
{
x1=fx;
x2=lx;
y1=fy;
y2=ly;
}
dx=x2-x1;
dy=y2-y1;
if ((!dx)&&(!dy)) return;
if (dy<0)
{
dy=-dy;
inc1=-1;
inc2=1;
}
else
{
inc1=1;
inc2=1;
}
if (dx>dy)
{
sub=dx-dy;
error=dy-(dx>>1);
remain=(dx+1)>>1;
do
{
PIXEL(x1,y1,1);
PIXEL(x2,y2,1);
x1+=inc2;
x2-=inc2;
if (error>=0)
{
y1+=inc1;
y2-=inc1;
error-=sub;
}
else error+=dy;
} while (--remain>0);
if (!(dx&1)) PIXEL(x1,y1,1);
}
else
{
sub=dy-dx;
error=dx-(dy>>1);
remain=(dy+1)>>1;
do
{
PIXEL(x1,y1,1);
PIXEL(x2,y2,1);
y1+=inc1;
y2-=inc1;
if (error>=0)
{
x1+=inc2;
x2-=inc2;
error-=sub;
}
else error+=dx;
} while (--remain>0);
if (!(dy&1)) PIXEL(x1,y1,1);
}
}
/* ---------------------- */
/* --- ECRIT DU TEXTE --- */
/* ---------------------- */
void writeText(char* text,int c,int posx,int posy)
{
unsigned int i;
unsigned int i2;
unsigned int pos1;
unsigned int pos2;
pos1=posy<<3;
pos1=(pos1<<8)+(pos1<<6)+(posx<<3); // convertit les positions
for (i=0 ; text[i] ; i++) // tant que le texte n'est pas fini
{
pos2=text[i]<<3; // va au caractere demande
for (i2=0 ; i2<8 ; i2++) // 8 lignes par caracteres
{
/* --- AFFICHE LES 8 PIXELS DE LA LIGNE --- */
if (ascii[pos2]&0x80) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
if (ascii[pos2]&0x40) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
if (ascii[pos2]&0x20) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
if (ascii[pos2]&0x10) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
if (ascii[pos2]&0x08) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
if (ascii[pos2]&0x04) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
if (ascii[pos2]&0x02) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
if (ascii[pos2]&0x01) buffer[pos1++]=c&0x00FF;
else buffer[pos1++]=(c&0xFF00)>>8;
pos1+=320-8;
pos2++;
}
pos1-=320*8-8;
}
}
Conclusion :
Voila, désolé c'est pas très commenté, c'est le premier programme qui fait de la 3D que je fait.
Le programme à l'avantage d'être compilable directement avec DJGPP, pas d'ASM et pas de libraire graphique comme Allegro, SDL,...
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.