2011-05-25

Dessine-moi la France

Comment obtenir un mesh de la France dans Blender ?












Tout d'abord, voici une vidéo, créée avec Blender, dans laquelle on survole la France, on peut voir les Alpes, puis le Massif Central, puis les Pyrénées, et enfin la France entière, avec la Corse :




Pour importer un mesh de la France dans Blender, il me fallait avant tout une référence du relief de la France. Chez les professionnels de la profession, cela s'appelle un Modèle Numérique de Terrain (MNT) : http://fr.wikipedia.org/wiki/Modèle_numérique_de_terrain.

Pour trouver ce genre de chose en France, le réflexe, c'est d'aller voir l'Institut Géographique National (IGN) : http://www.ign.fr/. En cherchant sur le site web de l'IGN, je suis effectivement tombé sur la "BD ALTI", et sur les phrases suivantes :

"Référentiel du relief sur la France, la BD ALTI (R) est une gamme complète de modèles numériques de terrain qui décrivent la forme du terrain à différentes échelles (du 1 : 25 000 au 1 : 1 000 000). La BD ALTI (R) au pas de 250m, 500m, 1 000m est gratuite pour un usage non commercial."

Bingo. C'est exactement ce qu'il me fallait. Youpi.

Donc à partir de la page suivante de l'IGN, on peut récupérer la "BD ALTI", selon différentes résolutions (différents "pas"), et sous différents formats :
http://professionnels.ign.fr/ficheProduitCMS.do?idDoc=5323461

J'ai récupéré 3 archives zip contenant ce MNT, en format ASC (en ASCII, très simples à lire), selon 3 "pas" : 250 mètres, 500 mètres, et 1000 mètres :
  • France_1000_ASC_L93.zip (2.1 MB)
  • France_500_ASC_L93.zip (4.4 MB)
  • France_250_ASC_L93.zip (13 MB)

J'ai tenté de faire un "addon" pour Blender, en python, à partir de tutoriels, pour ajouter un menu, dans le genre "Shift+A :: Add :: Mesh :: France", et en convertissant les fichiers ASC en instructions python pour générer des "vertices" et des "faces". Mais cet essai n'a pas abouti : "Not enough memory, tout ça".

J'ai ensuite tenté de convertir ces fichiers ASC en fichiers de vertex (vertices), sans définir les polygones ("faces"), puis d'utiliser un addon Blender nommé "Import-Export: Raw mesh format (.raw)". Cet addon est décrit ici : http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/Import-Export/Raw_Mesh_IO. Mais cet essai n'a pas abouti non plus : "Not enough memory, tout ça".

En regardant de plus près les différents formats de description d'objets (Mesh) que Blender sait importer ( http://www.blender.org/download/python-scripts/import-export/ ), je suis tombé sur cet addon : "Wavefront OBJ Importer/Exporter", qui est présent et actif par défaut sous Blender. D'après Wikipédia, le format "Wavefront .obj file" semble tout simple : http://en.wikipedia.org/wiki/Wavefront_.obj_file. Il suffit d'écrire "v x y z" pour définir un vertex de coordonnées (x, y, z). Et il suffit d'écrire "f a b c d" pour définir un polygone formé par les vertices numérotés (a, b, c, d).

Re-Bingo. Re-Youpi.

J'ai écrit un script perl pour lire les fichiers "*.ASC" du modèle numérique de terrain de l'IGN, et produire des fichiers "*.obj" au format Wavefront. Pour l'utiliser, il suffit de télécharger un des fichiers de l'IGN (gratuit pour un usage non commercial), d'extraire un fichier "ASC" (dézipper l'archive), de rendre le script perl exécutable ("chmod 755 asc2obl.pl"), puis de le lancer avec une commande telle que :


./asc2obj.pl MNT1000_L93_FRANCE.ASC > france.obj

J'ai pu ensuite importer les fichiers ".obj" représentant la France, en utilisant le menu "File :: Import :: Wavefront (.obj)" de Blender.

Quelques remarques tout de même :
  • Je ne peux pas importer les fichiers les plus précis (en tous cas pas sur ma machine), mais uniquement le fichier avec un pas de 1000 mètres, toujours pareil : "Not enough memory, tout ça".
  • Une fois le Mesh importé, l'interface de Blender rame sérieusement, à cause des 1.2 millions de vertices et des 1.2 millions de polygones. Et comme le dit le vieux sage des montagnes : "Quand tu as 1.2 millions de polygones sur un objet, tu ne cliques pas sur subsurf".
  • Bizarrement, les axes Y et Z sont intervertis, je pensais voir la France en vue de dessus, et je la vois en vue de face. Ce n'est pas un problème avec Blender, il suffit de sélectionner l'objet et de le tourner : "r x -90".
  • Comme les axes Y et Z sont intervertis, il faut aussi les intervertir lorsqu'on applique une image en tant que texture, dans le menu "Texture :: Image Mapping".
  • Pour que le rendu fasse un peu plus naturel, il faut cliquer sur "smooth", sinon, les reliefs sont moches.
  • Pour faire plus joli, j'ai un peu triché : j'ai accentué 3 fois la hauteur des montagnes. Là encore, avec Blender, c'est très simple : "s z 3". Ce n'est plus très réaliste, mais c'est plus sympa.

Pour la texture, j'ai utilisé une image qui se trouve elle aussi dans l'archive zip distribuée par l'IGN, dans un ".doc".

Et voilà. A partir de là, on peut imaginer plein de trucs, par exemple appliquer une texture de terrain plus réaliste, ou appliquer une texture de carte routière, ou faire tomber de la pluie avec le simulateur de fluides de Blender, et peut-être voir l'eau emprunter le lit des fleuves, ou bien utiliser un fluide plus visqueux, et simuler des écoulements de lave depuis des volcans. La limite, c'est l'imagination ! Hum ... Mouais ... Disons ... La limite, c'est l'imagination, et la RAM.

Quelques chiffres :
  • MNT1000_L93_FRANCE : fichier ASC de 4.7 MB, fichier obj de 61.5 MB, 1.2 millions de vertex (vertices), presque autant de polygones (faces).
  • MNT500_L93_FRANCE : fichier ASC de 18.7 MB, fichier obj de 258.7 MB, probablement 4.8 millions de vertex (vertices), presque autant de polygones (faces).
  • MNT250_L93_FRANCE : fichier ASC de 74.6 MB, fichier obj de 1.1 GB, probablement 19.2 millions de vertex (vertices), presque autant de polygones (faces).

Voici le script perl "asc2obj.pl" pour convertir les fichiers .ASC en fichiers .obj :


#!/usr/bin/perl -w
# torglut GPL 2011 (this perl script)
# You must first get data from http://www.ign.fr/
# Please read http://professionnels.ign.fr/ficheProduitCMS.do?idDoc=5323461
# for data, and its license, terms of use, etc.
#
# Usage of this script :
# ./asc2obj.pl <filename.asc> [colmin] [rowmin] [colmax] [rowmax] > <filename.obj>
# ./asc2obj.pl MNT1000_L93_FRANCE.ASC > france.obj
# ./asc2obj.pl MNT250_L93_FRANCE.ASC 4265 3545 4615 4285 > corse.obj
# Then you can import france.obj in Blender, using the wavefront importer

use strict;

my %c; # configuration

sub cr() {
    my $id = shift();
    my ($col, $row) = (0, 0);
    $col = 1 + (($id - 1) % $c{'ncols'});
    $row = 1 + (($id - 1) / $c{'ncols'});
    return(($col, $row));
}

sub parse_asc() {
    my $filename = shift() or die("error : input filename missing");
    my $colmin = shift() || 1;
    my $rowmin = shift() || 1;
    my $colmax = shift() || 9999;
    my $rowmax = shift() || 9999;
    my $fh;
    my ($x, $y, $z, $step, $factor);
    my ($a, $b, $c, $d);
    my $f = ''; # list of faces
    my $id = 0;
    my $realid = 0;
    my ($col, $row);

    open($fh, "<$filename") or die("error : cannot open file $filename");
    while (my $line = <$fh>) {
        $line =~ s/\n$//;
        $line =~ s/\r$//;
        if ($line =~ m/^\s*(\w+)\s+(\-?[\d\.]+)$/g) {
            $c{lc($1)} = $2; # get configuration from ASC file header
            if (defined($c{'ncols'})) {
                if ($colmax == 9999) { $colmax = $c{'ncols'}; }
                $c{'realcols'} = 1 + $colmax - $colmin;
                $step = 20.0 / $c{'ncols'};
                if (defined($c{'cellsize'})) {
                    $factor = 20.0 / ($c{'ncols'} * $c{'cellsize'});
                    if (defined($c{'nrows'})) {
                        if ($rowmax == 9999) { $rowmax = $c{'nrows'}; }
                        $y = ($factor * $c{'nrows'} * $c{'cellsize'}) / 2;
                    }
                }
            }
        } else {
            $line =~ s/$c{'nodata_value'}/0/g;
            $x = -10.0 - $step;
            while ($line =~ s/^\s*(-?\d+)//g) {
                $id += 1;
                $x += $step;
                ($col, $row) = &cr($id);
                next if (($col < $colmin) || ($col > $colmax) || ($row < $rowmin) || ($row > $rowmax));
                $realid += 1;
                $z = $factor * $1;
                printf("v %.3f %.3f %.3f\n", $x, $y, $z);
                if (($realid > $c{'realcols'}) && (($realid - 1) % ($c{'realcols'}))) {
                    $a = $realid;
                    $b = $realid - $c{'realcols'};
                    $c = $b - 1;
                    $d = $a - 1;
                    $f .= "f $a $b $c $d\n";
                }
            }
        }
        $y -= $step;
    }
    close($fh);
    print($f);
}

# main
&parse_asc(shift(), shift(), shift(), shift(), shift());

6 commentaires:

  1. bonjour,
    ça me génère bien un fichier.obj mais quand je l'importe dans blender, je me retrouve avec un plan plein de vertices dont les coordonnées en z sont toutes à 0 !
    dans le shell, j'ai plusieurs lignes du type :
    Use of uninitialized value $factor in multiplication (*) at ./asc2obl.pl line 66, <$fh> line 239.

    RépondreSupprimer
  2. Salut,

    C'est étrange. Tu utilises bien la ligne de commande suivante ?

    ./asc2obj.pl MNT1000_L93_FRANCE.ASC > france.obj

    RépondreSupprimer
  3. oui, sauf que je le fais avec la BD ALTI de la réunion... et ma version de blender est la 2.6a.

    RépondreSupprimer
  4. Ah ok. C'est parceque dans le fichier de la Réunion, le paramètre cellsize est un peu différent.
    Pour pouvoir le lire, il faut modifier la ligne 43 du script asc2obj.pl pour qu'elle contienne :

    if ($line =~ m/^\s*(\w+)\s+(\-?[\d\.]+)$/g) {

    J'ai modifié le script dans l'article, ça devrait aller mieux si tu lances :

    ./asc2obj.pl DEPT974.asc > reunion.obj

    RépondreSupprimer
  5. c'est super, ça fonctionne :)) merci beaucoup.

    RépondreSupprimer
  6. Bonjour;
    J'ai essaye de convertir des fichiers 75 avec ton script, 10MB au depart avec le fichier ASC;,mais seulement 23K à l'arrivee dqns le fichier OBJ ... qui ne donn rien dans BLEND. Par contre le fichier en 250 focntionne sans pb ? Help Please !!
    Merci à Toi

    RépondreSupprimer