Cube 3d en rotation / mode graphique vga 13h / djgpp

Description

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,&regs,&regs);	// 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,&regs,&regs);	// 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,&regs,&regs);	// INT 33h
	regs.x.ax=0x0002;		// MOV AX,0002h
	int86(0x33,&regs,&regs);	// 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,&regs,&regs);	// 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,...

Codes Sources

A voir également

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.