Blog sur Flash,Flex,Papervision,A.I.R., …

Création de hotspots dans un panorama

18 juin 2007 par PeZ

Les hotspots représentent des zones d'interactivité dans un panorama. Ils peuvent avoir pour but le déplacement de pièces en pièces, l'affichage d'informations, le lancement d'une vidéo, etc...

Pour démarrer en douceur , nous allons commencer par ajouter des hotspots sur une des visionneuses du post précédent.
Ceux-ci afficheront un simple texte lorsque l'on clique dessus.

I> Création des hotspots

Pour réaliser nos hotspots, nous allons ajouter un simple plan transparent devant chaque élément qui nous intéresse dans le panorama.
Cet exemple se base sur la visionneuse qui projete les 6 images (512*512) d'une pièce sur un cube (cf exemple ici).
Les images se trouvent ici, et le code source .

Commençons par décider quels seront nos objets interactifs. Par exemple, j'ai choisi le radiateur qui est sur l'image de la
face droite :

et l'armoire de la face gauche:

Les dimensions de ces zones sont respectivement de 221*124 et 332*329. Elles nous serviront un peu plus tard.

Pour ajouter nos plans qui serviront de zone sensible, rien de plus facile, cette méthode a d'ailleurs été vue dans un précédent billet. On crée un plan grâce à l'objet 'Plane' avec une texture et une taille puis on le dispose sur la scène.

Comme texture, on peut utiliser une simple texture couleur, puisque le plan sera transparent par la suite.

Pour calculer la taille du plan, il suffit de faire une règle de trois. Les images de 512*512 sont appliquées sur des plans
de 5000*5000 donc notre zone interactive qui fait 221*124 sur l'image sera représenté par un plan de 2158*1210 pour le radiateur et de 3242*3212 pouyr l'armoire. Pour les coordonnées x,y ont fait pareil.

Etant donnée que cette solution est assez lourde , un des prochains billets expliquera comment mettre en place un éditeur de hotspots.

Il ne reste plus qu'à placer les plans sur la scène. Il faut les placer un peu en dedans du cube pour que nos hotspots ne se
trouvent pas 'collés' sur celui-ci et que l'interaction avec la souris puisse fonctionner.

Vous devriez obtenir quelque chose comme ceci pour le moment.

On peut remarquer par certains moments, selon l'orientation de la caméra, que le plan tend à disparaitre. Cela est du à
l'algorithme d'affichage du moteur qui utilise le z-sorting pour des questions de rapidité. Lorsque 2 objets sont très
rapprochés , il peut arriver que certaines faces passent derrière d'autres alors qu'il ne le faudrait pas.(Plus d'infos
sur cette démo)
Il faut faire recours à un petit hack afin de forcer les zones sensibles à être rendus en dernier. Ca devrait fonctionner.

II> Ajout des événements souris

Il ne reste maintenant plus qu'à gérer les évenements souris sur nos plans. L'utilisation de la classe 'MovieScene3D' permet de créer une scène ou tous les objets sont rendus dans un container unique. Il suffit de faire référence à ce container et
d'y ajouter notre évenement :

Actionscript:
  1. //Ajout des évenements souris
  2. var planDynamique:Sprite = scene.getSprite( planObjet );
  3. planDynamique.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDown );
  4. planDynamique.addEventListener( MouseEvent.MOUSE_UP, _onMouseUp );

de définir les fonctions onMouseDown et onMouseUp et de rendre notre plan transparent

Actionscript:
  1. planDynamique.alpha = 0;

La version finale avec le code source. A venir, l'éditeur de hotspots.

Actionscript:
  1. package
  2. {
  3.     import flash.display.Sprite;
  4.     import flash.events.Event;
  5.     import flash.events.MouseEvent;
  6.     import flash.text.TextField;
  7.     import flash.text.TextFieldAutoSize;
  8.     import flash.text.TextFormat;
  9.    
  10.     import org.papervision3d.cameras.FreeCamera3D;
  11.     import org.papervision3d.scenes.MovieScene3D;
  12.    
  13.     import org.papervision3d.objects.Plane;
  14.     import org.papervision3d.materials.BitmapFileMaterial;
  15.     import org.papervision3d.materials.ColorMaterial;
  16.     import flash.display.DisplayObject;
  17.     import org.papervision3d.objects.DisplayObject3D;
  18.     import flash.text.TextField;
  19.  
  20.  
  21.     [SWF(width='800',height='512',backgroundColor='0x000000',frameRate='30')]
  22.    
  23.     public class VisionneuseCubeHotspots extends Sprite
  24.     {
  25.        
  26.         private static const CHEMIN_IMAGES:String = "../assets/";
  27.         private static var MAX_X_ROTATION:int = 50;
  28.         private static var taille:Number = 5000;
  29.        
  30.         private var container :Sprite;
  31.         private var scene :MovieScene3D;
  32.         private var camera :FreeCamera3D;
  33.         private var nullObject:DisplayObject3D = new DisplayObject3D();
  34.         private var plan:Plane;
  35.  
  36.         private var materials :Array;
  37.        
  38.         private var label:TextField;
  39.         private var labelText:String = "Clic sur un objet";
  40.  
  41.  
  42.            
  43.         public function VisionneuseCubeHotspots()
  44.         {
  45.            
  46.             configChampTexte();
  47.            
  48.             container = new Sprite();
  49.            
  50.             container.x = stage.stageWidth/2;
  51.             container.y = stage.stageHeight/2;
  52.            
  53.             addChild( container );
  54.                                    
  55.  
  56.             scene = new MovieScene3D( container );
  57.            
  58.  
  59.             camera = new FreeCamera3D();
  60.             camera.zoom = 1;
  61.             camera.focus = 400;
  62.             camera.z = 0;
  63.            
  64.             var centre:Number = taille/2;
  65.             materials = new Array();
  66.            
  67.             var boxGroup:DisplayObject3D = new DisplayObject3D();
  68.             var worldEnd:DisplayObject3D = new DisplayObject3D();
  69.            
  70.        
  71.             //Face Avant
  72.             var texture:BitmapFileMaterial = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_FR.jpg");
  73.             materials.push( texture );
  74.            
  75.             plan = new Plane( texture , taille, taille, 8, 8);
  76.             plan.z = centre;
  77.             boxGroup.addChild( plan );
  78.                    
  79.             //Face Droite
  80.             texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_RT.jpg");
  81.             materials.push( texture );
  82.            
  83.             plan = new Plane( texture , taille, taille, 8, 8);
  84.             plan.x = -centre;
  85.             plan.rotationY = -90;
  86.             boxGroup.addChild( plan );
  87.            
  88.             //Face Gauche
  89.             texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_LF.jpg");
  90.             materials.push( texture );
  91.                        
  92.             plan = new Plane( texture , taille, taille, 8, 8);
  93.             plan.x = centre;
  94.             plan.rotationY = 90;
  95.             boxGroup.addChild( plan );
  96.            
  97.             //Face Bas
  98.             texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_DN.jpg");
  99.             materials.push( texture );
  100.                        
  101.             plan = new Plane( texture , taille, taille, 8, 8);
  102.             plan.y = -centre;
  103.             plan.rotationX = -90;
  104.             boxGroup.addChild( plan );
  105.            
  106.             //Face Haut
  107.             texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_UP.jpg");
  108.             materials.push( texture );
  109.            
  110.             plan = new Plane( texture , taille, taille, 8, 8);
  111.             plan.y = centre;
  112.             plan.rotationX = 90;
  113.             boxGroup.addChild( plan );
  114.            
  115.             //Face Arriere
  116.             texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_BK.jpg");
  117.             materials.push( texture );
  118.            
  119.             plan = new Plane( texture , taille, taille, 8, 8);
  120.             plan.z = -centre;
  121.             plan.rotationY = -180;
  122.             boxGroup.addChild( plan );
  123.            
  124.             worldEnd.addChild( boxGroup );
  125.             worldEnd.addChild( nullObject );
  126.             scene.addChild(worldEnd);
  127.            
  128.            
  129.             /******** Création des hotspots ****************/
  130.  
  131.             //radiateur
  132.             var textureCouleur:ColorMaterial = new ColorMaterial();
  133.             textureCouleur.doubleSided = true;
  134.             materials.push(textureCouleur);
  135.            
  136.             plan = new Plane( textureCouleur, 2158, 1210, 4, 4 );
  137.             plan.y = -450;
  138.             plan.z = -1000;
  139.             plan.x = -centre + 100;
  140.             plan.rotationY = -90;
  141.             scene.addChild( plan );
  142.            
  143.             //Ajout des évenements souris
  144.             var planDynamique:Sprite = scene.getSprite( plan );
  145.             planDynamique.alpha = 0;
  146.             planDynamique.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDown );
  147.             planDynamique.addEventListener( MouseEvent.MOUSE_UP, _onMouseUp );
  148.            
  149.            
  150.            
  151.             //armoire
  152.             textureCouleur = new ColorMaterial();
  153.             textureCouleur.doubleSided = true;
  154.             materials.push(textureCouleur);
  155.            
  156.             plan = new Plane( textureCouleur, 3242, 3212, 4, 4 );
  157.             plan.y = 0;
  158.             plan.z = -600;
  159.             plan.x = centre - 100;
  160.             plan.rotationY = 90;
  161.             scene.addChild( plan );
  162.            
  163.             //Ajout des évenements souris
  164.             planDynamique = scene.getSprite( plan );
  165.             planDynamique.alpha = 0;
  166.             planDynamique.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDown );
  167.             planDynamique.addEventListener( MouseEvent.MOUSE_UP, _onMouseUp );
  168.                    
  169.             /*****************************************************/
  170.            
  171.                    
  172.  
  173.             stage.addEventListener( Event.ENTER_FRAME, onEnterFrame );
  174.        
  175.         }
  176.        
  177.         private function onEnterFrame( event: Event ): void
  178.         {
  179.            
  180.             camera.rotationY += (container.mouseX)/60;
  181.             camera.rotationX -= (container.mouseY)/60;
  182.            
  183.            
  184.             //on limite la rotation vertivale
  185.             if(camera.rotationX> VisionneuseCubeHotspots.MAX_X_ROTATION)
  186.             {
  187.                 camera.rotationX = VisionneuseCubeHotspots.MAX_X_ROTATION;
  188.             }
  189.             if(camera.rotationX <-VisionneuseCubeHotspots.MAX_X_ROTATION)
  190.             {
  191.                 camera.rotationX = -VisionneuseCubeHotspots.MAX_X_ROTATION;
  192.             }
  193.                    
  194.             scene.renderCamera( camera );
  195.            
  196.         }
  197.        
  198.         private function _onMouseDown(event:Event):void
  199.         {
  200.             addChild(label);
  201.                
  202.         }
  203.    
  204.         private function _onMouseUp(event:Event):void
  205.         {
  206.             removeChild(label);
  207.         }
  208.        
  209.         private function configChampTexte():void
  210.         {
  211.             label = new TextField();
  212.             label.autoSize = TextFieldAutoSize.LEFT;
  213.             label.background = true;
  214.             label.border = true;
  215.  
  216.             var format:TextFormat = new TextFormat();
  217.             format.font = "Verdana";
  218.             format.color = 0xFF0000;
  219.             format.size = 10;
  220.             format.underline = true;
  221.  
  222.             label.defaultTextFormat = format;
  223.             label.text = labelText;
  224.         }
  225.  
  226.  
  227.     }
  228.    
  229. }

No Tags
Publié dans Flash, Papervision3D posséde

7 Responses

  1. LutinCapuche dit:

    Merci beaucoup, je commence jsute à manipuler papervision, et tes tutos sont supers ! Bien expliquer, ca marche bien… un grand merci !

  2. PeZ dit:

    Merci beaucoup pour ton commentaire, ça fait plaisir :)

    Sympa ton blog aussi ;)

  3. kso dit:

    Juste une petite question, est-il possible de récupérer la référence de l’objet qui a été cliqué.

    Sinon super boulot!!

  4. PeZ dit:

    Dans les précédentes versions de P3D, il fallait passer par un tableau associatif ou une classe personnalisée pour récupérer la référence.

    Avec la version 1.5, tu peux y accèder directement depuis la propriété ‘displayObject3D’de l’événement ‘InteractiveScene3DEvent’ dispatché.

    Je vais regarder pour mettre les sources à jour à l’occase.

  5. kso dit:

    Effectivement, je ne savais pas qu’il y avait cette notion d’InteractiveScene. J’ai testé avec mon appli ça marche bien. :)

    Merci!!

  6. Mirel dit:

    Super, je n’ai pas encore essayé mais ca à l’air très interessant. Un grand merci!

  7. Cody Snider dit:

    Excellent example of hotspotting in a panoramic cube! I’ve been searching for this for quite some time now. Merci Beaucoup!

Laissez un commentaire

Note: La modération des commentaires est activée et votre envoi peut-etre différé.