Mon système

Avant de commencer, et histoire que vous sachez sur quoi s’appuie ce tuto, sachez que je tourne sur un Linux Mint 17.1 Rebecca (Ubuntu 14.04):

$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4

Ça, c'est fait! :bravo:

Prérequis

On va commencer par ce qu'on ne va pas compiler mais qui est nécessaire au bon fonctionnement des opérations, à savoir: Les packages.

D'une manière générale, je vous conseille de lire les READMEs fournis avec chaque archive de fichiers sources car ils contiennent de précieuses indications.

Petite liste non exhaustives des packages nécessaires:

  • zlib1g-dev: 1.2.8 chez moi.
  • libhdf5-dev: 1.8.11 chez moi.
  • libboost-thread1.54-dev: Alembic spécifie qu'il fonctionne de boost 1.44 à 1.48 et que les versions suivantes posent des soucis avec Python. Comme on ne va pas compiler PyAlembic ici on est tranquille.
  • graphviz: Pour la documentation Alembic. Pas obligatoire mais vous n'aurez pas les graphs d'héritage dans la documentation généré ce qui est toujours dommage. 2.36.0 chez moi.
  • cmake:  Le build system utilisé par Alembic. 2.8.0 ou supérieur, 2.8.12 chez moi.

Installez cmake-gui tant qu'à faire, vous risquez d'en avoir besoin.

Je ne mentionne bien évidemment pas build-essential :tux001: .

Ce qu'on va compiler

Alembic palsambleu! :grenadelauncher:

Plus précisément, ce script va compiler:

  • IlmBase 2.2.0
  • OpenEXR 2.2.0
  • Alembic 1.5.8

Pour votre information: Le README de Alembic spécifie qu'il ne nécessite qu'IlmBase 1.0.3 et OpenEXR 1.7.1. Vous pouvez toujours modifier mon script pour utiliser ces versions. J'ai teste et ça fonctionne également.

Le script

Le voici brut de décoffrage :

export MAINDIR=/home/user/code/alembic_test

mkdir my_env

wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz
tar -xzvf ilmbase-2.2.0.tar.gz
rm ilmbase-2.2.0.tar.gz
cd ilmbase-2.2.0
./configure --prefix=$MAINDIR/my_env
make -j8
make install

cd ../

export LD_LIBRARY_PATH=$MAINDIR/my_env/lib

wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz
tar -xzvf openexr-2.2.0.tar.gz
rm openexr-2.2.0.tar.gz
cd openexr-2.2.0
./configure --prefix=$MAINDIR/my_env --with-ilmbase-prefix=$MAINDIR/my_env
make -j8
make install

cd ../

wget https://github.com/alembic/alembic/archive/1.5.8.tar.gz
tar -xzvf 1.5.8.tar.gz
rm 1.5.8.tar.gz
cd alembic-1.5.8
cmake -DUSE_PYALEMBIC=OFF \
-DILMBASE_ROOT=../my_env \
-DCMAKE_SYSTEM_PREFIX_PATH=../my_env \
-DCMAKE_INSTALL_PREFIX=../my_env/usr/local ./
make -j8
make test
make install
doxygen Doxyfile

Explication ligne à ligne

export MAINDIR=/home/user/code/alembic_test

Cette première ligne est importante. On créé la variable MAINDIR à laquelle on assigne le chemin vers notre dossier principal dans lequel on va faire nos affaires.

mkdir my_env

Ici on créé un dossier my_env. C'est une méthode que j'utilise souvent: Afin d'éviter d'avoir à installer mes librairies fraîchement compilées directement sur mon système (ce qui peut potentiellement tout casser si la librairie est importante et mal compilée), je défini un dossier comme étant mon système (on parle souvent de prefix). Ça nécessite souvent de bricoler deux trois choses mais ça finit toujours par marcher :baffed:.

IlmBase

wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz

On récupère l'archive des fichiers source de IlmBase (lien trouvé ici). On se retrouve donc avec un jolie ilmbase-2.2.0.tar.gz dans sont dossier.

tar -xzvf ilmbase-2.2.0.tar.gz

Hahaha! La fameuse commande tar, impossible à retenir. :hihi:

En gros, on décompresse le contenu du fichier.

rm ilmbase-2.2.0.tar.gz

Une fois décompressé, je supprime l'archive parce que j'aime bien quand c'est propre. :gniarkgniark:

cd ilmbase-2.2.0

On rentre maintenant dans le dossier qu'on vient d'extraire.

./configure --prefix=$MAINDIR/my_env

Première apparition du prefix dont je vous ai parlé tout à l'heure. En gros, on exécute le script de configuration. Le script de configuration va s'assurer que tout ce qui est nécessaire à la compilation est présent sur le système. En lui spécifiant un prefix, on lui indique dans quel dossier on souhaite placer les fichiers compilés. Le $MAINDIR vient appeler la variable créer tout en début de script.

make -j8

D'ordinaire, la commande make s'occupe de compiler les fichiers sources. L'argument -j8 indique le nombre de job à utiliser lors de la compilation pour gagner du temps. Si vous avez ein grô PC, ein grô processeur et de la grosse mémoire, vous pouvez mettre ein grô chiffre (et ein-versement :smileFou: )

make install

Si la compilation c'est bien passée, vous pouvez faire un make install pour envoyer les fichiers compilés se dispatcher dans le dossier my_env.

cd ../

On sort du dossier. S'en est fini d'IlmBase!

OpenEXR

export LD_LIBRARY_PATH=$MAINDIR/my_env/lib

Alors alors, une petite explication s'impose: Durant la phase de configuration de la compilation d'OpenEXR, ce dernier génère un petit code utilisant la librairie IlmBase afin de vérifier certaines choses. Comme vous le savez, nous avons sauvé les fichiers d'IlmBase dans un environnement spécial, en dehors du système principale (dans my_env). Pour que la phase de configuration d'OpenEXR se passe bien, il faut ajouter le dossier contenant les librairies à la variable d'environnement utilisée pour trouver les librairies des binaires exécutés par le système.

C'est ce que fait cette ligne. (Plus d'infos sur LD_LIBRARY_PATH ici)

wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz
tar -xzvf openexr-2.2.0.tar.gz
rm openexr-2.2.0.tar.gz
cd openexr-2.2.0

Je ne vais pas m’étaler sur cette partie, c'est la même que précédemment.

./configure --prefix=$MAINDIR/my_env --with-ilmbase-prefix=$MAINDIR/my_env

Le script de configuration d'OpenEXR dispose d'un argument supplémentaire (--with-ilmbase-prefix) permettant de lui donner un chemin vers un emplacement non standard où est placé la librairie IlmBase à utiliser. Je ne rentre pas dans les détails. Si vous voulez comprendre, ouvrez le fichier configure et cherchez cette variable :dentcasse:.

make -j8
make install
cd ../

On compile, on installe dans my_env et on sort.

Alembic

Et voici la dernière ligne droite!

wget https://github.com/alembic/alembic/archive/1.5.8.tar.gz

Notez que j'ai récupéré le chemin de l'archive sur la page des releases.

tar -xzvf 1.5.8.tar.gz
rm 1.5.8.tar.gz
cd alembic-1.5.8

Je passe. :reveBinaire:

cmake -DUSE_PYALEMBIC=OFF \
-DILMBASE_ROOT=../my_env \
-DCMAKE_SYSTEM_PREFIX_PATH=../my_env \
-DCMAKE_INSTALL_PREFIX=../my_env/usr/local ./

Et voila la ligne! On va la décortiquer:

  • cmake: La commande du générateur de makefile de Alembic. Vous remarquez qu'Alembic n'utilise pas l'habituel configure qui est une méthode assez ancienne et laborieuse (il suffit d'ouvrir un script de configuration pour s'en convaincre). CMake permet une gestion plus fine des options de compilation pour générer des makefiles aux petits oignions.
  • USE_PYALEMBIC=OFF: On déclare (d'où le -D au début de chaque argument) la variable USE_PYALEMBIC à OFF. En faisant ça, on va simplement dire à CMake de ne pas générer la partie responsable de la compilation de PyAlembic (pour les raisons évoquées plus haut).
  • ILMBASE_ROOT=../my_env: On défini que toute les librairies sont à aller chercher dans my_env.
  • CMAKE_SYSTEM_PREFIX_PATH=../my_env: Le chemin à privilégier pour aller cherche les librairies nécessaires à la compilation (cf: doc).
  • CMAKE_INSTALL_PREFIX=../my_env/usr/local: L'équivalent du prefix d'un script configure. Notez que j'ai ajoute /usr/local car c'est l'endroit ou Alembic viendrait stocker ces librairies si on ne lui précisait pas.
  • ./: On spécifie simplement qu'il faut exécuter cmake dans le dossier courant.

Si vous souhaitez savoir ce qui ce passe sous le capot, exécutez cmake-gui ./ après avoir exécuté la ligne précédente.

make -j8

C'est pas si long que ça. :nannan:

make test

Comme la quantité de message affiché est énorme, vous ne saurez pas forcement clairement si la compilation c'est bien passé. Le plus simple pour tester est d'exécuter la suite de test fournie.

Si tout fonctionne:

make install

Allez dans my_env/usr/local/alembic-1.5.8/lib/static/:

alembic_static_001.png

Vous voila l'heureux propriétaire d'une librairie Alembic statique!

N'oubliez pas de générer la documentation:

doxygen Doxyfile 

Pour l'ouvrir (et vous en aurez besoin!): alembic-1.5.8/doc/html/index.html.

Le viewer

Allez dans my_env/usr/local/alembic-1.5.8/bin:

alembic_bin_dir_001.png

Des petites choses que je vous laisse essayer par vous même.

Pour le fun, faites:

./SimpleAbcViewer /path/to/any.abc

Et miracle!

abc_octopus_001.png

Gardez la touche shift enfoncé et naviguez comme dans Maya. (Des raccourcis sont disponible pour, entre autre, jouer l'animation)

Notez qu'il faut avoir précédemment setté la variable LD_LIBRARY_PATH pour pointer vers les librairies nécessaires (un message d'erreur dans le terminal vous le rappellera :trollface: )

Linker les librairies Alembics dans vos programme

Je vous préviens tout de suite cette partie sera encore plus chaotique que la précédente. :pasClasse:

Pour faire simple: On va écrire un petit programme mais on va surtout tenter de compiler ce programme avec les librairies.

Personnellement, j'utilise NetBeans IDE mais vous devriez pouvoir configurer ça sur n'importe quel IDE et/ou chaîne de compilation. Créez vous un projet vierge (abc_test dans mon cas).

Les headers

Voici la liste des dossiers des headers:

  • ../my_env/usr/local/alembic-1.5.8/include: Le dossier des headers d'Alembic
  • ../my_env/include/OpenEXR: Le dossier des headers de IlmBase
  • /usr/include: Le dossier des headers de votre système (pour HDF5 entre autre)
abc_compile_config_001.png

Le standard

Choisissez le standard C++11:

abc_compile_config_002.png

On va écrire du C++ de hipster!!! :laClasse: :

Les librairies

Et la liste des librairies statiques (l'ordre est important):

  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcGeom.a
  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcFactory.a
  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbc.a
  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcCoreHDF5.a
  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcCoreOgawa.a
  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcCoreAbstract.a
  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicOgawa.a
  • ../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicUtil.a
  • ../my_env/lib/libHalf.a
  • ../my_env/lib/libIex.a
  • ../my_env/lib/libIexMath.a (Pas sure que celle librairie existe avec IlmBase 1.0.3...)
  • ../my_env/lib/libIlmImf.a
  • ../my_env/lib/libIlmThread.a
  • ../my_env/lib/libImath.a
  • boost_thread
  • hdf5
  • hdf5_hl

Les trois dernières ne sont pas des liens directs car elles font partie du système. Ce qui nous donne:

abc_compile_config_003.png

Le programme

Allez! Faites moi un gros copier/coller :hehe: :

#include <Alembic/Abc/All.h>
#include <Alembic/AbcCoreFactory/All.h>

int main(int argc, char** argv) {

    // Create needed variables
    Alembic::AbcCoreFactory::IFactory factory;
    Alembic::AbcCoreFactory::IFactory::CoreType coreType;

    // open the abc file
    auto archive = factory.getArchive("/path/to/alembic_octopus.abc", coreType);

    if (archive.valid())
    {
        std::cout << "Archive name: " << archive.getName() << std::endl;
        std::cout << "Number TimeSampling: " << archive.getNumTimeSamplings() << std::endl;
        std::cout << "Archive version: " << archive.getArchiveVersion() << std::endl;

        // retrieve the top IObject of the hierarchy
        auto top = archive.getTop();
        std::cout << "top valid?: " << top.valid() << std::endl;
        std::cout << "top name: " << top.getName() << std::endl;
        std::cout << "top full name: " << top.getFullName() << std::endl;
        std::cout << "top num children: " << top.getNumChildren() << std::endl;

        auto parent = top.getParent();
        std::cout << "parent of the top valid?: " << parent.valid() << std::endl; // of course not!

        auto header = top.getHeader();        
        std::cout << "top header name: " << header.getName() << std::endl;
        std::cout << "top header full name: " << header.getFullName() << std::endl;

        auto metadata = top.getMetaData();
        std::cout << "metadata size: " << metadata.size() << std::endl;
        std::cout << "metadata serialize: " << metadata.serialize() << std::endl;

        // iterate over abc metadatas
        for(auto &it : metadata)
        {
            auto key   = it.first;
            auto value = it.second;
            std::cout << key << " = " << value << std::endl;
        }
    };
    return 0;
}

Et voici ce qu'affiche ce programme une fois exécuté:

Archive name: /home/narann/code/cpp/abc_test/alembic_octopus.abc
Number TimeSampling: 2
Archive version: 10000
top valid?: 1
top name: ABC
top full name: /
top num children: 1
parent of the top valid?: 0
top header name: ABC
top header full name: /
metadata size: 4
metadata serialize: _ai_AlembicVersion=Alembic 1.0.0 (built Aug  5 2011 16:14:46);_ai_Application=Maya 2012-2.1.SPI x64 AbcExport v1.0;_ai_DateWritten=Tue Aug  9 13:14:24 2011;_ai_Description=Exported from: /mcp/Alembic_Octopus_Example/alembic_octopus.mb
_ai_AlembicVersion = Alembic 1.0.0 (built Aug  5 2011 16:14:46)
_ai_Application = Maya 2012-2.1.SPI x64 AbcExport v1.0
_ai_DateWritten = Tue Aug  9 13:14:24 2011
_ai_Description = Exported from: /mcp/Alembic_Octopus_Example/alembic_octopus.mb

Si vous êtes arrivé jusqu'ici: Félicitation! Vous pouvez maintenant commencer a jouer avec Alembic. Je vous invite à fouiller les codes d'exemples (à commencer par celui ci) et à lire la documentation que vous avez générée (Mais si! Rappelez vous, dans ../alembic-1.5.8/doc/html/). :RTFM:

J'espère que ce billet vous aura aidé et donné envie de pousser tout ça plus loin.

Dorian

:marioCours: