Obligement - L'Amiga au maximum

Samedi 24 mai 2025 - 16:29  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : C - Les objets complexes en 3D
(Article écrit par Thomas Landspurg et extrait d'Amiga News Tech - mai 1992)


Afficher des objets complexes en 3D, c'est bien. Les animer, c'est mieux. Et plus difficile, aussi.

Il y a plusieurs types d'animations, la plus simple consistant à uniquement faire bouger l'objet dans l'espace. Mais c'est un peu juste si l'on veut aller plus loin et animer des objets complexes. Le type d'animation que nous allons voir consiste à dire qu'un objet est formé de plusieurs sous-objets, mobiles entre eux. Prenons comme exemple un oiseau. Pour l'animer, prenons comme point de départ le corps. On aurait ensuite les ailes et la tête, qui sont liées au corps mais mobiles par rapport à lui. C'est-à-dire que si le corps bouge, les ailes bougent aussi, mais les ailes peuvent aussi bouger de manière relative au corps (Cf. figure 1).

Ensuite, on a les extrémités des ailes, évidemment liées aux ailes elles-mêmes, mais également mobiles par rapport à elles. Il s'agit d'une construction hiérarchique, où chaque élément dépend du précédent. Cette organisation est bien connue des utilisateurs de Sculpt et autres modeleurs.

C

Il faut donc créer une structure de données qui puisse refléter cette hiérarchie. La structure objet comporte les éléments classiques pour un objet en 3D :
  • Les angles (ax, ay, az) et les positions relatives (cx, cy, cz) de l'élément.
  • Le nombre de points de cet élément (nb_pt).
  • Un pointeur sur les coordonnées des points qui composent cet élément (p_pt).
  • Le nombre de lignes (nb_lines).
  • Un pointeur sur le tableau des numéros des points de début et de fin qui forment les lignes de l'élément (p_lines).
Jusqu'à présent, rien que de très classique, mais c'est là que cela devient intéressant : on a aussi un pointeur sur un tableau contenant la liste des sous-éléments liés à cet élément (p_tab). En reprenant l'exemple de l'oiseau, à l'élément corps serait associé un tableau de pointeurs contenant les adresses des éléments ailes et tête. Chaque sous-élément peut lui-même être père de plusieurs autres éléments. Mais n'oublions pas que nous sommes en 3D temps réel et qu'il faudra bien se limiter à un moment ou à un autre, pour des questions de vitesse d'affichage.

Le programme

Une fois n'est pas coutume, il est écrit en C. Il n'y a pas de problème majeur à sa transcription en assembleur, mais il faut noter que le C se débrouille de manière très honorable question vitesse.

Le programme alloue deux écrans d'un plan de bits chacun pour le double tampon mémoire, puis les affiche alternativement. L'affichage de l'objet se fait de manière récursive dans la procédure "Aff_Obj". On commence par afficher le premier élément : calcul des rotations, passage de 3D en 2D et affichage. Ensuite, on appelle de nouveau la procédure Aff_Obj pour chacun des sous-éléments de l'élément courant, en additionnant ses angles de rotations et ses déplacements relatifs à ceux du père.

Note : Il y a une petite astuce pour l'affichage des lignes. Un couple (1, 3) indique qu'il faut tracer une ligne entre le point numéro 1 et le point numéro 3 de l'élément courant, mais un couple (1, -3) indique un tracé entre le point 1 de l'élément courant et le point 3 de l'élément précédent (c'est-à-dire l'élément père). Cela permet de tracer des lignes entre des points de l'élément père et de points de l'élément courant. Seul problème, le point de numéro 0 n'a pas d'opposé (-0 =0). On indique alors ce cas particulier en utilisant la valeur -MAX PT comme numéro de point.

Le listing ci-dessous contient comme exemple un oiseau battant des ailes. L'exemple d'utilisation est relativement simple et n'utilise pas toutes les possibilités de cette technique. A vous donc de trouver de superbes objets à animer, mais avant de vous lancer dans l'aventure, je vous conseille d'essayer de bien comprendre la structure des données.

/* Move par T.Landspurg		 */
/* A compiler avec "lc -Lm move" */

#include "exec/types.h"
#include <graphics/gfxbase.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <math.h>

USHORT quit_flag = FALSE;

long IntuitionBase=0;
struct	GfxBase *GfxBase=0;
struct Window *OpenWindow();
struct Screen *OpenScreen();

#define	DEPTH	1
#define	WIDTH	320
#define	HEIGHT	256
#define	MAX_PT	30

UWORD	ColorTable[]={0,0xf0};

typedef	struct	pt{
	int	x,y,z;
};
typedef	struct	line{
	int	deb,fin;
};
typedef	struct	pt_2d{
	int	x,y;
};
typedef	struct	obj{
	int	nb_pt;			/* Nombre de pt de l'element	*/
	int	nb_lines;		/* nombre de lignes		*/
	int	ax,ay,az;		/* Angles relatifs		*/
	int	cx,cy,cz;		/* Deplacements relatifs	*/
	struct	pt	*p_pt;		/* tableau des pts de l'elem	*/
	struct	line	*p_lines;	/* tableau des lignes		*/
	struct	obj	**p_tab;	/* tableau des elements fils	*/
};

/* LA TETE */
struct pt pt_tete[]={{-10,10,0},{10,10,0},{0,0,20},{0,5,0}};
struct	line	lines_tete[]={{0,1},{1,2},{2,0},{0,3},{1,3},{2,3}};
struct	obj	tete={4,6,0,0,0,0,0,40,pt_tete,lines_tete,NULL};

/* LES AILES ET BOUTS D'AILES */

struct	pt pt_aile[]={	{20,0,30},{20,0,0}	};
struct	pt pt_aile_bord[]={	{20,0,25},{20,0,5}	};
struct	line	lines_aile_bord[]={	{0,-MAX_PT},{1,-1},{0,1}	};
struct	line	lines_aile[]={	{0,-3},{1,-2},{0,1}	};
struct	line	lines_aile2[]={	{1,-1},{0,-MAX_PT},{0,1}	};
struct	obj	aile_bord={2,3,0,0,100,20,0,0,pt_aile_bord,lines_aile_bord,NULL};
struct	obj	aile_bord2={2,3,0,0,-100,20,0,0,pt_aile_bord,lines_aile_bord,NULL};

struct	obj	*tab_aile_bord[]={&aile_bord,NULL};
struct	obj	*tab_aile_bord2[]={&aile_bord2,NULL};

struct	obj	aile1={2,3,0,0,0,10,0,0,pt_aile,lines_aile,tab_aile_bord};
struct	obj	aile2={2,3,0,0,180,-10,0,0,pt_aile,lines_aile2,tab_aile_bord2};

/*  LE CORPS */

struct	pt pt_corps[]={
	{-10,10,30},{-5,10,-30},{5,10,-30},{10,10,30},{0,-5,10}
	};
struct	line	lines_corps[]={
	{0,1},{1,2},{2,3},{3,0},{4,0},{4,1},{4,2},{4,3}
	};
struct	obj	*tab_corps[]={&aile2,&aile1,&tete,NULL};
struct	obj	corp={5,8,0,0,0,0,0,0,pt_corps,lines_corps,tab_corps};

int	de=200;	/* distance de l'ecran a l'observateur */
int	d=600;	/* distance de l'observateur au pt 0   */

int	t_cos[360];	/* tables de sinus et cosinus */
int	t_sin[360];
/********************* Rotation dans le plan ************/
void	rot(angle,p_x,p_y)
int	angle,*p_x,*p_y;
{
	int	x,y;
	x= (*p_x)*t_cos[angle]+(*p_y)*t_sin[angle];
	y=-(*p_x)*t_sin[angle]+(*p_y)*t_cos[angle];

	*p_x=x>>8;
	*p_y=y>>8;
}
/***************** Calcul de la projection 3D->2D ******/
void	calc_coord(p_pt,p_x,p_y)
struct	pt	*p_pt;
int	*p_x,*p_y;
{
	if(d+p_pt->z!=NULL){
		*p_x= WIDTH/2+(p_pt->x*de)/(d+p_pt->z);
		*p_y=HEIGHT/2-(p_pt->y*de)/(d+p_pt->z);
	}
}
/************** Affichage de l'objet ******************/

void	AffObj(rp,p_obj,ax,ay,az,ox,oy,oz,old_pt)
struct	RastPort	*rp;
struct	obj	*p_obj;
int	ax,ay,az;
int	ox,oy,oz;
struct	pt_2d	*old_pt;
{
	int	i;
	struct	line	*p_lines;
	struct	pt	*p_pt,pt_cour;
	struct	obj	**p_temp;
	int	cx,cy,cz,deb,fin;
	struct	pt_2d	tab_pt[MAX_PT];

	/* Calcul du point de depart */
	cx=4*p_obj->cx;
	cy=4*p_obj->cy;
	cz=4*p_obj->cz;
	
	rot(az,&cx,&cy);
	rot(ax,&cy,&cz);
	rot(ay,&cz,&cx);
	/* nouveau angles de rotation */
	p_pt=p_obj->p_pt;
	ax=((360+ax+p_obj->ax))%360;
	ay=((360+ay+p_obj->ay))%360;
	az=((360+az+p_obj->az))%360;
	/* rotation et passage 3d/2D pour tt les points */
	for(i=0;i<p_obj->nb_pt;i++){
		pt_cour.x=4*(p_pt->x);
		pt_cour.y=4*(p_pt->y);
		pt_cour.z=4*(p_pt->z);
		rot(az,&pt_cour.x,&pt_cour.y);
		rot(ax,&pt_cour.y,&pt_cour.z);
		rot(ay,&pt_cour.z,&pt_cour.x);
		pt_cour.x+=cx+ox;
		pt_cour.y+=cy+oy;
		pt_cour.z+=cz+oz;
		calc_coord(&pt_cour,&tab_pt[i].x,&tab_pt[i].y);
		p_pt++;
	}
	p_pt=p_obj->p_pt;
	p_lines=p_obj->p_lines;
	/* affichage des lignes */
	for(i=0;i<p_obj->nb_lines;i++){
		deb=p_lines->deb;
		if(deb>=0){
			Move(rp,tab_pt[deb].x,tab_pt[deb].y);
		}else{
			if(deb==-MAX_PT)deb=0;
			Move(rp,old_pt[-deb].x,old_pt[-deb].y);
		}
		fin=p_lines->fin;
		if(fin>=0){
			Draw(rp,tab_pt[fin].x,tab_pt[fin].y);
		}else{
			if(fin==-MAX_PT)fin=0;
			Draw(rp,old_pt[-fin].x,old_pt[-fin].y);
		}
		p_lines++;
	}
	/***********************************************************/
	/* RECURSION: si il y a des elements fils, on les affiches */
	/***********************************************************/
	if(p_obj->p_tab!=NULL){
		p_temp=p_obj->p_tab;
		while((*p_temp)!=NULL){
			AffObj(rp,*p_temp,ax,ay,az,cx+ox,cy+oy,cz+oz,tab_pt);
			p_temp++;
		}
	}
}
/************** Programme principal ********************************/
main()
{
	int	i,dir;
	struct	RasInfo	ri;
	struct	BitMap	b1,b2,*b;
	struct ViewPort vp;
	struct	View	v,*oldview;
	struct RastPort rp1,rp2,*rp;
 
	IntuitionBase = OpenLibrary("intuition.library", 0);
	if (IntuitionBase == NULL)
	{
		printf("intuition is not here.  where are we?\n");
		goto cleanup1;
	}
	GfxBase =(struct GfxBase *) OpenLibrary("graphics.library", 0);
	oldview=GfxBase->ActiView;

	printf	("Initialisation tab cos et sin \n");
	for(i=0;i<360;i++){
		t_cos[i]=(int)(256.0*cos((float)i*2*PI/360));
		t_sin[(i+90)%360]=t_cos[i];
	}
	InitView(&v);
	v.ViewPort=&vp;

	InitVPort(&vp);
	vp.DWidth=WIDTH;
	vp.DHeight=HEIGHT;
	vp.RasInfo=&ri;
	vp.ColorMap=(struct	ColorMap *)GetColorMap(1<<DEPTH);
	
	InitBitMap(&b1,DEPTH,WIDTH,HEIGHT);
	InitBitMap(&b2,DEPTH,WIDTH,HEIGHT);
	for(i=0;i<DEPTH;i++){
		b1.Planes[i]=(PLANEPTR)AllocRaster(WIDTH,HEIGHT);
		b2.Planes[i]=(PLANEPTR)AllocRaster(WIDTH,HEIGHT);
	}
	InitRastPort(&rp1);
	InitRastPort(&rp2);
	rp1.BitMap=&b1;
	rp2.BitMap=&b2;
	ri.RxOffset=0;
	ri.RyOffset=0;
	ri.Next=NULL;
	LoadRGB4(&vp,ColorTable,1<<DEPTH);

	dir=2;
	for(i=0;i<200;i++){

		if((i%2)==1){
			b=&b1;
			rp=&rp1;
		}else{
			b=&b2;
			rp=&rp2;
		}
		Move(rp,0,0);
		ClearScreen(rp);
		SetAPen(rp,1);

		/* affichage de l'objet */
		AffObj(rp,&corp,0,0,0,0,-200,100,NULL);

		/* Calcul du mvt des ailes */
		corp.ay+=1;
		if((i%25)==0)dir=-dir;
		aile1.az+=2*dir;
		aile_bord.az+=dir;
		aile2.az-=2*dir;
		aile_bord2.az-=dir;
		tete.ay=-aile1.az/2;

		ri.BitMap=b;
		MakeVPort(&v,&vp);
		MrgCop(&v);
		LoadView(&v);

		WaitTOF();

	}	
cleanup3:
	for(i=0;i<DEPTH;i++){
		FreeRaster(b1.Planes[i],WIDTH,HEIGHT);
		FreeRaster(b2.Planes[i],WIDTH,HEIGHT);
	}
	FreeColorMap(vp.ColorMap);
	LoadView(oldview);
cleanup1:
	if (GfxBase != NULL) CloseLibrary(GfxBase);
	if (IntuitionBase != NULL) CloseLibrary(IntuitionBase);
	exit(0);
}

Mise à jour de mai 2025 : une archive contenant le listing adapté à vbcc, et avec l'exécutable compilé par vbcc, a été réalisée par Yann-Gaël Guéhéneuc et est disponible sur obligement.free.fr/files/ant3dcomplexobjects.lha.


[Retour en haut] / [Retour aux articles]