Suivez-nous sur X
|
|
|
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
|
|
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
|
|
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
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : C - Graphismes 3D en C (animation)
(Article écrit par Pascal Amiable et extrait d'Amiga News Tech - septembre 1990)
|
|
Ce mois-ci, je vous propose de la 3D animée. En effet, maintenant que nous avons tous les
outils nécessaires à l'affichage d'un objet 3D sur l'écran de notre Amiga, nous allons étudier
les moyens efficaces nous permettant de l'animer. Vous vous êtes sans doute rendu compte que
l'animation proposée le mois dernier
était lente et peu satisfaisante du point de vue visuel.
Il y a deux raisons à cela. Le recalcul systématique de la matrice de projection en double
précision est inutile et très coûteux en temps de calcul. Le tracé et l'effacement sur un
écran standard Intuition est visible. Nous allons essayer de résoudre ces deux problèmes.
Affichage sur écran avec double tampon mémoire
Au niveau de l'affichage, nous devons créer un écran accessible rapidement aux routines de
tracé et où l'on puisse afficher et effacer un objet non pas ligne par ligne (ce qui crée un
phénomène de scintillement disgracieux) mais en une seule fois. La solution est simple. On
crée un écran avec deux zones de tracé, et l'on écrit dans une des zones pendant que l'on
affiche l'autre. On permute ensuite les deux zones et le tour est joué !
C'est ce que l'on appelle un écran "double buffer" (double tampon mémoire).
Pour créer un écran à double tampon mémoire, plus question d'utiliser Intuition dont
la structure ralentit notablement les routines de tracé. Nous allons créer notre écran
directement à l'aide des fonctions de la bibliothèque graphic.library.
L'Amiga crée un écran à partir des instructions que vous lui donnez. Ces instructions doivent
être organisées sous la forme d'une structure dénommée "View". A un "View" on associe une ou
plusieurs zones d'affichage dénommées "ViewPort". Il est à noter que plusieurs ViewPort de
résolutions et de types différents peuvent cohabiter sur le même View. Chaque ViewPort est
défini par sa taille, sa position sur le View, son mode de visualisation, sa table de couleurs
et un pointeur sur une première structure "RasInfo".
Chaque structure RasInfo contient des informations concernant une des "BitMaps" (zone de traçage)
liée au ViewPort (position par rapport au View et pointeur sur la BitMap). Une BitMap est,
quant à elle, définie par sa taille (horizontale et verticale) et le nombre de plans de bits
qu'elle contient. Les structures RasInfos étant comme les ViewPorts chaînées entre elles.
A partir de cette brève description de principe, comment allons-nous définir notre écran à double
tampon mémoire. Nous allons commencer par déclarer un View et ViewPort soit :
struct View view;
struct ViewPort viewport;
|
Comme nous avons besoin de deux zones de traçage (deux tampons mémoire), il faut déclarer
deux structures RasInfo et deux BitMap :
struct RasInfo rasinfos[2];
struct BitMap bitmaps[2];
|
Maintenant, il ne nous reste plus qu'à initialiser tout cela (voir la fonction void ouvrecran()).
Donc, on initialise le View grâce à InitView(&view) et le ViewPort avec InitVPort(&viewport).
0n associe le ViewPort au View : view.viewport = &viewport et on sélectionne le mode associé au
View : view.Modes = MODE.
On initialise les deux BitMap à l'aide de la fonction InitBitMap(&bitmap[i],profondeur,largeur,hauteur).
On initialise les deux structures RasInfo (Cf. listing) et on remplit la structure ViewPort
(c'est très simple). Pour la table des couleurs, on commence par réserver une zone pour cette table.
viewport.ColorMap = (struct ColorMap*)GetColorMap(COLOR);
|
...et ensuite on charge cette table dans le ViewPort grâce à :
Load RGB4(&viewport,&colortable[0],COLOR);
|
Il nous reste à réserver les zones mémoire nécessaires aux deux bitMap et à remplir ces zones
avec zéro. On alloue chaque plan de la BitMap à l'aide de la fonction AllocRaster(largeur,hauteur)
et on les met à zéro à l'aide du Blitter avec :
BltClear((UBYTE*)bitmap[i].Planes[i],RASSIZE(largeur,hauteur),0);
|
Bien, maintenant que nous manque-t-il pour créer notre écran ? Déjà nous n'avons pas de crayons
pour écrire dans nos bitmaps. Pour combler ce manque, il nous faut définir deux structures "RastPort"
(une par BitMap) qui servent à écrire dans la mémoire. Pour chaque RastPort on appelle
InitRastPort(&rastport) et on associe au RastPort une BitMap : rastport[i]BitMap = &bitmap[i].
Ensuite, il faut préparer le double tampon mémoire pour l'affichage, c'est-à-dire qu'il va falloir
créer deux listes Copper différentes, une associée à chaque tampon mémoire. La séquence est la
suivante :
MakeVPort(&view,&viewport);
MrgCop(&view);
|
...génère la première liste Copper dont on va stocker les pointeurs dans LOF[DB1] et SHF[DB1].
On change dans le ViewPort le pointeur sur la structure RasInfo. C'est-à-dire que l'on pointe
sur la structure 2, viewport.RasInfo = &rasinfo[DB2]; et rasinfo[DB2].Next = &rasinfo[DB1];
(on réalise le chaînage).
On remet NULL les deux morceaux de liste Copper afin de les faire régénérer par l'Amiga.
view.LofCprList = NULL
view.SFCprlist = NULL
MakeVPort(&view,&viewport);
MrgCop(&view);
|
...génère la deuxième liste Copper, dont on stockera également les pointeurs dans LOF[DB2] et SHF[DB2].
Le programme principal
C'est presque terminé. Maintenant, il ne nous reste plus qu'à regarder le programme principal.
- 1. On appelle Init() qui ouvre la bibliothèque graphique graphic.library et sauvegarde
l'écran courant.
- 2. On charge l'objet courant à l'aide des fonctions chargepoint() et chargeligne().
- 3. On stocke dans deux tableaux les valeurs des sinus et cosinus par degré afin d'éviter de
les recalculer à chaque passage.
- 4. On appelle la fonction ouvrecran() qui initialise notre View;.
- 5. On calcule une première fois l'objet (rotations() et affiche()).
- 6. Dans la boucle principale (que l'on interrompt en appuyant sur le bouton de gauche
de la souris).
- 6a. On définit le RastPort que l'on va utiliser (on écrit dans la zone mémoire qui n'est pas affichée).
- 6b. On incrémente les deux angles de rotation.
- 6c. On fait tourner l'objet et on l'affiche.
- 6d. On affiche l'écran qui vient d'être dessiné et on recommence en changeant de Rastport.
- 7. Une fois sorti de la boucle, on recharge l'ancien écran : (LoadView(écran-sauvegardé));
et on libère tout ce que l'on a alloué (libère()).
Avec cette méthode, l'animation devient fluide, il ne nous reste plus qu'à accélérer le calcul de
la fonction rotation().
Nouvelle fonction rotation()
Pour diminuer les temps de calcul, l'idée est simple, éviter le recalcul des sinus et cosinus
à chaque fois. Pour ce faire, on stocke dans deux tables sinus[360] et cosinus[360] les valeurs
des sinus et cosinus tous les degrés et on les stocke sous forme de "float". Ensuite, on multiplie
dans la fonction rotation() les coordonnées par les valeurs associées de ces tables et on gagne du temps.
Sous Lattice v5.00
Une autre méthode que j'utilise avec le Lattice v5.00 pour accélérer le calcul est d'utiliser la
bibliothèque de fonctions mathffp.library. Cette bibliothèque utilise le format de calcul
Fast Floatingpoint de Motorola qui permet un gain de temps aux dépens de la précision (qui n'est
nullement importante ici). On compile donc le programme anim.c avec lc.ff.lcf anim.c
et le tour est joué.
Je ne sais si cela se fait automatiquement à la compilation sous les versions antérieures du
Lattice ou sous Manx. Il vous reste toutefois la possibilité de les utiliser en appelant directement
les fonctions de la bibliothèque. A noter qu'il est nécessaire d'avoir dans le tiroir Libs:
la bibliothèque mathffp.library pour que le programme fonctionne.
Les fichiers objet
Les fonctions chargeligne() et chargepoint() ayant été modifiées les tableaux de points et
de lignes ont été également modifiés. Un exemple valant mieux qu'un grand discours, voici l'exemple
d'une pyramide à base carrée.
Conclusion
Le programme est disponible en source et exécutable sur 3615 Comrev et enfin, si vous
voulez en savoir plus sur la programmation des écrans à l'aide de la graphic.library, je
vous conseille deux ouvrages : le ROM Kernel Manual en anglais qui est la référence de
Commodore pour le programmeur et l'excellent livre de Micro Application : Graphisme sur Amiga,
où vous trouverez dans la partie consacrée au langage C de nombreuses explications avec des
exemples très didactiques sur ce sujet.
Programme d'animation d'un objet dans l'espace
|