7

cerco un algoritmo di triangolazione veloce poligono che possono triangolare non molto complessi poligoni concavi 2D (senza buchi) in triangolo strisce pronto da inviare a OpenGL ES per disegnare usando GL_TRIANGLE_STRIP.poligono triangolazione in strisce triangolo per OpenGL ES

Sono a conoscenza di alcuni algoritmi, ma non riuscivo a trovare uno che si adatta alle mie esigenze:

  • http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
    • questo algoritmo funziona bene ma il problema è restituisce triangoli semplici, che si puo' t disegnare con GL_TRIANGLE_STRIP, è necessario utilizzare GL_TRIANGLES che non è molto efficiente su un numero elevato di vertici.
  • http://code.google.com/p/iphone-glu/
    • non ha alcun esempio associati e non riuscivo a trovare qualcuno che ha utilizzato con successo su iOS con OpenGL ES 2.0
    • Non so cosa si ritorna e si sembra che definisce anche i comandi OpenGL corrispondenti che non voglio - ho solo bisogno i triangoli indietro
    • di perdite di memoria

La piattaforma che sto sviluppando è: iOS, OpenGL ES 2.0, cocos2d 2.0.

Qualcuno mi può aiutare con un algoritmo? O qualsiasi altro consiglio sarà molto apprezzato.

+3

Sebbene un elenco di triangoli possa sembrare meno efficiente di una singola striscia triangolare, paga quando si dispone di più di un poligono concavo da disegnare (come un vero oggetto 3D costruito da essi). In questo caso puoi disegnare l'intero oggetto multi-poligono con una sola chiamata (molti tri-elenchi possono essere concatenati in uno), mentre con una soluzione a strisce triangolari devi disegnare ogni poligono in modo indiviso. Oggigiorno ridurre il numero di richiami di disegni è spesso un'idea migliore dello scricchiolio di oggetti in alcune primitive sofisticate, come le tri-strisce. Immagino che questo si applichi anche ai dispositivi ES di oggi. –

+1

Se si dispone di un oggetto intero, è preferibile trasformarlo in triangoli e inserirlo in una libreria che genererebbe strisce triangolari, ad esempio nvTriStrip o Stripifier. Ciò può essere fatto offline su PC, quindi non è necessario preoccuparsi di trasferire la libreria su iOS. I dispositivi in ​​genere supportano anche il riavvio primitivo, che consente di concatenare tristrips per renderli in grado di renderli utilizzando un singolo comando. E poi ci sono glMultiDrawElements() o strisce degenerate. –

risposta

9

In 2D e senza buchi, questo è abbastanza facile. Innanzitutto, è necessario interrompere il poligono su uno o più monotone polygons.

I poligoni monotono sono abbastanza semplici da trasformare in tristrips, basta ordinare i valori per , trovare il vertice più in alto e più in basso e quindi disporre di elenchi di vertici a destra ea sinistra (perché i vertici vieni in alcuni definiti, diciamo in senso orario, ordine). Quindi inizi con il vertice più in alto e aggiungi i vertici da sinistra e da destra in modo alternato.

Questa tecnica funzionerà con qualsiasi poligono 2D senza bordi autointersecanti, che include alcuni casi di poligoni con fori (i fori devono essere correttamente avvolti).

Si può provare e giocare con questo codice:

glMatrixMode(GL_PROJECTION); 
glLoadIdentity(); 
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity(); 
glTranslatef(-.5f, -.5f, 0); 

std::vector<Vector2f> my_polygon; 

my_polygon.push_back(Vector2f(-0.300475f, 0.862924f)); 
my_polygon.push_back(Vector2f(0.302850f, 1.265013f)); 
my_polygon.push_back(Vector2f(0.811164f, 1.437337f)); 
my_polygon.push_back(Vector2f(1.001188f, 1.071802f)); 
my_polygon.push_back(Vector2f(0.692399f, 0.936031f)); 
my_polygon.push_back(Vector2f(0.934679f, 0.622715f)); 
my_polygon.push_back(Vector2f(0.644893f, 0.408616f)); 
my_polygon.push_back(Vector2f(0.592637f, 0.753264f)); 
my_polygon.push_back(Vector2f(0.269596f, 0.278068f)); 
my_polygon.push_back(Vector2f(0.996437f, -0.092689f)); 
my_polygon.push_back(Vector2f(0.735154f, -0.338120f)); 
my_polygon.push_back(Vector2f(0.112827f, 0.079634f)); 
my_polygon.push_back(Vector2f(-0.167458f, 0.330287f)); 
my_polygon.push_back(Vector2f(0.008314f, 0.664491f)); 
my_polygon.push_back(Vector2f(0.393112f, 1.040470f)); 
// from wiki (http://en.wikipedia.org/wiki/File:Polygon-to-monotone.png) 

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

glLineWidth(6); 
glColor3f(1, 1, 1); 
glBegin(GL_LINE_LOOP); 
for(size_t i = 0, n = my_polygon.size(); i < n; ++ i) 
    glVertex2f(my_polygon[i].x, my_polygon[i].y); 
glEnd(); 
glPointSize(6); 
glBegin(GL_POINTS); 
for(size_t i = 0, n = my_polygon.size(); i < n; ++ i) 
    glVertex2f(my_polygon[i].x, my_polygon[i].y); 
glEnd(); 
// draw the original polygon 

std::vector<int> working_set; 
for(size_t i = 0, n = my_polygon.size(); i < n; ++ i) 
    working_set.push_back(i); 
_ASSERTE(working_set.size() == my_polygon.size()); 
// add vertex indices to the list (could be done using iota) 

std::list<std::vector<int> > monotone_poly_list; 
// list of monotone polygons (the output) 

glPointSize(14); 
glLineWidth(4); 
// prepare to draw split points and slice lines 

for(;;) { 
    std::vector<int> sorted_vertex_list; 
    { 
     for(size_t i = 0, n = working_set.size(); i < n; ++ i) 
      sorted_vertex_list.push_back(i); 
     _ASSERTE(working_set.size() == working_set.size()); 
     // add vertex indices to the list (could be done using iota) 

     for(;;) { 
      bool b_change = false; 

      for(size_t i = 1, n = sorted_vertex_list.size(); i < n; ++ i) { 
       int a = sorted_vertex_list[i - 1]; 
       int b = sorted_vertex_list[i]; 
       if(my_polygon[working_set[a]].y < my_polygon[working_set[b]].y) { 
        std::swap(sorted_vertex_list[i - 1], sorted_vertex_list[i]); 
        b_change = true; 
       } 
      } 

      if(!b_change) 
       break; 
     } 
     // sort vertex indices by the y coordinate 
     // (note this is using bubblesort to maintain portability 
     // but it should be done using a better sorting method) 
    } 
    // build sorted vertex list 

    bool b_change = false; 
    for(size_t i = 0, n = sorted_vertex_list.size(), m = working_set.size(); i < n; ++ i) { 
     int n_ith = sorted_vertex_list[i]; 
     Vector2f ith = my_polygon[working_set[n_ith]]; 
     Vector2f prev = my_polygon[working_set[(n_ith + m - 1) % m]]; 
     Vector2f next = my_polygon[working_set[(n_ith + 1) % m]]; 
     // get point in the list, and get it's neighbours 
     // (neighbours are not in sorted list ordering 
     // but in the original polygon order) 

     float sidePrev = sign(ith.y - prev.y); 
     float sideNext = sign(ith.y - next.y); 
     // calculate which side they lie on 
     // (sign function gives -1 for negative and 1 for positive argument) 

     if(sidePrev * sideNext >= 0) { // if they are both on the same side 
      glColor3f(1, 0, 0); 
      glBegin(GL_POINTS); 
      glVertex2f(ith.x, ith.y); 
      glEnd(); 
      // marks points whose neighbours are both on the same side (split points) 

      int n_next = -1; 
      if(sidePrev + sideNext > 0) { 
       if(i > 0) 
        n_next = sorted_vertex_list[i - 1]; 
       // get the next vertex above it 
      } else { 
       if(i + 1 < n) 
        n_next = sorted_vertex_list[i + 1]; 
       // get the next vertex below it 
      } 
      // this is kind of simplistic, one needs to check if 
      // a line between n_ith and n_next doesn't exit the polygon 
      // (but that doesn't happen in the example) 

      if(n_next != -1) { 
       glColor3f(0, 1, 0); 
       glBegin(GL_POINTS); 
       glVertex2f(my_polygon[working_set[n_next]].x, my_polygon[working_set[n_next]].y); 
       glEnd(); 
       glBegin(GL_LINES); 
       glVertex2f(ith.x, ith.y); 
       glVertex2f(my_polygon[working_set[n_next]].x, my_polygon[working_set[n_next]].y); 
       glEnd(); 

       std::vector<int> poly, remove_list; 

       int n_last = n_ith; 
       if(n_last > n_next) 
        std::swap(n_last, n_next); 
       int idx = n_next; 
       poly.push_back(working_set[idx]); // add n_next 
       for(idx = (idx + 1) % m; idx != n_last; idx = (idx + 1) % m) { 
        poly.push_back(working_set[idx]); 
        // add it to the polygon 

        remove_list.push_back(idx); 
        // mark this vertex to be erased from the working set 
       } 
       poly.push_back(working_set[idx]); // add n_ith 
       // build a new monotone polygon by cutting the original one 

       std::sort(remove_list.begin(), remove_list.end()); 
       for(size_t i = remove_list.size(); i > 0; -- i) { 
        int n_which = remove_list[i - 1]; 
        working_set.erase(working_set.begin() + n_which); 
       } 
       // sort indices of vertices to be removed and remove them 
       // from the working set (have to do it in reverse order) 

       monotone_poly_list.push_back(poly); 
       // add it to the list 

       b_change = true; 

       break; 
       // the polygon was sliced, restart the algorithm, regenerate sorted list and slice again 
      } 
     } 
    } 

    if(!b_change) 
     break; 
    // no moves 
} 

if(!working_set.empty()) 
    monotone_poly_list.push_back(working_set); 
// use the remaining vertices (which the algorithm was unable to slice) as the last polygon 

std::list<std::vector<int> >::const_iterator p_mono_poly = monotone_poly_list.begin(); 
for(; p_mono_poly != monotone_poly_list.end(); ++ p_mono_poly) { 
    const std::vector<int> &r_mono_poly = *p_mono_poly; 

    glLineWidth(2); 
    glColor3f(0, 0, 1); 
    glBegin(GL_LINE_LOOP); 
    for(size_t i = 0, n = r_mono_poly.size(); i < n; ++ i) 
     glVertex2f(my_polygon[r_mono_poly[i]].x, my_polygon[r_mono_poly[i]].y); 
    glEnd(); 
    glPointSize(2); 
    glBegin(GL_POINTS); 
    for(size_t i = 0, n = r_mono_poly.size(); i < n; ++ i) 
     glVertex2f(my_polygon[r_mono_poly[i]].x, my_polygon[r_mono_poly[i]].y); 
    glEnd(); 
    // draw the sliced part of the polygon 

    int n_top = 0; 
    for(size_t i = 0, n = r_mono_poly.size(); i < n; ++ i) { 
     if(my_polygon[r_mono_poly[i]].y < my_polygon[r_mono_poly[n_top]].y) 
      n_top = i; 
    } 
    // find the top-most point 

    glLineWidth(1); 
    glColor3f(0, 1, 0); 
    glBegin(GL_LINE_STRIP); 
    glVertex2f(my_polygon[r_mono_poly[n_top]].x, my_polygon[r_mono_poly[n_top]].y); 
    for(size_t i = 1, n = r_mono_poly.size(); i <= n; ++ i) { 
     int n_which = (n_top + ((i & 1)? n - i/2 : i/2)) % n; 
     glVertex2f(my_polygon[r_mono_poly[n_which]].x, my_polygon[r_mono_poly[n_which]].y); 
    } 
    glEnd(); 
    // draw as if triangle strip 
} 

Questo codice non è ottimale, ma dovrebbe essere facile da capire. All'inizio, viene creato un poligono concavo. Quindi viene creato un "working set" di vertici. Su quel set di lavoro, viene calcolata una permutazione che ordina i vertici con la loro coordinata . Quella permutazione viene quindi passata attraverso, cercando i punti di divisione. Una volta trovato un punto di divisione, viene creato un nuovo poligono monotono. Quindi, i vertici utilizzati nel nuovo poligono vengono rimossi dal set di lavoro e l'intero processo si ripete. Infine, il working set contiene l'ultimo poligono che non può essere diviso. Alla fine, i poligoni monotono sono resi, insieme all'ordinamento delle strisce triangolari. È un po 'disordinato, ma sono sicuro che lo capirete (questo è il codice C++, basta inserirlo in una finestra GLUT e vedere cosa fa).

Spero che questo aiuti ...

+0

@rakeshNS Quel soprannome effettivamente risale al passato. Al liceo, facevo tutti i tipi di compiti a casa per il resto della classe, e se cominciavano a preoccuparsi che non li avrei fatti in tempo e avrebbero perso crediti, li ho sempre rassicurati "non ti preoccupare, Non sono suina "... e si è bloccato. –