IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OpenGL Discussion :

Portage GLUtessellator pour récupérer les triangles, triangleFan et triangleStrip


Sujet :

OpenGL

  1. #1
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut Portage GLUtessellator pour récupérer les triangles, triangleFan et triangleStrip
    Bonjour,

    Actuellement j'utilise Poly2Tri et LibTessDotNet pour triangulariser des polygones quelconques avec des trous, issu de calculs faits avec Clipper.

    Je cherche à utiliser ou à trouver un portage en VB.NET ou en C# de la fonction tessellation OpenGL (GLUtessellator) complète

    Je m'explique...

    LibTessDotNet serait un portage de la méthode de GLUtessellator, mais elle ne peut générer en sortie qu'une liste de triangles

    Or, dans la fonction GLUtessellator, il y a cette fonctionnalité intéressante :

    OpenGL tessellator decides the most efficient primitive type while performing tessellation; GL_TRIANGLE, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP and GL_LINE_LOOP.
    http://www.songho.ca/opengl/gl_tessellation.html

    Ce que je voudrais faire, c'est utiliser (ou trouver un portage) de cette fonction pour récupérer, en sortie, un mélange de GL_TRIANGLE, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP.

    En effet, les GL_TRIANGLE_FAN et GL_TRIANGLE_STRIP sont bien plus économes en mémoire, tout en étant aussi rapides que les triangles indexés.

    J'ai besoin de récupérer en sortie les données GL_TRIANGLE, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP pour les stoker en mémoire pour :
    - faire la tessellation d'un polygone une seule fois
    - pouvoir travailler avec les données

    J'ai passé pas mal de temps à chercher sur Internet, mais
    - soit je trouve des fonctions de tessellation qui ne donnent que des triangles en vrac ou indexés
    - soit comment utiliser GLUtessellator pour de l'affichage via des callbacks

    En fait, dans mon programme, il faut considérer la performance de l'ensemble de la chaîne de fonctions qui réalise la triangularisation, le stockage et l'affichage.

    Du coup la méthode de triangularisation Poly2Tri qui est réputée est pénalisante car elle ne donne en sortie que des triangles non indexés ; dans de rares cas Poly2Tri échoue il faut une méthode de secours (dans mon cas LibTess)

    La méthode de LibTessDotNet est robuste mais les triangles sont de moins bonne qualité (trop pointus par rapport à Poly2Tri, ça donne du fil à retordre au logiciels CAO) ; LibTessDotNet donne en sortie des triangles indexés ce qui permet un usage optimal avec un OpenGL moderne. Mais il reste la consommation mémoire...

    Car pour des polygones en 2.5D (en 3D mais parallèle au plan XY) avec des GL_TRIANGLE_FAN et GL_TRIANGLE_STRIP je consomme :
    - 2,5 fois moins de mémoire qu'avec des triangles indexés
    - 3 fois moins de mémoire qu'avec des triangles non indexés

    En 2.5D on a seulement besoin des coordonnées X et Y des points ; on ne stocke par la normale pour chaque triangle car il y a une normale unique pour le polygone Z=1 ou Z=-1
    Les coordonnées X et Y se stockent sur 4 octets chacune (type Single)
    Les index sont des entiers sur 32 bits donc se stockent sur 4 octets (un entier sur 2 octets n'offre qu'une valeur maxi de 65535 ce qui est trop faible)
    Prenons un polygone triangulé en 'n' triangles :
    - sous forme de GL_TRIANGLE_FAN ou GL_TRIANGLE_STRIP il coute (n+2) points en stockage, donc (n+2) * 8 octets
    - sous forme de triangles indexés il faut stocker n points mais en plus il faut stocker 3 index par triangle donc n * 20 octets
    - sous forme de triangles non indexés il faut stocker 3 * n points donc n * 24 octets

    NB : un polygone triangularisé de 'n' sommets compte environ 'n' triangles

    NB : en 3D "pure" la différence s'estompe, à cause du stockage des normales à faire pour chaque triangle
    - GL_TRIANGLE_FAN ou GL_TRIANGLE_STRIP : un point X,Y,Z + une normale NX,NY,NZ par triangle => (n+2) * 24 octets
    - Triangles indexés il faut stocker un point X,Y,Z + une normale NX,NY,NZ + 3 index par triangle n * 36 octets (gain 30% "seulement")
    - sous forme de triangles non indexés il faut stocker 3 points X,Y,Z + une normale NX,NY,NZ par triangle donc n * 48 octets (gain 50% "seulement")

    Même en 3D GL_TRIANGLE_FAN ou GL_TRIANGLE_STRIP restent avantageux en occupation mémoire - 50% ou 30% de RAM consommée en moins je ne crache pas dessus !
    J'arrive à avoir de très grosses pièces 3D qui mettent le PC à l'épreuve, avec plusieurs Go de RAM utilisé

    S'agissant de la vitesse d'affichage, il n'y a pas d'intérêt :
    - avec du vieux code utilisant des triangles non indexés et du vieux matériel (goulet d'étranglement RAM -> GPU) on gagne, mais ça reste un cas particulier
    - avec du vieux code utilisant des triangles non indexés et du matériel récent on gagne un peu
    - avec du code moderne il n'y a pas de gain, ça peut même être plus lent (logique car tout ayant été fait au niveau du hardware et des pilotes pour optimiser au maximum les triangles indexés)

    A bientôt

    Merci
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  2. #2
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Bonjour

    j'ai trouvé quelques infos intéressantes

    L'algorithme de GLU Tesselator est décrit en détail ici : https://stackoverflow.com/questions/...tess-functions

    Cet algorithme génère des triangles plus ou moins en vrac et le regroupement est fait à la fin :

    Triangulation and Grouping
    We finish the line sweep before doing any triangulation. This is because even after a monotone region is complete, there can be further changes to its vertex data because of further vertex merging.

    After triangulating all monotone regions, we want to group the triangles into fans and strips. We do this using a greedy approach. The triangulation itself is not optimized to reduce the number of primitives; we just try to get a reasonable decomposition of the computed triangulation.
    "greedy approach" signifie approche gourmande donc c'est possiblement coûteux

    Dans le code de GLU Tesselator, ce regroupement est fait dans 'render.c'
    Ce code (et donc cette étape de regroupement) est absente de LibTessDotNet

    https://chromium.googlesource.com/ex...1a3f3c/libtess

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    /*
    ** License Applicability. Except to the extent portions of this file are
    ** made subject to an alternative license as permitted in the SGI Free
    ** Software License B, Version 1.1 (the "License"), the contents of this
    ** file are subject only to the provisions of the License. You may not use
    ** this file except in compliance with the License. You may obtain a copy
    ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
    ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
    ** 
    ** http://oss.sgi.com/projects/FreeB
    ** 
    ** Note that, as provided in the License, the Software is distributed on an
    ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
    ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
    ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
    ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
    ** 
    ** Original Code. The Original Code is: OpenGL Sample Implementation,
    ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
    ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
    ** Copyright in any portions created by third parties is as indicated
    ** elsewhere herein. All Rights Reserved.
    ** 
    ** Additional Notice Provisions: The application programming interfaces
    ** established by SGI in conjunction with the Original Code are The
    ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
    ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
    ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
    ** Window System(R) (Version 1.3), released October 19, 1998. This software
    ** was created using the OpenGL(R) version 1.2.1 Sample Implementation
    ** published by SGI, but has not been independently verified as being
    ** compliant with the OpenGL(R) version 1.2.1 Specification.
    **
    */
    /*
    ** Author: Eric Veach, July 1994.
    **
    ** $Date$ $Revision$
    ** $Header: //depot/main/gfx/lib/glu/libtess/render.c#5 $
    */
    #include <assert.h>
    #include <stddef.h>
    #include <gluos.h>
    #include "mesh.h"
    #include "render.h"
    #include "tess.h"
    #define TRUE 1
    #define FALSE 0
    /* This structure remembers the information we need about a primitive
     * to be able to render it later, once we have determined which
     * primitive is able to use the most triangles.
     */
    struct FaceCount {
      long		size;		/* number of triangles used */
      GLUhalfEdge	*eStart;	/* edge where this primitive starts */
      void		(*render)(GLUtesselator *, GLUhalfEdge *, long);
                                    /* routine to render this primitive */
    };
    static struct FaceCount MaximumFan( GLUhalfEdge *eOrig );
    static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig );
    static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
    static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
    static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart,
    			    long size );
    static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig );
    static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
    /************************ Strips and Fans decomposition ******************/
    /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
     * fans, strips, and separate triangles.  A substantial effort is made
     * to use as few rendering primitives as possible (ie. to make the fans
     * and strips as large as possible).
     *
     * The rendering output is provided as callbacks (see the api).
     */
    void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh )
    {
      GLUface *f;
      /* Make a list of separate triangles so we can render them all at once */
      tess->lonelyTriList = NULL;
      for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
        f->marked = FALSE;
      }
      for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
        /* We examine all faces in an arbitrary order.  Whenever we find
         * an unprocessed face F, we output a group of faces including F
         * whose size is maximum.
         */
        if( f->inside && ! f->marked ) {
          RenderMaximumFaceGroup( tess, f );
          assert( f->marked );
        }
      }
      if( tess->lonelyTriList != NULL ) {
        RenderLonelyTriangles( tess, tess->lonelyTriList );
        tess->lonelyTriList = NULL;
      }
    }
    static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
    {
      /* We want to find the largest triangle fan or strip of unmarked faces
       * which includes the given face fOrig.  There are 3 possible fans
       * passing through fOrig (one centered at each vertex), and 3 possible
       * strips (one for each CCW permutation of the vertices).  Our strategy
       * is to try all of these, and take the primitive which uses the most
       * triangles (a greedy approach).
       */
      GLUhalfEdge *e = fOrig->anEdge;
      struct FaceCount max, newFace;
      max.size = 1;
      max.eStart = e;
      max.render = &RenderTriangle;
      if( ! tess->flagBoundary ) {
        newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; }
        newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
        newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
        newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; }
        newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
        newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
      }
      (*(max.render))( tess, max.eStart, max.size );
    }
    /* Macros which keep track of faces we have marked temporarily, and allow
     * us to backtrack when necessary.  With triangle fans, this is not
     * really necessary, since the only awkward case is a loop of triangles
     * around a single origin vertex.  However with strips the situation is
     * more complicated, and we need a general tracking method like the
     * one here.
     */
    #define Marked(f)	(! (f)->inside || (f)->marked)
    #define AddToTrail(f,t)	((f)->trail = (t), (t) = (f), (f)->marked = TRUE)
    #define FreeTrail(t)	do { \
    			  while( (t) != NULL ) { \
    			    (t)->marked = FALSE; t = (t)->trail; \
    			  } \
    			} while(0) /* absorb trailing semicolon */
    static struct FaceCount MaximumFan( GLUhalfEdge *eOrig )
    {
      /* eOrig->Lface is the face we want to render.  We want to find the size
       * of a maximal fan around eOrig->Org.  To do this we just walk around
       * the origin vertex as far as possible in both directions.
       */
      struct FaceCount newFace = { 0, NULL, &RenderFan };
      GLUface *trail = NULL;
      GLUhalfEdge *e;
      for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) {
        AddToTrail( e->Lface, trail );
        ++newFace.size;
      }
      for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) {
        AddToTrail( e->Rface, trail );
        ++newFace.size;
      }
      newFace.eStart = e;
      /*LINTED*/
      FreeTrail( trail );
      return newFace;
    }
    #define IsEven(n)	(((n) & 1) == 0)
    static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig )
    {
      /* Here we are looking for a maximal strip that contains the vertices
       * eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
       * reverse, such that all triangles are oriented CCW).
       *
       * Again we walk forward and backward as far as possible.  However for
       * strips there is a twist: to get CCW orientations, there must be
       * an *even* number of triangles in the strip on one side of eOrig.
       * We walk the strip starting on a side with an even number of triangles;
       * if both side have an odd number, we are forced to shorten one side.
       */
      struct FaceCount newFace = { 0, NULL, &RenderStrip };
      long headSize = 0, tailSize = 0;
      GLUface *trail = NULL;
      GLUhalfEdge *e, *eTail, *eHead;
      for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) {
        AddToTrail( e->Lface, trail );
        ++tailSize;
        e = e->Dprev;
        if( Marked( e->Lface )) break;
        AddToTrail( e->Lface, trail );
      }
      eTail = e;
      for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) {
        AddToTrail( e->Rface, trail );
        ++headSize;
        e = e->Oprev;
        if( Marked( e->Rface )) break;
        AddToTrail( e->Rface, trail );
      }
      eHead = e;
      newFace.size = tailSize + headSize;
      if( IsEven( tailSize )) {
        newFace.eStart = eTail->Sym;
      } else if( IsEven( headSize )) {
        newFace.eStart = eHead;
      } else {
        /* Both sides have odd length, we must shorten one of them.  In fact,
         * we must start from eHead to guarantee inclusion of eOrig->Lface.
         */
        --newFace.size;
        newFace.eStart = eHead->Onext;
      }
      /*LINTED*/
      FreeTrail( trail );
      return newFace;
    }
    static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size )
    {
      /* Just add the triangle to a triangle list, so we can render all
       * the separate triangles at once.
       */
      assert( size == 1 );
      AddToTrail( e->Lface, tess->lonelyTriList );
    }
    static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f )
    {
      /* Now we render all the separate triangles which could not be
       * grouped into a triangle fan or strip.
       */
      GLUhalfEdge *e;
      int newState;
      int edgeState = -1;	/* force edge state output for first vertex */
      CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES );
      for( ; f != NULL; f = f->trail ) {
        /* Loop once for each edge (there will always be 3 edges) */
        e = f->anEdge;
        do {
          if( tess->flagBoundary ) {
    	/* Set the "edge state" to TRUE just before we output the
    	 * first vertex of each edge on the polygon boundary.
    	 */
    	newState = ! e->Rface->inside;
    	if( edgeState != newState ) {
    	  edgeState = newState;
              CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState );
    	}
          }
          CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
          e = e->Lnext;
        } while( e != f->anEdge );
      }
      CALL_END_OR_END_DATA();
    }
    static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size )
    {
      /* Render as many CCW triangles as possible in a fan starting from
       * edge "e".  The fan *should* contain exactly "size" triangles
       * (otherwise we've goofed up somewhere).
       */
      CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN ); 
      CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
      CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
      while( ! Marked( e->Lface )) {
        e->Lface->marked = TRUE;
        --size;
        e = e->Onext;
        CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
      }
      assert( size == 0 );
      CALL_END_OR_END_DATA();
    }
    static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size )
    {
      /* Render as many CCW triangles as possible in a strip starting from
       * edge "e".  The strip *should* contain exactly "size" triangles
       * (otherwise we've goofed up somewhere).
       */
      CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP );
      CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
      CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
      while( ! Marked( e->Lface )) {
        e->Lface->marked = TRUE;
        --size;
        e = e->Dprev;
        CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
        if( Marked( e->Lface )) break;
        e->Lface->marked = TRUE;
        --size;
        e = e->Onext;
        CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
      }
      assert( size == 0 );
      CALL_END_OR_END_DATA();
    }
    /************************ Boundary contour decomposition ******************/
    /* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one
     * contour for each face marked "inside".  The rendering output is
     * provided as callbacks (see the api).
     */
    void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh )
    {
      GLUface *f;
      GLUhalfEdge *e;
      for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
        if( f->inside ) {
          CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP );
          e = f->anEdge;
          do {
            CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
    	e = e->Lnext;
          } while( e != f->anEdge );
          CALL_END_OR_END_DATA();
        }
      }
    }
    /************************ Quick-and-dirty decomposition ******************/
    #define SIGN_INCONSISTENT 2
    static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
    /*
     * If check==FALSE, we compute the polygon normal and place it in norm[].
     * If check==TRUE, we check that each triangle in the fan from v0 has a
     * consistent orientation with respect to norm[].  If triangles are
     * consistently oriented CCW, return 1; if CW, return -1; if all triangles
     * are degenerate return 0; otherwise (no consistent orientation) return
     * SIGN_INCONSISTENT.
     */
    {
      CachedVertex *v0 = tess->cache;
      CachedVertex *vn = v0 + tess->cacheCount;
      CachedVertex *vc;
      GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
      int sign = 0;
      /* Find the polygon normal.  It is important to get a reasonable
       * normal even when the polygon is self-intersecting (eg. a bowtie).
       * Otherwise, the computed normal could be very tiny, but perpendicular
       * to the true plane of the polygon due to numerical noise.  Then all
       * the triangles would appear to be degenerate and we would incorrectly
       * decompose the polygon as a fan (or simply not render it at all).
       *
       * We use a sum-of-triangles normal algorithm rather than the more
       * efficient sum-of-trapezoids method (used in CheckOrientation()
       * in normal.c).  This lets us explicitly reverse the signed area
       * of some triangles to get a reasonable normal in the self-intersecting
       * case.
       */
      if( ! check ) {
        norm[0] = norm[1] = norm[2] = 0.0;
      }
      vc = v0 + 1;
      xc = vc->coords[0] - v0->coords[0];
      yc = vc->coords[1] - v0->coords[1];
      zc = vc->coords[2] - v0->coords[2];
      while( ++vc < vn ) {
        xp = xc; yp = yc; zp = zc;
        xc = vc->coords[0] - v0->coords[0];
        yc = vc->coords[1] - v0->coords[1];
        zc = vc->coords[2] - v0->coords[2];
        /* Compute (vp - v0) cross (vc - v0) */
        n[0] = yp*zc - zp*yc;
        n[1] = zp*xc - xp*zc;
        n[2] = xp*yc - yp*xc;
        dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
        if( ! check ) {
          /* Reverse the contribution of back-facing triangles to get
           * a reasonable normal for self-intersecting polygons (see above)
           */
          if( dot >= 0 ) {
    	norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2];
          } else {
    	norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2];
          }
        } else if( dot != 0 ) {
          /* Check the new orientation for consistency with previous triangles */
          if( dot > 0 ) {
    	if( sign < 0 ) return SIGN_INCONSISTENT;
    	sign = 1;
          } else {
    	if( sign > 0 ) return SIGN_INCONSISTENT;
    	sign = -1;
          }
        }
      }
      return sign;
    }
    /* __gl_renderCache( tess ) takes a single contour and tries to render it
     * as a triangle fan.  This handles convex polygons, as well as some
     * non-convex polygons if we get lucky.
     *
     * Returns TRUE if the polygon was successfully rendered.  The rendering
     * output is provided as callbacks (see the api).
     */
    GLboolean __gl_renderCache( GLUtesselator *tess )
    {
      CachedVertex *v0 = tess->cache;
      CachedVertex *vn = v0 + tess->cacheCount;
      CachedVertex *vc;
      GLdouble norm[3];
      int sign;
      if( tess->cacheCount < 3 ) {
        /* Degenerate contour -- no output */
        return TRUE;
      }
      norm[0] = tess->normal[0];
      norm[1] = tess->normal[1];
      norm[2] = tess->normal[2];
      if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
        ComputeNormal( tess, norm, FALSE );
      }
      sign = ComputeNormal( tess, norm, TRUE );
      if( sign == SIGN_INCONSISTENT ) {
        /* Fan triangles did not have a consistent orientation */
        return FALSE;
      }
      if( sign == 0 ) {
        /* All triangles were degenerate */
        return TRUE;
      }
      /* Make sure we do the right thing for each winding rule */
      switch( tess->windingRule ) {
      case GLU_TESS_WINDING_ODD:
      case GLU_TESS_WINDING_NONZERO:
        break;
      case GLU_TESS_WINDING_POSITIVE:
        if( sign < 0 ) return TRUE;
        break;
      case GLU_TESS_WINDING_NEGATIVE:
        if( sign > 0 ) return TRUE;
        break;
      case GLU_TESS_WINDING_ABS_GEQ_TWO:
        return TRUE;
      }
      CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
    			  : (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
    			  : GL_TRIANGLES );
      CALL_VERTEX_OR_VERTEX_DATA( v0->data ); 
      if( sign > 0 ) {
        for( vc = v0+1; vc < vn; ++vc ) {
          CALL_VERTEX_OR_VERTEX_DATA( vc->data ); 
        }
      } else {
        for( vc = vn-1; vc > v0; --vc ) {
          CALL_VERTEX_OR_VERTEX_DATA( vc->data ); 
        }
      }
      CALL_END_OR_END_DATA();
      return TRUE;
    }

    Mais ce qui est dommage, c'est que dans le code de LibTess, des régions sont triangularisés en Triangle Fan :

    https://github.com/memononen/libtess.../Source/tess.c

    extrait de code de tess.c (voyez le commentaire et la dernière partie de la fonction tessMeshTessellateMonoRegion

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    /* Each step consists of adding the rightmost unprocessed vertex to one
    * of the two chains, and forming a fan of triangles from the rightmost
    * of two chain endpoints.  Determining whether we can add each triangle
    * to the fan is a simple orientation test.  By making the fan as large
    * as possible, we restore the invariant (check it yourself).
    */
     
    int tessMeshTessellateMonoRegion( TESSmesh *mesh, TESSface *face )
    {
    	TESShalfEdge *up, *lo;
     
    	/* All edges are oriented CCW around the boundary of the region.
    	* First, find the half-edge whose origin vertex is rightmost.
    	* Since the sweep goes from left to right, face->anEdge should
    	* be close to the edge we want.
    	*/
    	up = face->anEdge;
    	assert( up->Lnext != up && up->Lnext->Lnext != up );
     
    	for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev )
    		;
    	for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext )
    		;
    	lo = up->Lprev;
     
    	while( up->Lnext != lo ) {
    		if( VertLeq( up->Dst, lo->Org )) {
    			/* up->Dst is on the left.  It is safe to form triangles from lo->Org.
    			* The EdgeGoesLeft test guarantees progress even when some triangles
    			* are CW, given that the upper and lower chains are truly monotone.
    			*/
    			while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext )
    				|| EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) {
    					TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, lo->Lnext, lo );
    					if (tempHalfEdge == NULL) return 0;
    					lo = tempHalfEdge->Sym;
    			}
    			lo = lo->Lprev;
    		} else {
    			/* lo->Org is on the left.  We can make CCW triangles from up->Dst. */
    			while( lo->Lnext != up && (EdgeGoesRight( up->Lprev )
    				|| EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) {
    					TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, up, up->Lprev );
    					if (tempHalfEdge == NULL) return 0;
    					up = tempHalfEdge->Sym;
    			}
    			up = up->Lnext;
    		}
    	}
     
    	/* Now lo->Org == up->Dst == the leftmost vertex.  The remaining region
    	* can be tessellated in a fan from this leftmost vertex.
    	*/
    	assert( lo->Lnext != up );
    	while( lo->Lnext->Lnext != up ) {
    		TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, lo->Lnext, lo );
    		if (tempHalfEdge == NULL) return 0;
    		lo = tempHalfEdge->Sym;
    	}
     
    	return 1;
    }
    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

Discussions similaires

  1. Réponses: 12
    Dernier message: 25/06/2006, 23h24
  2. Réponses: 1
    Dernier message: 07/06/2006, 18h56
  3. Réponses: 10
    Dernier message: 16/11/2005, 08h33
  4. problème avec strtok pour récupérer les vides
    Par manikou dans le forum MFC
    Réponses: 4
    Dernier message: 02/06/2005, 20h08
  5. Requete select pour récupérer les no match entre 2 tables
    Par Celina dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 16/12/2003, 11h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo