Support du VRML dans la biblioth`eque CImg

Transcription

Support du VRML dans la biblioth`eque CImg
Institut
Supérieur d’
Informatique de
Modélisation et de leurs
Applications
The
Cool
Image
Library
Projet ISIMA troisième année
Option F4 : Calcul et Modélisation Scientifique
Support du VRML dans la bibliothèque
CImg
Rédigé par :
Tuteur de projet :
G REGORY R AMI
DAVID T SCHUMPERL É
A NN ÉE 2010/2011
RAMI
REMERCIEMENTS
REMERCIEMENTS
Je tiens tout simplement à remercier M. David Tschumplerlé, mon tuteur de stage et créateur de CImg,
pour ses explications claires et sa très bonne présentation du projet qui m’ont permis de rentrer très rapidement dans le vif du sujet. J’ai ainsi pu avancer assez vite dans mon travail et le finir à temps. Merci
également pour avoir relevé, pendant la phase de test, les points qui m’avaient échappés.
Projet ISIMA 3ème année
RAMI
GLOSSAIRE
GLOSSAIRE
CImg :
Librairie open source développée en C++ contenant de nombreux outils facilitant le traitement
d’images.
Langage C++ : Langage de programmation informatique orienté objet.
Noeud :
Structure VRML représentée par l’utilisation d’un mot clé et généralement délimitée par des
accolades. Par exemple, le noeud ”Shape” permet de définir un objet 3D.
OFF :
Object File Format, type de fichier permettant de stocker la représentation d’un objet 3D en
spécifiant les polygones, représentant les faces de l’objet. C’est aujourd’hui le seul format
de fichier implémenté dans CImg permettant le stockage d’objets 3D.
Plugin :
Bout de programme qui rajoute des fonctionnalités à un programme principal.
Primitives :
Nom donné à la représentation d’une face (au sens d’un maillage) dans CImg. Une primitive
contient la liste des sommets composant la face d’un objet.
VRML :
Virtual Reality Modeling Language est à la fois un format de fichier et un langage qui permet
de représenter des objets 3D qui peuvent être mis en scène dans des univers 3D virtuels. Ce
langage est assez simple d’utilisation étant basé sur un principe de noeud décrivant
de manière logique l’univers 3D à représenter.
Projet ISIMA 3ème année
RAMI
RÉSUMÉ / ABSTRACT
RÉSUMÉ
La librairie CImg est une bibliothèque open source développée en C++ contenant un grand nombre
d’outils pour le traitement d’images. Cette librairie se définit par sa simplicité d’utilisation, sa portabilité
mais aussi par son efficacité et son extensibilité. À ce titre, le besoin s’est fait sentir de pouvoir prendre
en charge dans CImg le format VRML. Le Virtual Reality Modeling Language(VRML) est à la fois un
langage de présentation et un format de fichier. Comme son nom l’indique le VRML est un langage qui
permet de représenter des univers interactifs 3D virtuels. Les avantages de ce langage sont sa simplicité et
ses multiples fonctionnalités en comparaison du seul autre type de fichier pris en charge dans CImg pour la
3D : le format .off.
Le besoin d’avoir un support pour un tel format dans une librairie de traitement d’images est apparu
comme évident. Mon travail fut par conséquent de coder deux méthodes : load vrml() et save vrml() qui
permettent respectivement de charger un fichier VRML dans CImg et de sauvegarder un objet de CImg
dans un fichier VRML. Le codage de ces fonctions a été assez complexe et leurs validations a été effectuées
sur un ensemble de tests dans tous les cas particuliers possibles. La prise en charge de la texture également
ne marque pas la fin du développement de ces deux méthodes car VRML permet aussi des animations de
scènes assez poussées qui ne sont pas gérées par les fonctions développées ici.
Mots-clés : librairie CImg, format VRML, load vrml(), save vrml()
ABSTRACT
The CImg library is an open source library developed in C++ containing a variety of tools to process
images. This library is defined by its simplicity, its portability, and also by its efficiency and extensibility.
Therefore, the need appears to have a support of the VRML format in CImg. The Virtual Reality Modeling
Language (VRML) is all at once a presentation language and a file format. It is a language that enables
users to represent virtual 3D interactive universe. The qualities of this language are its simplicity and power
compared to the only other type of file supported by CImg for 3D : the .off format.
The need to support this type of file in an image processing library appeared obvious. My work was
consequently to develop two methods : load vrml() and save vrml() that enable respectively to load a
VRML file in CImg and to save a CImg object into a VRML file. The programming of these functions was
quite complicated and to be sure they met all the requirements, I ran a set of tests covering all possible
cases. The support of texturized images does not mean the end of these two functions programming. In fact,
VRML also enables powerful 3D scene animation that is not yet handled in CImg.
Keywords : CImg library, VRML format, load vrml(), save vrml()
Projet ISIMA 3ème année
RAMI
TABLE DES MATIÈRES
TABLE DES MATIÈRES
REMERCIEMENTS
GLOSSAIRE
RÉSUMÉ / ABSTRACT
TABLE DES MATIÈRES
TABLE DES FIGURES
INTRODUCTION
8
1 Présentation du problème
9
1.1 Rappel du sujet . . . . . .
1.2 Connaissance du problème
1.3 Analyse du problème . . .
1.4 Étude du problème . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 Déroulement de l’étude
2.1 Prise en main de CImg et VRML . .
2.1.1 La bibliothèque CImg . . . .
2.1.2 Le langage VRML . . . . .
2.2 Les algorithmes de principe . . . . .
2.3 Les choix d’implémentation . . . .
2.3.1 Implémentation de save vrml
2.3.2 Implémentation de load vrml
13
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3 Résultats et discussion
3.1 Résultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
CONCLUSION
RÉFÉRENCES BIBLIOGRAPHIQUES
ANNEXES
Projet ISIMA 3ème année
10
10
11
12
14
14
17
19
24
24
25
26
27
29
31
RAMI
TABLE DES FIGURES
TABLE DES FIGURES
1
2
3
4
5
6
7
Objet du projet . . . . . . . . . . . . . . . . . . . . .
Repérage des composantes d’un pavé droit dans CImg
Représentation d’un pavé droit dans CImg . . . . . . .
Code utilisé pour les tests des méthodes . . . . . . . .
Résultat de la méthode load vrml() . . . . . . . . . . .
Résultat de la méthode save vrml() . . . . . . . . . . .
Chronologie non exhaustive du projet . . . . . . . . .
Projet ISIMA 3ème année
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
15
16
27
29
29
30
RAMI
INTRODUCTION
INTRODUCTION
Dans le cadre de ma troisième année d’études à l’ISIMA, j’ai effectué en monôme un projet de 120 heures
autour de la librairie CImg, encadré par M. David Tschumperlé. La librairie CImg est une boı̂te à outils C++
qui contient un grand nombre de méthodes qui facilitent le traitement d’images. Cette bibliothèque posséde
un visualiseur d’objets 3D maillés, mais ne permet pas de lire beaucoup de formats de fichiers différents
pour ce type de données 3D (seulement les formats .OFF et .CIMGZ actuellement). Il apparait alors le
besoin d’être capable d’utiliser des formats de fichiers différents qui sont plus évolués et qui permettent
d’exploiter plus grandement les capacités de CImg. Le choix s’est porté sur le format de fichier VRML
(Virtual Markup Reality Language). Le VRML est un language et un format de fichier standardisé qui permet de représenter des objets 3D que l’on peut mettre en animation dans des univers virtuels 3D de manière
assez simple. Ce format est fort utilisé et a été accepté comme norme internationale par l’organisme ISO
(International Organization for Standardization).
Dans ce contexte, mon travail consistait à développer les fonctions C++ permettant la sauvegarde et
le chargement de données 3D maillées en format VRML. Ces fonctions load vrml() et save vrml() loin
d’exploiter toutes les capacités du VRML doivent permettre d’enregistrer et charger des objets 3D du plus
simple (cube,sphère,...) au plus complexe (formes 3D arbitraires). Si les deux méthodes développées sont
fonctionnelles quant à l’affichage d’objets 3D, la suite du projet était d’afficher des objets 3D texturés. Il
n’était pas demandé ici de développer la gestion d’animation d’objets 3D et les contraintes temporelles
l’empêchaient.
J’exposerai dans une première partie l’objet de l’étude. Dans un deuxième temps, je présenterai CImg et
VRML puis les algorithmes de principe des deux méthodes développées et enfin les difficultés rencontrées.
Pour finir, dans une troisième partie j’aborderai les résultats des tests que j’ai effectués afin de vérifier le
bon fonctionnement des méthodes développées et j’analyserai les résultats de ce projet.
Projet ISIMA 3ème année
Page 8
RAMI
PARTIE 1
PREMIÈRE PARTIE
Présentation du problème
Projet ISIMA 3ème année
Page 9
RAMI
PARTIE 1
1.1. Rappel du sujet
Le sujet de mon projet était de fournir des méthodes à la librairie CImg qui lui permettent de sauvegarder
ou de charger des objets maillés 3D dans le format VRML. Ce choix s’explique par la volonté d’une plus
grande flexibilité dans la gestion et le choix des formats de fichiers susceptibles d’être utilisés par la librairie
CImg. De plus, le language VRML est assez simple à prendre en mains et à comprendre. Ce format permet
également d’animer des objets 3D dans un univers virtuel ou encore d’appliquer des textures sur les objets
et tout cela toujours de manière assez simple.
L’objectif de tout mon travail consistait donc à coder deux fonctions en language C++. Ces deux fonctions
ont pour signature :
– CImg<T>& load vrml(const char *const filename, CImgList<tf>& primitives, CImgList<tc>&
colors)
– const CImg<T>& save vrml(const char *const filename,const CImgList<tf>& primitives, const
CImgList<tc>& colors, const char *const texturefile = 0) const
La première doit permettre de charger un objet ou un ensemble d’objets 3D et de les retranscrire au format
interne à CImg afin de pouvoir les utiliser dans la librairie CImg. À partir d’un nom de fichier ou d’un objet
de type File, on récupére la liste des primitives et des couleurs de l’objet 3D et l’on retourne la liste des
points de l’objet.
La seconde doit sauvegarder dans un fichier VRML un objet 3D de CImg désigné par ses primitives, ses
couleurs et sa texture s’il en a une.
1.2. Connaissance du problème
Actuellement la librairie CImg ne permet d’utiliser que deux types de fichiers pour la gestion d’images
3D, le format .off et .cimgz. Le format cimgz est un format propre à CImg et les fichiers OFF sont un format
de fichier très simple. En effet, un fichier OFF est toujours formé de la même façon : le mot clé OFF puis à
la ligne le nombre de sommets, le nombre de faces et le nombre de bords du modèle (cette valeur n’est pas
utile). Ensuite, les coordonnées des sommets sont listées, ainsi que les sommets composant chaque faces. La
librairie CImg contient déjà les fonctions save off() et load off() qui permettent respectivement de sauver et
charger un objet dans un fichier OFF. On veut pouvoir maintenant utiliser un format très répandu et reconnu
par la norme ISO : le format VRML. On veut donc pouvoir utiliser de la même façon que save off() et
load off() les fonctions save vrml() et load vrml().
Projet ISIMA 3ème année
Page 10
RAMI
PARTIE 1
1.3. Analyse du problème
Actuellement, si l’on fournit un fichier .off décrivant par exemple un cube, par la fonction load off(),
on charge le fichier en question dans CImg. Le fonctionnement est le suivant : la méthode load off() est
appelé par un objet de type CImg<tp> dans lequel est retourné la liste des sommets du cube. Puis la liste
des sommets composant les faces est stockée dans l’objet CImgList<tf>& primitives, on aura donc ici
dans ”primitives” une liste de 6 groupes de quatre points (un cube est composé de 6 faces définies par
quatre sommets). On aura finalement dans l’objet CImgList<tc>& colors la liste des couleurs de chaque
face, définie dans l’exemple du cube par une liste de 6 groupes de 3 valeurs (la couleur de chaque face est
définit par leur niveau de rouge, vert et bleu). On voudrait pouvoir réaliser la même chose avec la méthode
load vrml() pour des fichiers de type VRML.
De même, si actuellement on appelle la méthode save off() à partir d’un objet de type CImg<tp>
contenant une liste de points et qu’on fournit à la méthode un objet CImgList<tf>& primitives contenant
les faces, un objet CImgList<tc>& colors contenant les couleurs et le nom du fichier .off dans lequel
on veut sauvegarder cet objet on aura alors un fichier de type OFF contenant notre objet. On voudrait
également que cela fonctionne de la même manière pour la méthode save vrml().
La difficulté et le but du projet étaient de faire le lien entre les deux types de représentation : le VRML
et ses balises et CImg qui décompose l’objet en sommets, faces et couleurs. La figure 1 représente la
problèmatique :
F IG . 1 – Objet du projet
Projet ISIMA 3ème année
Page 11
RAMI
PARTIE 1
1.4. Étude du problème
Aujourd’hui lorsqu’on crée un objet 3D avec CImg, on ne peut que le sauvegarder au format .off. Ce
type de fichier n’est rien d’autre qu’un fichier texte contenant la liste des sommets et des faces composant
l’objet. Le langage VRML permet d’animer des scènes virtuelles et d’y incorporer une quantité illimitée
d’objets 3D. Devant la limitation des fichiers OFF le besoin se fait sentir, pour une librairie graphique
comme CImg, d’avoir recours à un type de fichier et de représentation plus évolués.
Dans cette optique, mon projet consiste à développer les méthodes load vrml() et save vrml(). J’ai
développé ces deux méthodes en C++ afin de pouvoir les incorporer directement dans CImg si leur
fonctionnement est assuré.
Pour m’assurer du bon fonctionnement de ces deux méthodes, j’ai essayé de dresser une liste exhaustive
de tous les cas pouvant être rencontrés dans leurs utilisations. Il s’agissait ensuite de tester les méthodes sur
tous ces cas particuliers, et d’adapter si besoin était, mon code.
Projet ISIMA 3ème année
Page 12
RAMI
PARTIE 2
DEUXIÈME PARTIE
Déroulement de l’étude
Projet ISIMA 3ème année
Page 13
RAMI
PARTIE 2
2.1. Prise en main de CImg et VRML
2.1.1 La bibliothèque CImg
La bibliothèque CImg est une librairie contenant un très grand nombre d’outils qui facilitent le traitement
des images. Il existe également des outils pour afficher des objets 3D, leur appliquer des transformations,
des textures, etc. À la base de toutes ces méthodes et ces outils il faut bien comprendre la façon dont CImg
stocke et représente les objets 3D.
Tout objet 3D dans CImg est représenté par trois éléments :
– Un objet de type CImg que l’on appellera points
– Un objet de type CImgList que l’on appellera primitives
– Un objet de type CImgList que l’on appellera colors
L’objet points contient les coordonnées des points composant l’objet 3D. On peut voir l’objet points
comme une liste de valeurs réelles avec la liste des abscisses, des ordonnées puis des côtes. Si l’on prend
l’exemple d’un parallélépipède rectangle, ou pavé droit, de longueur 10, de hauteur 20 et de profondeur 30
on obtient l’objet points suivant :
0 10 10 0 0 10 10 0 0 0 20 20 0 0 20 20 0 0 0 0 30 30 30 30
Ceci correspond à la liste des abscisses puis des ordonnées et finalement des côtes. Si l’on regroupe ces
valeurs par 3 toutes les 8 valeurs (il y a 8 sommets dans un pavé droit), on obtient les 8 sommets de notre
parallélépipède rectangle :
(0,0,0)
-> 0
(10,0,0) -> 1
(10,20,0) -> 2
(0,20,0) -> 3
(0,0,30) -> 4
(10,0,30) -> 5
(10,20,30) -> 6
(0,20,30) -> 7
La représentation est donc assez simple, on fait la liste des coordonnées dans une CImg qui est ici une
simple liste de valeurs.
Dans la représentation des points j’ai associé à chaque point un numéro. Ceci n’est pas anodin car il s’agit
en fait de la procédure utilisée par CImg pour repérer les points et ensuite pouvoir utiliser cette numérotation
dans la représentation des primitives. En effet, pour représenter les primitives de notre pavé droit, CImg va
utiliser un objet de type CImgList que l’on appelle primitives. Cet objet comme son nom l’indique est une
liste d’objets de type CImg qui sert à savoir quels sont les points à relier pour créer une face. Ces points
seront repérés par leurs indices. L’objet primitives décrit donc la composition de chacune des faces, dans
notre exemple un pavé droit a 6 faces, notre liste primitives contiendra donc 6 objets de type CImg. Dans
chacun de ces objets on trouvera les points composant la face. Dans notre exemple, l’objet primitives est
composé comme suit :
0 3 2 1
4 5 6 7
0 1 5 4
3 7 6 2
Projet ISIMA 3ème année
Page 14
RAMI
PARTIE 2
0 4 7 3
1 2 6 5
Le schéma ci-dessous reprend ce système de représentation :
F IG . 2 – Repérage des composantes d’un pavé droit dans CImg
Cette représentation est assez simple et elle correspond en tant que code C++ aux lignes suivantes :
CImgList<unsigned int> primitives ;
const CImg<float> points = CImg<float> : :box3d(primitives,10,20,30) ;
La première ligne crée simplement un objet de type CImgList qui va contenir des entiers non signés (entiers positifs). La deuxième ligne appelle la méthode box3d(CImgList<T> p,int x,int y,int z) qui retourne
les coordonnées d’un pavé droit de taille xyz et qui remplit la CImgList p avec les primitives correspondantes.
Il reste ensuite à définir la couleur de notre objet 3D. Pour cela, CImg utilise une CImgList que l’on
appellera ici colors. La méthode employée est de définir une couleur pour chaque primitive d’un objet, la
taille de colors sera donc toujours égale à celle de primitives. Chaque couleur est définie par trois valeurs :
un niveau de rouge, de vert et de bleu. L’objet colors est donc une liste d’objets de type CImg contenant
trois valeurs, cette liste d’objets correspond à chacune des primitives. Si l’on reprend l’exemple du pavé
droit, on va avoir 6 objets de type CImg donnant la couleur de chacune des 6 faces de notre pavé. Si par
exemple on veut que toutes les faces du pavé soient rouges, on obtient l’objet colors suivant :
255 0 0
255 0 0
255 0 0
255 0 0
255 0 0
255 0 0
Si on reprend le contenu de primitives, on obtient que la face composée des sommets 0 3 2 1 aura
Projet ISIMA 3ème année
Page 15
RAMI
PARTIE 2
la couleur (255,0,0), la face composée des sommets 4 5 6 7 aura la couleur (255,0,0) etc. Le code C++
correspondant est :
CImgList<float> colors ;
colors.insert(primitives.size(),CImg<unsigned char> : :vector(255,0,0)) ;
Dans la première ligne on crée simplement un objet de type CImgList qui va contenir des valeurs réelles
(type float). Dans la deuxième ligne on appelle la méthode insert (const unsigned int n, const CImg<t>
img) sur colors, ce qui va avoir pour effet de remplir colors avec n fois la CImg img. En effet, la méthode
vector(int r, int g, int b) retourne un vecteur contenant les valeurs r,g et b.
Le schéma ci-dessous récapitule la situation.
F IG . 3 – Représentation d’un pavé droit dans CImg
De la même manière que dans l’exemple précédent, on peut créer toute forme d’objets en utilisant des
méthodes comme sphere3d(primitives, rayon), cylinder3d(primitives, rayon, hauteur), cone3d(primitives,
rayon, hauteur), etc en lieu et place de box3d(primitives, x, y, z). On peut également créer n’importe quelle
forme d’objet tant que l’on respecte la structure des objets points, primitives et colors.
Lorsque l’on veut appliquer une texture à un objet dans CImg on utilise la méthode texturize object3d(CImgList<tp> primitives, CImgList<tc> colors, CImg<tt> texture).
On voit que l’on passe en argument à cette méthode les objets primitives et colors car c’est dans ces
structures que l’on va faire les changements correspondant à la texturisation de l’objet. Dans l’objet colors, la méthode texturize object3d va simplement ajouter toutes les couleurs composant l’image utilisée
comme texture correspondant à l’objet CImg<tt> texture. L’objet primitives est modifié de la manière suivante : pour toutes les primitives on ajoute, à la suite des indices, les coordonnées de la zone de l’image
que l’on va utiliser comme texture. On aura par conséquent dans primitives des vecteurs de la forme
(i0,i1,i2,x0,y0,x1,y1,x2,y2) où i0, i1 et i2 sont les indices des points composant la primitive et les points
Projet ISIMA 3ème année
Page 16
RAMI
PARTIE 2
(x0,y0), (x1,y1) et (x2,y2) correspondent aux sommets du triangle dans l’image utilisée comme texture de
la primitive.
La représentation sous CImg d’un objet 3D est très structurée et on va maintenant voir comment cette
représentation s’articule en VRML pour appréhender la façon dont se construit l’interface entre les deux
formats.
2.1.2 Le langage VRML
Dans toute la conduite du projet je me suis focalisé sur l’utilisation du VRML 2.0. Il existe plusieurs
versions du VRML qui différent dans leurs syntaxes, le choix s’est donc porté sur la version 2.0 la plus
récente à l’heure actuelle.
En langage VRML la représentation et le codage d’un objet sont très ”textuels”. Le langage VRML se
rapproche très fortement du langage HTML, en effet ici tout fonctionne sous forme de balise et mots clés.
Pour simplifier, et pour coller aux fonctions que j’ai développées, tout nouvel objet est déclaré par le mot
clé Shape. Dans une structure Shape il existe seulement 2 mots clés : geometry et appearance. ”geometry”
permet comme son nom l’indique de définir la géométrie de l’objet à créer tandis que avec la structure
appearance on va pouvoir modifier l’apparence de notre objet : sa couleur, sa texture, sa transparence ...
Si l’on reprend l’exemple précédent du pavé droit rouge, on obtient le fichier VRML suivant :
Shape {
geometry Box {
size 10 20 30
}
appearance Appearance {
material Material {
diffuseColor 1,0,0
}
}
}
La représentation en VRML est donc très simple. Si l’on analyse le code précédent, on observe qu’il
suffit de déclarer un noeud Shape pour définir la structure d’un objet. Ensuite le mot clé geometry permet
de préciser le type d’objet que l’on veut dessiner, ici une ”box” ou en terme plus mathématique : un pavé
droit. Dans ce noeud geometry on définit les propriétés de l’objet que l’on veut créer, ici les dimensions du
pavé. Ensuite on définit l’apparence de l’objet dans la structure appearance. Ici on veut donner une couleur
à notre objet, on utilise pour cela le mot clé Material. Dans ce noeud on définit la couleur que l’on souhaite
en niveau de rouge, vert et bleu de 0 à 1. Le terme diffuseColor permet simplement de dire que l’on veut
que l’intensité de la couleur soit diffuse. On peut également dans une structure geometry à la place de
“Box” utiliser “Cone”, “Cylinder” ou “Sphere” et définir dans le noeud les paramètres de chacun de ces
objets. De même dans une structure appearance on peut définir une texture par la commande suivante :
texture ImageTexture url[“nomFichier.jpg”]
Il est également possible de définir la texturisation de l’image pixel par pixel afin de choisir les parties de
l’image que l’on souhaitent texturiser.
Dans le noeud appearance, il y a la structure Material qui permet de définir tout aspect visuel d’un objet
Projet ISIMA 3ème année
Page 17
RAMI
PARTIE 2
autre que la texture. Ci-dessous la liste de ses modifications d’aspect :
– diffuseColor 1 0 0.5 définit la couleur de l’objet qui varie en fonction de l’angle de réflection de la
lumière sur l’objet.
– emissiveColor 0.2 0 0.1 définit une couleur propre à l’objet, comme s’il était luminescent.
– specularColor 0.2 0.6 0.2 définit la couleur des rayons lumineux réfléchis sur la surface de l’objet et
renvoyés vers le point de vue de l’utilisateur.
– ambientIntensity 0.3 coefficient entre 0 et 1 correspondant à l’influence de la lumière ambiante de la
scène sur l’objet.
– shininess 0.5 coefficient entre 0 et 1 correspondant au niveau de brillance de l’objet.
– transparency 0.3 coefficient entre 0 et 1 correspondant au niveau de transparence de l’objet.
Il existe également en langage VRML une manière de créer des objets 3D de formes quelconques.
Pour cela il faut déterminer les coordonnées de chaque point et la composition de chaque face, on peut
ensuite également déterminer une couleur pour chaque face. Il y a trois autres types de geometry qui
peuvent être utilisés : PointSet, IndexedLineSet et IndexedFaceSet.
Par PointSet on peut définir une série de points, IndexedLineSet permet de définir des séries de segments
et IndexedFaceSet permet de définir des séries de faces. Ci-après on retrouve la structure de ces trois types
de géométries :
geometry PointSet {
coord Coordinate {
point [
-2 0 2,
-2 0 -2,
]
}
}
geometry IndexedLineSet(IndexedFaceSet) {
coord Coordinate {
point [
]
-2 0 2,
-2 0 -2,
}
coordIndex [
0, 1, -1,
]
}
Les structures de IndexedLineSet et de IndexedFaceSet sont similaires. Le noeud coord permet de
donner les coordonnées de chacun des sommets composant l’objet à créer, chaque ligne étant composée
par l’abscisse, l’ordonnée, la côte du point. Ensuite, dans coordIndex on donne la liste des connexions à
faire entre les points, en désignant les points par leurs indices. Dans l’exemple ci-dessus on connecte le
point (-2,0,2) au point (-2,0,-2). La valeur -1 sert à désigner la fin de la liste d’indices.
Projet ISIMA 3ème année
Page 18
RAMI
PARTIE 2
2.2. Les algorithmes de principe
Dans cette partie je vais présenter rapidement les algorithmes de principe des deux méthodes que j’ai
codées.
Dans un premier temps je vais présenter l’algorithme de principe de la méthode save vrml() qui permet de
sauvegarder un objet créé dans CImg au format VRML.
save vrml() :
Si le fichier cible n’existe pas ou si l’objet à sauver est vide ou si ce n’est pas un objet 3D alors
Lever une exception
Parcourir CImgList colors pour déterminer si l’objet contient qu’une seule couleur
Ouvrir le fichier cible
Écrire “VRML V2.0 utf8” dans le fichier cible
Écrire “Shape
geometry IndexedFaceSet
coord Coordinate
point [” dans le fichier cible
Pour tous les points de l’objet
Écrire l’abscisse, l’ordonnée,la côte dans le fichier cible
Écrire “coordIndex[” dans le fichier cible
Pour toutes les primitives de l’objet
Définir l’objet color valant la couleur de la primitive courante si cette couleur est définie, la couleur par
défaut sinon
Définir le niveau de rouge comme étant la 1ère composante de color
Si le niveau de vert est défini alors
Définir la couleur associée
Sinon
Définir le niveau de vert égal au niveau de rouge
Si le niveau de bleu est défini alors
Définir la couleur associée
Sinon
Définir le niveau de bleu égal au niveau de vert
Écrire la liste des primitives dans le fichier cible
Si il y’a plus d’une couleur dans l’objet alors
Si on est à la première couleur alors
Ajouter cette couleur au vecteur listant les couleurs
Ajouter l’indice 0 à la chaine de caractères des indices de couleur
Sinon
Chercher dans le vecteur listant les couleurs si la couleur actuelle est déjà présente
Si la couleur n’est pas présente
Ajouter cette couleur au vecteur listant les couleurs
Ajouter l’indice taille du vecteur à la chaine de caractères des indices de couleur
Sinon
Chercher l’indice correspondant à l’endroit où se trouve la couleur dans le vecteur
Ajouter cette indice à la chaine de caractères des indices de couleur
Écrire ”]” dans le fichier cible
Projet ISIMA 3ème année
Page 19
RAMI
PARTIE 2
Si une texture a été définie alors
Écrire ”}
appearance Appearance {
texture ImageTexture {
url [nomDuFichier]
}
}
}” dans le fichier cible
Sinon
Si il y a plus d’une couleur dans l’objet alors
Écrire ”colorPerVertex FALSE
color Color {
color [” dans le fichier cible
Tant que le vecteur listant les couleurs n’est pas vide
Écrire la couleur dans le fichier cible
Supprimer l’élément du vecteur
Écrire ”]
}
colorIndex [” dans le fichier cible
Écrire la chaine de caractères des indices de couleur dans le fichier cible
Écrire ”]
}
}” dans le fichier cible
Sinon
Écrire ”}
appearance Appearance {
material Material {
diffuseColor niveauDeRouge, niveauDeVert, niveauDeBleu
}
}
}”
Si l’utilisateur n’a pas fourni le fichier cible sous forme d’objet File alors
Fermer le fichier cible
Dans un deuxième temps je vais présenter l’algorithme de principe de la méthode load vrml() qui permet
de charger un objet d’un fichier VRML dans des objets CImg en respectant la représentation type de CImg
vue dans la partie 2.1.
load vrml() :
Si le fichier source n’existe pas alors
Lever une exception
Ouvrir le fichier source
Lire le fichier source jusqu’à trouver autre chose qu’une ligne de commentaire
Si la ligne atteinte dans le fichier n’est pas un noeud VRML alors
Lever une exception
Tant que l’on n’a pas atteint le noeud ”Shape” ou la fin du fichier
Projet ISIMA 3ème année
Page 20
RAMI
PARTIE 2
Lire dans le fichier
Tant que l’on n’a pas atteint les noeuds ”geometry” ou ”appearance” ou la fin du fichier
Lire dans le fichier
Tant que l’on n’est pas à la fin du fichier
Ici il s’agit de déterminer quel type de géométrie nous trouvons dans le fichier et de faire les traitements
adéquats. Je ne vais expliciter l’algorithme que pour le cas du pavé droit, les autres cas étant très sensiblement similaires. En effet, il s’agit toujours de chercher les paramètres de l’objet puis d’appeler la bonne
méthode (box3d, sphere3d, ...).
Si l’on est au noeud ”Box” alors
Tant que nous n’avons pas trouvé la taille du pavé droit
Parcourir le fichier source
Si les 3 dimensions du pavé ne sont pas définies alors
Lancer une exception
Appeler la méthode box3d pour récupérer les points et les primitives de l’objet
Si une texture est définie alors
Charger l’image utilisée comme texture
Initialiser l’objet colorsTextured
Appeler la méthode texturize object3d
Si le nombre de points est différent de 0 (il y’a déjà des objets dans la scène) alors
Pour toutes les primitives
Décaler les 4 premières composantes du nombre de points déjà existants
Ajouter l’objet primitivesTemp ainsi créé à la CImgList primitives de l’objet appelant
Pour tous les points du pavé
Ajouter les abscisses, les ordonnées puis les côtes de ces points à la liste des Points
Mettre à jour le nombre de points total de la scène
Les autres conditions sont des ”sinon si” pour la sphère, le cylindre et le cône. Le traitement pour le noeud
PointSet est également assez similaire, même si pour les noeuds PointSet, IndexedLineSet et IndexedFaceSet je ne gère pas les textures. Cependant je vais définir l’algorithme pour les noeuds IndexedFaceSet et
IndexedLineSet, le traitement de ces deux noeuds étant le même, il se fait dans une même et unique boucle.
Sinon si l’on est au noeud IndexedFaceSet ou IndexedLineSet alors
Tant que l’on n’a pas trouvé la structure ”point [”
Parcourir le fichier source
Tant que l’on n’a pas parcouru tous les points
Si les 3 coordonnées du point sont définies alors
Ajouter les abscisses, les ordonnées puis les côtes de ces points à la liste des Points
incrémenter le nombre de points
Initialiser la CImgList primitivesTemp
Tant que l’on n’a pas trouvé la structure ”coordIndex”
Parcourir le fichier source
Tant que l’on n’a pas parcouru tous les indices
Si la ligne n’est pas un commentaire alors
Découper la ligne à chaque virgule
Tant qu’il y a des éléments issus de ce découpage
Projet ISIMA 3ème année
Page 21
RAMI
PARTIE 2
Ajouter ces indices au vecteur primitiveComponents
Pour tous les objets de primitiveComponents
Transférer les indices de primitiveComponents dans l’objet CImg temp
ajouter cet objet temp à primitivesTemp
Si le nombre de points est différent de 0 (il y a déjà des objets dans la scène) alors
Pour toutes les primitives
Décaler toutes les composantes du nombre de points déjà existant
Ajouter l’objet primitivesTemp ainsi créé à la CImgList primitives de l’objet appelant
Mettre à jour le nombre de points total de la scène
Tant que l’on n’est pas à la structure ”color [” ou à la fin du noeud IndexedFaceSet(ou IndexedLineSet) ou si l’on est à la fin du fichier
Si l’on est à la structure ”color [” alors
Mettre le booléen multipleColors à vrai
Tant que l’on n’a pas parcouru toutes les couleurs
Si la ligne n’est pas un commentaire alors
Ajouter la CImg contenant la couleur au vecteur listColors
Tant que l’on n’a pas trouvé la structure ”colorIndex”
Parcourir le fichier source
Tant que l’on n’a pas parcouru tous les indices de couleur
Si la ligne n’est pas un commentaire alors
Découper la ligne à chaque espace
Tant qu’il y a des éléments issus de ce découpage
Ajouter l’élément de listColors correspondant à l’indice dans l’objet colors
Sinon
Lever une exception comme quoi aucun noeud de geométrie valide n’a été trouvé dans le fichier
source
Si l’objet a une texture alors
Ajouter l’objet colorsTextured dans la CImgList colors
Réinitialiser la chaine de caractères contenant le chemin de l’image de texture
Tant que l’on n’a pas trouvé le noeud ”material” ou ”texture” ou ”Shape” ou qu’on n’a pas atteint la fin
du fichier
Parcourir le fichier source
Le reste du code consiste au traitement de l’apparence de l’objet. On considère ici que l’on a trouvé le
noeud ”material” ou ”texture” ou ”Shape” ou qu’on a atteint la fin du fichier. Le cas où l’on est à la fin du
fichier ne demande aucun traitement spécial dû à l’implémentation du reste du code.
Si l’on est au noeud ”material” alors
Tant que l’on n’est pas sorti du noeud material ou que l’on n’a pas atteint la fin du fichier
Si l’on est à la ligne ”diffuseColor” et que l’on ne récupére pas bien la couleur alors
Lancer une exception
Si l’on est au noeud ”texture ImageTexture” alors
Passer les booléens textureTest et colorDefined respectivement à vrai et faux
Tant que l’on n’a pas trouvé l’url
Parcourir le fichier source
Découper la ligne par le caractère ”
Récupérer le deuxième élément de ce découpage
copier cet élément dans la chaine de caractères contenant le chemin vers l’image texture
Projet ISIMA 3ème année
Page 22
RAMI
PARTIE 2
Si l’on est au noeud ”Shape” alors
Mettre le booléen textureTest à faux
Si il existe des primitives d’objet et si une couleur est définie et s’il n’y a pas de couleurs multiples et si
l’objet n’est pas texturisé alors
Insérer dans colors l’objet colorsTemp contenant les niveaux de rouge, vert et bleu
Réinitialiser le nombre de primitives et les niveaux RGB
Allouer la taille de l’objet appelant
Pour tous les éléments de l’objet appelant
Y insérer les points du vecteur listePoints
Retourner l’objet appelant
On peut voir que le code est assez simple et il était assez simple de l’implémenter. La plus grande
difficulté fut de bien comprendre la façon dont CImg représente un objet 3D de manière interne et comment
faire l’interface entre CImg et VRML.
Je vais donc présenter, dans la dernière section de cette partie 2, les difficultés rencontrées et les choix
d’implémentation réalisés.
Projet ISIMA 3ème année
Page 23
RAMI
PARTIE 2
2.3. Les choix d’implémentation
L’implémentation des méthodes save vrml et load vrml consistait à créer une interface entre CImg
et le format VRML. La plus grosse problèmatique a donc été de comprendre les deux mécanismes de
représentation d’objets 3D et, une fois qu’ils furent maitrisés, de créer des passerelles entre les deux. La
méthode save vrml est assez simple car elle utilise la facilité du VRML sans avoir à en gérer sa vastitude.
La méthode load vrml ne peut l’éviter, les problèmatiques d’implémentation furent donc plus nombreuses
pour cette méthode. Dans un premier temps, je vais détailler les choix d’implémentation de la méthode
save vrml.
2.3.1 Implémentation de save vrml
L’implémentation de la méthode save vrml est assez simple. En effet, le but de cette méthode est de
prendre un objet 3D représenté dans CImg par une CImg points, une CImgList primitives et une CImgList
colors et de sauver cet objet au format VRML avec un type de géométrie et une apparence. Lorsqu’on a un
objet dans CImg il est assez compliqué d’en déterminer la nature géométrique. C’est pour cette raison que
j’ai fait le choix de représenter tous les objets par le type IndexedFaceSet. La grande flexibilité de ce type
permet de représenter tout type d’objet 3D sans avoir à se soucier de sa nature.
La méthode est alors très simple, on parcourt notre CImg points et on copie les coordonnées des points
dans la structure ”point [ ... ]” d’IndexedFaceSet. On a ainsi la liste des sommets de notre objet qui est
déjà correctement représentée dans notre fichier VRML. Le traitement des primitives est le même, le type
IndexedFaceSet contient une structure ”coordIndex [...]” qui donne les connexions à réaliser entre les sommets pour former les primitives de notre objet. Il s’agit donc de récupérer chacune des primitives de la
CImgList primitives et de copier leur contenu dans la structure ”coordIndex [...]”.
La problèmatique pour cette méthode réside finalement simplement dans le traitement de l’apparence
des objets 3D. En effet, il faut considérer et traiter indépendamment les trois cas de figure :
– L’objet est composé d’une seul couleur
– L’objet est composé de plusieurs couleurs
– L’objet est texturisé
Prenons tout d’abord le cas des couleurs. Pour déterminer si l’objet est composé d’une ou plusieurs couleurs
il n’y a pas d’autre solution que de parcourir la CImgList colors et de s’arrêter dès que l’on trouve une
couleur différente de la première. C’est l’implémentation que j’ai mise en place. Si l’objet est composé
d’une seule couleur le traitement est très simple, il suffit d’utiliser le noeud ”material” et de préciser à la
suite de diffuseColor le niveau de rouge, vert et bleu issu de n’importe quel élément de la CImgList colors.
Si l’on a détecté au moins deux couleurs, on va utiliser la flexibilité de IndexedFaceSet qui permet de
définir une couleur pour chaque primitive. Il existe dans le type IndexedFaceSet la possibilité d’utiliser
le noeud ”color”. Ce noeud doit être utilisé de la façon suivante : on utilise la structure ”color [...]” à
l’intérieur de laquelle on définit la palette des couleurs utilisées dans l’objet puis on utilise la structure
”colorIndex [...]”. Dans cette structure colorIndex il faut donner les indices des couleurs définies dans color
[...] en fonction des couleurs que l’on veut donner à chaque primitive. Par exemple si on donne la suite
d’entier suivante dans colorIndex : 0 1 1, cela veut dire que la première primitive aura la première couleur
définie dans color [...] et la deuxième et troisième primitive auront la seconde couleur définie. La méthode
choisie fut donc de parcourir la CImgList colors et de stocker dans des vecteurs la couleur et l’indice de
cette couleur. À chaque tour de boucle, il fallait parcourir notre vecteur de couleurs pour s’assurer que la
nouvelle couleur n’était pas déjà définie. Si elle l’était, on ajoute son indice au vecteur des indices.
Projet ISIMA 3ème année
Page 24
RAMI
PARTIE 2
Enfin pour le cas des textures, la différence de représentation entre VRML et CImg n’a guère laissé le
choix quant à l’implémentation. En effet, en VRML il suffit de définir le noeud ”texture ImageTexture” et
de donner l’URL de l’image utilisée comme texture. En CImg, on ajoute les couleurs de l’image texture
dans colors et on définit les zones de l’image utilisées pour chaque primitive. Il n’y a donc aucune façon,
lorsqu’on est en CImg, de savoir quelle est l’URL de l’image, si ce n’est en la passant directement en
argument de la méthode save vrml.
2.3.2 Implémentation de load vrml
L’implémentation de la méthode load vrml() se révéla plus complexe car il fallait gérer la diversité du
langage VRML et la prise en charge de scènes à objets multiples dans CImg. La boucle principale de cette
fonction est un parcours du fichier VRML ligne par ligne. Dans ce parcours, on traite la géométrie et l’apparence de l’objet. Il a donc fallu gérer indépendamment les 7 types de géométrie du langage VRML excepté
pour IndexedFaceSet et IndexedLineSet qui, ayant tout à fait la même structure, sont gérés ensemble. Pour
les 4 types de géométrie box, sphere, cylinder et cone l’implémentation fût très simple car il suffisait d’utiliser les méthodes déjà existantes dans CImg : box3d, sphere3d, cylinder3d et cone3d.
La première difficulté vint de la possibilité d’avoir des scènes 3D avec plus d’un objet. En effet, la
méthode load vrml retourne un seul de chaque objet points, primitives et colors. Il fallait donc regrouper
toutes les informations sur tous les objets 3D dans un seul exemplaire de composants CImg. Pour la structure
points j’ai stocké les coordonnées dans une liste de points intermédiaire avant de copier cette liste dans la
CImg points. La liste de points intermédiaire m’a permis de gérer le cas où l’on a de multiples objets 3D
dans la scène. Comme je l’ai précisé dans la section 1 de cette partie 2, l’objet primitives donne les indices
des points composant une face. De fait, si l’on a déjà des points présents dans la scène, l’objet primitives va
faire référence aux mauvais points, j’ai donc fait un décalage des indices du nombre de points déjà présents
dans la scène. Le décalage n’est pas nécessaire pour la structure colors car l’ordre est implicite dans cette
structure. Toutefois, dans le décalage des indices de primitives, il faut être vigilant lorsque l’objet est texturé
de ne pas déplacer les valeurs de primitives faisant référence aux coordonnées de l’image de texture.
L’autre grande difficulté fût de bien gérer l’apparence des objets. Dans le cas d’un objet à couleurs
multiples, défini dans le type IndexedFaceSet ou IndexedLineSet, j’ai d’abord stocké dans un vecteur toutes
les couleurs définies dans la structure ”color[...]” puis directement copié chacune des couleurs indicées par
la liste issu de ”colorIndex[...]” dans la CImgList colors.
Une autre difficulté venait de la gestion des apparences quant à savoir si l’objet est texturé, composé d’une
seule couleur ou de plusieurs. J’ai réglé ce problème en utilisant 4 booléens qui m’ont permis de gérer les
4 cas possibles (on pose n le nombre de primitives de l’objet courant) :
– Aucune couleur n’est définie dans le fichier VRML, il faut dans ce cas ajouter dans colors n fois la
CImg correspondant à la couleur par défaut.
– Une seule couleur est définie, on copie dans colors n fois la CImg correspondant à la couleur unique
de l’objet.
– Plusieurs couleurs sont définies, cas détaillé ci-dessus.
– Une texture est définie, on utilise la méthode texturize object3d() implémentée dans CImg.
Le langage VRML permet également de définir à la fois des couleurs et des textures pour un même objet,
en texturisant seulement une partie de l’objet. Je n’ai pas géré ce cas-là tout comme d’autres fonctionnalités
du VRML.
Projet ISIMA 3ème année
Page 25
RAMI
PARTIE 3
TROISIÈME PARTIE
Résultats et discussion
Projet ISIMA 3ème année
Page 26
RAMI
PARTIE 3
3.1. Résultats
L’objectif de mon projet était de coder deux fonctions dans le langage C++. Ces fonctions étaient
sensées faire le lien entre deux types de représentation d’objet 3D : le VRML et CImg. Durant la phase de
développement du code j’ai régulièrement testé mes deux fonctions pour vérifier la justesse de mon code.
Cependant, une fois les deux méthodes complétement codées, j’ai essayé de lister tous les cas possibles
d’utilisation.
La liste des cas est similaire pour les deux méthodes :
1. un objet quelconque sans couleur ni texture
2. un objet quelconque avec une couleur unique
3. un objet quelconque avec au moins deux couleurs
4. un objet quelconque avec une texture
5. au moins deux objets avec des couleurs uniques
6. au moins deux objets avec des textures et des couleurs
Cette liste de tests m’a permis de repérer un grand nombre d’erreur d’implémentation et de faire les rectifications nécessaires pour essayer de satisfaire tous ces cas. J’ai effectué les tests tour à tour sur les méthodes
save vrml() et load vrml(). Pour tester ces méthodes, le code utilisé est représenté dans la figure 4.
F IG . 4 – Code utilisé pour les tests des méthodes
Pour la fonction load vrml() tous les cas ont été vérifiés avec succès. Le processus est de créer le fichier
.wrl approprié et d’observer l’affichage résultat de la méthode. Par exemple, pour le test numéro 6 de la
méthode load vrml(), j’ai fourni le fichier “FichierTest.wrl” suivant :
Shape {
Projet ISIMA 3ème année
Page 27
RAMI
PARTIE 3
appearance Appearance {
texture ImageTexture {
url [”brick1.jpg”]
}
}
geometry Box {
size 30 20 10
}
}
Shape {
geometry Box {
size 10 20 30
}
appearance Appearance {
material Material {
diffuseColor 0.831373,0.141176,0.125490
}
}
}
Shape {
geometry Sphere {
radius 2
}
appearance Appearance {
material Material {
diffuseColor 0.141176,0.125490,0.831373
}
}
}
Le résultat de l’exécution du code de test sur ce fichier VRML est représenté dans la figure 5 :
En ce qui concerne les tests pour la méthode save vrml(), ils sont tous concluants sauf le test numéro
6. Cela s’explique par la façon dont j’ai choisi d’implémenter les méthodes save vrml() et load vrml(). En
effet, j’ai fait le choix de coder save vrml() de sorte que je puisse l’utiliser en parallèle de load vrml().
C’est-à-dire, pouvoir appeler load vrml() pour charger un fichier VRML dans les objets points, primitives,
colors et les utiliser pour appeler la méthode save vrml(). Ainsi, le problème apparait lorsque l’on utilise
load vrml() avec un fichier qui décrit plusieurs objets dont certains ont une couleur et d’autres ont une
texture. À ce moment-là nous avons une CImg points et une CImgList primitives qui contiennent tous les
points et toutes les primitives de nos objets. Il est alors difficile de repérer quels points et primitives décrivent
tel ou tel objet, et lorsque la méthode save vrml est appelée, ce travail n’est pas fait. De plus, pour avoir le
chemin du fichier image utilisé comme texture, on doit le passer en argument de la méthode save vrml().
Lorsque dans cette méthode on repére que l’on a un chemin de fichier pour la texture, on décréte que notre
objet est texturisé on crée ainsi un noeud texture et on ne fait pas la différence entre l’apparence des deux
objets. Par exemple, pour le test numéro 6, j’ai pris deux objets : deux pavés droits qui ont pour dimensions
respectives (10,20,30) et (30,-5,-6). -5 et -6 indiquent seulement que l’on va dans les valeurs négatives du
repère. De plus le premier pavé est défini avec une texture de “brique” et le deuxième est défini avec une
couleur bleue. L’appel à la méthode save vrml() a généré un fichier .wrl dont l’affichage par la visionneuse
Projet ISIMA 3ème année
Page 28
RAMI
PARTIE 3
F IG . 5 – Résultat de la méthode load vrml()
Cortona 3D est donné dans la figure 6 :
F IG . 6 – Résultat de la méthode save vrml()
3.2. Discussion
On observe que les résultats obtenus sont bons et les plugins load vrml et save vrml sont aujourd’hui
fonctionnels et ils ont été ajoutés par Mr. David Tschumperlé à la librairie CImg. Cependant, on observe
qu’il reste beaucoup de travail à faire. Le projet était sensé mener à deux méthodes qui chargent et sauvent
des fichiers VRML faisant ainsi l’interface entre la bibliothèque CImg et le langage VRML.
Dans la phase de test on observe que la méthode save vrml() telle qu’elle est actuellement implémentée
ne permet pas de sauver plusieurs objets 3D ayant des couleurs et des textures. Ce problème peut-être résolu
en repensant l’implémentation des méthodes sous forme de listes d’objets 3D. Effectivement, lorsque l’on
utilise la méthode load vrml() à partir d’un fichier décrivant plusieurs objets 3D, on place dans une même
Projet ISIMA 3ème année
Page 29
RAMI
PARTIE 3
structure, “CImg points”, tous les sommets composant les objets. De même avec les structures “CImgList
colors” et “CImgList primitives”. On pourrait ainsi retourner, par cette méthode load vrml, une CImgList
de points et des CImgList de CImgList de primitives et colors. On aurait alors une division bien distincte
entre chacun des objets 3D défini et on pourrait ainsi restituer les caractéristiques de chaque objet et appeler
la méthode save vrml() sans aucune contrariété. Il faudrait bien évidemment adapter le code save vrml() à
une telle représentation de scènes.
Il y a également d’autres améliorations à faire afin que le code soit plus “propre”. Comme se débarasser
des inclusions de classes comme “vector” et “string” en utilisant à la place des objets CImg et CImgList.
On pourrait en outre adapter le code afin qu’il soit plus flexible à l’implémentation des fichiers VRML. Par
exemple, actuellement, pour utiliser une texture il faut la placer avant le noeud “geometry” d’un objet, cela
peut s’avérer génant si l’utilisateur ne respecte pas ces règles d’implémentation car l’erreur est difficilement
détectable.
De même, de par les nombreuses possibilités offertes par le langage VRML, il reste fort à faire dans la
poursuite de ce projet. On pourra donc implémenter les différents effets de couleur et de luminance proposés
par le VRML et faire la différence entre emissiveColor, diffuseColor et specularColor ou encore influer sur
la transparence et la brillance de l’objet par les options transparency et shininess. Le langage VRML permet
également de texturiser seulement des zones d’un objet. Ce genre d’option est tout à fait implémentable en
CImg.
On pourra également implémenter toutes les possibilités liées au noeud “Transform” qui permet d’appliquer
des transformations à notre objet. On peut par exemple faire subir des rotations, des translations ou encore
des changements d’échelle à notre objet. Il est également possible de réaliser des extrusions sur un objet ou
d’ajouter du texte dans une scène, là encore se sont des fonctionnalités qui peuvent être implémentées car
on en retrouve un équivalent dans CImg.
Il y a ensuite d’énormes possibilités sur l’animation de scènes et l’intéraction avec celles-ci. Le langage
VRML est donc très complet et il reste énormément de travail à accomplir si l’on veut avoir un support total
du VRML dans CImg.
La figure 3.4 récapitule la chronologie de mon travail.
F IG . 7 – Chronologie non exhaustive du projet
Projet ISIMA 3ème année
Page 30
RAMI
CONCLUSION
CONCLUSION
L’objectif de ce projet était de développer deux fonctions C++ load vrml() et save vrml() afin d’avoir
un support du langage VRML dans la bibliothèque CImg. Le résultat obtenu qui a servi a la phase de test
est l’affichage d’une scène 3D pour la méthode load vrml() et la création d’un fichier .wrl pour la méthode
save vrml(), fichier .wrl que l’on visualise par un visualiseur VRML. La consigne du projet était d’obtenir
un support du VRML pour des objets 3D maillés quelconques. Puis, si cela fonctionnait bien, de gérer les
primitives texturées. J’ai atteint ces deux objectifs en ayant toutefois géré les textures seulement pour les
objets 3D standards de la bibliothèque CImg.
Les principales difficultés que j’ai rencontré étaient principalement liées à la compréhension de la représentation des objets 3D dans CImg. Pouvoir gérer les différentes possibilités d’apparence d’un objet et choisir
la bonne implémentation en tant qu’objets CImg. Pour me simplifier un peu la tâche j’ai utilisé des objets
de type vector et de type string car ces types sont dynamiques. Cependant, l’inclusion d’en-tête réduit la
portabilité du code. Une autre difficulté fut le caractère assez complet du VRML qui en fait un langage très
étoffé, il a donc fallu gérer souvent de nombreux cas de figure et prendre en compte au fur et à mesure des
fonctionnalités ou possibilités que je n’avais pas envisagées. Ces problèmes je les ai assez bien résolus par
les différentes phases de tests.
Cette vastitude dans les capacités du langage VRML laisse la porte ouverte à l’extension du projet. On
pourra implémenter les différentes fonctionnalités offertes par le VRML que je n’ai pas eu le temps de
mettre en place. Je poursuivrai donc ce projet par la suite en continuant le développement de ces deux plugins. Outre des extensions, il faut aussi éliminer les inclusions réalisées et adapter le code en conséquence.
On pourra également gérer la texture d’objets créés par IndexedFaceSet.
Projet ISIMA 3ème année
Page 31
RAMI
RÉFÉRENCES BIBLIOGRAPHIQUES
RÉFÉRENCES BIBLIOGRAPHIQUES
Projet ISIMA 3ème année
RAMI
RÉFÉRENCES BIBLIOGRAPHIQUES
Webographie
[Site web officiel CImg] http ://cimg.sourceforge.net/, Octobre 2010
[Wikipedia VRML] http ://en.wikipedia.org/wiki/VRML, Octobre 2010
[Tutoriel VRML 2.0] http ://www.cs.iupui.edu/ aharris/webDesign/vrml/, Octobre 2010
[Fichier OFF] http ://shape.cs.princeton.edu/benchmark/documentation/off format.html, Octobre 2010
[Apprendre le VRML] http ://www.web3d-fr.com/tutoriels/Atelier-VRML.php, Novembre 2010
[VRML viewer] http ://www.cortona3d.com/Products/Cortona-3D-Viewer.aspx, Novembre 2010
[C++.com] http ://www.cplusplus.com/, Novembre 2010
Projet ISIMA 3ème année
RAMI
ANNEXES
ANNEXES
Projet ISIMA 3ème année
RAMI
ANNEXES
A. Code de la méthode save vrml()
Ci-dessous, le code complet de la méthode save vrml() commenté et indenté.
save vrml.cpp
1 // ! Save VRML files.
2 template<typename tf, typename tc>
3 const CImg<T>& save_vrml(const char *const filename,const CImgList<tf>& primitives, const CImgList<tc>& colors, const
char *const texturefile = 0) const {
4
return _save_vrml(0,filename,primitives,colors,texturefile);
5}
6
7 // ! Save VRML files.
8 template<typename tf, typename tc>
9 const CImg<T>& save_vrml(std::FILE *const file,const CImgList<tf>& primitives, const CImgList<tc>& colors, const char *
const texturefile = 0) const {
10
return _save_vrml(file,0,primitives,colors,texturefile);
11 }
12
13 // Save VRML files ( internal ) .
14 template<typename tf, typename tc>
15 const CImg<T>& _save_vrml(std::FILE *const file, const char *const filename, const CImgList<tf>& primitives, const CImgList
<tc>& colors, const char *const texturefile) const{
16
// Check that the user furnished a file to save the object and that the object is not empty.
17
if (! file && !filename)
18
throw CImgArgumentException(_cimg_instance "save_vrml() : Specified filename is (null).",cimg_instance);
19
if (is_empty())
20
throw CImgInstanceException(_cimg_instance "save_vrml() : Empty instance, for file ’%s’.",cimg_instance,
filename?filename:"(FILE*)");
21
// Check that the object we want to save is a 3D object .
22
CImgList<T> opacities;
23
char error_message[1024] = {0};
24
if (! is_object3d(primitives,colors,opacities,true,error_message))
25
throw CImgInstanceException(_cimg_instance "save_vrml() : Invalid specified 3d object, for file ’%s’ (%s
).",cimg_instance,filename?filename:"(FILE*)",error_message);
26
const CImg<tc> default_color(1,3,1,1,200);
27
// We open the file in which we will save the 3D object .
28
std::FILE * nfile;
29
if (file)
30
nfile = file;
31
else
32
nfile = cimg::fopen(filename,"w");
33
std::fprintf(nfile,"#VRML V2.0 utf8\n"); // We use the version 2.0 of VRML
34
// We copy the coordinates of all the points
35
std::fprintf(nfile,"Shape {\n\tgeometry IndexedFaceSet {\n\t\tcoord Coordinate {\n\t\t\tpoint [\n");
36
cimg_forX(*this,i)
37
std::fprintf(nfile,"\t\t\t\t%f %f %f,\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2) ) ) ;
38
std::fprintf(nfile,"\t\t\t]\n\t\t}\n\t\tcoordIndex [\n");
39
bool sameColor = true;
40
float r = colors[0][0]/255.0f, g = colors[0][1]/255.0f, b = colors[0][2]/255.0f;
41
std::vector<std::string> listColor;
42
std::string listColorPerFace("");
43
for ( int i=0;i<(int)colors.size();++i) {// Test if the object is composed of only one color
44
float valR = (colors[i][0])/255.0f, valG = (colors[i][1])/255.0f, valB = (colors[i][2])/255.0f;
45
if (r!=valR || g!=valG || b!=valB) { // If the object has different colors
46
sameColor = false;
47
i = colors.size();
48
}
49
}
50
cimglist_for(primitives,l) { // For each primitive
51
const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
52
const unsigned int psiz = primitives[l].size(), csiz = color.size();
53
float r = color[0]/255.0f, g, b;
54
if (csiz > 1)
55
g = color[1]/255.0f;
56
else
57
g = r/255.0f;
58
if (csiz > 2)
59
b = color[2]/255.0f;
60
else
61
b = g/255.0f;
62
switch (psiz) {
Projet ISIMA 3ème année
RAMI
ANNEXES
case 1 :
std::fprintf(nfile,"\t\t\t%u,-1\n",(unsigned int)primitives(l,0)); break;
case 6 :
case 2 :
std::fprintf(nfile,"\t\t\t%u,%u,-1\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1)); break;
case 9 :
case 3 :
std::fprintf(nfile,"\t\t\t%u,%u,%u,-1\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),(unsigned int)
primitives(l,1)); break;
case 12 :
case 4 :
std::fprintf(nfile,"\t\t\t%u,%u,%u,%u,-1\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),(unsigned
int)primitives(l,2),(unsigned int)primitives(l,1)); break;
63
64
65
66
67
68
69
70
71
72
73
}
if (! sameColor) { // If there are different colors we store on every loop the RGB values into the vector listColor
std::ostringstream oss;
oss << r << " " << g << " " << b << "\n";
if (listColor.size() == 0) {
listColor.push_back(oss.str());
listColorPerFace += "0"; // We store the indice of the color
}
else {
std::vector<std::string>::iterator it;
it = find (listColor.begin(), listColor.end(), oss.str());
std::ostringstream oss2;
if (it==listColor.end()) {
oss2 << " " << listColor.size();
listColorPerFace += oss2.str();
listColor.push_back(oss.str());
}
else {
int n = 0;
for (std::vector<std::string>::iterator iter = listColor.begin(); iter != it; iter++)
n++;
oss2 << " " << n;
listColorPerFace += oss2.str();
}
}
}
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 }
}
std::fprintf(nfile,"\t\t]\n");
if (texturefile) // If we have a texture instead of a color
std::fprintf(nfile,"\n\t}\n\tappearance Appearance {\n\t\ttexture ImageTexture {\n\t\t\turl [\"%s\"]\n\t
\t}\n\t}\n}",texturefile);
else {
if (! sameColor) { // If there are different colors we add all of them
std::fprintf(nfile,"\tcolorPerVertex FALSE\n\tcolor Color {\n\t\tcolor [\n");
while (! listColor.empty()) {
std::fprintf(nfile,"\t\t\t%s",(listColor.back()).c_str());
listColor.pop_back();
}
std::fprintf(nfile,"\t\t]\n\t}\n\tcolorIndex [\n\t\t");
std::fprintf(nfile,"%s",listColorPerFace.c_str());
std::fprintf(nfile,"\n\t]\n\t}\n}");
}
else // If there is only one color we add it with the Material node
std::fprintf(nfile,"\t}\n\tappearance Appearance {\n\t\tmaterial Material {\n\t\t\tdiffuseColor %f,%f
,%f\n\t\t}\n\t}\n}",colors[0][0]/255.0f,colors[0][1]/255.0f,colors[0][2]/255.0f);
}
if (! file)
cimg::fclose(nfile);
return * this ;
Projet ISIMA 3ème année
RAMI
ANNEXES
B. Code de la méthode load vrml()
Ci-dessous, le code complet de la méthode load vrml() commenté et indenté.
load vrml.cpp
1 // ! Load a 3d object from a .VRML file.
2 template<typename tf, typename tc> CImg<T>& load_vrml(const char *const filename, CImgList<tf>& primitives, CImgList<tc
>& colors) {
3
return _load_vrml(0,filename,primitives,colors);
4}
5
6 template<typename tf, typename tc>
7 static CImg<T> get_load_vrml(const char *const filename, CImgList<tf>& primitives, CImgList<tc>& colors) {
8
return CImg<T>().load_vrml(filename,primitives,colors);
9}
10
11 // ! Load a 3d object from a .VRML file.
12 template<typename tf, typename tc>
13 CImg<T>& load_vrml(std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors) {
14
return _load_vrml(file,0,primitives,colors);
15 }
16
17 template<typename tf, typename tc>
18 static CImg<T> get_load_vrml(std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors) {
19
return CImg<T>().load_vrml(file,primitives,colors);
20 }
21
22 template<typename tf, typename tc>
23 CImg<T>& _load_vrml(std::FILE *const file, const char *const filename,CImgList<tf>& primitives, CImgList<tc>& colors)
{
24
if (! file && !filename)
25
throw CImgArgumentException(_cimg_instance "load_vrml() : Specified filename is (null).",cimg_instance);
26
std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
27
char line[256] = {0};
28
int err;
29
// Skip comments, and read the first node
30
do {
31
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
32
}
33
while (! err || (err==1 && *line==’#’));
34
// Check for a vrml valid node
35
if (cimg::strncasecmp(line,"Shape",5) && cimg::strncasecmp(line,"Transform",9) && cimg::strncasecmp(line,"
NavigationInfo",14) && cimg::strncasecmp(line,"Billboard",9)) {
36
if (! file)
37
cimg::fclose(nfile);
38
throw CImgIOException(_cimg_instance "load_vrml() : VRML nodes not found in file ’%s’.",cimg_instance,
filename?filename:"(FILE*)");
39
}
40
// Look for the Shape node (as we do not manage the treatment for the other nodes yet )
41
while(cimg::strncasecmp(line,"Shape",5) && !feof(nfile))
42
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
43
if (feof(nfile)) {
44
if (! file)
45
cimg::fclose(nfile);
46
throw CImgIOException(_cimg_instance "load_vrml() : Shape node not found in file ’%s’.",cimg_instance,
filename?filename:"(FILE*)");
47
}
48
// Look for either geometry or appearance node
49
while(cimg::strncasecmp(line,"geometry",8) && cimg::strncasecmp(line,"appearance",10) && !feof(nfile))
50
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
51
if (feof(nfile)) { // If none of these nodes are defined
52
if (! file)
53
cimg::fclose(nfile);
54
throw CImgIOException(_cimg_instance "load_vrml() : geometry and appearance nodes not found in file ’%s
’.",cimg_instance,filename?filename:"(FILE*)");
55
}
56
std::vector<T> listePoints; // Intermediate list containing the points of the whole object
57
primitives.assign();
58
colors.assign();
59
int nbPointsTotal = 0, nbPrimitives = 0; // Count the number of points of the whole object and the number of primitives
60
float r = 0.7f, g = 0.7f, b = 0.7f; // RGB level of the object , the object is gray by default
Projet ISIMA 3ème année
RAMI
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
ANNEXES
bool colorDefined = true, multipleColors = false, textureTest = false; // Boolean used to know if a color is defined for an object , if this
object has multiple colors or if the object has a texture
char textureFile[256] = {0}; // Variable containing the name of the image used as a texture
while (! feof(nfile)) {
char type[256] = {0}, textureFileTemp[256] = {0};
colorDefined = true;
if (! cimg::strncasecmp(line,"geometry",8)) { // We are at the geometry node
std::sscanf(line,"geometry %s",type);// We are looking for the type of geometry to draw
const CImg<float> coords = CImg<float>::empty();// CImg used for the texturization of an object
CImgList<tc> colorsTextured;// CImgList used for the texturization of the color of an object
CImgList<tf> primitivesTemp;// Intermediate CImgList used to update the primitives of the whole object
if (! cimg::strncasecmp(type,"Box",3)) { // If the object to draw is a box
while(cimg::strncasecmp(line,"size",4) && !feof(nfile)) // We are looking for the size of the box
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) { // If no size is specified
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : size of box not defined in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
float X = 0, Y = 0, Z = 0; // The width, height and depth of the box
if (( err = std::sscanf(line,"size %f %f %f[ˆ\n] ",&X,&Y,&Z))!=3 && (err = std::sscanf(line,"size %f,%f,%f[ˆ\n
] ",&X,&Y,&Z))!=3) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read box size in file ’%s’.",
cimg_instance,filename?filename:"(FILE*)");
}
const CImg<T> pointsTemp = CImg<T>::box3d(primitivesTemp,(T)X,(T)Y,(T)Z); // We generate the primitives and the points of
the box
nbPrimitives = primitivesTemp.size(); // We save the number of primitives of the object
if (textureTest) { // If the object has a texture
const CImg<float> texture(textureFile);// We put the image used as a texture into a CImg object
colorsTextured.insert(primitivesTemp.size(),CImg<unsigned char>::vector(0,50,250));// We initialize the colorsTextured
list
pointsTemp.texturize_object3d(primitivesTemp,colorsTextured,texture,coords); // We texturize the object
nbPrimitives = 0;
}
if (nbPointsTotal) { // If there are already some objects in the scene
for ( int j=0;j<(int)primitivesTemp.size();j++) {
for ( int i=0;i<4;i++
primitivesTemp(j).at(i) += (tf)nbPointsTotal; // We shift the indices in the primitives to designate the right points
}
}
primitives.push_back(primitivesTemp); // We add the primitives of the box to the general primitives variable
for ( int i=0;i<(int)pointsTemp.size()/3;++i) { // We add the points into the temporary list in the right order
listePoints.push_back((T)pointsTemp.at(i));
listePoints.push_back((T)pointsTemp.at(i+8));
listePoints.push_back((T)pointsTemp.at(i+16));
}
nbPointsTotal += pointsTemp.size()/3; // We increase the number of points of the whole object
}
else if (! cimg::strncasecmp(type,"Sphere",6)) { // If the object to draw is a sphere
while(cimg::strncasecmp(line,"radius",6) && !feof(nfile))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : radius of sphere not defined in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
float R = 0;
if (( err = std::sscanf(line,"radius %f[ˆ\n] ",&R))!=1) { // We get the radius of the sphere
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read sphere radius in file ’%s’.",
cimg_instance,filename?filename:"(FILE*)");
}
const CImg<T> pointsTemp = CImg<T>::sphere3d(primitivesTemp,(T)R); // Compute the necessary points and primitives for a
sphere of radius R
nbPrimitives = primitivesTemp.size(); // We get the number of primitives to used on the attribution of a color , in case no
specific color is defined
if (textureTest) { // If the object has a texture
Projet ISIMA 3ème année
RAMI
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
ANNEXES
const CImg<float> texture(textureFile);// We put the image used as a texture into a CImg object
colorsTextured.insert(primitivesTemp.size(),CImg<unsigned char>::vector(0,50,250));// We initialize the colorsTextured
list
pointsTemp.texturize_object3d(primitivesTemp,colorsTextured,texture,coords); // We texturize the object
nbPrimitives = 0; // We set to 0 because there is no color to use
}
if (nbPointsTotal) { // If there are already some objects in the scene
for ( int j=0;j<(int)primitivesTemp.size();j++) {
for ( int i=0;i<3;i++)
primitivesTemp(j).at(i) += (tf)nbPointsTotal;
}
}
primitives.push_back(primitivesTemp);
for ( int i=0;i<(int)pointsTemp.size()/3;++i) {
listePoints.push_back((T)pointsTemp.at(i));
listePoints.push_back((T)pointsTemp.at(i+pointsTemp.size()/3));
listePoints.push_back((T)pointsTemp.at(i+2*pointsTemp.size()/3));
}
nbPointsTotal += pointsTemp.size()/3;
}
else if (! cimg::strncasecmp(type,"Cone",4)) { // If the object to draw is a cone
while(cimg::strncasecmp(line,"bottomRadius",12) && !feof(nfile) && cimg::strncasecmp(line,"height",6))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
float R = 0, H = 0;
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : bottom radius and height of cone not defined
in file ’%s’.",cimg_instance, filename?filename:"(FILE*)");
}
else if (! cimg::strncasecmp(line,"bottomRadius",12)) { // We find the bottom radius of the cone first
if (( err = std::sscanf(line,"bottomRadius %f[ˆ\n] ",&R))!=1) { // We get the radius into the variable R
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cone bottomRadius in file ’%
s’.",cimg_instance,filename?filename:"(FILE*)");
}
while (! feof(nfile) && cimg::strncasecmp(line,"height",6)) // We look for the height of the cone
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : height of cone not defined in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
if (( err = std::sscanf(line,"height %f[ˆ\n] ",&H))!=1) { // We get the height into the variable H
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cone height in file ’%s’.",
cimg_instance,filename?filename:"(FILE*)");
}
}
else { // We find the height of the cone first
if (( err = std::sscanf(line,"height %f[ˆ\n] ",&H))!=1) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cone height in file ’%s’.",
cimg_instance,filename?filename:"(FILE*)");
}
while (! feof(nfile) && cimg::strncasecmp(line,"bottomRadius",12))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : bottom radius of cone not defined in file
’%s’.",cimg_instance, filename?filename:"(FILE*)");
}
if (( err = std::sscanf(line,"bottomRadius %f[ˆ\n] ",&R))!=1) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cone bottom radius in file
’%s’.",cimg_instance,filename?filename:"(FILE*)");
}
Projet ISIMA 3ème année
RAMI
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
ANNEXES
}
const CImg<T> pointsTemp = CImg<T>::cone3d(primitivesTemp,(T)R,(T)H); // Compute the necessary points and primitives for a
cone of radius R and height H
nbPrimitives = primitivesTemp.size();
if (textureTest) { // If the object has a texture
const CImg<float> texture(textureFile);// We put the image used as a texture into a CImg object
colorsTextured.insert(primitivesTemp.size(),CImg<unsigned char>::vector(0,50,250)); // We initialize the
colorsTextured list
pointsTemp.texturize_object3d(primitivesTemp,colorsTextured,texture,coords); // We texturize the object
nbPrimitives = 0;
}
if (nbPointsTotal) {
for ( int j=0;j<(int)primitivesTemp.size();j++) {
for ( int i=0;i<3;i++)
primitivesTemp(j).at(i) += (tf)nbPointsTotal;
}
}
primitives.push_back(primitivesTemp);
for ( int i=0;i<(int)pointsTemp.size()/3;++i) {
listePoints.push_back((T)pointsTemp.at(i));
listePoints.push_back((T)pointsTemp.at(i+pointsTemp.size()/3));
listePoints.push_back((T)pointsTemp.at(i+2*pointsTemp.size()/3));
}
nbPointsTotal += pointsTemp.size()/3;
}
else if (! cimg::strncasecmp(type,"Cylinder",8)) { // If the object to draw is a cylinder
while(cimg::strncasecmp(line,"radius",6) && !feof(nfile) && cimg::strncasecmp(line,"height",6))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
float R = 0, H = 0;
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : radius or height of cylinder not
file ’%s’.",cimg_instance, filename?filename:"(FILE*)");
}
else if (! cimg::strncasecmp(line,"radius",6)) { // If we find the radius first
if (( err = std::sscanf(line,"radius %f[ˆ\n] ",&R))!=1) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cylinder radius
’.",cimg_instance,filename?filename:"(FILE*)");
}
while (! feof(nfile) && cimg::strncasecmp(line,"height",6)) // We now look for the height of the cylinder
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : height of cylinder not defined
’.",cimg_instance, filename?filename:"(FILE*)");
}
if (( err = std::sscanf(line,"height %f[ˆ\n] ",&H))!=1) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cylinder height
’.",cimg_instance,filename?filename:"(FILE*)");
}
}
else { // If we find the height first
if (( err = std::sscanf(line,"height %f[ˆ\n] ",&H))!=1) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cylinder height
’.",cimg_instance,filename?filename:"(FILE*)");
}
while (! feof(nfile) && cimg::strncasecmp(line,"radius",6))// We now look for the radius of the cylinder
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : radius of cylinder not defined
’.",cimg_instance, filename?filename:"(FILE*)");
}
if (( err = std::sscanf(line,"radius %f[ˆ\n] ",&R))!=1) {
Projet ISIMA 3ème année
defined in
in file ’%s
in file ’%s
in file ’%s
in file ’%s
in file ’%s
RAMI
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
ANNEXES
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : Failed to read cylinder radius in file ’%s
’.",cimg_instance,filename?filename:"(FILE*)");
}
}
const CImg<T> pointsTemp = CImg<T>::cylinder3d(primitivesTemp,(T)R,(T)H); // Compute the necessary points and primitives
for a cylinder of radius R and height H
if (textureTest) { // If the object has a texture
const CImg<float> texture(textureFile);// We put the image used as a texture into a CImg object
colorsTextured.insert(primitivesTemp.size(),CImg<unsigned char>::vector(0,50,250));// We initialize the colorsTextured
list
pointsTemp.texturize_object3d(primitivesTemp,colorsTextured,texture,coords); // We texturize the object
nbPrimitives = 0;
}
nbPrimitives = primitivesTemp.size();
if (nbPointsTotal) {
for ( int j=0;j<(int)primitivesTemp.size();j++) {
for ( int i=0;i<3;i++)
primitivesTemp(j).at(i) += (tf)nbPointsTotal;
}
}
primitives.push_back(primitivesTemp);
for ( int i=0;i<(int)pointsTemp.size()/3;++i) {
listePoints.push_back((T)pointsTemp.at(i));
listePoints.push_back((T)pointsTemp.at(i+pointsTemp.size()/3));
listePoints.push_back((T)pointsTemp.at(i+2*pointsTemp.size()/3));
}
nbPointsTotal += pointsTemp.size()/3;
}
else if (! cimg::strncasecmp(type,"PointSet",8)) { // If the object to draw is a set of points
while(cimg::strncasecmp(line,"point [",7) && !feof(nfile))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : points of pointSet node not defined in file
’%s’.",cimg_instance, filename?filename:"(FILE*)");
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
int nbPoints = 0;
while(cimg::strncasecmp(line,"]",1) && !feof(nfile)) { // while we did not get all the points and while we are not at the end of
the file
float X=0,Y=0,Z=0;
if (( err = std::sscanf(line,"%f %f %f,[ˆ\n] ",&X,&Y,&Z))==3 || (err = std::sscanf(line,"%f,%f,%f,[ˆ\n] ",&X
,&Y,&Z))==3) {
listePoints.push_back((T)X);
listePoints.push_back((T)Y);
listePoints.push_back((T)Z);
++nbPoints;
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
}
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : bad structure of pointSet node in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
primitivesTemp.assign();
for ( int i=0;i<nbPoints;++i) { // The primitive is only composed of the indice of the point itself
CImg<tf> temp(1,1,1,1,(tf)i);
primitivesTemp.push_back(temp);
}
if (nbPointsTotal) {
for ( int j=0;j<(int)primitivesTemp.size();j++) {
for ( int i=0;i<(int)primitivesTemp(j).size();i++)
primitivesTemp(j).at(i) += (tf)nbPointsTotal;
}
}
nbPrimitives = primitivesTemp.size();
primitives.push_back(primitivesTemp);
nbPointsTotal += nbPoints;
Projet ISIMA 3ème année
RAMI
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
ANNEXES
}
else if (! cimg::strncasecmp(type,"IndexedLineSet",14) || !cimg::strncasecmp(type,"IndexedFaceSet",14)) { // If the object
to draw is a set of lines or a set of faces
while(cimg::strncasecmp(line,"point [",7) && !feof(nfile))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : points of IndexedSet node not defined in file
’%s’.",cimg_instance, filename?filename:"(FILE*)");
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
int nbPoints = 0;
while(cimg::strncasecmp(line,"]",1) && !feof(nfile)) { // As long as there are points defined we add them to the list
float X=0,Y=0,Z=0;
if (( err = std::sscanf(line,"%f %f %f,[ˆ\n] ",&X,&Y,&Z))==3 || (err = std::sscanf(line,"%f,%f,%f,[ˆ\n] ",&X
,&Y,&Z))==3) {
listePoints.push_back((T)X);// We get the coordinates of the points into a list of points
listePoints.push_back((T)Y);
listePoints.push_back((T)Z);
++nbPoints;
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
}
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : bad structure of point vector node in file ’%
s’.",cimg_instance, filename?filename:"(FILE*)");
}
primitivesTemp.assign();
while(cimg::strncasecmp(line,"coordIndex [",12) && !feof(nfile)) // We are looking for the index of the points
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : coordIndex not furnished for IndexedSet node
in file ’%s’.",cimg_instance, filename?filename:"(FILE*)");
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
while(cimg::strncasecmp(line,"]",1) && !feof(nfile)) { // As long as there are indices
if (* line!=’#’) {
std::vector<tf> primitiveComponents;
char * pch;
pch = strtok (line,",");
while (pch != NULL && atof(pch)!=−1) { // We extract the list of indices and store them into a vector
if (!( int )count(primitiveComponents.begin(),primitiveComponents.end(),(tf)atof(pch)))
primitiveComponents.push_back((tf)atof(pch));
pch = strtok (NULL, ",");
}
CImg<tf> temp(1,primitiveComponents.size(),1,1);
for ( int i=0;i<(int)primitiveComponents.size();++i)
temp(0,i) = primitiveComponents.at(i);
primitivesTemp.push_back(temp);
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
}
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : bad structure of coordIndex in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
if (nbPointsTotal) {
for ( int j=0;j<(int)primitivesTemp.size();j++) {
for ( int i=0;i<(int)primitivesTemp(j).size();i++)
primitivesTemp(j).at(i) += (tf)nbPointsTotal;
}
}
nbPrimitives = primitivesTemp.size();
primitives.push_back(primitivesTemp);
nbPointsTotal += nbPoints;
while(cimg::strncasecmp(line,"color [",7) && cimg::strncasecmp(line,"}",1) && !feof(nfile))
Projet ISIMA 3ème année
RAMI
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
ANNEXES
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : bad structure of coordIndex in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
else if (! cimg::strncasecmp(line,"color [",7)) { // If there are different colors defined for each faces
multipleColors = true;
std::vector<CImg<tc> > listColors;
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
while(cimg::strncasecmp(line,"]",1) && !feof(nfile)) { // We add the list of all colors defined into the vector listColors
if (* line!=’#’) {
if (( err = std::sscanf(line,"%f %f %f[ˆ\n] ",&r,&g,&b))!=3) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : wrong number of color furnished in file
’%s’.",cimg_instance,filename?filename:"(FILE*)");
}
CImg<tc> img(3,1,1,1,(tc)(r*255),(tc)(g*255),(tc)(b*255));
listColors.push_back(img);
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
}
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : bad structure of color in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
else {
while(cimg::strncasecmp(line,"colorIndex [",12) && !feof(nfile))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : colorIndex not furnished for Color node
in file ’%s’.",cimg_instance, filename?filename:"(FILE*)");
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
while(cimg::strncasecmp(line,"]",1) && !feof(nfile)) { // We add the colors at the right index into the vector colors
if (* line!=’#’) {
char * pch;
pch = strtok (line," ");
while (pch != NULL) {
int indice = atoi(pch);
colors.insert(CImg<tc>::vector((tc)(listColors[indice])[0],(tc)(listColors[indice])[1],(tc)(
listColors[indice])[2]));
pch = strtok (NULL, " ");
}
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
}
}
}
}
else // If none of the known type of shape is defined
cimg::warn(_cimg_instance "load_vrml() : Failed to read type of geometry to draw from file ’%s’.",
cimg_instance,filename?filename:"(FILE*)");
if (textureTest) { // If the object considered is texturized
colors.push_back(colorsTextured);
*textureFile = 0;
}
while(cimg::strncasecmp(line,"appearance",10) && cimg::strncasecmp(line,"Shape",5) && !feof(nfile)) // We look for the
node appearance or for another shape
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
}
if (! cimg::strncasecmp(line,"appearance",10)) { // We are at the appearance node
while(cimg::strncasecmp(line,"texture ImageTexture",20) && cimg::strncasecmp(line,"diffuseColor",12) && !feof(
nfile)) // We are looking for a valid appearance node
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (! cimg::strncasecmp(line,"diffuseColor",12)) { // If the object as a unique diffuse color
Projet ISIMA 3ème année
RAMI
ANNEXES
if (( err = std::sscanf(line,"diffuseColor %f,%f,%f[ˆ\n] ",&r,&g,&b))!=3 && (err = std::sscanf(line,"
diffuseColor %f %f %f[ˆ\n] ",&r,&g,&b))!=3) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : wrong number of color furnished in file ’%s’.
",cimg_instance,filename?filename:"(FILE*)");
}
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
}
else if (! cimg::strncasecmp(line,"texture ImageTexture",20)) { // If there is a texture defined in the VRML file
textureTest = true;
colorDefined = false;
while(cimg::strncasecmp(line,"url",3) && !feof(nfile))
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
if (feof(nfile)) {
if (! file)
cimg::fclose(nfile);
throw CImgIOException(_cimg_instance "load_vrml() : texture not defined in file ’%s’.",
cimg_instance, filename?filename:"(FILE*)");
}
std::sscanf(line,"url [%s][ˆ\n] ",textureFileTemp); // We temporary put the name of the texture image into textureFileTemp
char * pch;
pch = strtok (textureFileTemp,"\"");
strcpy(textureFile,pch); // We put the url of the texture image into textureFile
}
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492 }
}
else if (! cimg::strncasecmp(line,"Shape",5)) // We have another shape node
textureTest = false; // We reinitialize the texture boolean
if (nbPrimitives && colorDefined && !multipleColors && !textureTest) { // If there is only one color defined we add it to the
colors CImgList or if no color is defined for an object , we add the default color
CImgList<tc> colorsTemp;
colorsTemp.insert(nbPrimitives,CImg<tc>::vector((tc)(r*255),(tc)(g*255),(tc)(b*255)));
colors.push_back(colorsTemp);
nbPrimitives = 0;
r = 0.7f, g = 0.7f, b = 0.7f;
}
err = std::fscanf(nfile,"%255[ˆ\n] ",line);
}
assign(listePoints.size()/3,3);
cimg_forX(*this,l) { // We add the points coordinates to the calling object
(* this ) (l,0) = (T)(listePoints.at(l*3)), (*this)(l,1) = (T)(listePoints.at(l*3+1)), (*this)(l,2) = (T)(listePoints.at(l*3+2));
}
if (! file)
cimg::fclose(nfile);
return * this ;
Projet ISIMA 3ème année