ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/hd/sm_ogl.c
Revision: 3.18
Committed: Mon Jul 21 22:30:18 2003 UTC (20 years, 9 months ago) by schorsch
Content type: text/plain
Branch: MAIN
CVS Tags: rad3R6P1, rad3R6
Changes since 3.17: +2 -2 lines
Log Message:
Eliminated copystruct() macro, which is unnecessary in ANSI.
Reduced ambiguity warnings for nested if/if/else clauses.

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: sm_ogl.c,v 3.17 2003/06/20 00:25:49 greg Exp $";
3 #endif
4 /*
5 * sm_ogl.c
6 *
7 * Rendering routines for triangle mesh representation utilizing OpenGL
8 *
9 * smClean(tmflag) : display has been wiped clean
10 * int tmflag;
11 * Called after display has been effectively cleared, meaning that all
12 * geometry must be resent down the pipeline in the next call to smUpdate().
13 * If tmflag is set, tone-mapping should be performed
14 *
15 * smUpdate(vp, qua) : update OpenGL output geometry for view vp
16 * VIEW *vp; : desired view
17 * int qua; : quality level (percentage on linear time scale)
18 *
19 * Draw new geometric representation using OpenGL calls. Assume that the
20 * view has already been set up and the correct frame buffer has been
21 * selected for drawing. The quality level is on a linear scale, where 100%
22 * is full (final) quality. It is not necessary to redraw geometry that has
23 * been output since the last call to smClean(). (The last view drawn will
24 * be vp==&odev.v each time.)
25 */
26 #include "standard.h"
27
28 #include <GL/gl.h>
29
30 #include "sm_flag.h"
31 #include "sm_list.h"
32 #include "sm_geom.h"
33 #include "sm.h"
34
35 int smClean_notify = TRUE; /*If TRUE:Do full redraw on next update*/
36 static int smCompute_mapping = TRUE; /*If TRUE:re-tonemap on next update */
37 static int smIncremental = FALSE; /*If TRUE: there has been incremental
38 rendering since last full draw */
39 #define NEW_TRI_CNT 1000 /* Default number of tris to allocate
40 space to sort for incremental update*/
41 #define SM_RENDER_FG 0 /* Render foreground tris only*/
42 #define SM_RENDER_BG 1 /* Render background tris only */
43 #define SM_RENDER_CULL 8 /* Perform view frustum culling */
44 #define BASE 1 /* Indicates base triangle */
45 #define DIR 2 /* Indicates triangle w/directional pts*/
46 /* FOR DISPLAY LIST RENDERING: **********************************************/
47 #define SM_DL_LEVELS 2 /* # of levels down to create display lists */
48 #define SM_DL_LISTS 42 /* # of qtree nodes in tree at above level:
49 should be 2*(4^(SM_DL_LEVELS+1)-1)/(4-1) */
50 static GLuint Display_lists[SM_DL_LISTS][2] = {0};
51 /****************************************************************************/
52
53 /* FOR APPROXIMATION RENDERING **********************************************/
54 typedef struct {
55 float dist; /* average distance */
56 BYTE rgb[3]; /* average color */
57 } QTRAVG; /* average quadtree value */
58
59 typedef struct {
60 QUADTREE qt; /* quadtree node (key & hash value) */
61 QTRAVG av; /* node average */
62 } QT_LUENT; /* lookup table entry */
63
64 static QT_LUENT *qt_htbl = NULL; /* quadtree cache */
65 static int qt_hsiz = 0; /* quadtree cache size */
66 /****************************************************************************/
67
68 /* For DEPTH SORTING ********************************************************/
69 typedef struct _T_DEPTH {
70 int tri;
71 double depth;
72 }T_DEPTH;
73 /**********************************************************************/
74
75 /*
76 * smClean(tmflag) : display has been wiped clean
77 * int tmflag;
78 * Called after display has been effectively cleared, meaning that all
79 * geometry must be resent down the pipeline in the next call to smUpdate().
80 * If tmflag is set, tone-mapping should be performed
81 */
82 smClean(tmflag)
83 int tmflag;
84 {
85 smClean_notify = TRUE;
86 smCompute_mapping = tmflag;
87 }
88
89 int
90 qtCache_init(nel) /* initialize for at least nel elements */
91 int nel;
92 {
93 static int hsiztab[] = {
94 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 0
95 };
96 register int i;
97
98 if (nel <= 0) { /* call to free table */
99 if (qt_hsiz) {
100 free((void *)qt_htbl);
101 qt_htbl = NULL;
102 qt_hsiz = 0;
103 }
104 return(0);
105 }
106 nel += nel>>1; /* 66% occupancy */
107 for (i = 0; hsiztab[i]; i++)
108 if (hsiztab[i] > nel)
109 break;
110 if (!(qt_hsiz = hsiztab[i]))
111 qt_hsiz = nel*2 + 1; /* not always prime */
112 qt_htbl = (QT_LUENT *)calloc(qt_hsiz, sizeof(QT_LUENT));
113 if (qt_htbl == NULL)
114 qt_hsiz = 0;
115 for (i = qt_hsiz; i--; )
116 qt_htbl[i].qt = EMPTY;
117 return(qt_hsiz);
118 }
119
120 QT_LUENT *
121 qtCache_find(qt) /* find a quadtree table entry */
122 QUADTREE qt;
123 {
124 int i, n;
125 register int ndx;
126 register QT_LUENT *le;
127
128 if (qt_hsiz == 0 && !qtCache_init(1))
129 return(NULL);
130 tryagain: /* hash table lookup */
131 ndx = (unsigned long)qt % qt_hsiz;
132 for (i = 0, n = 1; i < qt_hsiz; i++, n += 2) {
133 le = &qt_htbl[ndx];
134 if (QT_IS_EMPTY(le->qt) || le->qt == qt)
135 return(le);
136 if ((ndx += n) >= qt_hsiz) /* this happens rarely */
137 ndx = ndx % qt_hsiz;
138 }
139 /* table is full, reallocate */
140 le = qt_htbl;
141 ndx = qt_hsiz;
142 if (!qtCache_init(ndx+1)) { /* no more memory! */
143 qt_htbl = le;
144 qt_hsiz = ndx;
145 return(NULL);
146 }
147 /* copy old table to new and free */
148 while (ndx--)
149 if (!QT_IS_EMPTY(le[ndx].qt))
150 *qtCache_find(le[ndx].qt) = le[ndx];
151 free((void *)le);
152 goto tryagain; /* should happen only once! */
153 }
154
155 stCount_level_leaves(lcnt, qt) /* count quadtree leaf nodes at each level */
156 int lcnt[];
157 register QUADTREE qt;
158 {
159 if (QT_IS_EMPTY(qt))
160 return;
161 if (QT_IS_TREE(qt)) {
162 if (!QT_IS_FLAG(qt)) /* not in our frustum */
163 return;
164 stCount_level_leaves(lcnt+1, QT_NTH_CHILD(qt,0));
165 stCount_level_leaves(lcnt+1, QT_NTH_CHILD(qt,1));
166 stCount_level_leaves(lcnt+1, QT_NTH_CHILD(qt,2));
167 stCount_level_leaves(lcnt+1, QT_NTH_CHILD(qt,3));
168 }
169 else
170 if(QT_LEAF_IS_FLAG(qt))
171 lcnt[0]++;
172 }
173
174 QTRAVG *
175 qtRender_level(qt,v0,v1,v2,sm,lvl)
176 QUADTREE qt;
177 FVECT v0,v1,v2;
178 SM *sm;
179 int lvl;
180 {
181 FVECT a,b,c;
182 register QT_LUENT *le;
183 QTRAVG *rc[4];
184 TRI *tri;
185
186 if (QT_IS_EMPTY(qt)) /* empty leaf node */
187 return(NULL);
188 if (QT_IS_TREE(qt) && !QT_IS_FLAG(qt)) /* not in our frustum */
189 return(NULL);
190 if(QT_IS_LEAF(qt) && !QT_LEAF_IS_FLAG(qt)) /* not in our frustum */
191 return(NULL);
192 /* else look up node */
193 if ((le = qtCache_find(qt)) == NULL)
194 error(SYSTEM, "out of memory in qtRender_level");
195 if (QT_IS_TREE(qt) && (QT_IS_EMPTY(le->qt) || lvl > 0))
196 { /* compute children */
197 qtSubdivide_tri(v0,v1,v2,a,b,c);
198 rc[0] = qtRender_level(QT_NTH_CHILD(qt,0),v0,c,b,sm,lvl-1);
199 rc[1] = qtRender_level(QT_NTH_CHILD(qt,1),c,v1,a,sm,lvl-1);
200 rc[2] = qtRender_level(QT_NTH_CHILD(qt,2),b,a,v2,sm,lvl-1);
201 rc[3] = qtRender_level(QT_NTH_CHILD(qt,3),a,b,c,sm,lvl-1);
202 }
203 if (QT_IS_EMPTY(le->qt))
204 { /* let's make some data! */
205 int rgbs[3];
206 double distsum;
207 register int i, n;
208 /* average our triangle vertices */
209 rgbs[0] = rgbs[1] = rgbs[2] = 0;
210 distsum = 0.; n = 0;
211 if(QT_IS_TREE(qt))
212 { /* from subtree */
213 for (i = 4; i--; )
214 if (rc[i] != NULL)
215 {
216 rgbs[0] += rc[i]->rgb[0]; rgbs[1] += rc[i]->rgb[1];
217 rgbs[2] += rc[i]->rgb[2]; distsum += rc[i]->dist; n++;
218 }
219 }
220 else
221 { /* from triangle set */
222 S_ID *os;
223 S_ID s0, s1, s2,s_id;
224 int t_id;
225 TRI *tri,*t;
226
227 os = qtqueryset(qt);
228 for (i = os[0]; i; i--)
229 {
230 s_id = os[i];
231 t_id = SM_NTH_VERT(smMesh,s_id);
232 tri = t = SM_NTH_TRI(smMesh,t_id);
233 do
234 {
235 if(!SM_IS_NTH_T_BASE(sm,t_id))
236 {
237 n++;
238 s0 = T_NTH_V(t,0);
239 s1 = T_NTH_V(t,1);
240 s2 = T_NTH_V(t,2);
241 VCOPY(a,SM_NTH_WV(sm,s0));
242 VCOPY(b,SM_NTH_WV(sm,s1));
243 VCOPY(c,SM_NTH_WV(sm,s2));
244 distsum += SM_BG_SAMPLE(sm,s0) ? dev_zmax
245 : sqrt(dist2(a,SM_VIEW_CENTER(sm)));
246 distsum += SM_BG_SAMPLE(sm,s1) ? dev_zmax
247 : sqrt(dist2(b,SM_VIEW_CENTER(sm)));
248 distsum += SM_BG_SAMPLE(sm,s2) ? dev_zmax
249 : sqrt(dist2(c,SM_VIEW_CENTER(sm)));
250 rgbs[0] += SM_NTH_RGB(sm,s0)[0] + SM_NTH_RGB(sm,s1)[0]
251 + SM_NTH_RGB(sm,s2)[0];
252 rgbs[1] += SM_NTH_RGB(sm,s0)[1] + SM_NTH_RGB(sm,s1)[1]
253 + SM_NTH_RGB(sm,s2)[1];
254 rgbs[2] += SM_NTH_RGB(sm,s0)[2] + SM_NTH_RGB(sm,s1)[2]
255 + SM_NTH_RGB(sm,s2)[2];
256 }
257
258 t_id = smTri_next_ccw_nbr(smMesh,t,s_id);
259 t = SM_NTH_TRI(smMesh,t_id);
260
261 }while(t != tri);
262 }
263 n *= 3;
264 }
265 if (!n)
266 return(NULL);
267 le->qt = qt;
268 le->av.rgb[0] = rgbs[0]/n; le->av.rgb[1] = rgbs[1]/n;
269 le->av.rgb[2] = rgbs[2]/n; le->av.dist = distsum/(double)n;
270 }
271 if (lvl == 0 || (lvl > 0 && QT_IS_LEAF(qt)))
272 { /* render this node */
273 /* compute pseudo vertices */
274 VCOPY(a,v0); VCOPY(b,v1); VCOPY(c,v2);
275 normalize(a); normalize(b); normalize(c);
276 VSUM(a,SM_VIEW_CENTER(sm),a,le->av.dist);
277 VSUM(b,SM_VIEW_CENTER(sm),b,le->av.dist);
278 VSUM(c,SM_VIEW_CENTER(sm),c,le->av.dist);
279 /* draw triangle */
280 glColor3ub(le->av.rgb[0],le->av.rgb[1],le->av.rgb[2]);
281 glVertex3d(a[0],a[1],a[2]);
282 glVertex3d(b[0],b[1],b[2]);
283 glVertex3d(c[0],c[1],c[2]);
284
285 }
286 return(&le->av);
287 }
288
289
290 smRender_approx_stree_level(sm,lvl)
291 SM *sm;
292 int lvl;
293 {
294 QUADTREE qt;
295 int i;
296 FVECT t0,t1,t2;
297 STREE *st;
298
299 if (lvl < 0)
300 return;
301 st = SM_LOCATOR(sm);
302 glPushAttrib(GL_LIGHTING_BIT);
303 glShadeModel(GL_FLAT);
304 glBegin(GL_TRIANGLES);
305 for(i=0; i < ST_NUM_ROOT_NODES; i++)
306 {
307 qt = ST_ROOT_QT(st,i);
308 qtRender_level(qt,ST_NTH_V(st,i,0),ST_NTH_V(st,i,1),ST_NTH_V(st,i,2),
309 sm,lvl);
310 }
311 glEnd();
312 glPopAttrib();
313 }
314
315 /*
316 * smRender_approx(sm,qual,view)
317 * SM *sm; : mesh
318 * int qual; : quality level
319 * VIEW *view; : current view
320 *
321 * Renders an approximation to the current mesh based on the quadtree
322 * subdivision. The quadtree is traversed to a level (based upon the quality:
323 * the lower the quality, the fewer levels visited, and the coarser, and
324 * faster, the approximation). The quadtree triangles are drawn relative to
325 * the current viewpoint, with a depth and color averaged from all of the
326 * triangles that lie beneath the node.
327 */
328 smRender_approx(sm, qual,view)
329 SM *sm;
330 int qual;
331 VIEW *view;
332 {
333 int i, n,ntarget;
334 int lvlcnt[QT_MAX_LEVELS];
335 STREE *st;
336 int32 *active_flag;
337
338 if (qual <= 0)
339 return;
340 smCull(sm,view,SM_ALL_LEVELS);
341 /* compute rendering target */
342 ntarget = 0;
343
344 active_flag = SM_NTH_FLAGS(sm,T_ACTIVE_FLAG);
345 for(n=((SM_NUM_TRI(sm)+31)>>5) +1; --n;)
346 if(active_flag[n])
347 for(i=0; i < 32; i++)
348 if(active_flag[n] & (1L << i))
349 ntarget++;
350
351 ntarget = ntarget*qual/MAXQUALITY;
352 if (!ntarget)
353 return;
354 for (i = QT_MAX_LEVELS; i--; )
355 lvlcnt[i] = 0;
356
357 st = SM_LOCATOR(sm);
358 for(i=0; i < ST_NUM_ROOT_NODES;i++)
359 stCount_level_leaves(lvlcnt, ST_ROOT_QT(st,i));
360
361 for (ntarget -= lvlcnt[i=0]; i < QT_MAX_LEVELS-1; ntarget -= lvlcnt[++i])
362 if (ntarget < lvlcnt[i+1])
363 break;
364 /* compute and render target level */
365 smRender_approx_stree_level(sm,i);
366 }
367
368
369 #define GLVERTEX3V(v) glVertex3fv(v)
370
371
372 #define render_tri(v0,v1,v2,rgb0,rgb1,rgb2) \
373 {glColor3ub(rgb0[0],rgb0[1],rgb0[2]); GLVERTEX3V(v0); \
374 glColor3ub(rgb1[0],rgb1[1],rgb1[2]); GLVERTEX3V(v1); \
375 glColor3ub(rgb2[0],rgb2[1],rgb2[2]); GLVERTEX3V(v2);}
376
377 /*
378 * render_mixed_tri(v0,v1,v2,rgb0,rgb1,rgb2,b0,b1,b2)
379 * SFLOAT v0[3],v1[3],v2[3]; : triangle vertex coordinates
380 * BYTE rgb0[3],rgb1[3],rgb2[3]; : vertex RGBs
381 * int b0,b1,b2; : background or base vertex flag
382 *
383 * For triangles with one or more base or directional vertices.
384 * render base vertex color as average of the background and foreground
385 * vertex RGBs. The coordinates for a fg vertex are calculated by
386 * subtracting off the current view,normalizing, then scaling to fit
387 * into the current frustum.
388 */
389 render_mixed_tri(v0,v1,v2,rgb0,rgb1,rgb2,vp,vc,bg0,bg1,bg2)
390 SFLOAT v0[3],v1[3],v2[3];
391 BYTE rgb0[3],rgb1[3],rgb2[3];
392 FVECT vp,vc;
393 int bg0,bg1,bg2;
394 {
395 double d,p[3];
396 int j,cnt,rgb[3];
397
398 /* Average color from bg vertices */
399 cnt = 0;
400 if(bg0 == BASE || bg1==BASE || bg2 == BASE)
401 {
402 rgb[0] = rgb[1] = rgb[2] = 0;
403 if(bg0 != BASE)
404 {
405 IADDV3(rgb,rgb0);
406 cnt++;
407 }
408 if(bg1 != BASE)
409 {
410 IADDV3(rgb,rgb1);
411 cnt++;
412 }
413 if(bg2 != BASE)
414 {
415 IADDV3(rgb,rgb2);
416 cnt++;
417 }
418 IDIVV3(rgb,cnt);
419 }
420 if(bg0 == BASE)
421 glColor3i(rgb[0],rgb[1],rgb[2]);
422 else
423 glColor3ub(rgb0[0],rgb0[1],rgb0[2]);
424
425 if(!bg0)
426 {
427 VSUB(p,v0,vp);
428 normalize(p);
429 IADDV3(p,vc);
430 glVertex3dv(p);
431 }
432 else
433 GLVERTEX3V(v0);
434
435 if(bg1 == BASE)
436 glColor3i(rgb[0],rgb[1],rgb[2]);
437 else
438 glColor3ub(rgb1[0],rgb1[1],rgb1[2]);
439
440 if(!bg1)
441 {
442 VSUB(p,v1,vp);
443 normalize(p);
444 IADDV3(p,vc);
445 glVertex3dv(p);
446 }
447 else
448 GLVERTEX3V(v1);
449
450 if(bg2 == BASE)
451 glColor3i(rgb[0],rgb[1],rgb[2]);
452 else
453 glColor3ub(rgb2[0],rgb2[1],rgb2[2]);
454
455 if(!bg2)
456 {
457 VSUB(p,v2,vp);
458 normalize(p);
459 IADDV3(p,vc);
460 glVertex3dv(p);
461 }
462 else
463 GLVERTEX3V(v2);
464 }
465
466 /*
467 * smRender_bg_tris(sm,vp,t_flag,bg_flag,wp,rgb)
468 * SM *sm; : mesh
469 * FVECT vp; : current viewpoint
470 * int32 *t_flag,*bg_flag; : triangle flags: t_flag is generic,
471 * and bg_flag indicates if background tri;
472 * SFLOAT (*wp)[3];BYTE (*rgb)[3]; : arrays of sample points and RGB colors
473 *
474 * Sequentially traverses triangle list and renders all valid tris who
475 * have t_flag set, and bg_flag set.
476 */
477
478 smRender_bg_tris(sm,vp,t_flag,bg_flag,wp,rgb)
479 SM *sm;
480 FVECT vp;
481 int32 *t_flag,*bg_flag;
482 SFLOAT (*wp)[3];
483 BYTE (*rgb)[3];
484 {
485 double d;
486 int v0_id,v1_id,v2_id;
487 int i,n,bg0,bg1,bg2;
488 TRI *tri;
489
490 glMatrixMode(GL_MODELVIEW);
491
492 glPushMatrix();
493 glTranslated(vp[0],vp[1],vp[2]);
494 /* The points are a distance of 1 away from the origin: if necessary scale
495 so that they fit in frustum and are therefore not clipped away
496 */
497 if(dev_zmin >= 0.99)
498 {
499 d = (dev_zmin+dev_zmax)/2.0;
500 glScaled(d,d,d);
501 }
502 /* move relative to the new view */
503 /* move points to unit sphere at origin */
504 glTranslated(-SM_VIEW_CENTER(sm)[0],-SM_VIEW_CENTER(sm)[1],
505 -SM_VIEW_CENTER(sm)[2]);
506 glBegin(GL_TRIANGLES);
507 for(n=((SM_NUM_TRI(sm)+31)>>5) +1; --n;)
508 if(t_flag[n] & bg_flag[n])
509 for(i=0; i < 32; i++)
510 if(t_flag[n] & bg_flag[n] & (1L << i))
511 {
512 tri = SM_NTH_TRI(sm,(n<<5)+i);
513 v0_id = T_NTH_V(tri,0);
514 v1_id = T_NTH_V(tri,1);
515 v2_id = T_NTH_V(tri,2);
516 bg0 = SM_DIR_ID(sm,v0_id)?DIR:SM_BASE_ID(sm,v0_id)?BASE:0;
517 bg1 = SM_DIR_ID(sm,v1_id)?DIR:SM_BASE_ID(sm,v1_id)?BASE:0;
518 bg2 = SM_DIR_ID(sm,v2_id)?DIR:SM_BASE_ID(sm,v2_id)?BASE:0;
519 if(bg0==DIR && bg1==DIR && bg2==DIR)
520 render_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],rgb[v1_id],
521 rgb[v2_id])
522 else
523 render_mixed_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],
524 rgb[v1_id],rgb[v2_id],vp,SM_VIEW_CENTER(sm),bg0,bg1,bg2);
525 }
526 glEnd();
527
528 glPopMatrix();
529
530 }
531 /*
532 * smRender_new_bg_tris(sm,vp,new_flag,active_flag,bg_flag,wp,rgb)
533 * SM *sm; : mesh
534 * FVECT vp; : current viewpoint
535 * int32 *new_flag,*active,*bg_flag; : triangle flags: idicates if tri is,
536 * new,active,bg_flag
537 * SFLOAT (*wp)[3];BYTE (*rgb)[3]; : arrays of sample points and RGB colors
538 *
539 * Sequentially traverses triangle list and renders all valid tris who
540 * have new_flag set and active_flag and bg_flag set.
541 */
542 smRender_new_bg_tris(sm,vp,new_flag,active_flag,bg_flag,wp,rgb)
543 SM *sm;
544 FVECT vp;
545 int32 *new_flag,*active_flag,*bg_flag;
546 SFLOAT (*wp)[3];
547 BYTE (*rgb)[3];
548 {
549 double d;
550 int v0_id,v1_id,v2_id;
551 int i,n,bg0,bg1,bg2;
552 TRI *tri;
553
554 glMatrixMode(GL_MODELVIEW);
555
556 glPushMatrix();
557 glTranslated(vp[0],vp[1],vp[2]);
558 /* The points are a distance of 1 away from the origin: if necessary scale
559 so that they fit in frustum and are therefore not clipped away
560 */
561 if(dev_zmin >= 0.99)
562 {
563 d = (dev_zmin+dev_zmax)/2.0;
564 glScaled(d,d,d);
565 }
566 /* move relative to the new view */
567 /* move points to unit sphere at origin */
568 glTranslated(-SM_VIEW_CENTER(sm)[0],-SM_VIEW_CENTER(sm)[1],
569 -SM_VIEW_CENTER(sm)[2]);
570 glBegin(GL_TRIANGLES);
571 for(n=((SM_NUM_TRI(sm)+31)>>5) +1; --n;)
572 if(new_flag[n] & active_flag[n] & bg_flag[n])
573 for(i=0; i < 32; i++)
574 if(new_flag[n] & active_flag[n] & bg_flag[n] & (1L << i))
575 {
576 tri = SM_NTH_TRI(sm,(n<<5)+i);
577 v0_id = T_NTH_V(tri,0);
578 v1_id = T_NTH_V(tri,1);
579 v2_id = T_NTH_V(tri,2);
580 bg0 = SM_DIR_ID(sm,v0_id)?DIR:SM_BASE_ID(sm,v0_id)?BASE:0;
581 bg1 = SM_DIR_ID(sm,v1_id)?DIR:SM_BASE_ID(sm,v1_id)?BASE:0;
582 bg2 = SM_DIR_ID(sm,v2_id)?DIR:SM_BASE_ID(sm,v2_id)?BASE:0;
583 if(bg0==DIR && bg1==DIR && bg2==DIR)
584 render_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],rgb[v1_id],
585 rgb[v2_id])
586 else
587 render_mixed_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],
588 rgb[v1_id],rgb[v2_id],vp,SM_VIEW_CENTER(sm),bg0,bg1,bg2);
589 }
590 glEnd();
591
592 glPopMatrix();
593
594 }
595
596 /*
597 * render_base_tri(v0,v1,v2,rgb0,rgb1,rgb2,vp,b0,b1,b2)
598 * SFLOAT v0[3],v1[3],v2[3]; : triangle vertex coordinates
599 * BYTE rgb0[3],rgb1[3],rgb2[3]; : vertex RGBs
600 * FVECT vp; : current viewpoint
601 * int b0,b1,b2; : vertex base flag
602 *
603 * render base vertex color as average of the non-base vertex RGBs. The
604 * base vertex coordinate is taken as the stored vector, scaled out by
605 * the average distance to the non-base vertices
606 */
607 render_base_tri(v0,v1,v2,rgb0,rgb1,rgb2,vp,b0,b1,b2)
608 SFLOAT v0[3],v1[3],v2[3];
609 BYTE rgb0[3],rgb1[3],rgb2[3];
610 FVECT vp;
611 int b0,b1,b2;
612 {
613 int cnt;
614 int rgb[3];
615 double d;
616 double p[3];
617
618 cnt = 0;
619 rgb[0] = rgb[1] = rgb[2] = 0;
620 d = 0.0;
621 /* If all vertices are base: don't render */
622 if(b0&&b1&&b2)
623 return;
624 /* First calculate color and coordinates
625 for base vertices based on world space vertices*/
626 if(!b0)
627 {
628 IADDV3(rgb,rgb0);
629 d += DIST(v0,vp);
630 cnt++;
631 }
632 if(!b1)
633 {
634 IADDV3(rgb,rgb1);
635 d += DIST(v1,vp);
636 cnt++;
637 }
638 if(!b2)
639 {
640 IADDV3(rgb,rgb2);
641 d += DIST(v2,vp);
642 cnt++;
643 }
644 IDIVV3(rgb,cnt);
645 d /= (double)cnt;
646
647 /* Now render triangle */
648 if(b0)
649 {
650 glColor3ub(rgb[0],rgb[1],rgb[2]);
651 SUBV3(p,v0,vp);
652 ISCALEV3(p,d);
653 IADDV3(p,vp);
654 glVertex3dv(p);
655 }
656 else
657 {
658 glColor3ub(rgb0[0],rgb0[1],rgb0[2]);
659 GLVERTEX3V(v0);
660 }
661 if(b1)
662 {
663 glColor3ub(rgb[0],rgb[1],rgb[2]);
664 SUBV3(p,v1,vp);
665 ISCALEV3(p,d);
666 IADDV3(p,vp);
667 glVertex3dv(p);
668 }
669 else
670 {
671 glColor3ub(rgb1[0],rgb1[1],rgb1[2]);
672 GLVERTEX3V(v1);
673 }
674 if(b2)
675 {
676 glColor3ub(rgb[0],rgb[1],rgb[2]);
677 SUBV3(p,v2,vp);
678 ISCALEV3(p,d);
679 IADDV3(p,vp);
680 glVertex3dv(p);
681 }
682 else
683 {
684 glColor3ub(rgb2[0],rgb2[1],rgb2[2]);
685 GLVERTEX3V(v2);
686 }
687 }
688 /*
689 * smRender_fg_tris(sm,vp,t_flag,bg_flag,wp,rgb)
690 * SM *sm; : mesh
691 * FVECT vp; : current viewpoint
692 * int32 *t_flag,*bg_flag; : triangle flags: t_flag is generic,bg_flag
693 * indicates if background tri;
694 * SFLOAT (*wp)[3];BYTE (*rgb)[3]; : arrays of sample points and RGB colors
695 *
696 * Sequentially gos through triangle list and renders all valid tris who
697 * have t_flag set, and NOT bg_flag set.
698 */
699 smRender_fg_tris(sm,vp,t_flag,bg_flag,wp,rgb)
700 SM *sm;
701 FVECT vp;
702 int32 *t_flag,*bg_flag;
703 SFLOAT (*wp)[3];
704 BYTE (*rgb)[3];
705 {
706 TRI *tri;
707 int i,n,b0,b1,b2;
708 S_ID v0_id,v1_id,v2_id;
709
710 glBegin(GL_TRIANGLES);
711 for(n=((SM_NUM_TRI(sm)+31)>>5) +1; --n;)
712 if(t_flag[n])
713 for(i=0; i < 32; i++)
714 if(t_flag[n] & (1L << i) & ~bg_flag[n])
715 {
716 tri = SM_NTH_TRI(sm,(n<<5)+i);
717 v0_id = T_NTH_V(tri,0);
718 v1_id = T_NTH_V(tri,1);
719 v2_id = T_NTH_V(tri,2);
720 b0 = SM_BASE_ID(sm,v0_id);
721 b1 = SM_BASE_ID(sm,v1_id);
722 b2 = SM_BASE_ID(sm,v2_id);
723 if(b0 || b1 || b2)
724 render_base_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],
725 rgb[v1_id],rgb[v2_id],SM_VIEW_CENTER(sm),b0,b1,b2);
726 else
727 render_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],rgb[v1_id],
728 rgb[v2_id])
729
730 }
731 glEnd();
732
733 }
734
735 /*
736 * smRender_new_fg_tris(sm,vp,new_flag,active_flag,bg_flag,wp,rgb)
737 * SM *sm; : mesh
738 * FVECT vp; : current viewpoint
739 * int32 *new_flag,*active_flag,*bg_flag; : triangle flags: indicate if
740 * tri is new,active,background
741 * SFLOAT (*wp)[3];BYTE (*rgb)[3]; : arrays of sample points and RGB colors
742 *
743 * Sequentially gos through triangle list and renders all valid tris who
744 * have new_flag and active_flag set, and NOT bg_flag set.
745 */
746 smRender_new_fg_tris(sm,vp,new_flag,active_flag,bg_flag,wp,rgb)
747 SM *sm;
748 FVECT vp;
749 int32 *new_flag,*active_flag,*bg_flag;
750 SFLOAT (*wp)[3];
751 BYTE (*rgb)[3];
752 {
753 TRI *tri;
754 int i,n,b0,b1,b2;
755 S_ID v0_id,v1_id,v2_id;
756
757 glBegin(GL_TRIANGLES);
758 for(n=((SM_NUM_TRI(sm)+31)>>5) +1; --n;)
759 if(new_flag[n] & active_flag[n])
760 for(i=0; i < 32; i++)
761 if(new_flag[n] & active_flag[n] & (1L << i) & ~bg_flag[n])
762 {
763 tri = SM_NTH_TRI(sm,(n<<5)+i);
764 v0_id = T_NTH_V(tri,0);
765 v1_id = T_NTH_V(tri,1);
766 v2_id = T_NTH_V(tri,2);
767 b0 = SM_BASE_ID(sm,v0_id);
768 b1 = SM_BASE_ID(sm,v1_id);
769 b2 = SM_BASE_ID(sm,v2_id);
770 if(b0 || b1 || b2)
771 render_base_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],
772 rgb[v1_id],rgb[v2_id],SM_VIEW_CENTER(sm),b0,b1,b2);
773 else
774 render_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],rgb[v1_id],
775 rgb[v2_id])
776 }
777 glEnd();
778
779 }
780
781 /* Test for qsort to depth sort triangles */
782 int
783 compare_tri_depths(T_DEPTH *td1,T_DEPTH *td2)
784 {
785 double d;
786
787 d = td2->depth-td1->depth;
788 if(d > 0.0)
789 return(1);
790 if(d < 0.0)
791 return(-1);
792 return(0);
793 }
794
795
796 /*
797 * smOrder_new_tris(sm,vp,td)
798 * SM *sm; : mesh
799 * FVECT vp; : current viewpoint
800 * T_DEPTH *td; : holds returned list of depth sorted tris
801 *
802 * Creates list of all new tris, with their distance from the current
803 * viewpoint, and sorts the list based on this distance
804 */
805 T_DEPTH
806 *smOrder_new_tris(sm,vp)
807 SM *sm;
808 FVECT vp;
809 {
810 T_DEPTH *td;
811 int n,i,j,tcnt,v,size;
812 TRI *tri;
813 double d,min_d;
814 FVECT diff;
815 int32 *new_flag,*bg_flag,*active_flag;
816
817 td = (T_DEPTH *)tempbuf(NEW_TRI_CNT*sizeof(T_DEPTH),FALSE);
818 size = NEW_TRI_CNT;
819
820 tcnt=0;
821 new_flag = SM_NTH_FLAGS(sm,T_NEW_FLAG);
822 bg_flag = SM_NTH_FLAGS(sm,T_BG_FLAG);
823 active_flag = SM_NTH_FLAGS(sm,T_ACTIVE_FLAG);
824 for(n=((SM_NUM_TRI(sm)+31)>>5) +1; --n;)
825 if(active_flag[n] & new_flag[n] & ~bg_flag[n])
826 for(i=0; i < 32; i++)
827 if(active_flag[n] & new_flag[n] & (1L << i) & ~bg_flag[n])
828 {
829 tri = SM_NTH_TRI(sm,(n<<5)+i);
830 if(tcnt+1 >= size)
831 {
832 size += 100;
833 td = (T_DEPTH *)tempbuf(size*sizeof(T_DEPTH),TRUE);
834 }
835 td[tcnt].tri = (n << 5)+i;
836 min_d = -1;
837 for(j=0;j < 3;j++)
838 {
839 v = T_NTH_V(tri,j);
840 VSUB(diff,SM_NTH_WV(sm,v),vp);
841 d = DOT(diff,diff);
842 if(min_d == -1 || d < min_d)
843 min_d = d;
844 }
845 td[tcnt++].depth = min_d;
846 }
847 td[tcnt].tri = -1;
848 if(tcnt)
849 qsort((void *)td,tcnt,sizeof(T_DEPTH),compare_tri_depths);
850 return(td);
851 }
852
853 /*
854 * smUpdate_tm(sm) : Update the tone-mapping
855 * SM *sm; : mesh
856 *
857 */
858 smUpdate_tm(sm)
859 SM *sm;
860 {
861 int t = SM_TONE_MAP(sm);
862
863 if(t==0 || smCompute_mapping)
864 {
865 tmClearHisto();
866 tmAddHisto(SM_BRT(sm),SM_NUM_SAMP(sm),1);
867 if(tmComputeMapping(0.,0.,0.) != TM_E_OK)
868 return;
869 t = 0;
870 smCompute_mapping = FALSE;
871 }
872 tmMapPixels(SM_NTH_RGB(sm,t),&SM_NTH_BRT(sm,t),SM_NTH_CHR(sm,t),
873 SM_NUM_SAMP(sm)-t);
874 SM_TONE_MAP(sm) = SM_NUM_SAMP(sm);
875 }
876
877 /*
878 * smRender_inc(sm,vp) : Incremental update of mesh
879 * SM * sm; : mesh
880 * FVECT vp; : current view point
881 *
882 * If a relatively small number of new triangles have been created,
883 * do an incremental update. Render new triangles with depth buffering
884 * turned off, if the current viewpoint is not the same as canonical view-
885 * point, must use painter's approach to resolve visibility:first depth sort
886 * triangles, then render back-to-front.
887 */
888 smRender_inc(sm,vp)
889 SM *sm;
890 FVECT vp;
891 {
892 S_ID v0_id,v1_id,v2_id;
893 int i,n,b0,b1,b2;
894 TRI *tri;
895 SFLOAT (*wp)[3];
896 BYTE (*rgb)[3];
897 int32 *new_flag,*bg_flag,*active_flag;
898 T_DEPTH *td = NULL;
899
900
901 /* For all of the NEW and ACTIVE triangles (since last update):
902 Go through and sort on depth value (from vp). Turn
903 Depth Buffer test off and render back-front
904 */
905
906 /* Must depth sort if current view point is not same as canonical */
907 if(!EQUAL_VEC3(SM_VIEW_CENTER(sm),vp))
908 td = smOrder_new_tris(sm,vp);
909 wp = SM_WP(sm);
910 rgb =SM_RGB(sm);
911 new_flag = SM_NTH_FLAGS(sm,T_NEW_FLAG);
912 active_flag = SM_NTH_FLAGS(sm,T_ACTIVE_FLAG);
913 bg_flag = SM_NTH_FLAGS(sm,T_BG_FLAG);
914 /* Turn Depth Test off -- using Painter's algorithm */
915 glPushAttrib(GL_DEPTH_BUFFER_BIT);
916 glDepthFunc(GL_ALWAYS);
917
918 smRender_new_bg_tris(sm,vp,new_flag,active_flag,bg_flag,wp,rgb);
919 if(!td)
920 smRender_new_fg_tris(sm,vp,new_flag,active_flag,bg_flag,wp,rgb);
921 else
922 {
923 glBegin(GL_TRIANGLES);
924 for(i=0; td[i].tri != -1;i++)
925 {
926 tri = SM_NTH_TRI(sm,td[i].tri);
927 /* Dont need to check for valid tri because flags are
928 cleared on delete
929 */
930 v0_id = T_NTH_V(tri,0);
931 v1_id = T_NTH_V(tri,1);
932 v2_id = T_NTH_V(tri,2);
933 b0 = SM_BASE_ID(sm,v0_id);
934 b1 = SM_BASE_ID(sm,v1_id);
935 b2 = SM_BASE_ID(sm,v2_id);
936 if(b0 || b1 || b2)
937 render_base_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],
938 rgb[v1_id],rgb[v2_id],SM_VIEW_CENTER(sm),b0,b1,b2);
939 else
940 render_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],rgb[v1_id],
941 rgb[v2_id])
942 }
943 glEnd();
944 freebuf(td);
945 }
946 /* Restore Depth Test */
947 glPopAttrib();
948 }
949
950 /*
951 * smRender_qtree_dl(sm,qt,vp,wp,rgb,i,level_i,max_level,leaf_cnt,which)
952 * SM *sm; : mesh
953 * QUADTREE qt; : quadtree base node
954 * FVECT vp; : current viewpoint
955 * SFLOAT (*wp)[3]; : array of sample points
956 * BYTE (*rgb)[3]; : array of RGB values for samples
957 * int i,level_i,level,max_level,leaf_cnt;
958 * : variables to keep track of where
959 * we are in the quadtree traversal in order to map nodes to
960 * corresponding array locations, where nodes are stored in breadth-
961 * first order. i is the index of the current node,level_i is the
962 * index of the first node on the current quadtree level, max_level is
963 * the maximum number of levels to traverse, and leaf_cnt is the number
964 * of leaves on the current level
965 * int which; flag indicates whether to render fg or bg tris
966 *
967 *
968 * Render the tris stored in qtree using display lists. For each node at
969 * the leaf or max_level, call the display_list if it exists, else traverse
970 * down the subtree and render the nodes into a new display list which is
971 * stored for future use.
972 */
973 smRender_qtree_dl(sm,qt,vp,wp,rgb,i,level_i,level,max_level,leaf_cnt,which)
974 SM *sm;
975 QUADTREE qt;
976 FVECT vp;
977 SFLOAT (*wp)[3];
978 BYTE (*rgb)[3];
979 int i,level_i,level,max_level,leaf_cnt;
980 int which;
981 {
982 int j;
983
984 if(QT_IS_EMPTY(qt))
985 return;
986
987 if(QT_IS_LEAF(qt) || level == max_level)
988 {
989 if(QT_IS_LEAF(qt))
990 {
991 if(!QT_LEAF_IS_FLAG(qt))
992 return;
993 }
994 else
995 if(!QT_IS_FLAG(qt))
996 return;
997
998 if(!Display_lists[i][which])
999 {
1000 Display_lists[i][which] = i+1 + which*SM_DL_LISTS;
1001 glNewList(Display_lists[i][which],GL_COMPILE_AND_EXECUTE);
1002 smClear_flags(sm,T_NEW_FLAG);
1003 glBegin(GL_TRIANGLES);
1004 smRender_qtree(sm,qt,vp,wp,rgb,which,FALSE);
1005 glEnd();
1006 glEndList();
1007 }
1008 else
1009 {
1010 glCallList(Display_lists[i][which]);
1011 }
1012 }
1013 else
1014 if(QT_IS_FLAG(qt))
1015 {
1016 i = ((i - level_i)<< 2) + level_i + leaf_cnt;
1017 level_i += leaf_cnt;
1018 leaf_cnt <<= 2;
1019 for(j=0; j < 4; j++)
1020 smRender_qtree_dl(sm,QT_NTH_CHILD(qt,j),vp,wp,rgb,
1021 i+j,level_i,level+1,max_level,leaf_cnt,which);
1022 }
1023
1024 }
1025
1026 /*
1027 * smRender_qtree(sm,qt,vp,wp,rgb,which,cull) : Render the tris stored in qtree
1028 * SM *sm; : mesh
1029 * QUADTREE qt; : quadtree base node
1030 * FVECT vp; : current viewpoint
1031 * SFLOAT (*wp)[3] : array of sample points
1032 * BYTE (*rgb)[3] : array of RGB values for samples
1033 * int which; : flag indicates whether to render fg or bg tris
1034 * int cull; : if true, only traverse active (flagged) nodes
1035 *
1036 */
1037 smRender_qtree(sm,qt,vp,wp,rgb,which,cull)
1038 SM *sm;
1039 QUADTREE qt;
1040 FVECT vp;
1041 SFLOAT (*wp)[3];
1042 BYTE (*rgb)[3];
1043 int which,cull;
1044 {
1045 int i;
1046
1047 if(QT_IS_EMPTY(qt))
1048 return;
1049
1050 if(QT_IS_LEAF(qt))
1051 {
1052 TRI *t,*tri;
1053 S_ID *optr,s_id,v0_id,v1_id,v2_id;
1054 int bg0,bg1,bg2,t_id;
1055
1056 if(cull && !QT_LEAF_IS_FLAG(qt))
1057 return;
1058
1059 optr = qtqueryset(qt);
1060 for (i = QT_SET_CNT(optr);i > 0; i--)
1061 {
1062 s_id = QT_SET_NEXT_ELEM(optr);
1063 t_id = SM_NTH_VERT(smMesh,s_id);
1064 tri = t = SM_NTH_TRI(smMesh,t_id);
1065 do
1066 {
1067 if((!cull || SM_IS_NTH_T_ACTIVE(sm,t_id)) && !SM_IS_NTH_T_NEW(sm,t_id))
1068 {
1069 bg0 = SM_IS_NTH_T_BG(sm,t_id);
1070 if((which == SM_RENDER_FG && !bg0) || (which== SM_RENDER_BG && bg0))
1071 {
1072 v0_id = T_NTH_V(t,0);
1073 v1_id = T_NTH_V(t,1);
1074 v2_id = T_NTH_V(t,2);
1075 if(bg0)
1076 {
1077 bg0 = SM_DIR_ID(sm,v0_id)?DIR:SM_BASE_ID(sm,v0_id)?BASE:0;
1078 bg1 = SM_DIR_ID(sm,v1_id)?DIR:SM_BASE_ID(sm,v1_id)?BASE:0;
1079 bg2 = SM_DIR_ID(sm,v2_id)?DIR:SM_BASE_ID(sm,v2_id)?BASE:0;
1080 SM_SET_NTH_T_NEW(sm,t_id);
1081 if(bg0==DIR && bg1==DIR && bg2==DIR)
1082 render_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],rgb[v1_id],
1083 rgb[v2_id])
1084 else
1085 render_mixed_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],
1086 rgb[v1_id],rgb[v2_id],vp,SM_VIEW_CENTER(sm),bg0,bg1,bg2);
1087 }
1088 else
1089 {
1090 SM_SET_NTH_T_NEW(sm,t_id);
1091 bg0 = SM_BASE_ID(sm,v0_id);
1092 bg1 = SM_BASE_ID(sm,v1_id);
1093 bg2 = SM_BASE_ID(sm,v2_id);
1094 if(bg0 || bg1 || bg2)
1095 render_base_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],
1096 rgb[v1_id],rgb[v2_id],SM_VIEW_CENTER(sm),bg0,bg1,bg2);
1097 else
1098 render_tri(wp[v0_id],wp[v1_id],wp[v2_id],rgb[v0_id],rgb[v1_id],
1099 rgb[v2_id])
1100 }
1101 }
1102 }
1103 t_id = smTri_next_ccw_nbr(smMesh,t,s_id);
1104 t = SM_NTH_TRI(smMesh,t_id);
1105 }while(t!=tri);
1106 }
1107 }
1108 else
1109 if(!cull || QT_IS_FLAG(qt))
1110 for(i=0; i < 4; i++)
1111 smRender_qtree(sm,QT_NTH_CHILD(qt,i),vp,wp,rgb,which,cull);
1112 }
1113
1114
1115 /*
1116 * smRender_mesh(sm,view,cull) : Render mesh Triangles
1117 * SM *sm; : mesh
1118 * VIEW *view; : current view
1119 * int cull; : cull Flag
1120 *
1121 * If cull is TRUE, first mark tris in current
1122 * frustum and only render them. Normally, cull will be FALSE only if
1123 * it is known that all tris lie in frustum, e.g. after a rebuild
1124 *
1125 */
1126 smRender_mesh(sm,view,cull)
1127 SM *sm;
1128 VIEW *view;
1129 int cull;
1130 {
1131 SFLOAT (*wp)[3];
1132 BYTE (*rgb)[3];
1133 int i;
1134 STREE *st= SM_LOCATOR(sm);
1135
1136 wp = SM_WP(sm);
1137 rgb =SM_RGB(sm);
1138
1139 smClear_flags(sm,T_NEW_FLAG);
1140
1141 if(cull)
1142 smCull(sm,view,SM_ALL_LEVELS);
1143
1144 glPushAttrib(GL_DEPTH_BUFFER_BIT);
1145 glDisable(GL_DEPTH_TEST);
1146
1147 glMatrixMode(GL_MODELVIEW);
1148 glPushMatrix();
1149 /* move relative to the new view */
1150 glTranslated(view->vp[0],view->vp[1],view->vp[2]);
1151
1152 /* The points are a distance of 1 away from the origin: if necessary
1153 scale so that they fit in frustum and are therefore not clipped away
1154 */
1155 if(dev_zmin >= 0.99)
1156 {
1157 double d;
1158
1159 d = (dev_zmin+dev_zmax)/2.0;
1160 glScaled(d,d,d);
1161 }
1162 /* move points to unit sphere at origin */
1163 glTranslated(-SM_VIEW_CENTER(sm)[0],-SM_VIEW_CENTER(sm)[1],
1164 -SM_VIEW_CENTER(sm)[2]);
1165
1166 glBegin(GL_TRIANGLES);
1167 for(i=0; i < ST_NUM_ROOT_NODES; i++)
1168 smRender_qtree(sm,ST_ROOT_QT(st,i),view->vp,wp,rgb,SM_RENDER_BG,cull);
1169 glEnd();
1170
1171 glPopMatrix();
1172
1173 glEnable(GL_DEPTH_TEST);
1174
1175 glBegin(GL_TRIANGLES);
1176 for(i=0; i < ST_NUM_ROOT_NODES; i++)
1177 smRender_qtree(sm,ST_ROOT_QT(st,i),view->vp,wp,rgb,SM_RENDER_FG,cull);
1178 glEnd();
1179
1180 glPopAttrib();
1181 }
1182
1183 /*
1184 * smRender_mesh_dl(sm,view) : Render stree utilizing display lists
1185 * SM *sm; : mesh
1186 * VIEW *view; : current view
1187 */
1188 smRender_mesh_dl(sm,view)
1189 SM *sm;
1190 VIEW *view;
1191 {
1192 SFLOAT (*wp)[3];
1193 BYTE (*rgb)[3];
1194 STREE *st;
1195 int i;
1196
1197 if(SM_DL_LEVELS == 0)
1198 {
1199 if(!Display_lists[0][0])
1200 {
1201 Display_lists[0][0] = 1;
1202 glNewList(Display_lists[0][0],GL_COMPILE_AND_EXECUTE);
1203 smRender_mesh(sm,view,FALSE);
1204 glEndList();
1205 }
1206 else
1207 glCallList(Display_lists[0][0]);
1208
1209 return;
1210 }
1211 smClear_flags(sm,T_NEW_FLAG);
1212
1213 smCull(sm,view,SM_DL_LEVELS);
1214
1215 st = SM_LOCATOR(sm);
1216
1217 wp = SM_WP(sm);
1218 rgb =SM_RGB(sm);
1219
1220 /* For all active quadtree nodes- first render bg tris, then fg */
1221 /* If display list exists, use otherwise create/display list */
1222 glPushAttrib(GL_DEPTH_BUFFER_BIT);
1223 glDisable(GL_DEPTH_TEST);
1224
1225 glMatrixMode(GL_MODELVIEW);
1226 glPushMatrix();
1227
1228 /* move relative to the new view */
1229 glTranslated(view->vp[0],view->vp[1],view->vp[2]);
1230
1231 /* The points are a distance of 1 away from the origin: if necessary
1232 scale so that they fit in frustum and are therefore not clipped away
1233 */
1234 if(dev_zmin >= 0.99)
1235 {
1236 double d;
1237 d = (dev_zmin+dev_zmax)/2.0;
1238 glScaled(d,d,d);
1239 }
1240 /* move points to unit sphere at origin */
1241 glTranslated(-SM_VIEW_CENTER(sm)[0],-SM_VIEW_CENTER(sm)[1],
1242 -SM_VIEW_CENTER(sm)[2]);
1243 for(i=0; i < ST_NUM_ROOT_NODES; i++)
1244 smRender_qtree_dl(sm,ST_ROOT_QT(st,i),view->vp,wp,rgb,i,0,1,
1245 SM_DL_LEVELS,8,SM_RENDER_BG);
1246 glPopMatrix();
1247
1248 glEnable(GL_DEPTH_TEST);
1249 for(i=0; i < ST_NUM_ROOT_NODES; i++)
1250 smRender_qtree_dl(sm,ST_ROOT_QT(st,i),view->vp,wp,rgb,i,0,1,
1251 SM_DL_LEVELS,8,SM_RENDER_FG);
1252 glPopAttrib();
1253 }
1254
1255
1256
1257 /*
1258 * smRender_tris(sm,view,render_flag) : Render all of the mesh triangles
1259 * SM *sm : current geometry
1260 * VIEW *view : current view
1261 * int render_flag : if render_flag & SM_RENDER_CULL: do culling first
1262 *
1263 * Renders mesh by traversing triangle list and drawing all active tris-
1264 * background tris first, then foreground and mixed tris
1265 */
1266 smRender_tris(sm,view,render_flag)
1267 SM *sm;
1268 VIEW *view;
1269 int render_flag;
1270 {
1271 int32 *active_flag,*bg_flag;
1272 SFLOAT (*wp)[3];
1273 BYTE (*rgb)[3];
1274
1275 wp = SM_WP(sm);
1276 rgb = SM_RGB(sm);
1277 active_flag = SM_NTH_FLAGS(sm,T_ACTIVE_FLAG);
1278 bg_flag = SM_NTH_FLAGS(sm,T_BG_FLAG);
1279
1280 if(render_flag & SM_RENDER_CULL)
1281 smCull(sm,view,SM_ALL_LEVELS);
1282
1283 /* Render triangles made up of points at infinity by turning off
1284 depth-buffering and projecting the points onto a sphere around the view*/
1285 glPushAttrib(GL_DEPTH_BUFFER_BIT);
1286 glDisable(GL_DEPTH_TEST);
1287 smRender_bg_tris(sm,view->vp,active_flag,bg_flag,wp,rgb);
1288
1289 /* Render triangles containing world-space points */
1290 glEnable(GL_DEPTH_TEST);
1291 smRender_fg_tris(sm,view->vp,active_flag,bg_flag,wp,rgb);
1292
1293 glPopAttrib();
1294
1295 }
1296
1297 /* Clear all of the display lists */
1298 clear_display_lists()
1299 {
1300 int i;
1301 for(i=0; i< SM_DL_LISTS; i++)
1302 {
1303 if(Display_lists[i][0])
1304 { /* Clear the foreground display list */
1305 glDeleteLists(Display_lists[i][0],1);
1306 Display_lists[i][0] = 0;
1307 }
1308 if(Display_lists[i][1])
1309 { /* Clear the background display list */
1310 glDeleteLists(Display_lists[i][1],1);
1311 Display_lists[i][1] = 0;
1312 }
1313 }
1314 }
1315
1316 /*
1317 * qtClear_dl(qt,i,level_i,level,max_level,leaf_cnt) :clear display lists
1318 * QUADTREE *qt; : Quadtree node
1319 * int i; : index into list of display lists for this node
1320 * int level_i; : index for first node at this level
1321 * int level,max_level; : current level, maximum level to descend
1322 * int leaf_cnt; : number of leaves at this level
1323 *
1324 * For each node under this node that has its flag set: delete all
1325 * existing display lists. Display lists are stored in an array indexed as
1326 * if the quadtree was traversed in a breadth first order (indices 0-7 are
1327 * the 8 quadtree roots, indices 8-11 the first level children of root 0,
1328 * indices 12-15 the children of root 1, etc). It is assumes that the display
1329 * lists will only be stored for a small number of levels: if this is not
1330 * true, a hashing scheme would work better for storing/retrieving the
1331 * display lists
1332 */
1333 qtClear_dl(qt,i,level_i,level,max_level,leaf_cnt)
1334 QUADTREE qt;
1335 int i,level_i,level,max_level,leaf_cnt;
1336 {
1337 int j;
1338
1339 if(QT_IS_EMPTY(qt))
1340 return;
1341 if(QT_IS_LEAF(qt) || level== max_level)
1342 {
1343 if(QT_IS_LEAF(qt))
1344 {
1345 if(!QT_LEAF_IS_FLAG(qt))
1346 return;
1347 }
1348 else
1349 if(!QT_IS_FLAG(qt))
1350 return;
1351 if(Display_lists[i][0])
1352 {
1353 glDeleteLists(Display_lists[i][0],1);
1354 Display_lists[i][0] = 0;
1355 }
1356 if(Display_lists[i][1])
1357 {
1358 glDeleteLists(Display_lists[i][1],1);
1359 Display_lists[i][1] = 0;
1360 }
1361 }
1362 else
1363 if(QT_IS_FLAG(qt))
1364 {
1365 /* Calculate the index for the first child given the values
1366 of the parent at the current level
1367 */
1368 i = ((i - level_i)<< 2) + level_i + leaf_cnt;
1369 level_i += leaf_cnt;
1370 leaf_cnt <<= 2;
1371 for(j=0; j < 4; j++)
1372 qtClear_dl(QT_NTH_CHILD(qt,j),i+j,level_i,level+1,max_level,
1373 leaf_cnt);
1374 }
1375 }
1376
1377 /*
1378 * smInvalidate_view(sm,view) : Invalidate rendering representation for view
1379 * SM *sm; : mesh
1380 * VIEW *view; : current view
1381 *
1382 * Delete the existing display lists for geometry in the current
1383 * view frustum: Called when the geometry in the frustum has been changed
1384 */
1385 smInvalidate_view(sm,view)
1386 SM *sm;
1387 VIEW *view;
1388 {
1389 int i;
1390
1391 if(SM_DL_LEVELS == 0)
1392 {
1393 if(Display_lists[0][0])
1394 {
1395 glDeleteLists(Display_lists[0][0],1);
1396 Display_lists[0][0] = 0;
1397 }
1398 return;
1399 }
1400 /* Mark qtree nodes/tris in frustum */
1401 smCull(sm,view,SM_DL_LEVELS);
1402
1403 /* Invalidate display_lists in marked qtree nodes */
1404 for(i=0; i < ST_NUM_ROOT_NODES; i++)
1405 qtClear_dl(ST_ROOT_QT(SM_LOCATOR(sm),i),i,0,1,SM_DL_LEVELS,8);
1406
1407 }
1408
1409
1410 /*
1411 * smRender(sm,view, qual): render OpenGL output geometry
1412 * SM *sm; : current mesh representation
1413 * VIEW *view; : desired view
1414 * int qual; : quality level (percentage on linear time scale)
1415 *
1416 * Render the current mesh:
1417 * recompute tone mapping if full redraw and specified:
1418 * if moving (i.e. qual < MAXQUALITY)
1419 * render the cached display lists, if quality drops
1420 * below threshold, render approximation instead
1421 * if stationary
1422 * render mesh geometry without display lists, unless up-to-date
1423 * display lists already exist.
1424 */
1425 smRender(sm,view,qual)
1426 SM *sm;
1427 VIEW *view;
1428 int qual;
1429 {
1430 /* Unless quality > MAXQUALITY, render using display lists */
1431 if(qual <= MAXQUALITY)
1432 {
1433 /* If quality above threshold: render mesh*/
1434 if(qual > (MAXQUALITY*2/4) )
1435 /* render stree using display lists */
1436 smRender_mesh_dl(sm,view);
1437 else
1438 {
1439 /* If quality below threshold, use approximate rendering */
1440 smRender_approx(sm,qual,view);
1441 }
1442 }
1443 else
1444 {
1445 /* render stree without display lists */
1446 smRender_mesh(sm,view,TRUE);
1447 }
1448 }
1449
1450
1451 /*
1452 * smUpdate(view, qual) : update OpenGL output geometry
1453 * VIEW *view; : desired view
1454 * int qual; : quality level (percentage on linear time scale)
1455 *
1456 * Draw new geometric representation using OpenGL calls. Assume that the
1457 * view has already been set up and the correct frame buffer has been
1458 * selected for drawing. The quality level is on a linear scale, where 100%
1459 * is full (final) quality. It is not necessary to redraw geometry that has
1460 * been output since the last call to smClean().(The last view drawn will
1461 * be view==&odev.v each time.)
1462 */
1463 smUpdate(view,qual)
1464 VIEW *view;
1465 int qual;
1466 {
1467
1468 /* Is there anything to render? */
1469 if(!smMesh || SM_NUM_TRI(smMesh)<=0)
1470 return;
1471
1472 /* Is viewer MOVING?*/
1473 if(qual < MAXQUALITY)
1474 {
1475 if(smIncremental)
1476 smUpdate_tm(smMesh);
1477
1478 /* Render mesh using display lists */
1479 smRender(smMesh,view,qual);
1480 return;
1481 }
1482 /* Viewer is STATIONARY */
1483 /* Has view moved epsilon from canonical view? (epsilon= percentage
1484 (SM_VIEW_FRAC) of running average of the distance of the sample points
1485 from the canonical view */
1486 if(DIST(view->vp,SM_VIEW_CENTER(smMesh)) > SM_ALLOWED_VIEW_CHANGE(smMesh))
1487 {
1488 /* Must rebuild mesh with current view as new canonical view */
1489 smRebuild(smMesh,view);
1490 /* Existing display lists and tonemapping are no longer valid */
1491 clear_display_lists();
1492 smCompute_mapping = FALSE;
1493 smUpdate_tm(smMesh);
1494 /* Render all the triangles in the new mesh */
1495 smRender(smMesh,view,qual+1);
1496 }
1497 else
1498 /* Has a complete redraw been requested ?*/
1499 if(smClean_notify)
1500 {
1501 if(smIncremental)
1502 smUpdate_tm(smMesh);
1503 smIncremental = FALSE;
1504 smRender(smMesh,view,qual);
1505 }
1506 else
1507 {
1508 smUpdate_tm(smMesh);
1509 /* If number of new triangles relatively small: do incremental update */
1510 /* Mark Existing display lists in frustum invalid */
1511 if(!smIncremental)
1512 {
1513 smInvalidate_view(smMesh,view);
1514 smIncremental = TRUE;
1515 }
1516 smRender_inc(smMesh,view->vp);
1517 }
1518 /* This is our final update iff qual==MAXQUALITY and view==&odev.v */
1519 if( (qual >= MAXQUALITY) && (view == &(odev.v)))
1520 {
1521 /* reset rendering flags */
1522 smClean_notify = FALSE;
1523 if(smIncremental)
1524 smClear_flags(smMesh,T_NEW_FLAG);
1525 qtCache_init(0);
1526 }
1527
1528 }
1529
1530
1531
1532
1533
1534
1535