Open Inventor
Partie 2

WB01624_.gif (281 octets) RETOUR

Formes et propriétés

Les formes

Open Inventor reconnaît un certain nombre de formes.

Formes simples

  • les cuboïdes (largeur, hauteur, profondeur) : SoCube
  • les cônes (hauteur, rayon de la base) : SoCone
  • les sphères (rayon) : SoSphere
  • les cylindres (hauteur et rayon) : SoCylinder

Formes complexes

  • ensemble de facettes, ensemble indexé de facettes :

SoFaceSet et SoIndexedFaceSet

  • ensemble de segments, ensemble indexé de segments :

SoLineSet et SoIndexedLineSet

  • bande de triangles, bande indexée de triangles

SoTriangleStripSet et SoIndexedTriangleStripSet

  • ensemble de points

SoPointSet

  • maillage quadrangulaire

SoQuadMesh

  • courbes et surfaces NURBS

SoNurbsCurve et SoNurbsSurface

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 :

  • SoMaterial

Champ

Rôle

ambientColor (SoMFColor)

Couleur réfléchie en réponse à la lumière ambiante présente dans la scène

diffuseColor (SoMFColor)

Couleur de base de l'objet

specularColor (SoMFColor)

Couleur spéculaire

emissiveColor (SoMFColor)

Lumière émise

shininess (SoSFFloat)

Coefficient de réflexion

transparency (SoSFFloat)

Coefficient de transparence

 

  • SoDrawStyle

Champ

Rôle

style (SoSFEnum)

Définit le style de tracé:
SoDrawStyle::FILLED
SoDrawStyle::LINES
SoDrawStyle::POINTS
SoDrawStyle::INVISIBLE

pointSize (SoSFFloat)

Rayon des points

lineWidth (SoSFFloat)

Epaisseur des lignes

linePattern (SoSFUShort)

Motif des ligne

 

  • SoLightModel

Champ

Rôle

model (SoSFEnum)

Définit le modèle d'illumination:
SoLightModel::BASE_COLOR
SoLightModel::PHONG

 

  • SoEnvironment

Champ

Rôle

ambientIntensity (SoSFFloat)

Intensité de la lumière ambiante

ambientColor (SoSFColor)

Couleur de la lumière ambiante

attenuation (SoSFVect3f)

Définit la décroissance de la lumière avec la distance à la source lumineuse (quadratique, linéaire ou constante)

fogType (SoSFEnum)

Type de brouillard :
SoEnvironment::NONE
SoEnvironment::HAZE
SoEnvironment::FOG
SoEnvironment::SMOKE

fogColor (SoSFColor)

Couleur du brouillard

fogVisibility (SoSFFloat)

Distance à laquelle le brouillard masque totalement les objets de la scène

 

  • SoShapeHints

Champ

Rôle

vertexOrdering (SoSFEnum)

Ordre des faces:
SoShapeHints::UNKNOWN_ORDERING
SoShapeHints::CLOCKWISE
SoShapeHints::COUNTERCLOCKWISE

shapeType (SoSFEnum)

Type de forme:
SoShapeHints::UNKNOWN_SHAPE_TYPE
SoShapeHints::SOLID

faceType (SoSFEnum)

Type de face:
SoShapeHints::UNKNOWN_FACE_TYPE
SoShapeHints::CONVEX

creaseAngle (SoSFFloat)

non documenté

 

  • SoComplexity

Champ

Rôle

type (SoSFEnum)

Type de complexité:

SoComplexity::OBJECT_SPACE

basée sur les objets

SoComplexity::SCREEN_SPACE

basée sur la taille des objets

SoComplexity::BOUNDING_BOX

remplace les objets par des boites englobantes

value (SoSFFloat)

Précision désirée (de 0.0 à 1.0)

textureQuality (SoSFFloat)

Qualité du texturage (de 0.0 à 1.0)

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:

  • SoTranslation

SoTranslation

Rôle

translation (SoSFVec3F)

Translation

 

  • SoRotation.

SoRotation

Rôle

rotation (SoSFRotation)

Rotation autour d'un axe

 

  • SoScale.

SoScale

Rôle

scaleFactor (SoSFFloat)

Zoom en x, y et z

La classe SoTranslation permet d’enchaîner plusieurs de ces transformations.

SoTransform

Rôle

translation (SoSFVec3F)

Translation

rotation (SoSFVec3F)

Rotation autour d'un axe

scaleFactor (SoSFFloat)

Zoom en x, y et z

scaleOrientation (SoSFRotation)

Rotation à appliquer avant le zoom

center (SoSFVec3f)

Point central pour la rotation et le zoom

Les senseurs

Senseurs : objets pouvant être attachés à la base de données pour répondre :

  • à des changements de la base de données (senseurs de données),

  • à des événements temporels (senseurs temporels).

sous la forme de l’exécution d’une fonction.

Les senseurs de données

Trois classes de senseurs de données :

  • SoFieldSensor,

  • SoNodeSensor,

  • SoPathSensor,

pouvant être attachés à un champ, un noeud ou un chemin.

Les fonctions associées sont mises en file d’attente (avec priorité) si la donnée est modifiée, pour une exécution quand le processeur est disponible.

Procédure d’utilisation

(1) Construction du noeud senseur

(2) Ecriture et affectation de la fonction associée

(3) Affectation d’une 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 l’utilisateur

senseur : pointeur sur le senseur ayant lancé la fonction

-> (3) Les fonctions membres setPriority(priorite), getPriority() et getDefaultPriority() permettent respectivement :

  • d’affecter une priorité à un senseur (nombre d’autant plus grand que la priorité est importante),

  • de lire la priorité d’un senseur,

  • d’affecter la priorité par défaut (100).

-> (4) Utilisation de la fonction membre attach du senseur avec comme paramètre l’adresse du champ, du noeud ou du chemin à attacher au senseur.

Exemple

Attachement d’un senseur de champ à la position d’une 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 :

  • SoAlarmSensor,

  • SoTimerSensor.

Procédure d’utilisation

(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 :

  • de fixer l’instant d’exécution

  • de fixer le temps restant avant l’instant d’exécution.

SoTimerSensor : les fonctions membres setInterval(interval) et setBaseTime(time) permettent respectivement :

  • de fixer l’intervalle entre deux exécutions

  • de fixer l’instant d’activation du senseur.

-> (4) Utilisation de la fonction membre schedule.

Exemple

Par l’utilisation de deux senseurs, on réalise une animation (rotation d’une 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 d’objets destinés à être connectés à des champs et utilisés pour animer des parties d’une scène ou induire des contraintes entre différents éléments d’une 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 d’une connection simple entre champs

Deux champs de noeuds différents peuvent être connectés de manière à ce que tout changement de l’un entraîne le changement de l’autre (et réciproque).

-> Utilisation de la fonction connectFrom de SoField :

void connectFrom(SoField *champ) ;

exemple

xform->rotation.connectFrom(&camera->orientation) ;

Réalisation d’une connection entre un champ et la sortie d’un 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 d’une entrée vers une sortie.

Quand une valeur en entrée d’un 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 à l’horloge temps réelle de l’ordinateur.

Trois classes :

  • SoElapsedTime : donne le temps écoulé depuis son démarrage

  • SoOneShot : marche pour un temps prédéterminé puis s’arrête

  • SoTimeCounter : compteur cyclique entre deux valeurs min et max. Un pas peut être spécifié. La fréquence de variation min-max peut être spécifiée.

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 l’opération à réaliser.

L’opération peut prendre une des valeurs suivantes:

  • CLEAR

  • SET

  • A

  • NOT_A

  • B

  • NOT_B

  • A_OR_B

  • NOT_A_OR_B

  • A_OR_NOT_B

  • NOT_A_OR_NOT_B

  • A_AND_B

  • NOT_A_AND_B

  • A_AND_NOT_B

  • NOT_A_AND_NOT_B

  • A_EQUALS_B

  • A_NOT_EQUALS_B

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

  • En entrée :

    • a,b,c,d,e,f,g,h : SoMFFloat

    • A,B,C,D,E,F,G,H : SoMFVec3f

    • expression : SoMFString

  • En sortie :

    • oa,ob,oc,od : SoEngineOutput (SoMFFloat)

    • oA,oB,oC,oD : SoEngineOutput (SoMFVec3f)

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.

  • SoKeyboardEvent : événements clavier

  • SoLocation2Event : déplacements de la souris

  • SoMouseButtonEvent : clics de bouton souris

Ces événements sont gérés automatiquement par Inventor.

Quand un événement arrive :

  • génération d’un SoEvent du type adapté,

  • transmission automatique aux noeuds de la scène (parcours en profondeur du graphe de scène) jusqu’à trouver un noeud à même de l’utiliser.

Inventor proposent plusieurs méthodes pour la gestion des événements :

(1) Utilisation du mécanisme automatique d’Inventor 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 l’exécution d’une 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 d’un graphe

La classe SoWriteAction par l’intermédiaire de sa fonction writeAction permet l’écriture d’un graphe de scène sur stdout.

Lecture d’un fichier

La classe SoInput permet la lecture d’un fichier pour générer un graphe de scène.

Le fichier est au format inventor (iv). Il s’agit d’un format proche de celui des fichiers VRML.

Les fonctions openFile(char *nom) et closeFile() réalisent l’ouverture et la fermeture du fichier.

La fonction SoSeparator *readAll(* SoInput) de SoDB permet la lecture d’un fichier iv et rend comme résultat un pointeur sur separator représentant la racine du graphe.