Open Inventor |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Formes et propriétés Les formes Open Inventor reconnaît un certain nombre de formes. Formes simples
Formes complexes
Exemples Création d'un obélisque par assemblage de facettes static float vertices[28][3] = { { 0,30,0},{-2,27,2},{ 2,27,2}, { 0,30,0},{-2,27,-2},{-2,27,2}, { 0,30,0},{ 2,27,-2},{-2,27,-2}, { 0,30,0},{ 2,27,2},{ 2,27,-2}, {-2,27,2},{-4,0,4},{ 4,0,4},{ 2,27,2}, {-2,27,-2},{-4,0,-4},{-4,0,4},{-2,27,2}, { 2,27,-2},{ 4,0,-4},{-4,0,-4},{-2,27,-2}, { 2,27,2},{ 4,0,4},{ 4,0,-4},{ 2,27,-2}}; static long numvertices[8] = {3,3,3,3,4,4,4,4}; SoSeparator *makeObeliskFaceSet() { SoSeparator *obelisk = new SoSeparator(); obelisk->ref(); SoMaterial *myMaterial = new SoMaterial; myMaterial->diffuseColor.setValue(.4,.4,.4); obelisk->addChild(myMaterial); SoCoordinate3 *myCoords = new SoCoordinate3; myCoords->point.setValues(0,28,vertices); obelisk->addChild(myCoords); SoFaceSet *myFaceSet = new SoFaceSet; myFaceSet->numVertices.setValues(0,8,numvertices); obelisk->addChild(myFaceSet); obelisk->unrefNoDelete(); return obelisk; } Création d'un drapeau au moyen d'une bande de triangles static float vertexPositions[40][3] = { { 0, 12 , 0},{ 0, 15, 0}, {2.1, 12.1, -.2},{2.1, 14.6, -.2}, { 4, 12.5, -.7},{ 4, 14.5, -.7}, {4.5, 12.6, -.8},{4.5, 14.4, -.8}, { 5, 12.7, -1},{ 5, 14.4, -1}, {4.5, 12.8, -1.4},{4.5, 14.6, -1.4}, { 4, 12.9, -1.6},{ 4, 14.8, -1.6}, {3.3, 12.9, -1.8},{3.3, 14.9, -1.8}, { 3, 13 , -2.0},{ 3, 14.9, -2.0}, {3.3, 13.1, -2.2},{3.3, 15.0, -2.2}, { 4, 13.2, -2.5},{ 4, 15.0, -2.5}, { 6, 13.5, -2.2},{ 6, 14.8, -2.2}, { 8, 13.4, -2},{ 8, 14.6, -2}, { 10, 13.7, -1.8},{ 10, 14.4, -1.8}, { 12, 14 , -1.3},{ 12, 14.5, -1.3}, { 15, 14.9, -1.2},{ 15, 15 , -1.2}, {-.5, 15, 0},{-.5,0, 0}, { 0,15,.5},{ 0,0,.5}, { 0, 15,-.5},{ 0,0,-.5}, {-.5,15, 0},{-.5,0, 0} }; static long numVertices[2] = { 32,8 }; SoSeparator *makePennant() { SoSeparator *result = new SoSeparator; result->ref(); SoCoordinate3 *myCoords = new SoCoordinate3; myCoords->point.setValues(0, 40, vertexPositions); result->addChild(myCoords); SoTriangleStripSet *s = new SoTriangleStripSet; s->numVertices.setValues(0, 2, numVertices); result->addChild(s); result->unrefNoDelete(); return result; } Création d'une arche au moyen d'un maillage quadrangulaire static float vertexPositions[160][3] = { {-13.0, 0.0, 1.5},{-10.3, 13.7, 1.2}, { -7.6, 21.7, 1.0},{ -5.0, 26.1, 0.8}, { -2.3, 28.2, 0.6},{ -0.3, 28.8, 0.5}, { 0.3, 28.8, 0.5},{ 2.3, 28.2, 0.6}, { 5.0, 26.1, 0.8},{ 7.6, 21.7, 1.0}, { 10.3, 13.7, 1.2},{ 13.0, 0.0, 1.5}, {-10.0, 0.0, 1.5},{ -7.9, 13.2, 1.2}, { -5.8, 20.8, 1.0},{ -3.8, 25.0, 0.8}, { -1.7, 27.1, 0.6},{ -0.2, 27.6, 0.5}, { 0.2, 27.6, 0.5},{ 1.7, 27.1, 0.6}, { 3.8, 25.0, 0.8},{ 5.8, 20.8, 1.0}, { 7.9, 13.2, 1.2},{ 10.0, 0.0, 1.5}, {-10.0, 0.0,-1.5},{ -7.9, 13.2,-1.2}, { -5.8, 20.8,-1.0},{ -3.8, 25.0,-0.8}, { -1.7, 27.1,-0.6},{ -0.2, 27.6,-0.5}, { 0.2, 27.6,-0.5},{ 1.7, 27.1,-0.6}, { 3.8, 25.0,-0.8},{ 5.8, 20.8,-1.0}, { 7.9, 13.2,-1.2},{ 10.0, 0.0,-1.5}, {-13.0, 0.0,-1.5},{-10.3, 13.7,-1.2}, { -7.6, 21.7,-1.0},{ -5.0, 26.1,-0.8}, { -2.3, 28.2,-0.6},{ -0.3, 28.8,-0.5}, { 0.3, 28.8,-0.5},{ 2.3, 28.2,-0.6}, { 5.0, 26.1,-0.8},{ 7.6, 21.7,-1.0}, { 10.3, 13.7,-1.2},{ 13.0, 0.0,-1.5}, {-13.0, 0.0, 1.5},{-10.3, 13.7, 1.2}, { -7.6, 21.7, 1.0},{ -5.0, 26.1, 0.8}, { -2.3, 28.2, 0.6},{ -0.3, 28.8, 0.5}, { 0.3, 28.8, 0.5},{ 2.3, 28.2, 0.6}, { 5.0, 26.1, 0.8},{ 7.6, 21.7, 1.0}, { 10.3, 13.7, 1.2},{ 13.0, 0.0, 1.5} }; SoSeparator *makeArch() { SoSeparator *result = new SoSeparator; result->ref(); SoMaterial *myMaterial = new SoMaterial; myMaterial->diffuseColor.setValue(.78,.57,.11); result->addChild(myMaterial); SoCoordinate3 *myCoords = new SoCoordinate3; myCoords->point.setValues(0,60,vertexPositions); result->addChild(myCoords); SoQuadMesh *myQuadMesh = new SoQuadMesh; myQuadMesh->verticesPerRow = 12; myQuadMesh->verticesPerColumn = 5; result->addChild(myQuadMesh); result->unrefNoDelete(); return result; } Les propriétés Les noeuds propriété d'Inventor dérivent de SoNode. Il en existe une quarantaine. Les plus importants sont :
Les transformations géométriques Les noeuds transformation s'accumulent sur la transformation géométrique courante. Les translation, rotation et zoom sont possibles, et correspondent aux classes:
La classe SoTranslation permet denchaîner plusieurs de ces transformations.
Senseurs : objets pouvant être attachés à la base de données pour répondre :
sous la forme de lexécution dune fonction. Les senseurs de données Trois classes de senseurs de données :
pouvant être attachés à un champ, un noeud ou un chemin. Les fonctions associées sont mises en file dattente (avec priorité) si la donnée est modifiée, pour une exécution quand le processeur est disponible. Procédure dutilisation (1) Construction du noeud senseur (2) Ecriture et affectation de la fonction associée (3) Affectation dune priorité (4) Attachement à un champ, un noeud ou un chemin (5) Effacement -> (2) La fonction associée est de prototype static void fonction(void *data, SoSensor *senseur) ; data : donnée pouvant être employée par lutilisateur senseur : pointeur sur le senseur ayant lancé la fonction -> (3) Les fonctions membres setPriority(priorite), getPriority() et getDefaultPriority() permettent respectivement :
-> (4) Utilisation de la fonction membre attach du senseur avec comme paramètre ladresse du champ, du noeud ou du chemin à attacher au senseur. Exemple Attachement dun senseur de champ à la position dune caméra. La fonction associée affiche la position de la caméra. #include <Inventor/SoDB.h> #include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> #include <Inventor/nodes/SoCamera.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/sensors/SoFieldSensor.h> static void cameraChangedCB(void *data,SoSensor *) { SoCamera *viewerCamera = (SoCamera *)data; SbVec3f cameraPosition = viewerCamera->position.getValue(); printf("Camera position: (%g,%g,%g)\n", cameraPosition[0], cameraPosition[1], cameraPosition[2]); } void main(int argc,char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s filename.iv\n",argv[0]); exit(1); } Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); SoInput inputFile; if (inputFile.openFile(argv[1]) == FALSE) { fprintf(stderr,"Could not open %s\n",argv[1]); exit(1); } SoSeparator *root = SoDB::readAll(&inputFile); root->ref(); SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); myViewer->setSceneGraph(root); myViewer->setTitle("Camera Sensor"); myViewer->show(); SoCamera *camera = myViewer->getCamera(); SoFieldSensor *mySensor = new SoFieldSensor(cameraChangedCB, camera); mySensor->attach(&camera->position); SoXt::show(myWindow); SoXt::mainLoop(); } Les senseurs temporels Contrairement aux senseurs de données, les senseurs temporels ne sont pas attachés à une information du programme, mais simplement exécutés à un instant prédéfini. Deux classes de senseurs temporels :
Procédure dutilisation (1) Construction du noeud senseur (2) Ecriture et affectation de la fonction associée (3) Affectation des paramètres temporels (4) Activation (5) Effacement -> (3) SoAlarmSensor : les fonctions membres setTime(time) et setTimeFromNow(time) permettent respectivement :
SoTimerSensor : les fonctions membres setInterval(interval) et setBaseTime(time) permettent respectivement :
-> (4) Utilisation de la fonction membre schedule. Exemple Par lutilisation de deux senseurs, on réalise une animation (rotation dune scène) par alternance de séquences de 5 secondes à 1 image par seconde, puis 10 images par seconde. #include <Inventor/SoDB.h> #include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> #include <Inventor/nodes/SoCone.h> #include <Inventor/nodes/SoRotation.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoTransform.h> #include <Inventor/sensors/SoTimerSensor.h> static void rotatingSensorCallback(void *data, SoSensor *) { // Rotate an object... SoRotation *myRotation =(SoRotation *) data; SbRotation curRotation = myRotation->rotation.getValue(); curRotation = SbRotation( SbVec3f(0,0,1),M_PI/90.0) * curRotation; myRotation->rotation.setValue(curRotation); } static void schedulingSensorCallback(void *data, SoSensor *) { SoTimerSensor *rotatingSensor = (SoTimerSensor *)data; rotatingSensor->unschedule(); if (rotatingSensor->getInterval() == 1.0) rotatingSensor->setInterval(1.0/10.0); else rotatingSensor->setInterval(1.0); rotatingSensor->schedule(); } void main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s filename.iv\n", argv[0]); exit(1); } Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); SoSeparator *root = new SoSeparator; root->ref(); SoRotation *myRotation = new SoRotation; root->addChild(myRotation); SoTimerSensor *rotatingSensor = new SoTimerSensor(rotatingSensorCallback, myRotation); rotatingSensor->setInterval(1.0); rotatingSensor->schedule(); SoTimerSensor *schedulingSensor = new SoTimerSensor(schedulingSensorCallback, rotatingSensor); schedulingSensor->setInterval(5.0); schedulingSensor->schedule(); SoInput inputFile; if ( inputFile.openFile(argv[1]) == FALSE ) { fprintf(stderr,"Could not open file %s\n", argv[1]); exit(1); } root->addChild(SoDB::readAll(&inputFile)); SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); myViewer->setSceneGraph(root); myViewer->setTitle("Two Timers"); myViewer->show(); SoXt::show(myWindow); SoXt::mainLoop(); } Les moteurs Les moteurs (engines) sont des classes dobjets destinés à être connectés à des champs et utilisés pour animer des parties dune scène ou induire des contraintes entre différents éléments dune scène. Un moteur est une boite noire qui reçoit des valeurs en entrée, réalise certaines opérations sur ces données et copie les résultats sur une ou plusieurs sorties. Les entrées dérivent de champs et sont donc de type SoField. Les sorties sont de type SoEngineOutput. Une modification des champs en entrée entraîne la réévaluation des sorties par le moteur. Celles-ci étant connectées à des champs, entraînent leurs modifications. Réalisation dune connection simple entre champs Deux champs de noeuds différents peuvent être connectés de manière à ce que tout changement de lun entraîne le changement de lautre (et réciproque). -> Utilisation de la fonction connectFrom de SoField : void connectFrom(SoField *champ) ; exemple xform->rotation.connectFrom(&camera->orientation) ; Réalisation dune connection entre un champ et la sortie dun moteur -> Utilisation de la fonction connectFrom de SoField : void connectFrom(SoEngineOutput *sortiedemoteur) ; exemple texte->string.connectFrom(&temps->timeOut) ; Les portes Le noeud SoGate est un moteur qui permet de copier sélectivement des valeurs dune entrée vers une sortie. Quand une valeur en entrée dun réseau de moteurs subit un changement, cette valeur est automatiquement répercutée. Si cette valeur change constamment, les répercutions sont constantes. Un noeud SoGate permettra de restreindre les transmissions. Quand on crée un noeud SoGate, on passe en paramètre le type de la donnée à transmettre, puis on établit les conections en entrée et en sortie. Le champ enable autorisera le passage en continu. Quand le champ trigger est touché (touch() ou setValue()) une valeur est envoyée. Exemple SoGate *Gt = new SoGate(SoMFFloat::getClassTypeId()); SoElapsedTime *bDuckTime = new SoElapsedTime; Gt->input->connectFrom(&bDuckTime->timeOut); bDuckRotXYZ->angle.connectFrom(Gt->output); Gt->enable.setValue(!gate->enable.getValue()); Moteurs d'animation Utilisés pour animer des objets. Ils sont automatiquement connectés en entrée à lhorloge temps réelle de lordinateur. Trois classes :
Exemple #include <~~~~~> #include <Inventor/engines/SoCompose.h> #include <Inventor/engines/SoElapsedTime.h> void main(int , char **argv) { Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); SoSeparator *root = new SoSeparator; root->ref(); SoPerspectiveCamera *myCamera = new SoPerspectiveCamera; myCamera->position.setValue(-2.0,-2.0,5.0); myCamera->heightAngle = M_PI/2.5; myCamera->nearDistance = 2.0; myCamera->farDistance = 7.0; root->addChild(myCamera); root->addChild(new SoDirectionalLight); SoTranslation *slideTrans = new SoTranslation; root->addChild(slideTrans); SoTransform *initialTrans = new SoTransform; initialTrans->translation.setValue(-5.,0.,0.); initialTrans->scaleFactor.setValue(10.,10.,10.); initialTrans->rotation.setValue(SbVec3f(1,0,0), M_PI/2.); root->addChild(initialTrans); SoInput myInput; if (!myInput.openFile("~~~~~")) exit (1); SoSeparator *figureObject = SoDB::readAll(&myInput); if (figureObject == NULL) exit (1); root->addChild(figureObject); SoElapsedTime *myCounter = new SoElapsedTime; SoComposeVec3f *sDist = new SoComposeVec3f; sDist->x.connectFrom(&myCounter->timeOut); slideTrans->translation.connectFrom(&sDist->vector); SoXtRenderArea *myRA = new SoXtRenderArea(myWindow); SbViewportRegion myRegion(myRA->getSize()); myRA->setSceneGraph(root); myRA->setTitle("Sliding Man"); myRA->show(); SoXt::show(myWindow); SoXt::mainLoop(); } Moteurs arithmétiques Les moteurs arithmétiques ont en entrée et en sortie des champs à valeurs multiples. Une valeur simple est automatiquement convertie. Les valeurs en sorties sont des tableaux si les champs en entrée létaient. Le moteur booléen Classe SoBoolOperator Deux entrées booléennes et une entrée de type SoSFEnum pour indiquer lopération à réaliser. Lopération peut prendre une des valeurs suivantes:
Deux sorties output et inverse. Le champ inverse est faux si output est vrai et inverse. Exemple : les canards #include <~~~~~~> myMousePressCB(void *userData, SoEventCallback *eventCB){ SoGate *gate =(SoGate *) userData; const SoEvent *event = eventCB->getEvent(); if (SO_MOUSE_PRESS_EVENT(event,ANY)) { gate->enable.setValue(!gate->enable.getValue()); eventCB->setHandled(); } } void main(int , char **argv) { Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); SoSeparator *root = new SoSeparator; root->ref(); SoPerspectiveCamera *myCamera = new SoPerspectiveCamera; myCamera->position.setValue(0.,-4.,8.0); myCamera->heightAngle = M_PI/2.5; myCamera->nearDistance = 1.0; myCamera->farDistance = 15.0; root->addChild(myCamera); root->addChild(new SoDirectionalLight); SoRotationXYZ *globalRotXYZ = new SoRotationXYZ; globalRotXYZ->axis = SoRotationXYZ::X; globalRotXYZ->angle = M_PI/9; root->addChild(globalRotXYZ); SoSeparator *pond = new SoSeparator; root->addChild(pond); SoTranslation *pondTranslation = new SoTranslation; pondTranslation->translation.setValue(0.,-6.725,0.); pond->addChild(pondTranslation); SoMaterial *waterMaterial = new SoMaterial; waterMaterial->diffuseColor.setValue(0., 0.3, 0.8); pond->addChild(waterMaterial); SoCylinder *waterCylinder = new SoCylinder; waterCylinder->radius.setValue(4.0); waterCylinder->height.setValue(0.5); pond->addChild(waterCylinder); SoMaterial *rockMaterial = new SoMaterial; rockMaterial->diffuseColor.setValue(0.8,0.23,0.03); pond->addChild(rockMaterial); SoSphere *rockSphere = new SoSphere; rockSphere->radius.setValue(0.9); pond->addChild(rockSphere); SoInput myInput; if (!myInput.openFileduck.iv")) exit (1); SoSeparator *duckObject = SoDB::readAll(&myInput); if (duckObject == NULL) exit (1); SoSeparator *bDuck = new SoSeparator; root->addChild(bDuck); SoRotationXYZ *bDuckRotXYZ = new SoRotationXYZ; bDuck->addChild(bDuckRotXYZ); SoTransform *bInitTransform = new SoTransform; bInitTransform->translation.setValue(0.,0.,3.5); bInitTransform->scaleFactor.setValue(6.,6.,6.); bDuck->addChild(bInitTransform); bDuck->addChild(duckObject); SoSeparator *sDuck = new SoSeparator; root->addChild(sDuck); SoRotationXYZ *sDuckRotXYZ = new SoRotationXYZ; sDuck->addChild(sDuckRotXYZ); SoTransform *sInitTransform = new SoTransform; sInitTransform->translation.setValue(0.,-2.2,1.5); sInitTransform->scaleFactor.setValue(4.,4.,4.); sDuck->addChild(sInitTransform); sDuck->addChild(duckObject); SoGate *bDGate = new SoGate(SoMFFloat::getClassTypeId()); SoElapsedTime *bDuckTime = new SoElapsedTime; bDGate->input->connectFrom(&bDuckTime->timeOut); bDuckRotXYZ->axis = SoRotationXYZ::Y; bDuckRotXYZ->angle.connectFrom(bDGate->output); SoEventCallback *myEventCB = new SoEventCallback; myEventCB->addEventCallback( SoMouseButtonEvent::getClassTypeId(), myMousePressCB,bDuckGate); root->addChild(myEventCB); SoBoolOperation *myBoolean = new SoBoolOperation; myBoolean->a.connectFrom(&bDuckGate->enable); myBoolean->operation = SoBoolOperation::NOT_A; SoGate *sDGate = new SoGate(SoMFFloat::getClassTypeId()); SoElapsedTime *sDuckTime = new SoElapsedTime; sDGate->input->connectFrom(&sDuckTime->timeOut); sDGate->enable.connectFrom(&myBoolean->output); sDuckRotXYZ->axis = SoRotationXYZ::Y; // Y axis sDuckRotXYZ->angle.connectFrom(sDGate->output); SoXtRenderArea *myRenderArea = new SoXtRA(myWindow); myRA->setSceneGraph(root); myRA->setTitle("Duck and Duckling"); myRA->show(); SoXt::show(myWindow); SoXt::mainLoop(); } Le moteur calculateur Classe SoCalculator
Expression est une expression arithmétique permettant de calculer les champs en sortie au moyen des entrées. Exemple #include <~~~~~> void main(int,char **argv) { Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); SoSeparator *root = new SoSeparator; root->ref(); SoPerspectiveCamera *myCamera = new SoPerspectiveCamera; myCamera->position.setValue(-0.5, -3.0, 19.0); myCamera->nearDistance = 10.0; myCamera->farDistance = 26.0; root->addChild(myCamera); root->addChild(new SoDirectionalLight); SoRotationXYZ *globalRotXYZ = new SoRotationXYZ; globalRotXYZ->axis = SoRotationXYZ::X; globalRotXYZ->angle = M_PI/7; root->addChild(globalRotXYZ); SoInput myInput; if (!myInput.openFile("flowerPath.iv")) exit (1); SoSeparator *flowerPath = SoDB::readAll(&myInput); if (flowerPath == NULL) exit (1); root->addChild(flowerPath); SoSeparator *flowerGroup = new SoSeparator; root->addChild(flowerGroup); if (!myInput.openFile("flower.iv")) exit (1); SoSeparator *flower= SoDB::readAll(&myInput); if (flower == NULL) exit (1); SoTranslation *dTranslation = new SoTranslation; SoTransform *initialTransform = new SoTransform; flowerGroup->addChild(dTranslation); initialTransform->scaleFactor.setValue(10.,10.,10.); initialTransform->translation.setValue(0.,0.,5.); flowerGroup->addChild(initialTransform); flowerGroup->addChild(flower); // Set up an engine to calculate the motion path: // r = 5*cos(5*theta); // x = r*cos(theta); // z = r*sin(theta) // Theta is incremented using a time counter engine, // and converted to radians using an expression in // the calculator engine. SoCalculator *calcXZ = new SoCalculator; SoTimeCounter *thetaCounter = new SoTimeCounter; thetaCounter->max = 360; thetaCounter->step = 4; thetaCounter->frequency = 0.075; calcXZ->a.connectFrom(&thetaCounter->output); calcXZ->expression.set1Value(0,"ta=a*M_PI/180"); calcXZ->expression.set1Value(1,"tb=5*cos(5*ta)"); calcXZ->expression.set1Value(2,"td=tb*cos(ta)"); calcXZ->expression.set1Value(3,"te=tb*sin(ta)"); calcXZ->expression.set1Value(4,"oA=vec3f(td,0,te)"); dTranslation->translation.connectFrom(&calcXZ->oA); SoXtRA *myRenderArea = new SoXtRenderArea(myWindow); myRA->setSceneGraph(root); myRA->setTitle("Flower Dance"); myRA->show(); SoXt::show(myWindow); SoXt::mainLoop(); } Gestion des événements Les événements entraînant un rafraichissement de la fenêtre de dessin sont gérées automatiquement. Utilisation de la classe SoEvent et de ses classes dérivées pour la détection des autres événements.
Ces événements sont gérés automatiquement par Inventor. Quand un événement arrive :
Inventor proposent plusieurs méthodes pour la gestion des événements : (1) Utilisation du mécanisme automatique dInventor selon lequel certains noeuds gèrent eux-même les événements. (2) Utilisation de noeuds dédiés à la détection dévénements et au lancement de fonctions associées. (3) Existence de deux autres méthodes non décrites ici. Méthode (1) Utilisation des méthodes de réponses aux événements produitent par Inventor. Les noeuds dérivés de SoNode ne réagissent pas aux événements. Les noeuds dérivés de SoGroup assurent la transmission de lévénement à leurs fils. Les noeuds manipulateurs réagissent automatiquement aux événements. Méthode (2) Un noeud spécial SoEventCallback est ajouté au graphe de scène pour lancer lexécution dune fonction en réponse à un événement paramétré par le programmeur. Lors du parcours du graphe pour un SoEvent, ce noeud poura y répondre. Pour indiquer quels SoEvents interessent le noeud SoEventCallback ainsi que la fonction à appeler, on utilise la fonction membre addEventCallback(). SoEventCallback *EventCB = new SoEventCallback; EventCB->addEventCallback( SoMouseButtonEvent::getClassTypeId(), fonction,userdata); Gestion de fichiers Affichage dun graphe La classe SoWriteAction par lintermédiaire de sa fonction writeAction permet lécriture dun graphe de scène sur stdout. Lecture dun fichier La classe SoInput permet la lecture dun fichier pour générer un graphe de scène. Le fichier est au format inventor (iv). Il sagit dun format proche de celui des fichiers VRML. Les fonctions openFile(char *nom) et closeFile() réalisent louverture et la fermeture du fichier. La fonction SoSeparator *readAll(* SoInput) de SoDB permet la lecture dun fichier iv et rend comme résultat un pointeur sur separator représentant la racine du graphe. |