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 (programmation de base)
(Article écrit par Pascal Amiable et extrait d'Amiga News Tech - mai 1990)
|
|
Chers lecteurs et lectrices, Bonjour ! Comme promis le mois dernier,
nous allons continuer notre exploration de l'univers 3D, avec à la clef ce mois-ci un petit programme qui
vous permettra d'afficher un objet à l'écran suivant un angle de vision de votre choix et à l'aide d'une projection simple.
Nous verrons comment ouvrir un écran et tracer droites et points sur cet écran. Comment écrire un fichier de
points et de droites représentant un objet et pouvoir le relire ensuite. La projection dimétrique qui nous servira
pour l'affichage de notre objet sur l'écran sera décrite avec d'autres projections le mois prochain. Je vous
fournis néanmoins la fonction qui transforme notre objet 3D en objet écran afin d'avoir un programme complet
pour ce mois-ci. Bien commençons donc sans plus attendre.
Ouverture des bibliothèques Intuition et Graphics
Pour créer un écran, nous allons nous servir de la bibliothèque Intuition. De même, pour tracer les droites et
afficher les points, nous aurons recours au module Graphics. Mais pour utiliser ces différentes fonctions,
il faut d'abord ouvrir ces deux bibliothèques. On utilise pour ce faire, la fonction OpenLibrary().
On passe à cette fonction deux paramètres : le nom de la bibliothèque (char*) et le numéro de version
(long). Elle renvoie un pointeur sur une structure associée à la bibliothèque que l'on veut ouvrir.
On aura à définir le numéro de version (00 et à déclarer les pointeurs de structures en global. soit :
Et :
A partir de là, on peut écrire une fonction "init()" qui ouvrira les deux bibliothèques à l'aide d'OpenLibrary() :
Ouverture de l'écran
Maintenant que les bibliothèques sont ouvertes, nous allons pouvoir créer un écran. La création
d'un écran s'effectue simplement en deux étapes.
- Primo : on initialise une structure NewScreen qui va définir les caractéristiques de notre écran.
- Secundo : on appelle la fonction OpenScreen (NewScreen) qui renvoie un pointeur sur la structure
Screen représentant l'écran qui a été ouvert.
Nous allons regarder quelles informations il nous faut communiquer à la structure NewScreen. D'abord,
la position du coin haut et gauche de l'écran (LeftEdge/TopEdge). Il est vivement recommandé
d'initialiser LeftEdge à 0. Ensuite la largeur (320,640) et la hauteur de l'écran (200,256,400,512)
(Width,Height). Il faudra faire attention à positionner les valeurs nécessaires dans le champ
ViewModes pour certaines résolutions (voir ci-dessous).
Le nombre de plans graphiques (Depth) donne le nombre de couleurs utilisables simultanément (1 = 2 couleurs,
2 = 4 couleurs, 3 = 8 couleurs, 4 = 16 couleurs et 5 = 32
couleurs). Les champs DetailPen et BlockPen représentent respectivement la couleur du texte de la barre
de menu et du fond de cette barre.
ViewMode représente le type d'écran, si on le met à NULL on a un écran basse résolution. Si on met
HIRES, on a un écran haute résolution et avec INTERLACED on passe en mode entrelacé (HIRES + INTERLACED =
640x400 en NTSC ou 640x512 en PAL). Le type d'écran vaut CUSTOMSCREEN dans le cas qui nous intéresse.
Le champ Font pointe sur la police de caractères utilisée. S'il vaut NULL on a par défaut la police Topaz.
Le champ DefaultTitle contient le titre de l'écran qui apparaît dans la barre de menu. Le champ Gadgets
contient la liste des gadgets "système" liés à l'écran (passage arrière/avant, barre de déplacement, etc.).
Enfin, le champ CustomBitmap pointe sur une structure "Bitmap" spéciale créé par le programmeur.
Dans notre petit programme, nous allons créer un écran basse résolution 320x200, avec 32 couleurs.
La barre de menu non apparente (couleur = 0) sans titre et sans gadgets, bref tout ce
qu'il y a de plus simple. Après avoir initialisé cette structure, il suffit d'appeler la fonction
OpenScreen (& NewScreen). On déclare en global un pointeur sur une structure Screen qui va nous servir
à récupérer le résultat de OpenScreen().
On déclare de la même façon OpenScreen() pour éviter quelques grognements du compilateur.
...et c'est fini !
On écrit notre fonction ouvrecran() très facilement. La voici :
La dernière ligne du programme nécessite une petite explication ; "rp" est un pointeur sur le RastPort
de l'écran. Ce pointeur est nécessaire pour effectuer toutes les opérations de traçage. On déclare
donc en global ce pointeur struct RastPort *rp; et comme indiqué dans la fonction
ouvrecran(), on récupère l'adresse de l'écran rp=&(ecran->RastPort);.
Le chargement des points et lignes
Bien, maintenant voyons comment créer un fichier (ou plutôt deux) pour représenter un objet.
Dans un esprit de simplification et de réduction du source, j'ai décidé de représenter un objet
sous forme de deux fichiers texte, un pour les points, un pour les lignes. Ces fichiers sont
organisés comme suit :
Pour le fichier "point.dat" en coordonnées homogènes :
- 1re ligne : nombre de points. Exemple : 5
- 2e ligne : coordonnée en X. Exemple : 10.0
- 3e ligne : coordonnée en Y. Exemple : 10.0 P1(10.0,10.0,10.0,1.0)
- 4e ligne : coordonnée en Z. Exemple : 10.0
- 5e ligne : coordonnée en W. Exemple : 1.0
- Idem pour les quatre autres points.
Pour le fichier ligne.dat, on indique les numéros des points extrémités et origines L0(premier
point P0, extrémité P3) ;
- 1re ligne : nombre de lignes. Exemple : 8
- 2e ligne : origine de L0. Exemple : 0 L0(P0,P3).
- 3e ligne : extrémité de L0. Exemple : 3
- Idem pour les sept autres lignes.
Maintenant que nous avons défini la représentation de nos fichiers, il est très simple d'écrire
une fonction permettant de lire ces fichiers. Je vous propose ci-après deux fonctions, chargepoints()
et chargelignes(), qui permettent de lire les fichiers point.dat et ligne.dat et, à partir des
données lues, d'initialiser les tableaux de points et de droites. La méthode est bien simple. On
ouvre le fichier, on lit la première ligne qui représente le nombre de points (ou lignes) à lire
et ensuite on a deux boucles "for" imbriquées qui initialisent les tableaux.
On déclarera en global les tableaux de lignes et de points en coordonnées homogènes 3D comme
expliqué le mois dernier :
Tant qu'on y est, on déclare le tableau de point écran.
Ainsi, que deux entrées int nbrligne, nbrpoint qui recevront respectivement le nombre de lignes
et de points de l'objet. A partir de là, on écrit nos deux fonctions. Voici le code source :
La transformation de l'objet pour affichage
Voici maintenant la fonction transforme() qui permet de transformer notre objet 3D en
objet écran à l'aide d'une projection dimétrique. Par manque de place, cette fonction sera décrite
le mois prochain. En attendant voici le source en C :
Maintenant que nous avons la projection dimétrique qui nous permet de transformer nos points
3D en points écran, il nous reste à voir comment afficher notre objet en coordonnées écran.
Les deux instructions qui vont nous permettre de tracer à l'écran les lignes représentant le contenu
de l'objet sont Move et Draw. La fonction Move() positionne le RastPort (que l'on peut assimiler
à la pointe d'un crayon) aux coordonnées XE, YE spécifiées en paramètres.
Ainsi, dans notre programme, si l'on considère la ligne Li[P0,P1] pour se positionner sur
le point origine de la ligne on tapera :
La fonction Draw() trace une droite de la position courante du RastPort jusqu'aux coordonnées XE2,
YE2 spécifiées en paramètres.
Si l'on reprend notre ligne Li[P0,P1], on tracera le segment de droite en tapant :
A partir de là, la fonction affiche(short) qui affiche l'objet est très simple :
Le paramètre couleur est utilisé par la fonction SetApen(struct RastPort *, short) qui détermine
quel registre de couleur sera utilisé par le RastPort pour le tracé (couleur du crayon).
Le reste
Que reste-t-il à faire désormais ? Eh bien, plus grand chose à vrai dire :
1. Déclarer quelques "include" utiles. En voici la liste :
2. Écrire un programme principal dont l'utilité est d'appeler dans un ordre logique les différentes
fonctions que l'on a décrites précédemment. On déclarera en global les deux angles teta et alpha
de la projection.
NB : CIAAPRA est défini comme :
Il s'agit de l'adresse physique d'un registre machine : le Port A du CIA A.
C'est barbare, mais c'est très court !
3. Enfin, avant de terminer le programme, on aura pris soin de refermer l'écran et les bibliothèques.
Voici la fonction qui fait cela ;
C'est fini, il ne vous reste plus qu'à taper ce petit programme et à créer un objet de
votre choix sous forme de fichiers.
Mise à jour de septembre 2020 : 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/ant3dgraphicsdrawlines.lha.
Conclusion
Comme il ne me reste que très peu de place, je me contenterai de vous souhaiter une excellente
lecture en vous retrouvant dans un mois pour la description des différentes projections possibles
et autres transformations amusantes.
|