Problème de Color Picking
Bonjour tout le monde.
J'ai un ensemble de données regroupées en cellules.
Chaque cellule contiennent au moins un polygone (des tétras dans le majeur des cas).
Je souhaite mettre en place un color picking. La sélection s'effectuerait en fonction des cellules.
Pour cela, j'ai créé une couleur unique par cellule:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| package elements.colors;
public class UniqueColorID {
public static short MAX_NB_COLOR = 255;
public static byte MIN_BYTE_VALUE = Byte.MIN_VALUE;
public static byte MAX_BYTE_VALUE = Byte.MAX_VALUE;
private static short[] currentColorId = {0,0,0,MAX_NB_COLOR};
private static byte[] currentByteColorId = {MIN_BYTE_VALUE, MIN_BYTE_VALUE, MIN_BYTE_VALUE, MIN_BYTE_VALUE};
private byte[] colorByteId = new byte[4];
public UniqueColorID() {
colorByteId[0] = currentByteColorId[0];
colorByteId[1] = currentByteColorId[1];
colorByteId[2] = currentByteColorId[2];
colorByteId[3] = currentByteColorId[3];
updateCurrentByteColorId();
}
//Met à jour le couleur courrante
// Nous avons plus de 4 Milliards de possibilités
private void updateCurrentByteColorId() {
//Ajoute + 1 au rouge
currentByteColorId[0]++;
//Si le rouge est supérieur au MAX_BYTE_VALUE
if(currentByteColorId[0] >= MAX_BYTE_VALUE) {
//On réinitialise le rouge et on ajoute +1 au vert
currentByteColorId[0] = MIN_BYTE_VALUE;
currentByteColorId[1]++;
//Si le vert est supérieur au MAX_BYTE_VALUE
if(currentByteColorId[1] >= MAX_BYTE_VALUE) {
//On réinitialise le vert et on ajoute +1 au bleu
currentByteColorId[1] = MIN_BYTE_VALUE;
currentByteColorId[2]++;
//Si le bleu est supérieur au MAX_BYTE_VALUE
if(currentByteColorId[2] >= MAX_BYTE_VALUE) {
//On réinitialise le bleu et on ajoute +1 au alpha
currentByteColorId[2] = MIN_BYTE_VALUE;
currentByteColorId[3]++;
//Si le canal alpha est supérieur au MAX_BYTE_VALUE
if(currentByteColorId[3] >= MAX_BYTE_VALUE) {
//On réinitialise le canal alpha
currentByteColorId[3] = MIN_BYTE_VALUE;
}
}
}
}
}
public byte[] getByteColorId() {
return colorByteId;
}
public byte getByteRedId() {
return colorByteId[0];
}
public byte getByteGreenId() {
return colorByteId[1];
}
public byte getByteBlueId() {
return colorByteId[2];
}
public byte getByteAlphaId() {
return colorByteId[3];
}
//Récupère l'index associé à la couleur
public static int getIndexFromUniqueColorID(short[] pixel) {
int index = -1;
if(pixel != null && pixel.length == 4) {
index = (pixel[0] + (MAX_NB_COLOR*pixel[1] + pixel[1]) +
(MAX_NB_COLOR*pixel[2] + pixel[2]) +
(MAX_NB_COLOR*(pixel[3]-MAX_NB_COLOR/2) + (pixel[3]-MAX_NB_COLOR/2)));
}
return index;
}
public static int getIndexFromUniqueColorID(byte[] pixel) {
int index = -1;
if(pixel != null && pixel.length == 4) {
short[] pix = new short[4];
//Transforme un tableau de byte en un tableau short.
for(int i = 0; i<pixel.length; i++) {
if(pixel[i] >= 0)
pix[i] = pixel[i];
else
pix[i] = (short) (pixel[i] + 128);
}
return getIndexFromUniqueColorID(pix);
}
return index;
}
public static int getIndexFromUniqueColorID(byte red, byte green, byte blue, byte alpha) {
byte[] pixel = {red, green, blue, alpha};
return getIndexFromUniqueColorID(pixel);
}
//Créer un tableau de fausses couleurs
public static byte[] buildByteColorIds(int sizeBuffer) {
int i = 0;
byte[] colorsId = new byte[sizeBuffer*4];
UniqueColorID colorId;
int index = 0;
while(i<sizeBuffer) {
colorId = new UniqueColorID();
colorsId[index++] = colorId.getByteRedId();
colorsId[index++] = colorId.getByteGreenId();
colorsId[index++] = colorId.getByteBlueId();
colorsId[index++] = colorId.getByteAlphaId();
i++;
}
return colorsId;
}
} |
Pour information je suis en java donc on peut oublier les unsigned char... De plus j'utilise la bibliothèque LWJGL 2.5.
Pour appliquer le tableau de fausses couleurs je le fais de la manière suivante:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
private int initDisplayList() {
ArrayList<Vertex3D> vertices;
//Le tableau de fausses couleurs
byte[] colorsId = UniqueColorID.buildByteColorIds(loader.getNbCells());
int indexId = 0;
int res = GL11.glGenLists(1);
GL11.glNewList(res, GL11.GL_COMPILE);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glBegin(GL11.GL_TRIANGLES);
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
//Pour chaque cellules
for (Cell c : CellManagement.getCells()) {
//On applique une fausse couleur unique pour tous les polygones de la cellule
GL11.glColor4ub(colorsId[indexId++], colorsId[indexId++],
colorsId[indexId++], colorsId[indexId++]);
for (Polygon3D e : c.getAllPolygon3D()) {
vertices = e.getVertexList();
int index = 1;
for (Vertex3D v : vertices) {
GL11.glVertex3d(v.getX(), v.getY(), v.getZ());
index++;
}
}
}
GL11.glEnd();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEndList();
return res;
} |
Maintenant que j'ai ma display list de mon modèle en fausse couleur, il suffit d'utiliser la fonction glReadPixels qui permet de récupérer des informations sur un ou plusieurs pixels à proximité d'une zone donnée:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
public void picking() {
GL11.glPushMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
//Remplie la matrice GL_MODELVIEW avec le modèle de fausses couleurs
GL11.glCallList(modelDisplayList);
GL11.glFlush();
IntBuffer viewport = BufferUtils.createIntBuffer(16);
//Récupère le viewport
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);
ByteBuffer pixels = BufferUtils.createByteBuffer(40);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_FLAT);
GL11.glDisable(GL11.GL_BLEND);
//Récupère la couleur de pixel associé à la position MouseX et MouseY (corrigé par la hauteur de la fenètre)
GL11.glReadPixels(mouseListener.prevMouseX, viewport.get(3)
- mouseListener.prevMouseY, 1, 1, GL11.GL_RGBA,
GL11.GL_UNSIGNED_BYTE, pixels);
System.out.println("pixel[r,g,b,a] : " + pixels.get(0) + ", "
+ pixels.get(1) + ", " + pixels.get(2) + ", " + pixels.get(3));
// pixel in background?
if (pixels.get(0) == -1 && pixels.get(1) == -1 && pixels.get(2) == -1
&& pixels.get(3) == -1) {
System.out.println("Background");
} else {
//Récupère l'index de la cellule
int index = UniqueColorID.getIndexFromUniqueColorID(pixels.get(0),
pixels.get(1), pixels.get(2), pixels.get(3));
System.out.println("Cell " + index + ": " +
loader.getCellList().getCell(index));
}
}
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_FLAT);
GL11.glEnable(GL11.GL_BLEND);
mouseListener.pickingMode = false;
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glPopMatrix();
GL11.glFlush();
} |
Voici maintenant mon problème: lorsque je clique sur une des faces d'un tétra (qui appartient à une cellule), parfois il sélectionne un autre tétra ou trouve que c'est l'arrière plan.
Je ne vois pas pourquoi dans certains cas mon picking ne fonctionne pas correctement...
Avez-vous des idées?
Merci d'avance
PS: Si vous voulez le code en entier je peux vous l'envoyer en PM
Edit:
J'ai trouvé une erreur: lors de la lecture du pixel, j'ai une valeur de la couleur différente de celle passer dans la displaying list (notamment la valeur du canal alpha).