SOFTWARE > Linguaggi di programmazione e scripting

[C] Morphos Opengl

(1/4) > >>

raistlin77it:
Ciao a tutti.
Vi sottopongo un problemino che non riesco a risolvere, sotto MorphOS.

Stò realizzando un "homebrew"  che utilizza le SDL/GL.
Visto che lo programmo su pc per comodità ed ho già una beta funzionante, mi sono deciso a compilarlo anche per MorphOS, giusto per vedere se il tutto funziona anche lì (prima di finire il gioco e poi debuggare 3000000 di righe), ed ho trovato un piccolo problema, non riesco a selezionare correttamente gli oggetti in 3d, cosa che su pc funziona benissimo.

Per selezionare gli oggetti io uso il metodo del color picking, quindi tramite la funzione glReadPixels.

All'inizio pensavo fosse un problema di endianness tra pc e mini, ma anche swappando i vari byte, non riesco a selezionare correttamente gli oggetti.


Per farla breve ho creato un listato apposta che basta compilare su Mos per verificare :

--------------------------------------------------------------
--- Codice: ---#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GL/gl.h>
#include "SDL/SDL.h"
#include "SDL/SDL_opengl.h"


#define RENDER 1 // No ray-plane intersection (i haven't studied the vectors properly )
#define SELECT 2 // So old-good color selection

#define MORPHOS 1

#define SWITCH_INT(a) { char s_i, *p_i;p_i= (char *)&(a);s_i=p_i[0]; p_i[0]=p_i[3]; p_i[3]=s_i;s_i=p_i[1]; p_i[1]=p_i[2]; p_i[2]=s_i; }

int mode = RENDER;

int FRAMES_PER_SECOND = 50; //The frames per second const
int frame = 0; //Keep track of the current frame
int startTicks = 0;
int currentTicks=0;

int SCREENW=800;
int SCREENH=600;

int EDITORMODE=0;       // old stuff from the editor 0=Add,1=Paint,2=Del,3=load,4=save,5=new,6=texture
int OLDEDITORMODE=0;    //
int GRID=0;             //




int mousex,mousey;

unsigned int nIndex;




void DrawSelection() {

    glDisable(GL_LIGHTING);
    glDisable( GL_TEXTURE_2D );
    nIndex=10000;
   
    glColor3ub ( nIndex % 256, (nIndex>>8) % 256, (nIndex>>16) % 256);
    glBegin(GL_QUADS);
        glVertex3f( -0.5f,  -0.5f,   +0.5f);
        glVertex3f( +0.5f,  -0.5f,   +0.5f);
        glVertex3f( +0.5f,  +0.5f,   +0.5f);
        glVertex3f( -0.5f,  +0.5f,   +0.5f);
    glEnd();

}



void mouseMove (int x, int y) {
   
  static int x_pos = 0;
  static int y_pos = 0;

  x_pos = x;
  y_pos = y;

}

void ProcessPick() {



    unsigned int col;
    GLint viewport[4];

    glGetIntegerv(GL_VIEWPORT,viewport);
   


    glReadPixels( mousex, viewport[3]-mousey, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &col);
    SWITCH_INT(col);

   
    printf(" color number %un",nIndex);
    printf(" color founded %un",col);

    printf(" color put %u ,%u ,%un",nIndex%256,(nIndex>>8) % 256, (nIndex>>16) % 256);  
    printf(" color get %u ,%u ,%un",col%256,(col>>8) % 256, (col>>16) % 256);  
 
}






int main(int argc, char *argv[]) {  

   
    SDL_Surface *screen;    


    int done;
   
    SDL_Event event;
    Uint8* keystates;

    // Slightly different SDL initialization
    if ( SDL_Init(SDL_INIT_VIDEO| SDL_INIT_AUDIO) != 0 ) {
        printf("Unable to initialize SDL: %sn", SDL_GetError());
        return 1;
    }

    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*

    screen = SDL_SetVideoMode( SCREENW, SCREENH, 32, SDL_OPENGL /*| SDL_FULLSCREEN*/); // *changed*
    if ( !screen ) {
printf("Unable to set video mode: %sn", SDL_GetError());
return 1;
}


    glViewport( 0, 0, SCREENW, SCREENH );
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, 1.0 * SCREENW/SCREENH, 0.1, 400.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

   
    glEnable (GL_DEPTH_TEST);

    glEnable (GL_COLOR_MATERIAL);




    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    gluLookAt(0,0,18,0.0,0.0,0.0,0.0,1.0,0.0);

    done=0;


    glClearColor( 0, 0, 0, 0);    

   

    while(!done){
   
       
       
    startTicks=SDL_GetTicks();
 

    // Clear the screen before drawing
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);



        while(SDL_PollEvent(&event)) {
       
                     
            switch (event.type) {
 
                case SDL_MOUSEMOTION:
            mouseMove (event.button.x, event.button.y);
             mousex=event.button.x;
                  mousey=event.button.y;
                break;
                     
                case SDL_KEYDOWN:
             if (event.key.keysym.sym == SDLK_ESCAPE) done=1;
                break;
           
                case SDL_MOUSEBUTTONDOWN :
                    if (SDL_GetMouseState (NULL,NULL) & SDL_BUTTON_LMASK) {
                        mousex=event.button.x;
                        mousey=event.button.y;
                        mode = SELECT;
                }                
                break;
       
                default:
                break;
      }
        }
       

        if (mode == SELECT) DrawSelection(); else DrawSelection();

    if (mode == SELECT) {
    ProcessPick();
    mode = RENDER;
    } else     SDL_GL_SwapBuffers();
   



               
    if( currentTicks < 1000 / FRAMES_PER_SECOND  ) SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - currentTicks );
   
    }
   
    SDL_Quit();
   
return 0;
}
--- Termina codice ---
------------------------------------------------

succede che per ogni componente R,G,B, che ottengo su morphos, manca sempre un'unità al valore trovato ad esempio se metto R=232,G=3,B=0, ottengo R=231,B=2,G=0; e quindi ho una selezione sbagliata.
Ho postato il listato anche su mzone, ma per ora nessuno mi ha detto nulla.
Non è che ci potete buttare un'occhio e vedere dove c'è l'errore?
Ripeto su pc tolta la funzione di swap dei byte funziona a meraviglia.

TheKaneB:
Non l'ho debuggato perchè non ne ho il tempo, ma dando un'occhiata veloce al codice ed alla tua descrizione posso sicuramente notare un difetto di approccio da non trascurare.

In sostanza vuoi effettuare il picking di un oggetto usando il colore che questo mostrerà in fase di rendering (in viewport space), mentre tu conosci solo il colore "originale" (cioè nell'object space).

vedo che hai usato questa orribile tecnica http://www.codeguru.com/Cpp/G-M/opengl/ ... .php/c5579
in giro esistono altre guide ancora più oscene, che sfruttano il GL_SELECT. Beh, lasciamo stare... :-)

E' chiaro come il sole che la tecnica può funzionare solo se tutta la trafila utilizza un numero di bit sufficiente a non perdere informazioni per strada. In particolare, non è detto che internamente i colori siano sempre RGB24, ma possono anche scendere a RGB16 o RGB15 in base a vari meccanismi interni. Anche se tutta la trafila fosse comunque perfettamente calibrata come numero di bit per canale, potresti sempre avere a che fare con dei calcoli che introducono errori ed approssimazioni (in questo caso introduce un bias verso il nero).

Piuttosto che stare ad impazzire dietro alle conversioni di colore del driver GL di MorphOS (che magari potrebbe cambiare da versione a versione), devi implementare il picking in modo corretto. Come si fa? Con la proiezione inversa dallo schermo verso il World Space.

- Prendi un pixel sulla viewport
- lo moltiplichi per l'inversa della matrice di proiezione sul near plane e sul far clipping plane (trovandoti così 2 punti perpendicolari allo schermo)
- prendi il vettore che unisce questi due punti ed effettua un test ray-box intersection per ciascun oggetto della scena (proiettando ogni volta il vettore di interezione per l'inversa della modelview di ogni oggetto)
- prendi tutti gli oggetti così intersecati e ordinali in base alla distanza dal near plane
[ passaggi opzionali se vuoi fare il picking con precisione al poligono ]
- partendo dall'oggetto più vicino, moltiplica il vettore di prima per l'inversa della matrice di modelview di quell'oggetto, e ti ritrovi nell'object space.
- effettua un test Ray-Triangle su ciascun triangolo (aiutati con un quadtree o, meglio ancora, con un modello scheletale se i tuoi modelli sono troppo carichi di poligoni)
- se becchi almeno un triangolo hai trovato l'oggetto corretto.


link utili (presi più o meno a casaccio da Google)
http://www.mt-wudan.com/wu_tut.php?t=gl_mouseray
http://www.euclideanspace.com/threed/re ... /index.htm
http://www.euclideanspace.com/maths/alg ... /index.htm
http://eigenclass.blogspot.com/2008/10/ ... ngbox.html

Enjoy!

raistlin77it:

--- Citazione da: "TheKaneB" ---Non l'ho debuggato perchè non ne ho il tempo, ma dando un'occhiata veloce al codice ed alla tua descrizione posso sicuramente notare un difetto di approccio da non trascurare.

In sostanza vuoi effettuare il picking di un oggetto usando il colore che questo mostrerà in fase di rendering (in viewport space), mentre tu conosci solo il colore "originale" (cioè nell'object space).

--- Termina citazione ---

certo, però se fai le cose fatte bene il colore originale lo conservi :D


--- Citazione da: "TheKaneB" ---vedo che hai usato questa orribile tecnica http://www.codeguru.com/Cpp/G-M/opengl/ ... .php/c5579
in giro esistono altre guide ancora più oscene, che sfruttano il GL_SELECT. Beh, lasciamo stare... :-)

--- Termina citazione ---
non ho usato lo stack di nomi perchè con le nuove opengl è deprecato , questo metodo funziona sempre e mi permette di non studiare le matrici (io programmo per hobby nelle ore di "sonno")


--- Citazione da: "TheKaneB" ---E' chiaro come il sole che la tecnica può funzionare solo se tutta la trafila utilizza un numero di bit sufficiente a non perdere informazioni per strada. In particolare, non è detto che internamente i colori siano sempre RGB24, ma possono anche scendere a RGB16 o RGB15 in base a vari meccanismi interni. Anche se tutta la trafila fosse comunque perfettamente calibrata come numero di bit per canale, potresti sempre avere a che fare con dei calcoli che introducono errori ed approssimazioni (in questo caso introduce un bias verso il nero).

--- Termina citazione ---
ecco a questo non ci avevo pensato, mi sa che i driver per essere performanti  usano qualche truchetto sporco :(


--- Citazione da: "TheKaneB" ---Piuttosto che stare ad impazzire dietro alle conversioni di colore del driver GL di MorphOS (che magari potrebbe cambiare da versione a versione), devi implementare il picking in modo corretto. Come si fa? Con la proiezione inversa dallo schermo verso il World Space.

- Prendi un pixel sulla viewport
- lo moltiplichi per l'inversa della matrice di proiezione sul near plane e sul far clipping plane (trovandoti così 2 punti perpendicolari allo schermo)
- prendi il vettore che unisce questi due punti ed effettua un test ray-box intersection per ciascun oggetto della scena (proiettando ogni volta il vettore di interezione per l'inversa della modelview di ogni oggetto)
- prendi tutti gli oggetti così intersecati e ordinali in base alla distanza dal near plane
[ passaggi opzionali se vuoi fare il picking con precisione al poligono ]
- partendo dall'oggetto più vicino, moltiplica il vettore di prima per l'inversa della matrice di modelview di quell'oggetto, e ti ritrovi nell'object space.
- effettua un test Ray-Triangle su ciascun triangolo (aiutati con un quadtree o, meglio ancora, con un modello scheletale se i tuoi modelli sono troppo carichi di poligoni)
- se becchi almeno un triangolo hai trovato l'oggetto corretto.

--- Termina citazione ---
certo disegnando 2 volte la scena ho  il doppio di lavoro, ma sono al massimo 8000 tris al frame

--- Citazione da: "TheKaneB" ---link utili (presi più o meno a casaccio da Google)
http://www.mt-wudan.com/wu_tut.php?t=gl_mouseray
http://www.euclideanspace.com/threed/re ... /index.htm
http://www.euclideanspace.com/maths/alg ... /index.htm
http://eigenclass.blogspot.com/2008/10/ ... ngbox.html

Enjoy!
--- Termina citazione ---
Eh lo so, però non ho proprio voglia di studiarmi i vettori e le matrici, poi insomma, ho 5000 linee di "sudatissimo" codice, non voglio riscrivero :( :(

UFF :( :(

TheKaneB:
eh ti capisco ma questa è la minestra, o la mangi o ti butti dalla finestra  :lol:

Fare grafica 3D senza conoscere la geometria analitica è possibile solo se ti affidi a dei sistemi estremamente semplificati e automatizzati, del tipo "clicca un pupino e trascinalo nel posto giusto".
Anche usando un engine già pronto (UDK, Ogre, Irrlicht, ecc...) avrai SEMPRE bisogno di almeno un pochino di geometria, altrimenti ogni volta che vedi una matrice sarai costretto a copia-incollare codice a casaccio preso da internet senza comprenderne il significato, e così ad libitum finchè non trovi per caso un codice che funziona (cosa estremamente improbabile, perchè la matematica si fa con le formule ed i teoremi, non copiando gli esercizi :-) ).

Comunque, questa è l'unica tecnica seria ed efficiente. Tutte le altre sono perfettamente inutili, perchè quello che risparmi in geometria lo perdi in capelli.

Ad esempio, se vuoi modificare il tuo codice per funzionare nonostante il bias cromatico, dovresti crearti i vettori RGB del colore target e di quello "pickato" dalla viewport, fare la differenza e sommare i quadrati delle componenti R,G,B del vettore differenza. Ottieni la cosiddetta "distanza quadrata" tra i due vettori (è quasi come la distanza, ma risparmi una radice quadrata). Se la distanza quadrata è minore o uguale ad una certa soglia, allora i due vettori sono abbastanza vicini tra loro (i due colori sono quasi uguali).

Puoi usare questo accrocchio per rendere funzionante il tuo codice, ma non dire in giro che sono stato io a consigliartelo, ho una reputazione di game developer da difendere  :lol:

Z80Fan:
Aspetta, forse c'è una soluzione più semplice, in base al tipo di lavoro che deve fare:
ad esempio, sto scrivendo una specie di clone di Minecraft, e dato che il mondo è formato da cubi regolari, partendo dalla posizione del giocatore e dalla direzione in cui guarda, posso (con qualche algoritmo di rasterizzazione) trovare facilmente tutti i cubi presenti sulla linea dello sguardo.

Bisognerebbe conoscere più in dettaglio lo scopo del programma, per trovare eventuali semplificazioni.

Navigazione

[0] Indice dei post

[#] Pagina successiva

Vai alla versione completa