====== KINECT/MULTITOUCH ======
* Porteur du projet : Audrey Herd-Smith, Morgane Guillaume
* Date : 04/2015
* Fichiers du code de base: {{:wiki:projets:gaite:kinect_multi_hotpoint_commentee.zip|}}
===== Description du projet =====
Comment joindre le son et son acteur avec l'espace ?
L'installation proposée permet de créer un univers graphique scénique et visuel tout en offrant une grande liberté d'intervention sur la musique. Elle s'adapte au titre Love Like a Sunset de Phoenix. Ce morceau, comme une accumulation sonore, renvoie au voyage. Un résultat abstrait se présente suite aux expériences graphiques opérées sur des visuels de paysages (montagne). Ils permettent une superposition des traits et aplats crées, renvoyant à la manipulation possible des samples de Phoenix. Chque sample correspond à un module (volume pyramidale) et s'active au paysage d'une main en dessous. Ces zones de réactivité sont générées par la Kinect et le programme sous Processing.
===== Matériaux =====
Kinect avec connection USB pour PC/MAC + vidéoprojecteur
===== Tutoriel d'installation de la kinect =====
Librairies nécessaires pour Kinect:
* OPENNI (PC)
* SDK Kinect: driver (PC)
* SimpleOpenNI
Fichier d'installation :
{{:wiki:projets:gaite:installation_kinect.zip|}}
===== Photos =====
{{:wiki:projets:gaite:01.jpg?200|}}
{{:wiki:projets:gaite:02.jpg?200|}}
{{:wiki:projets:gaite:03.jpg?200|}}
{{:wiki:projets:gaite:05.jpg?200|}}
{{:wiki:projets:gaite:04.jpg?200|}}
{{:wiki:projets:gaite:vid-20150203-wa0000.mp4|}}
==== code processing (mise à jour 09 Avril 2015)====
Attention : deux onglets
// LIBRAIRIES
import processing.opengl.*; // librairie qui initialise la 3D
import SimpleOpenNI.*; // librairie de la kinect
import ddf.minim.*; // librairie de son
// VARIABLES GLOBALES
SimpleOpenNI kinect;
float rotation = 0;
Minim minim;
// Déclaration d'un TABLEAU dynamique "ArrayList" contenant les objets de type "AudioPlayer" (les sons) ( qui sont associés aux cubes/hotpoint)
ArrayList playList;
// Déclaration d'un TABLEAU dynamique "ArrayList" contenant les objets de type "Hotpoint" (cubes spatiaux)
ArrayList trigers;
int nbHotpoint = 8 ; // nombre de hotpoints (cubes spatiaux)
// Déclaration d'un TABLEAU dynamique "ArrayList" de PVector ( de vecteurs) contenant les positions des cubes spatiaux
ArrayList trigerPos;
// Déclaration d'un TABLEAU dynamique "ArrayList" d' IMAGES
ArrayList images;
// DEFINTION DE LA POSITION exact des hotpoints (cubes spatiaux) dans l'espace sachant que 1000 pixels = 1m
//IRCAM (devant CS)
float posX3 = -750, posY3 = 000, posZ3 = 2100;
//CS (tout au fond gauche)
float posX13 = -550, posY13 = 000, posZ13 = 2500;
//Reich(tout au fond droite)
float posX30 = 550, posY30 = 000, posZ30 = 2600;
//Oiseau (devant reich)
float posX32 = 750, posY32 = 000, posZ32 = 2200;
//Guit
float posX35 = -500, posY35 = 000, posZ35 = 1800;
//AC
float posX36 = 200, posY36 = 000, posZ36 = 1300;
//Nu
float posX38 = 500, posY38 = 000, posZ38 = 1800;
//HH
float posX54 = -200, posY54 = 000, posZ54 = 1300;
////////////////////////////////////////////SETUP
void setup() {
size(2048, 1100, OPENGL); //taille ecran de la salle : 2048*1100 // ECRAN ORDI PORTABLE MORGANE 2048*745
kinect = new SimpleOpenNI(this); // instancie la librairie SimpleOpenNi
kinect.enableDepth(); // permet la profondeur
minim = new Minim(this);
// CREATION DE LA LISTE (Arraylist) DE SONS (playList) initialement vide
playList = new ArrayList();
// REMPLISSAGE DE LA LISTE (par importation de sons dans la playlist)
//IRCAM
playList.add(minim.loadFile("IRCAM3.mp3"));
//CS
playList.add(minim.loadFile("CS1.mp3"));
//reich
playList.add(minim.loadFile("Reich.mp3"));
//Oiseau
playList.add(minim.loadFile("GtrOiseaux2.mp3"));
//Guit
playList.add(minim.loadFile("GuitC.mp3"));
//AC
playList.add(minim.loadFile("AC.mp3"));
//Nu
playList.add(minim.loadFile("Nu1.mp3"));
//HH
playList.add(minim.loadFile("HH3.mp3"));
// CREATION DE LA LISTE (Arraylist) DE POSITIONS (PVector) des Hotpoints (cubes sapatiux) initialement vide
trigerPos = new ArrayList();
// REMPLISSAGE DE LA LISTE
//ircam
trigerPos.add( new PVector(posX3,posY3,posZ3));
//CS
trigerPos.add( new PVector(posX13,posY13,posZ13));
//Reich
trigerPos.add( new PVector(posX30,posY30,posZ30));
//Oiseau
trigerPos.add( new PVector(posX32,posY32,posZ32));
//Guit
trigerPos.add( new PVector(posX35,posY35,posZ35));
//AC
trigerPos.add( new PVector(posX36,posY36,posZ36));
//Nu
trigerPos.add( new PVector(posX38,posY38,posZ38));
//HH
trigerPos.add( new PVector(posX54,posY54,posZ54));
// CREATION DE LA LISTE (Arraylist) D'IMAGES initialement vide
images = new ArrayList();
// REMPLISSAGE DE LA LISTE d'images
//ircam
images.add(loadImage("araignee.png"));
//CS
images.add(loadImage("gris1.png"));
//Reich
images.add(loadImage("gris2.png"));
//Oiseau
images.add(loadImage("gris3.png"));
//Guit
images.add(loadImage("mont1.png"));
//AC
images.add(loadImage("gris0.png"));
//Nu
images.add(loadImage("mont2.png"));
//HH
images.add(loadImage("vert.png"));
// CREATION DE LA LISTE (Arraylist) HOTPOINTS (Cube spatiaux) "trigers" initialement vide -----> verifier que c'est bien la liste des hotpoints
trigers = new ArrayList(nbHotpoint);
// INITIALISATION DE LA LISTE DES HOTPOINTS (cubes spatiaux): on crée tous les hotPoints de la liste - on utilise le consructeur 2
for (int i = 0; i < nbHotpoint; i++) {
trigers.add( new Hotpoint(trigerPos.get(i), //ajout des données de positions aux hotpoints
100, 100,100, //dimension des hotpoints(/cubes spatiaux) (largeur, hauteur, profondeur)
playList.get(i) , // position du cube // largeur,hauteur,profondeur // son associé
images.get(i))) ;
}
}
/////////////////////////////////////FIN SETUP
//////////////////////////////////// DRAW
void draw() {
background(#FFFFFF); // fond de la fenêtre
kinect.update(); // mise à jour des données de la kinect
translate(width/2, height/2,-200); // permet de placer la représentation dans l'esapce // LE DERNIER NOMBRE INDIQUE LA PROFONDEUR Z DE LA REPRESENTAION
rotateX(radians(180));// determine le placement la représentation et permet une rotation
//translate(0, 0, 1400); // detemine le placement de la représentation sur l'axe des y
//rotateY(radians(map(mouseX, 0, width, -180, 180))); // determine les valeur du mouvement de la rotation exercer avec la souris
//translate(0, 0, -1500); // determine l'échelle de la représentation dans l'axe des z (profondeur)
stroke(#000055); // couleur des lignes de l'environnement (les gens qui se mettent en face de la kinect, le mur au fond ...)
strokeWeight(0); //épaisseur traits de l'environnement
PVector[] depthPoints = kinect.depthMapRealWorld(); // création du tableau de vecteur depthPoints : ce sont les points de l'environement, ils sont donnés par la kinect avec la méthode propre à elle qui s'appelle depthMapRealWorld
for (int i = 0; i < depthPoints.length; i+=10) { // boucle permettant de créer tout de 10 en 10 les points detecté par la kinect par depthPoint
PVector currentPoint = depthPoints[i]; // créer les vecteurs du tableau des points(en cours d'analyse) depthPoint qui s'appelera currentPoint
for (Hotpoint HP : trigers) {
HP.check(currentPoint);
}
point(currentPoint.x, currentPoint.y, currentPoint.z); //points qui determinent/visualisent l'environnement (le/les corps, les objets, les murs ...)
}
for (Hotpoint HP : trigers) { // pour tous les Hotpoint de la liste "trigers"
if(HP.sound.isPlaying()){
HP.apparaitre();
}
if (HP.isHit()) {// On effectue les actions suivantes uniquement si le cube (Hotpoint) concerné
// est touché dans la boucle draw() actuelle et n'était pas touché dans la boucle draw() précédente
//i.e. ssi currentlyHit() == true (le cube est en train d'être touché dans la boucle draw() actuelle) et wasJustHit==false (le cube n'était pas touchée dans la boucle draw() précédente)
// si la musique ne joue pas alors on lance la musique en boucle
if( !HP.sound.isPlaying()){
HP.sound.play();
HP.sound.loop();
HP.apparaitre();
}
else { // sinon, si la musique joue, on l'arrête et on rembobine la bande
HP.sound.pause();
HP.sound.rewind();
}
}// tant que le cube est touché, on laisse jouer la musique car on ne rentre pas dans le test if(HP.isHit())...
//dessine le hotPoint (cube) coloré avec une transparence plus ou moins importante en fonction du porcentage d'inclusion
HP.draw();//execution de méthode draw issue de la class hotpoint (création des hotpoints) :
HP.clear(); // garde en mémoire si le hotPoint est touché ou non pour la boucle draw() suivante
// cette action fixe la valeur de l'attrubut wasJustHit:
}
}
///////////////////////////////////////////////FIN DRAW
Second onglet :
//création d'une classe créant les cubes interactifs
class Hotpoint {
PVector center;
color fillColor;
color strokeColor;
float profondeur; // profondeur du carré
float largeur; //largeur du carré
float hauteur; //hauteur du carré
float pointsIncluded;
int maxPoints;
boolean wasJustHit;
int threshold; // seuil
AudioPlayer sound;
PImage visuel;
int Imgx,Imgy;
float x=-900;
float y=-500;
////////////////////////////////////////// CONSTRUCTEUR
Hotpoint(PVector pos, float larg, float haut, float prof, AudioPlayer s,PImage v ) { //constructeur de Hotpoint, déterminé par la position dans l'espace du cube (x,y,z) et la taille du cube (x,y,z)
center = pos; //création du vecteur attribut de Hotpoint, c'est la position dans l'espace. Il commence au centre du cube
pointsIncluded = 0; // les points de profondeur vont être analyser, commencer à zéro
maxPoints = 1000;
threshold = 0; // threshold = seuil
largeur=larg;
profondeur=prof;
hauteur=haut;
fillColor = strokeColor = color(random(255), random(255), random(255)); //on commence avec une valeur aléatoire, qui sera redéfinie par setColor
// MUSIQUE associée au Hotpoint (cubes spatiaux) lorsqu'il est touché
sound = s ;
// IMAGE associée au Hotpoint (cubes spatiaux) lorsqu'il est touché
visuel = v ;
}
////////////////////////////////////METHODES
void setThreshold( int newThreshold ){ // changer la valeur du seuil avec celle écrit entre parenthèse lorsque la méthode est appelée
threshold = newThreshold;
}
void setMaxPoints(int newMaxPoints) { // changer la valeur maximum de points
maxPoints = newMaxPoints;
}
void setColor(float red, float blue, float green){ // changer la couleur des cubes avec les variable red, blue, green
fillColor = strokeColor = color(red, blue, green); // les contours et la surface est de la même couleur
}
boolean check(PVector point) { //création de la méthode vrai/faux (booléenne) appelé check, elle intervient sur le vecteur point
boolean result = false; //par défaut le résultat est négatif
if (point.x > center.x - largeur/2 && point.x < center.x + largeur/2) { //si la valeur en x du point analysé (c'est un point de profondeur définie dans la fenêtre programme principale)
// n'est pas comprise entre la première valeur x du cube et la dernière
//(si l'abscisse de point est inférieure à l'abscisse du centre du cube Moins la moitié de la largeur du cube (= la fin du cube en x)
// ET si l'abscisse de point est inférieure à l'abscisse du centre du cube Plus la moitié de la largeur du cube (= le début du cube en x)
if (point.y > center.y - hauteur/2 && point.y < center.y + hauteur/2) { //de même que pour x vérifier avec y
if (point.z > center.z - profondeur/2 && point.z < center.z + profondeur/2) { //de même que pour x vérifier avec z
result = true; // si le point de profondeur analysé est compris dans les valeurs x,y et z du cube, alors changer la variable result en vrai
pointsIncluded++; //incrémenter pointIncluded de 1
}
}
}
return result; // réécrir le result, le résultat booléin.
}
void apparaitre() {
x=x+random(-3,3); //cepermet de faire bouger l'image
y=y+random(-3,3);
image(visuel,x,y); // x et y sont définis dans les attributs
// image(visuel,-900 ,-500);
}
// void draw spécifique à la classe Hotpoint
void draw() {
pushMatrix(); // créer un cube avec un style qui lui est propre
//placer le cube selon PVector center
translate(center.x, center.y, center.z);
// remplir le cube de couleur définie par fillColor, mais changer Alpha selon le pourcentage percentIncluded,
// il dépend de la présence ou non d'un point de profondeur depthPoint dans le cube
fill(red(fillColor), blue(fillColor), green(fillColor), 255 * percentIncluded());
//définir les contours selon les couleurs définies précédemment, toujours les laisser apparente (alpha = 255)
stroke(red(strokeColor), blue(strokeColor), green(strokeColor), 255);
// dessiner le cube selon size, une taille définie précedemment, box s'écrit aussi box(w,h,l)
box(largeur,hauteur, profondeur); // Determiner la largeur, la taille et la profondeur de la box
popMatrix(); //fin de la démarquation de style
}
float percentIncluded() { //création de la valeur de pourcentage
//map permet de changer le nom pointsIncluded
return map(pointsIncluded, 0, maxPoints, 0, 1); // return permet de remplacer la valeur de pointIncluded dans le reste du code, il sera utilisé plus haut pour définir l'alpha du cube
}
boolean currentlyHit() {
return pointsIncluded > threshold; // vérification : les points de la main inclus dans l'objet sont supérieur à ceux du seuil de l'objet (threshold), vrai ou faux ? (décrit par la boolean)
}
boolean isHit() {
return currentlyHit() && !wasJustHit; // vérification si le cube est touché ou non
}
void clear() {
wasJustHit = currentlyHit(); // quand la main rentre dans l'objet, si la vérification : les points de la main inclus dans l'objet sont supérieur à ceux du seuil de l'objet (threshold),
pointsIncluded = 0; // alors, les points de la mains inclus dans l'objet sont remis à 0
}
}
==== Fichier audio pour faire tourner le code ====
Télécharger les fichiers audios suivants, et les placer dans le dossier "data" de votre "sketch folder" :
* IRCAM3.mp3
* CS1.mp3
* Reich.mp3
* GtrOiseaux2.mp3
* GuitC.mp3
* AC.mp3
* Nu1.mp3
* HH3.mp3
------------------------------- A COMPLETER !!!!!!