Création de hotspots dans un panorama
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 là.
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 :
-
//Ajout des évenements souris
-
var planDynamique:Sprite = scene.getSprite( planObjet );
-
planDynamique.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDown );
-
planDynamique.addEventListener( MouseEvent.MOUSE_UP, _onMouseUp );
de définir les fonctions onMouseDown et onMouseUp et de rendre notre plan transparent
-
planDynamique.alpha = 0;
La version finale avec le code source. A venir, l'éditeur de hotspots.
-
package
-
{
-
import flash.display.Sprite;
-
import flash.events.Event;
-
import flash.events.MouseEvent;
-
import flash.text.TextField;
-
import flash.text.TextFieldAutoSize;
-
import flash.text.TextFormat;
-
-
import org.papervision3d.cameras.FreeCamera3D;
-
import org.papervision3d.scenes.MovieScene3D;
-
-
import org.papervision3d.objects.Plane;
-
import org.papervision3d.materials.BitmapFileMaterial;
-
import org.papervision3d.materials.ColorMaterial;
-
import flash.display.DisplayObject;
-
import org.papervision3d.objects.DisplayObject3D;
-
import flash.text.TextField;
-
-
-
[SWF(width='800',height='512',backgroundColor='0x000000',frameRate='30')]
-
-
public class VisionneuseCubeHotspots extends Sprite
-
{
-
-
private static const CHEMIN_IMAGES:String = "../assets/";
-
private static var MAX_X_ROTATION:int = 50;
-
private static var taille:Number = 5000;
-
-
private var container :Sprite;
-
private var scene :MovieScene3D;
-
private var camera :FreeCamera3D;
-
private var nullObject:DisplayObject3D = new DisplayObject3D();
-
private var plan:Plane;
-
-
private var materials :Array;
-
-
private var label:TextField;
-
private var labelText:String = "Clic sur un objet";
-
-
-
-
public function VisionneuseCubeHotspots()
-
{
-
-
configChampTexte();
-
-
container = new Sprite();
-
-
container.x = stage.stageWidth/2;
-
container.y = stage.stageHeight/2;
-
-
addChild( container );
-
-
-
scene = new MovieScene3D( container );
-
-
-
camera = new FreeCamera3D();
-
camera.zoom = 1;
-
camera.focus = 400;
-
camera.z = 0;
-
-
var centre:Number = taille/2;
-
materials = new Array();
-
-
var boxGroup:DisplayObject3D = new DisplayObject3D();
-
var worldEnd:DisplayObject3D = new DisplayObject3D();
-
-
-
//Face Avant
-
var texture:BitmapFileMaterial = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_FR.jpg");
-
materials.push( texture );
-
-
plan = new Plane( texture , taille, taille, 8, 8);
-
plan.z = centre;
-
boxGroup.addChild( plan );
-
-
//Face Droite
-
texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_RT.jpg");
-
materials.push( texture );
-
-
plan = new Plane( texture , taille, taille, 8, 8);
-
plan.x = -centre;
-
plan.rotationY = -90;
-
boxGroup.addChild( plan );
-
-
//Face Gauche
-
texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_LF.jpg");
-
materials.push( texture );
-
-
plan = new Plane( texture , taille, taille, 8, 8);
-
plan.x = centre;
-
plan.rotationY = 90;
-
boxGroup.addChild( plan );
-
-
//Face Bas
-
texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_DN.jpg");
-
materials.push( texture );
-
-
plan = new Plane( texture , taille, taille, 8, 8);
-
plan.y = -centre;
-
plan.rotationX = -90;
-
boxGroup.addChild( plan );
-
-
//Face Haut
-
texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_UP.jpg");
-
materials.push( texture );
-
-
plan = new Plane( texture , taille, taille, 8, 8);
-
plan.y = centre;
-
plan.rotationX = 90;
-
boxGroup.addChild( plan );
-
-
//Face Arriere
-
texture = new BitmapFileMaterial(VisionneuseCubeHotspots.CHEMIN_IMAGES+"rendu_cubique_BK.jpg");
-
materials.push( texture );
-
-
plan = new Plane( texture , taille, taille, 8, 8);
-
plan.z = -centre;
-
plan.rotationY = -180;
-
boxGroup.addChild( plan );
-
-
worldEnd.addChild( boxGroup );
-
worldEnd.addChild( nullObject );
-
scene.addChild(worldEnd);
-
-
-
/******** Création des hotspots ****************/
-
-
//radiateur
-
var textureCouleur:ColorMaterial = new ColorMaterial();
-
textureCouleur.doubleSided = true;
-
materials.push(textureCouleur);
-
-
plan = new Plane( textureCouleur, 2158, 1210, 4, 4 );
-
plan.y = -450;
-
plan.z = -1000;
-
plan.x = -centre + 100;
-
plan.rotationY = -90;
-
scene.addChild( plan );
-
-
//Ajout des évenements souris
-
var planDynamique:Sprite = scene.getSprite( plan );
-
planDynamique.alpha = 0;
-
planDynamique.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDown );
-
planDynamique.addEventListener( MouseEvent.MOUSE_UP, _onMouseUp );
-
-
-
-
//armoire
-
textureCouleur = new ColorMaterial();
-
textureCouleur.doubleSided = true;
-
materials.push(textureCouleur);
-
-
plan = new Plane( textureCouleur, 3242, 3212, 4, 4 );
-
plan.y = 0;
-
plan.z = -600;
-
plan.x = centre - 100;
-
plan.rotationY = 90;
-
scene.addChild( plan );
-
-
//Ajout des évenements souris
-
planDynamique = scene.getSprite( plan );
-
planDynamique.alpha = 0;
-
planDynamique.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDown );
-
planDynamique.addEventListener( MouseEvent.MOUSE_UP, _onMouseUp );
-
-
/*****************************************************/
-
-
-
-
stage.addEventListener( Event.ENTER_FRAME, onEnterFrame );
-
-
}
-
-
private function onEnterFrame( event: Event ): void
-
{
-
-
camera.rotationY += (container.mouseX)/60;
-
camera.rotationX -= (container.mouseY)/60;
-
-
-
//on limite la rotation vertivale
-
if(camera.rotationX> VisionneuseCubeHotspots.MAX_X_ROTATION)
-
{
-
camera.rotationX = VisionneuseCubeHotspots.MAX_X_ROTATION;
-
}
-
if(camera.rotationX <-VisionneuseCubeHotspots.MAX_X_ROTATION)
-
{
-
camera.rotationX = -VisionneuseCubeHotspots.MAX_X_ROTATION;
-
}
-
-
scene.renderCamera( camera );
-
-
}
-
-
private function _onMouseDown(event:Event):void
-
{
-
addChild(label);
-
-
}
-
-
private function _onMouseUp(event:Event):void
-
{
-
removeChild(label);
-
}
-
-
private function configChampTexte():void
-
{
-
label = new TextField();
-
label.autoSize = TextFieldAutoSize.LEFT;
-
label.background = true;
-
label.border = true;
-
-
var format:TextFormat = new TextFormat();
-
format.font = "Verdana";
-
format.color = 0xFF0000;
-
format.size = 10;
-
format.underline = true;
-
-
label.defaultTextFormat = format;
-
label.text = labelText;
-
}
-
-
-
}
-
-
}
No Tags


26 juin 2007 a 7:02 pm
Merci beaucoup, je commence jsute à manipuler papervision, et tes tutos sont supers ! Bien expliquer, ca marche bien… un grand merci !
26 juin 2007 a 8:09 pm
Merci beaucoup pour ton commentaire, ça fait plaisir
Sympa ton blog aussi
7 août 2007 a 2:40 pm
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!!
8 août 2007 a 1:31 pm
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.
9 août 2007 a 3:49 pm
Effectivement, je ne savais pas qu’il y avait cette notion d’InteractiveScene. J’ai testé avec mon appli ça marche bien.
Merci!!
1 octobre 2007 a 4:22 pm
Super, je n’ai pas encore essayé mais ca à l’air très interessant. Un grand merci!
3 octobre 2007 a 2:53 pm
Excellent example of hotspotting in a panoramic cube! I’ve been searching for this for quite some time now. Merci Beaucoup!