ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/hd/rhd_odraw.c
Revision: 3.2
Committed: Sun Dec 20 20:37:53 1998 UTC (25 years, 3 months ago) by gwlarson
Content type: text/plain
Branch: MAIN
Changes since 3.1: +68 -27 lines
Log Message:
improved sample insertion to take better samples
fixed and improved tone mapping

File Contents

# User Rev Content
1 gwlarson 3.1 /* Copyright (c) 1998 Silicon Graphics, Inc. */
2    
3     #ifndef lint
4     static char SCCSid[] = "$SunId$ SGI";
5     #endif
6    
7     /*
8     * Routines for drawing samples using depth buffer checks.
9     */
10    
11     #include "standard.h"
12    
13     #include <sys/types.h>
14     #include <GL/glx.h>
15     #include <GL/glu.h>
16    
17     #include "random.h"
18     #include "rhd_odraw.h"
19    
20     #ifndef DEPTHEPS
21     #define DEPTHEPS 0.02 /* depth epsilon */
22     #endif
23     #ifndef SAMPSPERBLOCK
24     #define SAMPSPERBLOCK 1024 /* target samples per image block */
25     #endif
26     #ifndef SFREEFRAC
27     #define SFREEFRAC 0.2 /* fraction to free at a time */
28     #endif
29     #ifndef MAXFAN
30     #define MAXFAN 32 /* maximum arms in a triangle fan */
31     #endif
32     #ifndef MINFAN
33     #define MINFAN 4 /* minimum arms in a triangle fan */
34     #endif
35     #ifndef FANSIZE
36     #define FANSIZE 3.5 /* fan sizing factor */
37     #endif
38    
39     #define NEWMAP 01 /* need to recompute mapping */
40     #define NEWRGB 02 /* need to remap RGB values */
41 gwlarson 3.2 #define NEWHIST 04 /* clear histogram as well */
42 gwlarson 3.1
43     struct ODview *odView; /* our view list */
44     int odNViews; /* number of views in our list */
45    
46     struct ODsamp odS; /* sample values */
47    
48     static int needmapping; /* what needs doing with tone map */
49    
50    
51     #define SAMP32 (32*(2*sizeof(short)+sizeof(union ODfunion)+sizeof(TMbright)+\
52     6*sizeof(BYTE))+sizeof(int4))
53    
54     int
55     odInit(n) /* initialize drawing routines */
56     int n;
57     {
58     int nbytes, i, j, k, nextsamp, count, blockdiv;
59     int res[2];
60    
61     if (odNViews) { /* deallocate view structures */
62     for (i = 0; i < odNViews; i++) {
63     free((char *)odView[i].bmap);
64     if (odView[i].emap != NULL)
65     free((char *)odView[i].emap);
66     }
67     free((char *)odView);
68     odView = NULL;
69     odNViews = 0;
70     }
71     if (n && n != odS.nsamp) {
72     /* round space up to nearest power of 2 */
73     nbytes = (n+31)/32 * SAMP32;
74     for (i = 1024; nbytes > i-8; i <<= 1)
75     ;
76     n = (i-8)/SAMP32 * 32;
77 gwlarson 3.2 needmapping = NEWHIST;
78 gwlarson 3.1 }
79     if (n != odS.nsamp) { /* (re)allocate sample array */
80     if (odS.nsamp)
81     free(odS.base);
82     odS.nsamp = 0;
83     if (!n)
84     return(0);
85     nbytes = (n+31)/32 * SAMP32;
86     odS.base = (char *)malloc(nbytes);
87     if (odS.base == NULL)
88     return(0);
89     /* assign larger alignment types earlier */
90     odS.f = (union ODfunion *)odS.base;
91     odS.redraw = (int4 *)(odS.f + n);
92     odS.ip = (short (*)[2])(odS.redraw + n/32);
93     odS.brt = (TMbright *)(odS.ip + n);
94     odS.chr = (BYTE (*)[3])(odS.brt + n);
95     odS.rgb = (BYTE (*)[3])(odS.chr + n);
96     odS.nsamp = n;
97     }
98     if (!n)
99     return(0);
100     /* allocate view information */
101     count = 0; /* count pixels */
102     for (i = 0; dev_auxview(i, res) != NULL; i++)
103     count += res[0]*res[1];
104     odView = (struct ODview *)malloc(i*sizeof(struct ODview));
105     if (odView == NULL)
106     return(0);
107     odNViews = i;
108     blockdiv = sqrt(count/(n/SAMPSPERBLOCK)) + 0.5;
109     if (blockdiv < 8) blockdiv = 8;
110     nextsamp = 0; count /= blockdiv*blockdiv; /* # blocks */
111     while (i--) { /* initialize each view */
112     odView[i].emap = NULL;
113     odView[i].dmap = NULL;
114     dev_auxview(i, res);
115     odView[i].hhi = res[0];
116     odView[i].hlow = (res[0] + blockdiv/2) / blockdiv;
117     if (odView[i].hlow < 1) odView[i].hlow = 1;
118     odView[i].vhi = res[1];
119     odView[i].vlow = (res[1] + blockdiv/2) / blockdiv;
120     if (odView[i].vlow < 1) odView[i].vlow = 1;
121     j = odView[i].hlow*odView[i].vlow;
122     odView[i].bmap = (struct ODblock *)malloc(
123     j * sizeof(struct ODblock));
124     if (odView[i].bmap == NULL)
125     return(0);
126     DCHECK(count<=0 | nextsamp>=n,
127     CONSISTENCY, "counter botch in odInit");
128     if (!i) count = j;
129 gwlarson 3.2 while (j--) { /* initialize blocks & free lists */
130     odView[i].bmap[j].pthresh = FHUGE;
131 gwlarson 3.1 odView[i].bmap[j].first = k = nextsamp;
132     nextsamp += odView[i].bmap[j].nsamp =
133     (n - nextsamp)/count--;
134     odView[i].bmap[j].free = k;
135     while (++k < nextsamp)
136     odS.nextfree(k-1) = k;
137     odS.nextfree(k-1) = ENDFREE;
138     odView[i].bmap[j].nused = 0;
139     }
140     }
141     CLR4ALL(odS.redraw, odS.nsamp); /* clear redraw flags */
142 gwlarson 3.2 for (i = odS.nsamp; i--; ) { /* clear values */
143 gwlarson 3.1 odS.ip[i][0] = odS.ip[i][1] = -1;
144 gwlarson 3.2 odS.brt[i] = TM_NOBRT;
145     }
146     needmapping |= NEWMAP; /* compute new map on update */
147 gwlarson 3.1 return(odS.nsamp); /* return number of samples */
148     }
149    
150     #undef SAMP32
151    
152    
153     int
154     sampcmp(s0, s1) /* sample order, descending proximity */
155     int *s0, *s1;
156     {
157     register double diff = odS.closeness(*s1) - odS.closeness(*s0);
158    
159     return (diff > FTINY ? 1 : diff < -FTINY ? -1 : 0);
160     }
161    
162    
163     int
164 gwlarson 3.2 odAllocBlockSamp(vn, hh, vh, prox) /* allocate sample from block */
165     int vn, hh, vh;
166     double prox;
167 gwlarson 3.1 {
168     int si[SAMPSPERBLOCK+SAMPSPERBLOCK/4];
169 gwlarson 3.2 int hl, vl;
170 gwlarson 3.1 VIEW *vw;
171     FVECT ro, rd;
172     int res[2];
173     register struct ODblock *bp;
174 gwlarson 3.2 register int i, j;
175     /* get block */
176     hl = hh*odView[vn].hlow/odView[vn].hhi;
177     vl = vh*odView[vn].vlow/odView[vn].vhi;
178 gwlarson 3.1 bp = odView[vn].bmap + vl*odView[vn].hlow + hl;
179 gwlarson 3.2 if (prox > bp->pthresh)
180     return(-1); /* worse than free list occupants */
181     /* check for duplicate pixel */
182     for (i = bp->first+bp->nsamp; i-- > bp->first; )
183     if (hh == odS.ip[i][0] && vh == odS.ip[i][1]) { /* found it! */
184     /* search free list for it */
185     if (i == bp->free)
186     break; /* special case */
187     if (bp->free != ENDFREE)
188     for (j = bp->free; odS.nextfree(j) != ENDFREE;
189     j = odS.nextfree(j))
190     if (odS.nextfree(j) == i) {
191     odS.nextfree(j) =
192     odS.nextfree(i);
193     bp->nused++;
194     goto gotit;
195     }
196     if (prox >= 0.999*odS.closeness(i))
197     return(-1); /* previous sample is fine */
198     goto gotit;
199     }
200     if (bp->free != ENDFREE) { /* allocate from free list */
201 gwlarson 3.1 i = bp->free;
202     bp->free = odS.nextfree(i);
203     bp->nused++;
204 gwlarson 3.2 goto gotit;
205 gwlarson 3.1 }
206     DCHECK(bp->nsamp<=0, CONSISTENCY,
207     "no available samples in odAllocBlockSamp");
208     DCHECK(bp->nsamp > sizeof(si)/sizeof(si[0]), CONSISTENCY,
209     "too many samples in odAllocBlockSamp");
210     /* free some samples */
211     if ((vw = dev_auxview(vn, res)) == NULL)
212     error(CONSISTENCY, "bad view number in odAllocBlockSamp");
213     for (i = bp->nsamp; i--; ) /* figure out which are worse */
214     si[i] = bp->first + i;
215     qsort((char *)si, bp->nsamp, sizeof(int), sampcmp);
216 gwlarson 3.2 i = bp->nsamp*SFREEFRAC + .5; /* put them into free list */
217     if (i >= bp->nsamp) i = bp->nsamp-1; /* paranoia */
218     bp->pthresh = odS.closeness(si[i]); /* new proximity threshold */
219 gwlarson 3.1 while (--i > 0) {
220     odS.nextfree(si[i]) = bp->free;
221     bp->free = si[i];
222     bp->nused--;
223     }
224 gwlarson 3.2 i = si[0]; /* use worst sample */
225     gotit:
226     odS.ip[i][0] = hh;
227     odS.ip[i][1] = vh;
228     odS.closeness(i) = prox;
229     return(i);
230 gwlarson 3.1 }
231    
232    
233     odSample(c, d, p) /* add a sample value */
234     COLR c;
235     FVECT d, p;
236     {
237     FVECT disp;
238 gwlarson 3.2 double d0, d1, h, v, prox;
239 gwlarson 3.1 register VIEW *vw;
240 gwlarson 3.2 int hh, vh;
241 gwlarson 3.1 int res[2];
242     register int i, id;
243    
244     DCHECK(odS.nsamp<=0, CONSISTENCY, "no samples allocated in odSample");
245     /* add value to each view */
246     for (i = 0; (vw = dev_auxview(i, res)) != NULL; i++) {
247     DCHECK(i>=odNViews, CONSISTENCY, "too many views in odSample");
248     CHECK(vw->type!=VT_PER, INTERNAL,
249     "cannot handle non-perspective views");
250     if (p != NULL) { /* compute view position */
251     VSUB(disp, p, vw->vp);
252     d0 = DOT(disp, vw->vdir);
253     if (d0 <= vw->vfore+FTINY)
254     continue; /* too close */
255     } else {
256     VCOPY(disp, d);
257     d0 = DOT(disp, vw->vdir);
258     if (d0 <= FTINY) /* behind view */
259     continue;
260     }
261     h = DOT(disp,vw->hvec)/(d0*vw->hn2) + 0.5 - vw->hoff;
262     if (h < 0. || h >= 1.)
263     continue; /* left or right */
264     v = DOT(disp,vw->vvec)/(d0*vw->vn2) + 0.5 - vw->voff;
265     if (v < 0. || v >= 1.)
266     continue; /* above or below */
267     hh = h * res[0];
268     vh = v * res[1];
269     if (odView[i].dmap != NULL) { /* check depth */
270     d1 = odView[i].dmap[vh*res[0] + hh];
271     if (d1 < 0.99*FHUGE && (d0 > (1.+DEPTHEPS)*d1 ||
272     (1.+DEPTHEPS)*d0 < d1))
273     continue; /* occlusion error */
274     }
275     if (p != NULL) { /* compute closeness (sin^2) */
276     d1 = DOT(disp, d);
277 gwlarson 3.2 prox = 1. - d1*d1/DOT(disp,disp);
278 gwlarson 3.1 } else
279 gwlarson 3.2 prox = 0.;
280     /* allocate sample */
281     id = odAllocBlockSamp(i, hh, vh, prox);
282     if (id < 0)
283     continue; /* not good enough */
284 gwlarson 3.1 /* convert color */
285     tmCvColrs(&odS.brt[id], odS.chr[id], c, 1);
286     if (imm_mode | needmapping) /* if immediate mode */
287     needmapping |= NEWRGB; /* map it later */
288     else /* else map it now */
289     tmMapPixels(odS.rgb[id], &odS.brt[id], odS.chr[id], 1);
290     SET4(odS.redraw, id); /* mark for redraw */
291     }
292     }
293    
294    
295 gwlarson 3.2 odRemap(newhist) /* recompute tone mapping */
296     int newhist;
297 gwlarson 3.1 {
298     needmapping |= NEWMAP|NEWRGB;
299 gwlarson 3.2 if (newhist)
300     needmapping |= NEWHIST;
301 gwlarson 3.1 }
302    
303    
304     odRedraw(vn, hmin, vmin, hmax, vmax) /* redraw view region */
305     int vn, hmin, vmin, hmax, vmax;
306     {
307     int i, j;
308     register struct ODblock *bp;
309     register int k;
310    
311     if (vn<0 | vn>=odNViews)
312     return;
313     /* check view limits */
314     if (hmin < 0) hmin = 0;
315     if (hmax >= odView[vn].hhi) hmax = odView[vn].hhi-1;
316     if (vmin < 0) vmin = 0;
317     if (vmax >= odView[vn].vhi) vmax = odView[vn].vhi-1;
318     if (hmax <= hmin | vmax <= vmin)
319     return;
320     /* convert to low resolution */
321     hmin = hmin * odView[vn].hlow / odView[vn].hhi;
322     hmax = hmax * odView[vn].hlow / odView[vn].hhi;
323     vmin = vmin * odView[vn].vlow / odView[vn].vhi;
324     vmax = vmax * odView[vn].vlow / odView[vn].vhi;
325     /* mark block samples for redraw, inclusive */
326     for (i = hmin; i <= hmax; i++)
327     for (j = vmin; j <= vmax; j++) {
328     bp = odView[vn].bmap + j*odView[vn].hlow + i;
329     for (k = bp->nsamp; k--; )
330     if (odS.ip[bp->first+k][0] >= 0)
331     SET4(odS.redraw, bp->first+k);
332     }
333     }
334    
335    
336     odDepthMap(vn, dm) /* assign depth map for view */
337     int vn;
338     GLfloat *dm;
339     {
340     double d0, d1;
341     int i, j, hmin, hmax, vmin, vmax;
342     register int k, l;
343    
344     if (dm == NULL) { /* free edge map */
345 gwlarson 3.2 if (vn<0 | vn>=odNViews)
346     return; /* too late -- they're gone! */
347 gwlarson 3.1 if (odView[vn].emap != NULL)
348     free((char *)odView[vn].emap);
349     odView[vn].emap = NULL;
350     odView[vn].dmap = NULL;
351     return;
352     }
353 gwlarson 3.2 DCHECK(vn<0 | vn>=odNViews, CONSISTENCY,
354     "bad view number in odDepthMap");
355 gwlarson 3.1 odView[vn].dmap = dm; /* initialize edge map */
356     if (odView[vn].emap == NULL) {
357     odView[vn].emap = (int4 *)malloc(
358     FL4NELS(odView[vn].hlow*odView[vn].vlow)*sizeof(int4));
359     if (odView[vn].emap == NULL)
360     error(SYSTEM, "out of memory in odDepthMap");
361     }
362     CLR4ALL(odView[vn].emap, odView[vn].hlow*odView[vn].vlow);
363     /* compute edge map */
364     vmin = odView[vn].vhi; /* enter loopsville */
365     for (j = odView[vn].vlow; j--; ) {
366     vmax = vmin;
367     vmin = j*odView[vn].vhi/odView[vn].vlow;
368     hmin = odView[vn].hhi;
369     for (i = odView[vn].hlow; i--; ) {
370     hmax = hmin;
371     hmin = i*odView[vn].hhi/odView[vn].hlow;
372     for (l = vmin; l < vmax; l++) { /* vertical edges */
373     d1 = dm[l*odView[vn].hhi+hmin];
374     for (k = hmin+1; k < hmax; k++) {
375     d0 = d1;
376     d1 = dm[l*odView[vn].hhi+k];
377     if (d0 > (1.+DEPTHEPS)*d1 ||
378     (1.+DEPTHEPS)*d0 < d1) {
379     SET4(odView[vn].emap,
380     j*odView[vn].hlow + i);
381     break;
382     }
383     }
384     if (k < hmax)
385     break;
386     }
387     if (l < vmax)
388     continue;
389     for (k = hmin; k < hmax; k++) { /* horizontal edges */
390     d1 = dm[vmin*odView[vn].hhi+k];
391     for (l = vmin+1; l < vmax; l++) {
392     d0 = d1;
393     d1 = dm[l*odView[vn].hhi+k];
394     if (d0 > (1.+DEPTHEPS)*d1 ||
395     (1.+DEPTHEPS)*d0 < d1) {
396     SET4(odView[vn].emap,
397     j*odView[vn].hlow + i);
398     break;
399     }
400     }
401     if (l < vmax)
402     break;
403     }
404     }
405     }
406     }
407    
408    
409     odUpdate(vn) /* update this view */
410     int vn;
411     {
412     int i, j;
413     register struct ODblock *bp;
414     register int k;
415    
416     DCHECK(vn<0 | vn>=odNViews, CONSISTENCY,
417     "bad view number in odUpdate");
418     /* need to do some tone mapping? */
419     if (needmapping & NEWRGB) {
420     if (needmapping & NEWMAP) {
421 gwlarson 3.2 if (needmapping & NEWHIST)
422     tmClearHisto();
423 gwlarson 3.1 if (tmAddHisto(odS.brt,odS.nsamp,1) != TM_E_OK)
424     return;
425     if (tmComputeMapping(0.,0.,0.) != TM_E_OK)
426     return;
427     for (k = odS.nsamp; k--; ) /* redraw all */
428     if (odS.ip[k][0] >= 0)
429     SET4(odS.redraw, k);
430     }
431     if (tmMapPixels(odS.rgb,odS.brt,odS.chr,odS.nsamp) != TM_E_OK)
432     return;
433     needmapping = 0; /* reset flag */
434     }
435     /* draw each block in view */
436 gwlarson 3.2 for (j = odView[vn].vlow; j--; )
437     for (i = 0; i < odView[vn].hlow; i++) {
438 gwlarson 3.1 /* get block */
439     bp = odView[vn].bmap + j*odView[vn].hlow + i;
440     /* do quick, conservative flag check */
441     for (k = (bp->first+bp->nsamp+31)>>5;
442     k-- > bp->first>>5; )
443     if (odS.redraw[k])
444     break; /* non-zero flag */
445     if (k < bp->first>>5)
446     continue; /* no flags set */
447     for (k = bp->nsamp; k--; ) /* sample by sample */
448     if (CHK4(odS.redraw, bp->first+k)) {
449     odDrawBlockSamp(vn, i, j, bp->first+k);
450     CLR4(odS.redraw, bp->first+k);
451     }
452     }
453     }
454    
455    
456     #if 0
457     static
458     clip_end(p, o, vp) /* clip line segment to view */
459     GLshort p[3];
460     short o[2];
461     register struct ODview *vp;
462     {
463     if (p[0] < 0) {
464     p[1] = -o[0]*(p[1]-o[1])/(p[0]-o[0]) + o[1];
465     p[2] = -o[0]*p[2]/(p[0]-o[0]);
466     p[0] = 0;
467     } else if (p[0] >= vp->hhi) {
468     p[1] = (vp->hhi-1-o[0])*(p[1]-o[1])/(p[0]-o[0]) + o[1];
469     p[2] = (vp->hhi-1-o[0])*p[2]/(p[0]-o[0]);
470     p[0] = vp->hhi-1;
471     }
472     if (p[1] < 0) {
473     p[0] = -o[1]*(p[0]-o[0])/(p[1]-o[1]) + o[0];
474     p[2] = -o[1]*p[2]/(p[1]-o[1]);
475     p[1] = 0;
476     } else if (p[1] >= vp->vhi) {
477     p[0] = (vp->vhi-1-o[1])*(p[0]-o[0])/(p[1]-o[1]) + o[0];
478     p[2] = (vp->vhi-1-o[1])*p[2]/(p[1]-o[1]);
479     p[1] = vp->vhi-1;
480     }
481     }
482     #endif
483    
484    
485     static int
486     make_arms(ar, cp, vp, sz) /* make arms for triangle fan */
487     GLshort ar[MAXFAN][3];
488     short cp[2];
489     register struct ODview *vp;
490     double sz;
491     {
492     int na, dv;
493     double hrad, vrad, phi0, phi;
494     register int i;
495    
496     DCHECK(sz > 1, CONSISTENCY, "super-unary size in make_arms");
497     na = MAXFAN*sz*sz + 0.5; /* keep area constant */
498     if (na < MINFAN) na = MINFAN;
499     hrad = FANSIZE*sz*vp->hhi/vp->hlow;
500     vrad = FANSIZE*sz*vp->vhi/vp->vlow;
501     if (hrad*vrad < 2.25)
502     hrad = vrad = 1.5;
503     phi0 = (2.*PI) * frandom();
504     dv = OMAXDEPTH*sz + 0.5;
505     for (i = 0; i < na; i++) {
506     phi = phi0 + (2.*PI)*i/na;
507     ar[i][0] = cp[0] + tcos(phi)*hrad + 0.5;
508     ar[i][1] = cp[1] + tsin(phi)*vrad + 0.5;
509     ar[i][2] = dv;
510     /* clip_end(ar[i], cp, vp); */
511     }
512     return(na);
513     }
514    
515    
516     static int
517     depthchange(vp, x0, y0, x1, y1) /* check depth discontinuity */
518     register struct ODview *vp;
519     int x0, y0, x1, y1;
520     {
521     register double d0, d1;
522    
523     DCHECK(x0<0 | x0>=vp->hhi | y0<0 | y0>=vp->vhi,
524     CONSISTENCY, "coordinates off view in depthchange");
525    
526     if (x1<0 | x1>=vp->hhi | y1<0 | y1>=vp->vhi)
527     return(1);
528    
529     d0 = vp->dmap[y0*vp->hhi + x0];
530     d1 = vp->dmap[y1*vp->hhi + x1];
531    
532     return((1.+DEPTHEPS)*d0 < d1 || d0 > (1.+DEPTHEPS)*d1);
533     }
534    
535    
536     static
537     clip_edge(p, o, vp) /* clip line segment to depth edge */
538     GLshort p[3];
539     short o[2];
540     register struct ODview *vp;
541     {
542     int x, y, xstep, ystep, rise, rise2, run, run2, n;
543    
544     DCHECK(vp->dmap==NULL, CONSISTENCY,
545     "clip_edge called with no depth map");
546     x = o[0]; y = o[1];
547     run = p[0] - x;
548     xstep = run > 0 ? 1 : -1;
549     run *= xstep;
550     rise = p[1] - y;
551     ystep = rise > 0 ? 1 : -1;
552     rise *= ystep;
553     rise2 = run2 = 0;
554     if (rise > run) rise2 = 1;
555     else run2 = 1;
556     n = rise + run;
557     while (n--) /* run out arm, checking depth */
558     if (run2 > rise2) {
559     if (depthchange(vp, x, y, x+xstep, y))
560     break;
561     x += xstep;
562     rise2 += rise;
563     } else {
564     if (depthchange(vp, x, y, x, y+ystep))
565     break;
566     y += ystep;
567     run2 += run;
568     }
569     if (n < 0) /* found something? */
570     return;
571     if (run > rise)
572     p[2] = (x - o[0])*p[2]/(p[0] - o[0]);
573     else
574     p[2] = (y - o[1])*p[2]/(p[1] - o[1]);
575     p[0] = x;
576     p[1] = y;
577     }
578    
579    
580     static int
581     getblock(vp, h, v) /* get block index */
582     register struct ODview *vp;
583     register int h, v;
584     {
585     if (h<0 | h>=vp->hhi | v<0 | v>=vp->vhi)
586     return(-1);
587     return(h*vp->hlow/vp->hhi + v*vp->vlow/vp->vhi*vp->hlow);
588     }
589    
590    
591     odDrawBlockSamp(vn, h, v, id) /* draw sample in view block */
592     int vn, h, v;
593     register int id;
594     {
595     GLshort arm[MAXFAN][3];
596     int narms, blockindex, bi1;
597     register struct ODview *vp;
598     double size;
599     int home_edges;
600     register int i;
601    
602     vp = odView + vn;
603     blockindex = v*vp->hlow + h;
604     DCHECK(odS.ip[id][0]*vp->hlow/vp->hhi != h |
605     odS.ip[id][1]*vp->vlow/vp->vhi != v,
606     CONSISTENCY, "bad sample position in odDrawBlockSamp");
607     DCHECK(vp->bmap[blockindex].nused <= 0,
608     CONSISTENCY, "bad in-use count in odDrawBlockSamp");
609     /* create triangle fan */
610     size = 1./sqrt((double)vp->bmap[blockindex].nused);
611     narms = make_arms(arm, odS.ip[id], vp, size);
612     if (vp->emap != NULL) { /* check for edge collisions */
613     home_edges = CHK4(vp->emap, blockindex);
614     for (i = 0; i < narms; i++)
615     /* the following test is flawed, because we could
616     * be passing through a block on a diagonal run */
617     if (home_edges ||
618     ( (bi1 = getblock(vp, arm[i][0], arm[i][1]))
619     != blockindex &&
620     (bi1 < 0 || CHK4(vp->emap, bi1)) ))
621     clip_edge(arm[i], odS.ip[id], vp);
622     }
623     /* draw triangle fan */
624     glColor3ub(odS.rgb[id][0], odS.rgb[id][1], odS.rgb[id][2]);
625     glBegin(GL_TRIANGLE_FAN);
626     glVertex3s((GLshort)odS.ip[id][0], (GLshort)odS.ip[id][1], (GLshort)0);
627     for (i = 0; i < narms; i++)
628     glVertex3sv(arm[i]);
629     glVertex3sv(arm[0]); /* connect last to first */
630     glEnd();
631     }