so3d • SO3_appli_1

SO3d, le moteur Ogre de Scol : première application

Développé par i-maginer, et plus particulièrement par Mehdi, ce nouveau moteur qui équipe Scol depuis la version 6 ne connaît que openspace3d comme application (sauf erreur de ma part).
Ce nouveau moteur, encore en finalisation, offre pourtant de superbes potentialités, même face à la concurrence, puisque totalement basé sur Ogre. Pour cela, il faut bien entendu commencer par développer des applications !

Voici pour débuter une application très simple qui va afficher un mesh en rotation :

screenshot

Le source complet contient, outre le code ci-dessous, un exemple de mesh (cylinder.mesh) et son matériau associé (scene.material), le fichier .chm en cours lors de la création de cette application et qui liste les fonctions Scol du nouveau moteur (SO3Engine_SCOLFunctions.chm). Cependant, pour être certain de ne rien perdre, mieux vaut télécharger cette liste depuis le svn public.
Pour télécharger l'ensemble (recommandé), cliquez simplement ici (724 ko, archive au format libre 7z).

Note : le cylindre tout simple a été modélisé sous Linux avec Blender 2.48a, exporté par le script "Ogre Meshes" de Michael Reimpell. Inutile de convertir le mesh au format xml lors de l'export.
Cette application a été initialement codée avec la version 6.0.103a de Scol.


Vue d'ensemble

De nombreuses différences apparaissent logiquement entre ce moteur et l'ancien (au niveau du code, s'entend, pour le reste, il n'y a pas photo entre les deux ! :) ). Quand on débute, un des points importants à avoir en tête est qu'on doit charger les fichiers avant de les utiliser, d'une part, et qu'il est bon de les charger en remontant le graphe (par exemple, les textures, puis les matériaux puis les meshes ...) d'autre part.

De plus, le format des fichiers n'est plus le même, ce qui explique en partie le premier point. Ils obéissent aux spécifications Ogre et les m3d ne pourront donc pas être utilisés (il n'existe pas de moulinette pour les convertir et l'intérêt serait médiocre au vue des piètres performances de l'ancien moteur).
Pour reprendre l'exemple ci-dessus, on aura un fichier de texture (.jpg, ... ou .dds), un fichier de description du matériau (.material) et un fichier de mesh (.mesh).

Après avoir créé l'interface 2d de façon tout à fait classique, on initialise la scène 3d (fonction 'init3d') :

  1. on crée le buffer en premier,
  2. on crée une nouvelle session,
  3. le shell racine,
  4. la caméra et sa configuration,
  5. une lumière ambiante,
  6. la structure de surface de rendu,
  7. les groupes de ressources,
  8. et l'initialisation de l'arborescence Scol des ressources.
Seuls les deux premiers doivent garder leur rang.

Les groupes jouent comme des masques et permettent donc de lier (délier) des objets des ressources qui leurs sont couplées.


Principales fonctions utilisées du SO3Engine

Quelques éléments succints d'explications sur les fonctions 3d du code source proposé.

  1. SO3BufferCreate -> fun [ObjWin I I] ObjWin
    Création du buffer de la fenêtre afin d'y afficher le rendu. On ajoute les arguments de type entier 'largeur' et 'hauteur'. A priori, mieux vaut exécuter cette fonction avant d'intégrer quoi que ce soit dans la session. Retour 'nil' si erreur.

  2. SO3SceneCreate -> fun [Chn S] SO3_SCENE
    Création d'une nouvelle session 3d, indispensable avant toute intégration 3d. Les arguments sont le canal dans le lequel la session sera créée et le nom de cette session. Retournera 'nil' s'il y a une erreur quelque part. Notez que le type 'session' du SO3Engine n'est plus 'S3d' (ancien moteur) mais SO3_SCENE.

  3. SO3SceneNodeCreate -> fun [SO3_SCENE S] SO3_OBJECT
    Création d'un noeud dans le graph (l'arborescence) de la scène. Il en faut au moins un dans une scène (le noeud racine). Retourne 'nil' en cas d'erreur. Remarquez le type SO3_OBJECT qui remplace le 'H3d' de l'ancien moteur.

  4. SO3CameraCreate -> fun [SO3_SCENE S] SO3_OBJECT
    Création d'une caméra (paramètre par défaut qui sont bien sur modifiable ultérieurement). Indispensable pour que le rendu puisse s'effectuer ! Retourne 'nil si une erreur survient. La caméra est généralement liée à un noeud spécifique que l'on peut déplacer mais ce n'est pas une obligation :)

  5. SO3ObjectSetPosition -> fun [SO3_SCENE SO3_OBJECT [F F F]] I
    Place un objet (argument 2) de la scène (argument 1) à la position (argument 3) dans le référentiel. Notez que la position (x, y, z) est en flottant. La valeur 0 est retournée si le placement est un succès.

  6. SO3ObjectLink -> fun [SO3_SCENE SO3_OBJECT SO3_OBJECT] I
    Lie un objet (argument 2) à un autre objet (argument 3) dans une scène donnée (argument 1). Argument 3 sera donc le père d'argument 2. Retournera 'nil' en cas de problème.

  7. SO3ViewportCreate -> fun [Chn ObjWin SO3_SCENE SO3_OBJECT F F F F I] SO3_VIEWPORT
    Création de la structure de gestion de la surface de rendu : le canal à utiliser, la fenêtre, la scène à y afficher, la caméra, les positions et la taille du rendu et, enfin, un indice de session (une session, ce sera le 0, soit 0x0 en hexa). Retourne 'nil' si erreur.

  8. SO3GroupCreate -> fun [S] I
    Création d'un groupe d'objets et/ou de ressources

  9. SO3InitSCOLResourceLocations
    Initialisation des paritions Scol pouvant contenir des ressources. Cette fonction est à utiliser avant de charger des objets 3d quelconques. La valeur de retour est sans intérêt.

  10. SO3SceneLoadResource -> fun [SO3_SCENE S S I] SO3_OBJECT
    Chargement d'un fichier (argument 2) en tant que ressource disponible dans la session (argument 1) et appartenent au groupe (argument 3). L'argument 4 est un flag qui peut prendre l'une de ces valeurs : SO3_RESOURCE_MESH, SO3_RESOURCE_MATERIAL, SO3_RESOURCE_TEXTURE, SO3_RESOURCE_SKELETON, SO3_RESOURCE_GPUPROGRAM ou SO3_RESOURCE_HIGHLEVELGPUPROGRAM. Retourne 0 si tout est ok ou 'nil' avec explication si problème.

  11. SO3SceneLoadEntity -> fun [SO3_SCENE S S] SO3_OBJECT
    SO3SceneLoadEntityWithIndex -> fun [SO3_SCENE S S S S S] SO3_OBJECT
    Charge un objet dans la scène, en supposant que celui-ci (et les fichiers ressources qui en dépendent tels que matériaux, textures, ... sont également disponibles). L'argument 2 est le fichier du mesh (de l'objet), l'argument 3 est le nom qu'aura cette objet. Les arguments 4 et 5 de la seconde fonction fournissent l'index matériau et le nom du groupe auquel appartient l'objet. Retourne l'objet créé ou 'nil' si erreur.

  12. SO3RenderFrame -> fun [] I
    Demande qu'un rendu soit effectué de la scène courante avec la caméra courante, etc. Retourne 0 si tout s'est bien passé.

Code source

/* Application basique d'utilisation de la librairie SO3engine de Scol : moteur 3d intégrant Ogre3D. Version 1.0 Réalisée avec la version 6.0103a de Scol Par Stéphane Bisaro, aka iri (http://www.irizone.net) Diffusion et modification libre du code Sans garantie */ typeof win = ObjWin;; // fenêtre de l'appli var winw = 800;; // sa largeur var winh = 600;; // sa hauteur // 3d struct SC3D = [ // structure décrivant la scène session : SO3_SCENE, // session active camera : SO3_OBJECT, // objet caméra shellcamera : SO3_OBJECT, // noeud père de la caméra shell : SO3_OBJECT, // noeud racine des meshes objects : [ [ I SO3_OBJECT ] r1 ], // meshes (identifiant et mesh) view : SO3_VIEWPORT // structure interne de gestion de la surface de rendu ] mkSC3D;; typeof scn = SC3D;; // la structure en question :) typeof renderTimer = Timer;; // timer : à chaque top, une demande de rendu est exécutée typeof renderPeriod = I;; // période du timer var renderFramerate = 96;; // framerate max // destruction de l'appli fun end()= // destruction du timer _deltimer renderTimer; // destruction de la session SO3SceneDelete scn.session; // destruction de la structure de surface SO3ViewportDestroy scn.view; // destruction de la VM associée à l'appli _closemachine;; // rendu de la scène -> affichage dans la fenêtre fun rendering(obj, u)= // un seul objet dans la scène, pas de complication ;-). On le fait tourner ... let hd scn.objects -> [_ o] in SO3ObjectRotate scn.session o 0.01 [0.6 0.1 0.2] SO3_LOCAL_TS; // demande de rendu SO3RenderFrame; 0;; fun loadMaterial(file)= // chargement de la ressource (du matériau ici) _fooS if (SO3SceneLoadResource scn.session file "Material" SO3_RESOURCE_MATERIAL) == nil then strcatn "Load resource : " :: file :: " : FAIL !" :: nil else strcatn "Load resource : " :: file :: " : OK !" :: nil; 0;; fun loadMesh(file)= // chargement du fichier .mesh (faultatif) _fooS if (SO3SceneLoadResource scn.session file "Mesh" SO3_RESOURCE_MESH) == nil then strcatn "Load resource : " :: file :: " : FAIL !" :: nil else strcatn "Load resource : " :: file :: " : OK !" :: nil; let sizelist scn.objects -> n in // chargement du mesh. Les deux fonctions peuvent être utilisées dans ce cas. // let SO3SceneLoadEntity scn.session file strcat itoa n ".Cylinder" -> o in let SO3SceneLoadEntityWithIndex scn.session file strcat "mesh" itoa n itoa n "Mesh" -> o in _fooS if o == nil then // erreur, l'objet n'est pas créé (sauf si 'file' est invalide, il y a une explication dans le log) strcatn "Load mesh : " :: file :: " : FAIL !" :: nil else // ok, l'objet est crée ( // liaison de l'objet au noeud père (ici le shell) SO3ObjectLink scn.session o scn.shell; // position de l'objet dans son référentiel SO3ObjectSetPosition scn.session o [0.0 0.0 0.0]; // mise à l'échelle SO3ObjectSetScale scn.session o [54.0 52.0 47.0]; // ajout de l'objet aux autres set scn.objects = [n+1 o] :: scn.objects; strcatn "Load mesh : " :: file :: " : OK !" :: nil ); 0;; fun init3d()= _fooS "------------ Init 3d"; // création du buffer pour la fenêtre _fooS if ( SO3BufferCreate win winw winh ) != nil then "Create buffer : OK !" else "Create buffer : FAIL !"; // création de la session (scène) _fooS if ( set scn.session = SO3SceneCreate _channel "newSession" ) != nil then "Create session : OK !" else "Create session : FAIL !"; // création du shell père _fooS if ( set scn.shell = SO3SceneNodeCreate scn.session "shell") != nil then "Create shell : OK !" else "Create shell : FAIL !"; // création de la caméra _fooS if ( set scn.camera = SO3CameraCreate scn.session "Camera" ) != nil then "Create camera : OK !" else "Create camera : FAIL !"; // création du noeud de la caméra dans l'arbre de la scène _fooS if ( set scn.shellcamera = SO3SceneNodeCreate scn.session "ShellCamera" ) != nil then "Create camera node : OK !" else "Create camera node : FAIL !"; // placement initial de ce noeud dans la scène 3d SO3ObjectSetPosition scn.session scn.shellcamera [0.0 0.0 0.0]; // liaison de la caméra à ce noeud SO3ObjectLink scn.session scn.camera scn.shellcamera; // placement initial de la caméra en référence à son père (ce noeud puisque dorénavant liée) SO3ObjectSetPosition scn.session scn.camera [0.0 0.0 500.0]; // création d'une lumière ambiante, couleur en 24 bits hexa _fooS if ( SO3SceneSetAmbientLight scn.session 0x00ff00 ) then "Create ambiant light : OK !" else "Create ambiant light : FAIL !"; // création de la surface de rendu (la taille de la surface est donnée en fonction de la taille de la fenêtre // par exemple " 0.0 0.0 1.0 1.0 " indique que la surface de rendu s'effectuera sur la totalité de la fenêtre _fooS if ( set scn.view = SO3ViewportCreate _channel win scn.session scn.camera 0.0 0.0 1.0 1.0 0x0 ) != nil then "Create viewport : OK !" else "Create viewport : FAIL !"; // définition de la couleur de fond, en 24 bits hexa (r g b alpha) SO3ViewportSetBackgroundColor scn.view 0x55555500; // creation des groupes SO3GroupCreate "Mesh"; SO3GroupCreate "Material"; _fooSList SO3GroupList; // initialisation de l'arborescence Scol pour l'emplacement des ressources disponibles SO3InitSCOLResourceLocations; _fooS "------------ Init 3d END"; 0;; fun init()= // création du timer et définition de sa callback let _gettimerscapabilities -> [mint _] in set renderPeriod = if (1000 / renderFramerate) < mint then mint else 1000 / renderFramerate; set renderTimer = _rfltimer _starttimer _channel renderPeriod @rendering 0; 0;; fun cbWinEnd(obj, u)= end;; fun init2d()= // création de la fenêtre set win = _CRwindow _channel nil 50 25 winw winh WN_NORMAL " demo app SO3d"; // callback lors de la destruction de la fenêtre _CBwinDestroy win @cbWinEnd 0; 0;; fun main()= _showconsole; // initialisation de la structure SC3D set scn = mkSC3D [ nil nil nil nil nil nil ]; // initialisation 2d init2d; // intialisation 3d init3d; // initialisation du reste init; // chargement du materiau en premier (d'abord les ressources dont dépend le mesh, ici, juste un materiau) loadMaterial "tests/so3d/scene.material"; // chargement du mesh loadMesh "tests/so3d/cylinder.mesh"; 0;;

Liste des articles de la rubrique so3d :

SO3_appli_1