FreeWRL/FreeX3D  3.0.0
Tess.c
1 /*
2 
3 
4 ???
5 
6 */
7 
8 /****************************************************************************
9  This file is part of the FreeWRL/FreeX3D Distribution.
10 
11  Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
12 
13  FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
14  it under the terms of the GNU Lesser Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  FreeWRL/FreeX3D is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
25 ****************************************************************************/
26 
27 #include <config.h>
28 #include <system.h>
29 #include <display.h>
30 #include <internal.h>
31 
32 #include <libFreeWRL.h>
33 
34 #include "../vrml_parser/Structs.h"
35 #include "../main/headers.h"
36 
37 
38 
39 #if defined(_MSC_VER)
40 typedef void (__stdcall *_GLUfuncptr)();
41 #endif
42 
43 /* WIN32 p.411 openGL programmers guide - windows needs CALLBACK, unix not */
44 #ifndef CALLBACK
45 #define CALLBACK
46 #endif
47 
48 
49 /*********************************************************************
50  * General tessellation functions
51  *
52  * to use the tessellation function, you have to
53  * let global_tess_polyrep point towards a structure.
54  * global_tess_polyrep->ntri is the first index number which will
55  * be filled by the routines (which is the number of triangles
56  * already represented in global_tess_polyrep)
57  * global_tess_polyrep->cindex and global_tess_polyrep->coords have
58  * to point towards enough memory.
59  * (And you have to give gluTessVertex a third argument, in which
60  * the new coords are written, it has to be a vector of
61  * GLDOUBLE s with enough space)
62  * After calling gluTessEndPolygon() these vector will be filled.
63  * global_tess_polyrep->ntri will contain the absolute
64  * number of triangles in global_tess_polyrep after tessellation.
65  */
66 
67 
68 typedef struct pTess{
69  int global_IFS_Coords[TESS_MAX_COORDS]; //200,000
70 }* ppTess;
71 void *Tess_constructor(){
72  void *v = MALLOCV(sizeof(struct pTess));
73  memset(v,0,sizeof(struct pTess));
74  return v;
75 }
76 void Tess_init(struct tTess *t){
77  //public
78 //int global_IFS_Coord_count=0;
79 
80  //private
81  t->prv = Tess_constructor();
82  {
83  ppTess p = (ppTess)t->prv;
84  t->global_IFS_Coords = p->global_IFS_Coords;
85  }
86 }
87 //ppTess p = (ppTess)gglobal()->Tess.prv;
88 
89 /* OpenGL-ES 2.0 does not have tessellator */
90 /* and now all the callback functions, which will be called
91  by OpenGL automatically, if the Polygon is specified */
92 
93 void CALLBACK FW_tess_begin(GLenum e) {
94  /*printf(" FW_tess_begin e = %s\n", (e == GL_TRIANGLES ? "GL_TRIANGLES" : "UNKNOWN")); */
95  /* we only should get GL_TRIANGLES as type, because
96  we defined the edge_flag callback */
97  /* check, if the structure is there */
98  if(e!=GL_TRIANGLES)
99  freewrlDie("Something went wrong while tessellating!");
100 }
101 
102 void CALLBACK FW_tess_end(void) {
103  /*printf("FW_tess_end: Tesselation done.\n"); */
104  /* nothing to do */
105 }
106 
107 void CALLBACK FW_tess_edgeflag(GLenum flag) {
108  /*printf("FW_tess_edgeflag: An edge was done (flag = %d).\n", flag); */
109  /* nothing to do, this function has to be registered
110  so that only GL_TRIANGLES are used */
111 }
112 
113 void CALLBACK FW_IFS_tess_vertex(void *p) {
114  int *dp;
115  ttglobal tg = gglobal();
116  dp =(int*)p;
117 
118  if (tg->Tess.global_IFS_Coord_count == TESS_MAX_COORDS) {
119  /* printf ("FW_IFS_tess_vertex, too many coordinates in this face, change TESS_MAX_COORDS\n"); */
120  /*
121  global_IFS_Coord_count++;
122  global_IFS_Coords[global_IFS_Coord_count] =
123  global_IFS_Coords[global_IFS_Coord_count-1];
124  */
125  } else {
126  //printf ("FW_IFS_tess_vertex, global_ifs_coord count %d, pointer %d\n",tg->Tess.global_IFS_Coord_count,*dp);
127  //if(*dp < 0){
128  // printf("dp pointer = %p\n",dp);
129  //}
130  tg->Tess.global_IFS_Coords[tg->Tess.global_IFS_Coord_count++] = *dp;
131  }
132 
133 }
134 
135 void CALLBACK FW_tess_error(GLenum e) {
136  /* Prints out tesselation errors. Older versions of at least MESA would
137  give errors, so for now at least, lets just ignore them.
138  */
139  printf("FW_tess_error %d: >%s<\n",(int) e,GL_ERROR_MSG);
140 }
141 
142 
143 
144 void CALLBACK FW_tess_combine_text_data (GLDOUBLE c[3], GLfloat *d[4], GLfloat w[4], void **out,void *polygondata) {
145 /* Component_Text Combiner
146  printf("FW_tess_combine data\n");
147  printf("combine c:%lf %lf %lf\ndw: %f %f %f %f\n\n",
148  c[0],c[1],c[2],w[0],w[1],w[2],w[3]);
149  printf ("vertex 0 %lf %lf %lf, 1 %lf %lf %lf, 2 %lf %lf %lf, 3 %lf %lf %lf\n",
150  *d[0]->x,*d[0]->y,*d[0]->z,
151  *d[1]->x,*d[1]->y,*d[1]->z,
152  *d[2]->x,*d[2]->y,*d[2]->z,
153  *d[3]->x,*d[3]->y,*d[3]->z);
154 
155  printf ("d %d %d %d %d\n",d[0],d[1],d[2],d[3]);
156  printf ("d %f %f %f %f\n",*d[0],*d[1],*d[2],*d[3]);
157  printf ("new coord %d\n",nv);
158 */
159  if(0){
160  GLDOUBLE *nv = MALLOC(GLDOUBLE *, sizeof(GLDOUBLE)*3);
161 
162  nv[0] = c[0];
163  nv[1] = c[1];
164  nv[2] = c[2];
165  *out = nv;
166  }else{
167  int FW_pointctr, RAI_indx;
168  text_combiner_data *cbdata;
169  float *coords;
170  //GLDOUBLE *nv = MALLOC(GLDOUBLE *, sizeof(GLDOUBLE)*6);
171  ttglobal tg = gglobal();
172  cbdata = (text_combiner_data*) polygondata;
173 
174  //OpenGL Redbook says we must malloc a new point.
175  //but in our Component_Text system, that just means adding it to our
176  //over-malloced list of points actualCoords[]
177  // and to a few other lists of indexes etc as we do in FW_NewVertexPoint() in Component_Text
178  FW_pointctr = *(cbdata->counter);
179  RAI_indx = *(cbdata->riaindex);
180  tg->Tess.global_IFS_Coords[RAI_indx] = FW_pointctr;
181  coords = (float *)cbdata->coords;
182  coords[FW_pointctr*3+0] = (float)c[0];
183  coords[FW_pointctr*3+1] = (float)c[1];
184  coords[FW_pointctr*3+2] = (float)c[2];
185  cbdata->ria[(*cbdata->riaindex)] = FW_pointctr;
186  *out = &cbdata->ria[(*cbdata->riaindex)]; //tell FW_IFS_tess_vertex the index of the new point
187  //printf("combiner, out pointer = %p nv pointer = %p\n",out,*out);
188  //THE SECRET TO COMBINDER SUCCESS? *out == (p) in FW_IFS_tess_vertex(void *p)
189  *(cbdata->counter) = FW_pointctr + 1;
190  (*cbdata->riaindex)++;
191  }
192 }
193 
194 void CALLBACK FW_tess_combine_polyrep_data (GLDOUBLE c[3], GLfloat *d[4], GLfloat w[4], void **out,void *polygondata) {
195 /* PolyRep Combiner (not properly implemented as of Aug 5, 2016)
196  printf("FW_tess_combine data\n");
197  printf("combine c:%lf %lf %lf\ndw: %f %f %f %f\n\n",
198  c[0],c[1],c[2],w[0],w[1],w[2],w[3]);
199  printf ("vertex 0 %lf %lf %lf, 1 %lf %lf %lf, 2 %lf %lf %lf, 3 %lf %lf %lf\n",
200  *d[0]->x,*d[0]->y,*d[0]->z,
201  *d[1]->x,*d[1]->y,*d[1]->z,
202  *d[2]->x,*d[2]->y,*d[2]->z,
203  *d[3]->x,*d[3]->y,*d[3]->z);
204 
205  printf ("d %d %d %d %d\n",d[0],d[1],d[2],d[3]);
206  printf ("d %f %f %f %f\n",*d[0],*d[1],*d[2],*d[3]);
207  printf ("new coord %d\n",nv);
208 */
209  if(1){
210  GLDOUBLE *nv = MALLOC(GLDOUBLE *, sizeof(GLDOUBLE)*3);
211 
212  nv[0] = c[0];
213  nv[1] = c[1];
214  nv[2] = c[2];
215  *out = nv;
216  // doesn't render right: http://dug9.users.sourceforge.net/web3d/tests/CAD/test_IFS_concave_combiner.x3d
217  /*
218  geometry DEF FUNNYU IndexedFaceSet {
219  convex FALSE
220  solid FALSE
221  coordIndex [ 0 1 2 3 4 5 6 7 -1]
222  coord Coordinate {
223  # x-swap-x inner bottom u point criss crossed to force combiner
224  point [ -2 -2 0, -2 2 0, -1 2 0, 1 -1 0, -1 -1 0, 1 2 0, 2 2 0, 2 -2 0,]
225  }
226  }
227  */
228 
229  }else{
230  //Aug 3, 2016 this doesn't work, didn't pick through polyrep, don't use.
231  /*
232  Current polyrep Algo: ignor opengl tips on combiner, and instead try and capture the index into
233  the original node coord, texcoord, normal via
234  tg->Tess.global_IFS_Coords[tg->Tess.global_IFS_Coord_count++] = *dp;
235  in the vertex callback, as we do for Text
236  Complication: when adding a point, the result may be more triangles, for which there needs to be more
237  normals and texcoords etc.
238 
239  Hypothesis: the node orig-to-triangle approach in genpolyrep was to save memory back in 2003. We don't need it now.
240  Proposed polyrep algo A:
241  1. copy node orig data to packed
242  a) de-index
243  b) convert to double for tess
244  c) pack ie [double xyz float rgb float norm float texcoord] for tess, in over-malloced packed array
245  2. tesselate
246  a) add combiner generated pack-points to the bottom of packed array
247  b) out= weighted combined as redbook shows
248  3. copy tesselated to polyrep
249  a) convert to float
250  b) un-pack
251  c) copy unpacked to polyrep for shader
252 
253  Proposed polyrep algo B:
254  1. in combiner, malloc combiner points, texcoords, normals, color-per-vertex on extension arrays
255  pass index into extension arrays to *out with a -ve sentinal value, for capture by global_IFS_Coords[] in vertex callback
256  2. in make_polyrep and make_extrusion, when using global_IFS_Coords[] array, watch for -ve index and
257  de-index from the extension arrays
258 
259  Proposed polyrep algo C?
260  - when setting the vertexes, instead of giving [double xyz float rgb], give [double xyz int index]
261  - then in here, the 4 float *d points coming in will deliver index.
262  - then (somehow) use those indexes
263 
264 
265  */
266  //polyrep_combiner_data *cbdata;
267  }
268 }
269 
270 /* Some tesselators will give back garbage. Lets try and remove it */
271 /* Text handles errors better itself, so this is just used for Extrusions and IndexedFaceSets */
272 void verify_global_IFS_Coords(int max) {
273  int count;
274  ttglobal tg = gglobal();
275 
276  for (count = 0; count < tg->Tess.global_IFS_Coord_count; count++) {
277  /*printf ("verifying count %d; val is %d, max %d\n",
278  count,global_IFS_Coords[count],max); */
279  if ((tg->Tess.global_IFS_Coords[count] < 0) ||
280  (tg->Tess.global_IFS_Coords[count] >= max)) {
281 
282  if (count == 0) {
283  tg->Tess.global_IFS_Coords[count] = 0;
284  } else {
285  tg->Tess.global_IFS_Coords[count] = tg->Tess.global_IFS_Coords[count-1];
286  }
287 
288  }
289  }
290 }
291 
292 void CALLBACK FW_tess_combine (GLDOUBLE c[3], void *d[4], GLfloat w[4], void **out) {
293  GLDOUBLE *nv = MALLOC(GLDOUBLE *, sizeof(GLDOUBLE)*3);
294  /*printf("FW_tess_combine c:%lf %lf %lf\ndw: %f %f %f %f\n\n",
295  c[0],c[1],c[2],w[0],w[1],w[2],w[3]); */
296  nv[0] = c[0];
297  nv[1] = c[1];
298  nv[2] = c[2];
299  *out = nv;
300 }
301 
302 
303 /* next function has to be called once, after an OpenGL context is made
304  and before tessellation is started */
305 
306 void CALLBACK XXtessA() { printf ("GLU_TESS_BEGIN\n"); }
307 void CALLBACK XXtessB() { printf ("GLU_TESS_BEGIN_DATA\n"); }
308 void CALLBACK XXtessC() { printf ("GLU_TESS_EDGE\n"); }
309 void CALLBACK XXtessD() { printf ("GLU_TESS_EDGE_FLAG_DATA\n"); }
310 void CALLBACK XXtessE() { printf ("GLU_TESS_VERTEX\n"); }
311 void CALLBACK XXtessF() { printf ("GLU_TESS_VERTEX_DATA\n"); }
312 void CALLBACK XXtessG() { printf ("GLU_TESS_END\n"); }
313 void CALLBACK XXtessH() { printf ("GLU_TESS_END_DATA\n"); }
314 void CALLBACK XXtessI() { printf ("GLU_TESS_COMBINE_DATA\n"); }
315 void CALLBACK XXtessJ() { printf ("GLU_TESS_ERROR\n"); }
316 void CALLBACK XXtessK() { printf ("GLU_TESS_ERROR_DATA\n"); }
317 
318 
319 void new_tessellation(void) {
320  ttglobal tg = gglobal();
321  tg->Tess.global_tessobj=FW_GLU_NEW_TESS();
322  if(!tg->Tess.global_tessobj)
323  freewrlDie("Got no memory for Tessellation Object!");
324 
325  /* register the CallBackfunctions */
326  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj,GLU_TESS_BEGIN,(_GLUfuncptr)FW_tess_begin);
327  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj,GLU_TESS_EDGE_FLAG,(_GLUfuncptr)FW_tess_edgeflag);
328  //FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj,GLU_VERTEX,(_GLUfuncptr)FW_IFS_tess_vertex);
329  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj,GLU_TESS_VERTEX,(_GLUfuncptr)FW_IFS_tess_vertex);
330  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj,GLU_TESS_ERROR,(_GLUfuncptr)FW_tess_error);
331  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj,GLU_TESS_END,(_GLUfuncptr)FW_tess_end);
332  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj, GLU_TESS_COMBINE_DATA,(_GLUfuncptr)FW_tess_combine_polyrep_data); //default combiner, Text must reset to this after doing its own FW_tess_combine_text_data
333  //FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj, GLU_TESS_COMBINE,(_GLUfuncptr)FW_tess_combine);
334 
335  /* Unused right now. */
336 /*
337  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_BEGIN, (_GLUfuncptr)XXtessA);
338  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_BEGIN_DATA,(_GLUfuncptr)XXtessB);
339  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_EDGE_FLAG,(_GLUfuncptr)XXtessC);
340  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_EDGE_FLAG_DATA,(_GLUfuncptr)XXtessD);
341  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_VERTEX,(_GLUfuncptr)XXtessE);
342  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_VERTEX_DATA,(_GLUfuncptr)XXtessF);
343  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_END,(_GLUfuncptr)XXtessG);
344  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_END_DATA,(_GLUfuncptr)XXtessH);
345  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_COMBINE_DATA,(_GLUfuncptr)XXtessI);
346  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_ERROR,(_GLUfuncptr)XXtessJ);
347  FW_GLU_TESS_CALLBACK(global_tessobj, GLU_TESS_ERROR_DATA,(_GLUfuncptr)XXtessK);
348 */
349 /* */
350 }
351 void register_Text_combiner(){
352  //called before tesselating Text in Component_Text.c
353  ttglobal tg = gglobal();
354  if(tg->Tess.global_tessobj){
355  //FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj, GLU_TESS_COMBINE_DATA,(_GLUfuncptr)NULL);
356  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj, GLU_TESS_COMBINE_DATA,(_GLUfuncptr)FW_tess_combine_text_data);
357  }
358 }
359 void register_Polyrep_combiner(){
360  //called after tesselating Text in Component_Text.c, so in make_polyrep and make_extrusion in GenPolyrep.c this will be the default
361  ttglobal tg = gglobal();
362  if(tg->Tess.global_tessobj){
363  //FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj, GLU_TESS_COMBINE_DATA,(_GLUfuncptr)NULL);
364  FW_GLU_TESS_CALLBACK(tg->Tess.global_tessobj, GLU_TESS_COMBINE_DATA,(_GLUfuncptr)FW_tess_combine_polyrep_data);
365  }
366 }
367 /* next function should be called once at the end, but where? */
368 void destruct_tessellation(void) {
369  ttglobal tg = gglobal();
370  FW_GLU_DELETETESS(tg->Tess.global_tessobj);
371  printf("Tessellation Object deleted!\n");
372 }
373 
Definition: Tess.c:68