Académique Documents
Professionnel Documents
Culture Documents
Moteur graphique 3D
Casio fx-9860G
Olivier Lanneau - NineStars
01/05/2019
Lien vers planète casio :
https://www.planet-casio.com/Fr/forums/lecture_sujet.php?id=14525
Main
Scene A
Scene B
...
Scene A
draw update
fps max fps constants
Cette distinction isole le moteur graphique du reste du jeu afin d’éviter une
dépendance entre la partie graphique et physique. Ainsi on profite d'un maximum
d’images par seconde tout en ayant une physique contrôlée.
void launch();
void update();
void draw();
void terminate();
Etant donné que passer d’une scène à autre déclenche l’appel de launch et
terminate, les scènes ne doivent contenir aucune information à sauvegarder.
Toutes les information à sauvegarder se trouvent dans game.
Avant de créer une nouvelle, il est nécessaire d’aborder des notions sur le
fonctionnement de Windmill : Les repères et les scènes.
Le repère propre :
Y
C'est dans ce repère que sont définis les triangles et
rectangles composants un objet.
X
Z monde
Le repère monde :
Le repère écran :
Z écran
C'est le repère propre à l'écran. Il possède un X écran
troisième axe perpendiculaire à l’écran
représentant la distance du pixel par rapport Y écran
à la caméra
Voici l'arborescence de la structure des données nécessaires pour créer une carte.
Coordonnées Type
Image
Axe de
Poly 1 Textures
rotation
Paramètres de
Objet 1
la texture
Modèle Poly 2 Coordonnées
Cette arborescence parait lourde, mais une fois que les textures et les polygones
sont crées, la création d'une carte est très rapide puisqu'il suffit de les réutiliser.
La première étape est de définir une carte, c’est à dire, un ensemble d’objets à
afficher dans le monde virtuel, avec quelques options supplémentaires.
objets est un pointeur sur une liste d'objets et nb_objets le nombre d'objets dans
cette liste.
Horizon indique si la ligne d'horizon est affichée sur cette carte.
Sol indique si le motif du sol est affiché sur cette carte.
Il y a donc 2 objets sur cette carte, ces objets sont définies dans le tableau
objets_exemple, la ligne d’horizon ainsi que le sol sont affichés.
Remarque :
Les deux objets font références au même modèle modele_exemple. Les deux
objets vont donc être basés sur les mêmes polygones, cependant il seront
positionnés différemment sur la carte. Il n’est pas nécessaire de modéliser deux
fois le même objet.
Maintenant que nous avons vu comment créer un objet, passons à l’étape suivante.
Le modèle 3D !
Pour des soucis de mémoire, de performance et de simplicité, la création d’un
modèle 3D est réduite à l’utilisation de triangles et de rectangles, créés de façon
indépendantes. Il ne s’agit pas d’un réseau maillé de points.
D'abord :
- Quelle est la taille de la maison ?
- Comment décomposer la maison en triangles et rectangles ?
- Où mettre l'origine de son repère propre ?
Taille de la maison
On choisit une maison de 5m de long (sur X) par 2.5m de large (sur Y) par 3m de
haut (sur Z).
Le toit fera 1m de haut (sur Z).
Remarque : Quand il faut choisir les dimensions d'un modèle, il ne faut pas
raisonner en pixel mais en mètres. Les pixels n'ont rien à voir avec les dimensions
d'un modèle et le jeu ne sera pas plus rapide si la maison fait 16x8x4 ou 153x17x13.
Décomposition
Elle sera composée de 4 rectangles oranges, 2 rectangles verts et 2 triangles bleus.
Origine
L'origine de son repère sera dans un coin.
Remarque : L’objet tourne autour du point 0, c’est à dire l’origine de son repère
propre. Ici le coin de la maison.
Il est important d’avoir une réponse à ces trois question avant de commencer un
modèle.
1m
Z
3m Y
2.5m
X
5m
Pour un soucis de performance, les coordonnées des points d'un polygone sont des
entiers. Si l'on indiquait les coordonnées des polygones directement en mètres il ne
serait pas possible de faire des détails inférieurs au mètre (car il faudrait un
nombre entre 0 et 1)
Ainsi on pose une convention d'échelle entre les coordonnées réelles et les
coordonnées des points des polygones. Un bon compromis est :
10
Z
30 Y
25
X
50
30 Y
50
Il est conseillé pour chaque polygone de s’imaginer face à lui, et de dessiner un
petit repère xyz avec les côte de largeur et hauteur.
Il est important de noter que de ce point de vue est à l’extérieur de la maison. On
regarde le mur depuis l’extérieur.
30
50
Y
Cependant, ce côté là du rectangle ne nous intéresse pas, seul l’extérieur nous
intéresse donc on se place sur le premier point de vue.
Le triangle
Un triangle est défini par une texture pour la face avant, une texture pour la face
arrière, et 3 points dans l'espace.
Le repère dans lequel sont définie ces points est le repère propre de l'objet.
2
Pour définir une face, l’ordre de déclaration des
points est important afin d’afficher la texture de
devant et pas celle de dos.
En tournant dans le sens trigo, c’est la texture avant
tex_front qui est affichée.
0 1
Le rectangle
0 1
30
0 X 1
50
On applique la texture tex_exemple sur la face avant, aucune texture sur la face
arrière, et les 3 point sont définis. Pour rappel, le fait d’avoir stipulé via le premier
paramètre que le polygone est un rectangle, a automatiquement crée le dernier
point.
Pour simplifier la création des polygones, il est recommandé de créer des variables
de compilateur :
#define L 50// longueur X
#define H 30// hauteur Z
10
Z
30 Y
25
X
50
10
0 1
30
Y
25
Le tout est décalé sur X de 50.
{TRIANGLE, &tex_exemple, NULL, 50,0,30, 50,25,30, 50,12,40}
Même logique que pour le rectangle. Une remarque toutefois sur le dernier point.
Sa coordonnées sur Y est 12 et non 25/2=12.5 car les coordonnées doivent être des
nombres entiers. Dans notre cas, on pourrait très bien changer les dimensions de la
maison et imposer une largeur de 2,6m soit 26. Cela garantit la symétrie, mais
restons comme cela.
#define L 50 // longueur X
#define W 25 // largueur Y
#define W2 12 // largeur divise par deux
#define H 30 // hauteur mur Z
#define HT 40 // hauteur toit Z
const Poly modele_maison[] = {
// les murs
{RECTANGLE, &tex_exemple, NULL, 0,0,0, L,0,0, 0,0,H},
{RECTANGLE, &tex_exemple, NULL, L,0,0, L,W,0, L,0,H},
{RECTANGLE, &tex_exemple, NULL, L,W,0, 0,W,0, L,W,H},
{RECTANGLE, &tex_exemple, NULL, 0,W,0, 0,0,0, 0,W,H},
// prolongement mur jusqu au toit
{TRIANGLE, &tex_exemple, NULL, L,0,H, L,W,H, L,W2,HT},
{TRIANGLE, &tex_exemple, NULL, 0,W,H, 0,0,H, 0,W2,HT},
// le toit
{RECTANGLE, &tex_exemple, NULL, L,W,H, 0,W,H, L,W2,HT},
{RECTANGLE, &tex_exemple, NULL, 0,0,H, L,0,H, 0,W2,HT},
};
Ensuite, il faut créer les objets en tant que tel, au dessus, il ne s’agit que d’un
modèle.
Object* liste_exemple[] = {
&maison1, &maison2
};
Nous avons fait l’étape la plus délicate. Elle demande une bonne vision dans
l'espace et de l'habitude. Ne pas hésiter à griffonner sur papier, marquer les axes,
et commenter chaque poly pour noter son utilité.
Le plus difficile a été fait ! Maintenant tout va aller très vite.
Coordonnées Type
Image
Axe de
Poly 1 Textures
rotation
Paramètres de
Objet 1
la texture
Modèle Poly 2 Coordonnées
Liste d'objets Objet 2
Paramètres de
Carte
collision
...
Paramètres de
la map
...
Une image est codée en binaire à l'aide d'un logiciel de type Sprite Coder, la
largeur et la hauteur de l’image doivent être une puissance de deux (2, 4, 8, 16,
32, ...). La largeur et la hauteur peuvent être différentes.
La texture choisie en exemple est, je vous l’accorde, très étrange. C’est par contre
un bon exemple car elle est asymétrique, ce sera davantage parlant pour la suite
des exemples.
Général
Une texture se compose d'une image, un masque, des dimensions en pixel de
l'image, des dimensions réelles, du décalage et trois marqueurs pour la
transparence, un marqueur de décoration et le mode miroir.
struct Texture
{
const unsigned char* sprite;
const unsigned char* mask;
const char pixel_width;
const char pixel_height;
const char real_width;
const char real_height;
const float offset;
const bool transparent;
const bool decoration;
const bool mirror;
};
Les attributes pixels_height et pixel_width correspondent à la taille en pixel du
sprite, soit 32 et 32 dans notre cas. Ces tailles peuvent être différentes l’une de
l’autre mais doivent impérativement être des multiples de 2.
Taille réelle
Prenons le premier mur de notre maison (soit selon la convention choisie un mur de
5x3 mètres, explication plus haut), vu de face, sur lequel on applique
tex_exemple.
Offset
L’exemple du dessus est appliqué à un rectangle, voyons maintenant comment cela
se passe avec un triangle.
Offset = 0.5
Offset = 1.0
Ce paramètre sera utilisé sur les deux triangles bleu de notre exemple.
Masque de transparence
Le masque de transparence permet d’afficher une texture uniquement quand le
même pixel du masque est noir.
Autres attributs
Le paramètre transparent indique que les pixels blancs de l'image sont
transparents.
Le paramètre decoration permet d’afficher la texture en priorité. Il s’affichera au
dessus de tout autre polygone avec le quel il devrait se confondre.
Le paramètre mirror indique si la texture est affichée en mode miroir sur la face
arrière.
Remarque : Les dimensions réelles d'une texture n'ont aucune raison d'être les
mêmes que les dimensions en pixels.
Bilan
Tout les éléments de la chaine sont maitrisés, voici le code final pour dessiner la
maison :
// TEXTURES
const unsigned char sprite_exemple[]={
0xff,0xff,0xff,0xff,
0x87,0xff,0xfc,0x01,
0xb4,0x00,0x05,0xfd,
0xb4,0x00,0x05,0xfd,
0x84,0x00,0x04,0x01,
0xfc,0x00,0x07,0xff,
0xc0,0x00,0x00,0x01,
0xc0,0xfc,0x00,0x01,
0xc3,0x86,0x00,0x01,
0xcc,0x03,0x00,0x01,
0xc9,0x99,0x00,0x01,
0xc9,0x99,0x07,0xff,
0xc8,0x01,0x04,0x01,
0xc8,0x01,0x04,0x01,
0xc8,0xf1,0x04,0x01,
0xcc,0x62,0xff,0xff,
0xc6,0x06,0x80,0x21,
0xc3,0xfc,0x80,0x21,
0xc0,0x00,0x80,0x21,
0xc0,0x00,0xff,0xff,
0xc0,0x00,0x04,0x01,
0xc0,0x00,0x04,0x01,
0xc0,0x00,0x04,0x01,
0xc0,0x00,0x07,0xff,
0xc0,0x00,0x00,0x01,
0xc0,0x00,0x80,0x01,
0xfc,0x01,0xc0,0x3f,
0xc4,0x03,0xe0,0x3f,
0xa4,0x07,0x70,0x33,
0x94,0x0e,0x38,0x33,
0x8f,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
};
// MODELE
#define L 50 // longueur X
#define W 25 // largueur Y
#define W2 12 // largeur divise par deux
#define H 30 // hauteur mur Z
#define HT 40 // hauteur toit Z
// OBJET
Object maison1 = { 33,-50, 0, 0, N, modele_maison, 8};
Object maison2 = {100, 12, 0, 90, Z, modele_maison, 8};
Object* liste_exemple[] = {
&maison1, &maison2
};
// CARTE
Map map_exemple = {liste_exemple, 2, true, true};
En ajoutant différentes textures, comme une texture de brique pour les murs et de
paille pour le toit, on obtient un rendu plus réaliste :
Les murs en longueur sont bien vides. Nous allons voir comment ajouter des
détails.
Pour cela nous ajoutons deux rectangles sur chaque façade représentant des
fenêtres.
On utilise l’image suivante :
Remarque :
Pour s’assurer un meilleur rendu, il faut placer les poly avec une texture de type
décoration en dernier dans la liste des polygones du modèle.
Nous venons de rajouter 2 polygones à notre modèle, il faut penser à mettre à jour
la taille du modèle dans les objets :
Une fois un objet placé comme souhaité, noter ses coordonnées x, y, z ainsi que
son angle puis reportez en dur dans le fichier.
Après la prochaine compilation, les objets seront là où vous venez de les placer.
Windmill windmill;
windmill.Windmill(&tex_black, &tex_white);
windmill.load_map(map);
windmill.set_camera(&camera);
windmill.draw();