Retour table des matières Java 3D
Les animations sont un type de comportement particulier qui modifie l'aspect d'une scène 3D de manière prédéterminée au cours du temps.
Dans ce chapitre, nous avons séparé les animations temporelles en deux paragraphes principaux. Le premier concerne les transformations classiques d'un objet dans le temps comme sa position, sa couleur, sa transparence etc ..., le second concerne un type particulier de transformation dans le temps : le morphing. Dans ce dernier cas, c'est la géométrie de l'objet qui va évoluer dans le temps.
1. Animation temporelle
La classe Alpha
Toutes les animations temporelles (y compris le morphing) sont basées sur un opérateur nommé opérateur alpha dont la classe porte le nom Alpha.
La classe Alpha va permettre de convertir une valeur temporelle en valeur alpha qui varie dans l'intervalle [0,1]. Les comportements d'animation utiliseront cette valeur de alpha.
Comme souvent une image vaut mieux qu'un long discours, voici une illustration extraite de la documentation de Sun montrant la correspondance entre l'opérateur alpha et le temps :
Correspondance entre le temps et l'opérateur alpha
Les phases d'une animation temporelles sont les suivantes :
Nous allons commenter plus en détail ces paramètres en étudiant un des constructeurs de la classe Alpha :
public Alpha(int loopCount,
int mode,
int triggerTime,
int phaseDelayDuration,
int increasingAlphaDuration,
int increasingAlphaRampDuration,
int alphaAtOneDuration,
int decreasingAlphaDuration,
int decreasingAlphaRampDuration,
int alphaAtZeroDuration)
loopCount définit le nombre de cycles d'évolution de alpha. Une valeur de -1 signifie un nombre infini de cycles.
mode indique si ce sont les paramètres montant (increasing) ou de descendant (decreasing) ou les deux qui sont actifs, les valeurs possibles pour mode sont INCREASING_ENABLE, DECREASING_ENABLE ou une combinaison des deux (typiquement le "ou" logique).
triggerTime : durée en ms au bout de laquelle le comportement va se déclencher.
phaseDelayDuration : durée en ms au bout de laquelle le comportement va effectivement se déclencher
(après triggerTime).
increasingAlphaDuration : période en ms pendant laquelle alpha va évoluer de 0 à 1
increasingAlphaRampDuration : période en ms permettant d'adoucir la phase d'accélération et de décélération en début et en fin de phase montante (la valeur de increasingAlphaRampDuration est limitée à la moitié de increasingAlphaDuration).
alphaAtOneDuration : durée en ms pendant laquelle alpha reste à 1.
decreasingAlphaDuration : période en ms pendant laquelle alpha va évoluer de 1 à 0
decreasingAlphaRampDuration : période en ms permettant d'adoucir la phase d'accélération et de décélération en début et en fin de phase descendante.
alphaAtZeroDuration : période en ms pendant laquelle alpha reste à 0
On peut noter que le constructeur par défaut :
public Alpha()
fournit les valeurs par défaut suivantes :
loopCount = -1
mode = INCREASING_ENABLE
startTime = start time système (System.currentTimeMillis())
phaseDelayDuration = 0
increasingAlphaDuration = 1000
increasingAlphaRampDuration = 0
alphaAtOneDuration = 0
decreasingAlphaDuration = 0
decreasingAlphaRampDuration = 0
alphaAtZeroDuration = 0
Il existe bien sûr tous les accesseurs nécessaires pour modifier tous ces paramètres une fois l'objet alpha créé.
On notera également que les méthodes pause() et resume() de la classe Alpha permettent d'interrompre et de reprendre l'évolution temporelle de l'opérateur alpha.
Après avoir étudié cette classe Alpha, nous allons voir que tous les comportements étudiés dans les paragraphes qui viennent sont basés sur l'opérateur alpha.
alpha peut être utilisé comme facteur d'interpolation d'un angle compris entre 0 (alpha = 0) et PI (alpha = 1) par exemple, on obtient ainsi un angle de rotation qui va varier entre
0 et PI au cours du temps. Cette variation entre 0 et PI sera proportionnelle à celle de alpha entre 0 et 1 que nous avons construit au préalable.
alpha peut être aussi utilisé comme facteur d'interpolation de couleurs, de transparence, de géométrie, etc ....
On mesure ici toute la puissance de l'outil.
RotationInterpolator
La classe RotationInterpolator permet de modifier les composantes rotationnelles d'un objet TransformGroup en interpolant linéairement l'angle de rotation.
Voici les deux constructeurs de cette classe :
public RotationInterpolator(Alpha alpha, TransformGroup target)
alpha est l'interpolateur de l'angle de rotation. Cet angle varie dans l'intervalle [0;2xPI]
target est la cible à laquelle on va appliquer ce comportement.
Par défaut, l'axe de rotation est l'axe Y du repère 3D.
public RotationInterpolator(Alpha alpha, TransformGroup target,
Transform3D axisOfTransform,
float minimumAngle, float maximumAngle)
alpha est l'interpolateur de l'angle de rotation. Cet angle varie dans l'intervalle (en radians) [minimumAngle;maximumAngle]
target est la cible à laquelle on va appliquer ce comportement
axisOfTransform n'est pas l'axe de rotation. C'est une transformation qui, appliquée à l'axe Y du repère 3D, donne l'axe de rotation. Si axisOfTransform vaut l'identité, alors l'axe de rotation est Y. Pour que l'axe de rotation soit X par exemple, il faudrait que axisOfTransform soit une rotation de -PI/2 autour de Z (une rotation de -PI/2 autour de Z de l'axe Y donne bien l'axe X).
minimumAngle : valeur minimum de l'angle de la rotation
maximumAngle : valeur maximum de l'angle de rotation
Dans ce paragraphe, nous avons écrit un petit exemple illustrant l'usage de la classe RotationInterpolator.
On remarquera dans cette exemple que l'on utilise l'expression :
rotationAlpha.setStartTime(System.currentTimeMillis());
lors de la construction de l'arbre de scène.
Ceci est indispensable car sinon, le startTime par défaut prend la valeur du temps système lors de la création du canvas 3D. Or entre la création du canvas 3D et la création de l'arbre de scène, il peut s'écouler quelques millisecondes qui font que l'interpolateur aura une origine temporelle un peut trop ancienne. C'est pourquoi on recale cette origine temporelle au moment même où l'on crée l'interpolateur.
Toutefois, si le calage temporel semble correct lorsqu'on lance l'application en standalone ou en mode applet avec Firefox, il en est tout autre avec Internet Explorer. Chacun pourra juger sur sa machine ...
Arborescence d'une scène avec un interpolateur de type RotationInerpolator
Le fichier RotationInterpolatorTest.java présente le code source complet de cet exemple.
Exécutez l'applet RotationInterpolatorTest.html pour voir le résultat.
PositionInterpolator
Cette classe permet de modifier les composantes de translation d'un objet TransformGroup en interpolant linéairement la position de l'objet.
Les deux constructeurs de cette classe ressemblent beaucoup à ceux de la classe RotationInterpolator :
public PositionInterpolator(Alpha alpha, TransformGroup target)
alpha est l'interpolateur de position de l'objet. Cette position varie dans l'intervalle [0;1]
target est la cible à laquelle on va appliquer ce comportement.
Par défaut, l'objet se déplace selon l'axe X du repère 3D.
public PositionInterpolator(Alpha alpha, TransformGroup target,
Transform3D axisOfTransform,
float startPosition, float endPosition )
alpha est l'interpolateur de position de l'objet. Cette position varie dans l'intervalle [startPosition;endPosition]
target est la cible à laquelle on va appliquer ce comportement
axisOfTransform n'est pas l'axe de la translation. C'est une transformation qui, appliquée à l'axe X du repère 3D, donne l'axe de translation. Si axisOfTransform vaut l'identité, alors l'axe de translatioon est X. Pour que l'axe de translation soit Y par exemple, il faudrait que axisOfTransform soit une rotation de PI/2 autour de Z (une rotation de PI/2 autour de Z de l'axe X donne bien l'axe Y).
startPosition : valeur minimum de la position de l'objet sur l'axe de translation.
endPosition : valeur maximum de la position de l'objet sur l'axe de translation.
Le fichier PositionInterpolatorTest.java présente le code source complet de cet exemple.
Exécutez l'applet PositionInterpolatorTest.html pour voir le résultat.
ColorInterpolator
Après avoir vu deux interpolateurs qui permettaient de modifier la mosition d'un objet (composantes rotation ou translation), nous allons voir maintenant un exemple avec un interpolateur permettant de modifier sa couleur.
Nous rappelons qu'un objet peut être coloré de plusieurs manières :
- couleurs des sommets
- couleurs des attributs de la classe ColoringAttributes
- combinaison des différentes couleurs de la classe Material (ambiante, émission, diffuse et spéculaire)
L'interpolateur de type ColorInterpolator va nous permettre de modifier une ou plusieurs de ces couleurs qui ont été attribuées à l'objet grâce à la classe Material.
Nous rappelons également que, à part la couleur d'émission, toutes les autres couleurs de la classe Material ne sont visibles que sous l'effet de l'éclairage. Nous allons donc étudier un petit exemple où la scène 3D sera éclairée par une source de couleur ambiante et par une source directionnelle (qui va donc faire ressortir les couleurs diffuse et spéculaire du matériau).
Nous allons utiliser le constructeur suivant pour la classe ColorInterpolator :
public ColorInterpolator(Alpha alpha, Material target,
Color3f startColor, Color3f endColor)
alpha est l'interpolateur de la couleur de l'objet
target est le materiau de l'objet dont la couleur va être modifiée par l'interpolateur
startColor : couleur de début
endColor : couleur de fin
Par défaut, c'est la couleur diffuse du matériau qui est modifiée par l'interpolateur. Mais on peut également modifier d'autres couleur du matériau, notamment à l'aide de la méthode setColorTarget() de la classe Material :
public void setColorTarget(int colorTarget)
colorTarget est la couleur du matériau qui sera modifiée par l'interpolateur, par défaut c'est la couleur diffuse, mais elle peut être également :
couleur ambiante (colorTarget = Material.AMBIENT)
couleurs ambiante et diffsue (colorTarget = Material.AMBIENT_AND_DIFFUSE)
couleur diffuse (colorTarget = Material.DIFFUSE)
couleur d'émission (colorTarget = Material.EMISSIVE)
couleur spéculaire (colorTarget = Material.SPECULAR)
Attention :
J'ai constaté qu'il y avait un bug dans la version OpenGL de Java 3D : l'interpolateur de couleur ne fonctionne pas pour la couleur d'émission. En revanche, si on utilise la version DirectX de Java 3D, il n'y a pas de problèmes.
Le fichier ColorInterpolatorTest.java présente le code source complet de cet exemple.
Exécutez l'applet ColorInterpolatorTest.html pour voir le résultat.
Dans cet exemple, les couleurs ambiantes, diffuses et d'émission vont se mélanger avec une couleur diffuse qui va varier du bleu vers le jaune. On remarquera que du côté de l'éclairage directionnel, ce sont les couleurs diffuse et d'émission qui sont prépondérantes (devant la couleur ambiante qui n'est mise en valeur que par la source lumineuse ambiante).
Le rouge de la couleur d'émission mélangé au jaune de la couleur diffuse de départ redonne du rouge tandis que le rouge mélangé à la couleur diffuse de fin qui est bleue donne du magenta, c'est pourquoi les facettes qui sont les plus éclairées par la source lumineuse directionnelle (on rappelle que les facettes les plus éclairées sont celles dont les normales sont parallèles aux rayons lumineux) varient du jaune au mangenta.
Nous avons également écrit un exemple mélant deux interpolateurs : PositionInterpolator et ColorInterpolator :
Notre objet 3D (une sphère) change de couleur tout en changeant de position.
Le fichier PosColorInterpolator.java présente le code cource complet de cet exemple.
Exécutez l'applet PosColorInterpolator.html pour voir le résultat.
ScaleInterpolator
Cet interpolateur va nous permettre de modifier l'échelle (la taille) d'un objet 3D.
Les deux constructeurs de cette classe sont similaires à ceux des classes RotationInterpolator et PositionInterpolator :
public ScaleInterpolator(Alpha alpha, TransformGroup target)
alpha est l'interpolateur d'échelle de l'objet, le facteur d'échelle varie par défaut de 0.1 à 1.
target est la cible à laquelle on va appliquer ce comportement.
Par défaut cette transformation d'échelle est appliquée dans le repère 3D de base dont l'origine est (0,0,0).
public ScaleInterpolator(Alpha alpha, TransformGroup target, Transform3D axisOfTransform, float minimumScale, float maximumScale)
alpha est l'interpolateur d'échelle qui va varier de minimumScale à maximumScale
target est la cible à laquelle on va appliquer ce comportement
axisOfTransform est une transformation 3D (plutôt qu'un axe en fait) qui, appliquée au repère 3D de Java 3D va donner le repère dans lequel la transformation d'échelle sera appliquée.
Le fichier ScaleInterpolatorTest.java présente le code source complet de cet exemple.
Exécutez l'applet ScaleInterpolatorTest.html pour voir le résultat.
TransparencyInterpolator
Nous allons voir maintenant un interpolateur permettant de modifier la transparence d'un objet. Cet interpolateur est basé sur la classe TransparencyInterpolator dont nous décrivons les deux constructeurs :
public TransparencyInterpolator(Alpha alpha, TransparencyAttributes target)
alpha est l'interpolateur de transparence
target représente les attributs de transparence de l'objet
Dans ce constructeur par défaut, l'interpolateur va faire varier l'apparence dans l'intervalle [0,1].
public TransparencyInterpolator(Alpha alpha, TransparencyAttributes target,
float minimumTransparency, float maximumTransparency)
alpha est l'interpolateur de transparence
target représente les attributs de transparence de l'objet. La transparence pourra varier dans l'intervalle [minimumTransparency,maximumTransparency]
Pour la construction d'un objet de type TransparencyAttributes, on se referera au chapitre 5 sur les apparences, dans le paragraphe transparence.
Le fichier TransparencyInterpolatorTest.java présente le code source complet de cet exemple.
Exécutez l'applet TransparencyInterpolatorTest.html pour voir le résultat.
PositionPathInterpolator
Nous avons vu l'interpolateur de type PositionInterpolator qui permettait de faire évoluer la position d'un objet dans le temps. Cependant, la trajectoire était définie par un seul et unique segment rectiligne.
Le comportement de type PositionPathInterpolator va nous permettre d'animer un objet selon une trajectoire composée de plusieurs segments rectilignes.
Ces différents segments qui vont constituer la trajectoire de l'objet vont être définis à l'aide d'une série de couples [noeud,position]. A chaque noeud (qui correspond en fait à une valeur de l'interpolateur alpha), on va associer une position de notre objet dans le repère 3D grâce au constructeur de la classe PositionPathInterpolator :
public PositionPathInterpolator(Alpha alpha, TransformGroup target,
Transform3D axisOfTransform,
float[] knots, Point3f[] positions)
alpha est l'interpolateur pour la position et l'angle de rotation.
target est la cible à laquelle on va appliquer ce comportement.
axisOfTransform est la transformation qui définit le repère 3D (par rapport au repère 3D par défaut de la scène 3D) dans lequel le comportement va agir.
knots est un tableau de valeurs intermédiaires pour les valeurs de l'interpolateur alpha
positions est une tableau de points (associé au tableau knots) qui définit les positions intermédiaires de l'objet 3D.
Dans l'exemple que nous allons étudier sur le comportement PositionPathInterpolator, nouq avons défini les tableaux knots et positions de la façon suivante :
// Valeurs intermediaires de l'interpolateur alpha
float[] noeuds = new float[] {0f, 0.1f, 0.9f, 1f};
// Positions intermediaires associees aux valeurs intermediaires de
// l'interpolateur alpha
Point3f[] positions = new Point3f[] {
new Point3f(-0.8f, 0.0f, 0.0f),
new Point3f(-0.4f, 0.5f, 0.0f),
new Point3f( 0.4f, -0.5f, 0.0f),
new Point3f( 0.8f, 0.0f, 0.0f)
};
Ainsi, dans ce cas précis, nous avons 4 positions associées à 4 valeurs de alpha qui correspondent à 4 valeurs temporelles. Par exemple, si la valeur increasingAlphaDuration de notre interpolateur alpha vaut 1000 ms, cela se traduira par le fait que notre objet sera à la position :
P1 = [-0.8,0,0] au temps t1 = 0
P2 = [-0.4,0.5,0] au temps t2 = 1000*0.1 = 100 ms
P3 = [0.4,-0.5,0] au temps t3 = 1000*0.9 = 900 ms
P4 = [0.8,0,0] au temps t4 = 1000 * 1 = 1000 ms
Entre les positions P1 et P2 par exemple, notre objet se déplacera linéairement entre les temps t2 = 100 ms et t3 = 900 ms.
Le fichier PositionPathInterpolatorTest.java présente le code source complet de cet exemple.
Exécutez l'applet PositionPathInterpolatorTest.html pour voir le résultat.
RotPosScalePathInterpolator
Nous allons maintenant compliquer un peu l'affaire par rapport au comportement précédent en appliquant simultanément 3 interpolateurs à un objet 3D : rotation, position et taille.
Java 3D nous fournit une classe permettant d'élaborer un tel comportement "multiple", il s'agit de RotPosScalePathInterpolator, classe au nom très explicite.
Elle possède un constructeur unique qui se présente comme suit :
public RotPosScalePathInterpolator(Alpha alpha, TransformGroup target,
Transform3D axisOfTransform, float[] knots,
Quat4f[] quats, Point3f[] positions, float[] scales)
alpha est l'interpolateur pour la position, la rotation et l'échelle.
target est la cible à laquelle on va appliquer ce comportement.
axisOfTransform est la transformation qui définit le repère
3D (par rapport au repère 3D par défaut de la scène 3D) dans lequel le comportement va agir.
knots est un tableau de valeurs intermédiaires pour les valeurs de l'interpolateur alpha
quats est un tableau de quaternions (objets qui définissent une rotation) associé au tableau knots qui définit les rotations intermédiaires de l'objet 3D.
positions est
un tableau de points (associé au tableau knots) qui définit les positions intermédiaires de l'objet 3D
scales est un tableau de facteurs d'échelle (associé au tableau knots) qui définit les facteurs d'échelle intermédiaires de l'objet 3D.
On voit que cet interpolateur se construit de façon logique par rapport aux autres que nous avons vus précédemment. Cependant, il est nécessaire de s'attarder davantage sur un objet que nous n'avons pas encore étudié et qui est très utile pour décrire des rotations : le quaternion, représenté en autres en Java 3D par la classe Quat4f.
En mathématiques, il est possible de construire des rotations dans le plan en deux dimension grâce aux nombres complexes de type z = x + iy. On peut faire de même dans l'espace à 3 dimension pour une rotation autour d'un axe grâce à un type de nombre appelés quaternions et qui s'écrivent de la façon suivante :
q = x + iy + jz + kw
C'est ce nombre qui est représenté par la classe Quat4f au cas où les 4 nombres [x,y,z,w] sont flottants et par Quat4d si [x,y,z,w] sont des doubles.
En Java3D, il est existe un moyen extrêmement simple de construire une rotation autour d'un axed à l'aide d'un quaternion, il suffit de construire un objet de type Quat4f en utilisant le constructeur par défaut de la classe Quat4f puis de déterminer l'axe et l'angle de la rotation en utilisant la méthode set() de la classe Quat4f :
public void set(AxisAngle4f a)
Initialise le quaternion avec l'axe et l'angle de la rotation contenus dans l'objet a.
Dans l'exemple que nous allons étudier pour ce paragraphe, nous allons construire un petit cube qui va passer par 4 positions intermédiaires (comme pour l'exemple précédent avec l'interpolateur PositionPathInterpolator), ce cube aura une taille différente pour chacune de ces positions intermédiaires, et on va également lui appliquer une rotation différente à chacune de ces positions. Plus précisément, en ce qui concerne les rotations intermédiaires :
Position 1 : pas de rotation
Position 2 : rotation autour de l'axe X
Position 3 : rotation autour de l'axe Y
Position 4 : rotation autour de l'axe Z
Cela signifie par exemple, que lorsque notre cube se déplacera entre les positions 2 et 3, il va tourner autour de Y pendant son déplacement.
Attention :
Il y a un piège à propos des rotations.
Si l'on veut une rotation autour de Y entre les positions 2 et 3, alors que en position 2 le cube a déjà subi une rotation autour de X, il faudra que en position 3 la rotation intermédiaire soit "incrémentale" par rapport à celle déjà effectuée autour de X, c'est à dire qu'il faut une rotation autour de X suivie d'une rotation autour de Y.
De la sorte, la différence des rotations entre les positions 2 et 3 est bien une rotation autour de Y.
Le fichier RotPosScalePathInterpolatorTest.java présente le code source complet de cet exemple.
Exécutez l'applet RotPosScalePathInterpolatorTest.html pour voir le résultat.
2. Morphing
Enfin, pour clôturer ce chapitre consacré aux animations et interpolateurs, nous allons étudier un exemple qui sera sûrement le plus spectaculaire parmi ceux que nous venons de voir.
Il s'agit de morphing, c'est à dire de transformer progressivement au cours du temps un objet A pour qu'il prenne l'apparence d'un objet B. C'est le genre d'effet que l'on voit du plus en plus souvent au cinéma par exemple.
Dans le cadre de ce paragraphe, nous allons rester beaucoup plus modeste et nous allons étudier un exemple simple qui consiste à transformer progressivement un cube en une pyramide.
L'exemple de morphing que nous allons étudier concerne la forme (la géométrie) de l'objet mais également les couleurs des sommets.
La règle de base fondamentale pour ce genre de transformation est que l'objet de départ doit avoir exactement le même nombre de sommets (ou vertices) que l'objet d'arrivée, sans quoi la transformation ne peut pas se produire.
Il existe d'autres régles très importantes pour que le mophing soit valide, nous vous conseillons de vous reporter à l'aide Javadoc de Java 3D sur la classe Morph pour plus de détails.
C'est cette classe Morph qui va permettre la transformation d'un objet en un autre, elle contient un tableau d'objets de type GeometryArray qui partagent tous la même apparence, il n'est pas possible d'avoir du morphing d'apparence avec la classe Morph.
Le constructeur de cette classe se présente comme suit :
public Morph(GeometryArray[] geometryArrays)
geometryArrays est le tableau des géométries de tous les objets qui vont constituer le morphing
Cette classe Morph possède également un attribut qui correspond au poids de chaque géométrie enregistrée dans l'objet de type Morph. Si le premier élément du tableau geometryArray est un cube et que son poids est 1, alors l'objet que représente la classe Morph sera le cube. Si le deuxième élément de tableau geometryArray est une pyramide et que son poids est 1, alors l'objet que représente la classe Morph sera la pyramide.
Il doit y avoir autant de poids que de géométries dans le tableau geometryArrays et la somme des poids doit impérativement être égale à 1.
Si notre objet Morph possède deux géométries (un cube et une pyramide) et que le poids de chacun est de 0.5, alors l'objet représenté par la classe Morph sera une sorte d'objet "batard" dont chaque sommet d'indice i (ou vertex) sera à mi-chemin entre le sommet d'indice i du cube (départ) et le sommet d'indice i de la pyramide (arrivée).
On comprend maintenant pourquoi il faut impérativement que chaque géométrie ait le même nombre de sommets : le sommet d'indice i de la première géométrie va évoluer pour se positionner à la place du sommet d'indice i de la deuxième géométrie, et ainsi de suite.
Les poids associés à chaque géométrie d'un objet de type Morph peuvent être lus et accédés grâce aux méthodes :
public void setWeights(double[] weights)
weights est le tableau des poids du morphing : le nombre d'éléments du tableau doit être égal au nombre de géométries du morphing et la somme des poids doit être égale à 1.
public double[] getWeights()
Renvoie le tableau des poids du morphing
Une façon élégante de faire une animation à base de morphing consiste à écrire un comportement dont la méthode processStimulus() se déclenche à chaque image de l'animation. Et à chaque image on aura une valeur du poids de chaque géométrie qui sera différente et qui va évoluer au cours du temps grâce à un interpolateur de type Alpha.
C'est cette idée que nous allons mettre en oeuvre pour cet exemple qui va constituer à voir un cube se transformer en pyramide au cours du temps. Nous avons parlé de géométrie dans le sens ou les positions des sommets vont évoluer du cube vers la pyramide, mais les couleurs de chaque sommet font également partie de la géométrie et elles vont donc évoluer si le sommet d'indice i du cube n'a pas la même couleur que le sommet d'indice i de la pyramide.
Pour que la méthode processStimulus() se déclenche à chaque image rendue, il faut que le critère soit le suivant :
WakeupOnElapsedFrames wakeupCondition = new WakeupOnElapsedFrames(0);
wakeupOn(wakeupCondition);
Le paramètre 0 signifie ici que processStimulus() sera déclenchée lorsque chaque image sera rendue, cela garantit la fluidité de notre animation.
La méthode processStimulus() de notre comportement que nous avons implémenté dans la classe MorphingBehavior va permettre de relier le tableau des poids du morphing avec l'interpolateur alpha :
/**
* Methode appelee a chaque fois qu'une frame est affichee
* @param criteria critere ayant declenche le stimulus
*/
public void processStimulus(Enumeration criteria) {
WakeupCriterion critere;
double alphaValue = alpha.value();
// On boucle sur les criteres ayant declenche le comportement
while (criteria.hasMoreElements()) {
// On recupere le premier critere de l'enumeration
critere = (WakeupCriterion)criteria.nextElement();
// On ne traite que les criteres correspondant a WakeupOnElapsedFrames
if (critere instanceof WakeupOnElapsedFrames) {
weights[0] = 1.0 - alphaValue;
weights[1] = alphaValue;
morph.setWeights(weights);
}
} // fin while (criteria.hasMoreElements())
// Une fois le stimulus traite, on reinitialise le comportement
this.wakeupOn(wakeupCondition);
}
L'archive src.zip contient le code source complet de cet exemple.
Exécutez l'applet Morphing.html pour voir le résultat.
Chapitre 07 : Interactions << >> Chapitre 09 : Outils et aspects avancés de Java 3D