1 /*   shim3d.c
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *            National Center for Biotechnology Information (NCBI)
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government do not place any restriction on its use or reproduction.
13 *  We would, however, appreciate having the NCBI and the author cited in
14 *  any work or product based on this material
15 *
16 *  Although all reasonable efforts have been taken to ensure the accuracy
17 *  and reliability of the software and data, the NLM and the U.S.
18 *  Government do not and cannot warrant the performance or results that
19 *  may be obtained by using this software or data. The NLM and the U.S.
20 *  Government disclaim all warranties, express or implied, including
21 *  warranties of performance, merchantability or fitness for any particular
22 *  purpose.
23 *
24 * ===========================================================================
25 *
26 * File Name:  shim3d.c
27 *
28 * Author:  Lewis Geer
29 *
30 * Version Creation Date:   1/26/99
31 *
32 * $Revision: 6.81 $
33 *
34 * File Description: Shim functions to replace Viewer3D with OpenGL
35 *
36 * Modifications:
37 * --------------------------------------------------------------------------
38 * $Log: shim3d.c,v $
39 * Revision 6.81  2016/09/02 15:08:15  ucko
40 * shim3d.c (OGL_Reset): conditionally call OGL_ClearBoundBox, to avoid a
41 * (historical?) potential crash, per a long-standing Debian/Ubuntu patch
42 * that might be redundant nowadays.
43 *
44 * Revision 6.80  2016/09/02 15:06:30  ucko
45 * shim3d.c: accommodate newer versions of libpng, which don't directly
46 * expose the jmpbuf field, as already done in Debian/Ubuntu packages.
47 *
48 * Revision 6.79  2002/03/28 13:35:48  kans
49 * only include MoreCarbonAccessors.h if not OS_UNIX_DARWIN
50 *
51 * Revision 6.78  2001/05/25 19:46:58  vakatov
52 * Nested comment typo fixed
53 *
54 * Revision 6.77  2001/04/23 16:05:45  juran
55 * Include MoreCarbonAccessors.h, which now has GetPortText{Font,Face,Size}().
56 *
57 * Revision 6.76  2001/04/21 02:36:10  juran
58 * Very simple Carbon fixes.
59 *
60 * Revision 6.75  2001/04/18 16:33:54  kans
61 * moved define to first column
62 *
63 * Revision 6.74  2000/07/28 21:05:54  lewisg
64 * more c++ fixes
65 *
66 * Revision 6.73  2000/07/27 16:34:46  lewisg
67 * more c++ fixes
68 *
69 * Revision 6.72  2000/07/27 13:37:31  lewisg
70 * more c++ fixes
71 *
72 * Revision 6.71  2000/07/25 12:38:26  thiessen
73 * change C++-style comments to C
74 *
75 * Revision 6.70  2000/07/24 22:31:21  thiessen
76 * fix header conflict
77 *
78 * Revision 6.69  2000/07/22 20:13:42  thiessen
79 * fix header conflict
80 *
81 * Revision 6.68  2000/07/21 18:55:58  thiessen
82 * allow dynamic slave->master transformation
83 *
84 * Revision 6.67  2000/05/16 17:38:44  thiessen
85 * do glGenLists after context init on X11 - for Mesa 3.2
86 *
87 * Revision 6.66  2000/04/20 18:53:57  thiessen
88 * misc tweaks/fixes
89 *
90 * Revision 6.65  2000/04/20 17:47:18  thiessen
91 * tweak OpenGL drawing region position
92 *
93 * Revision 6.64  2000/04/19 17:56:46  thiessen
94 * added background color in OpenGL
95 *
96 * Revision 6.63  2000/04/17 15:54:27  thiessen
97 * add cylinder arrows; misc graphics tweaks
98 *
99 * Revision 6.62  2000/04/03 18:23:46  thiessen
100 * add arrowheads to strand bricks
101 *
102 * Revision 6.61  2000/03/27 14:47:31  thiessen
103 * widen logo slightly
104 *
105 * Revision 6.60  2000/03/24 20:34:59  lewisg
106 * add blast from file, bug fixes, get rid of redundant code, etc.
107 *
108 * Revision 6.59  2000/03/24 19:59:20  thiessen
109 * draw new logo in OpenGL
110 *
111 * Revision 6.58  2000/03/22 23:42:22  lewisg
112 * timing loop for animation
113 *
114 * Revision 6.57  2000/03/15 16:59:53  thiessen
115 * fix highlighting, other minor bugs
116 *
117 * Revision 6.56  2000/03/09 17:55:18  thiessen
118 * changes to palette handling for 8-bit OpenGL
119 *
120 * Revision 6.55  2000/03/08 21:46:13  lewisg
121 * cn3d saves viewport, misc bugs
122 *
123 * Revision 6.54  2000/03/06 18:35:22  thiessen
124 * fixes for 8-bit color
125 *
126 * Revision 6.53  2000/02/28 19:53:08  kans
127 * if macintosh, include <gl.h> and <glu.h>, not equivalent <GL/gl.h> and <GL/glu.h>
128 *
129 * Revision 6.52  2000/02/26 13:30:41  thiessen
130 * capped cylinders and worms for visible ends
131 *
132 * Revision 6.51  2000/02/26 00:01:41  thiessen
133 * OpenGL improvements, progress on cleanup of Show/Hide
134 *
135 * Revision 6.50  2000/02/16 14:01:40  thiessen
136 * warning on OGL color black or alpha 0 (if _DEBUG)
137 *
138 * Revision 6.49  2000/02/10 17:48:10  thiessen
139 * added OGL zoom out
140 *
141 * Revision 6.48  2000/01/25 22:58:13  thiessen
142 * added animation of conf-ensembles
143 *
144 * Revision 6.47  2000/01/19 15:22:33  thiessen
145 * working off-screen GL rendering and PNG output on Mac
146 *
147 * Revision 6.46  2000/01/18 14:57:41  lewisg
148 * move OGL_qobj initialization to OGL_CreateViewer
149 *
150 * Revision 6.45  2000/01/14 21:40:39  lewisg
151 * add translucent spheres, ion labels, new cpk, fix misc bugs
152 *
153 * Revision 6.44  2000/01/12 15:13:00  thiessen
154 * fixed minor OpenGL memory leak
155 *
156 * Revision 6.43  2000/01/12 14:58:14  thiessen
157 * added progress monitor for PNG save
158 *
159 * Revision 6.42  2000/01/11 02:55:15  thiessen
160 * working off-screen OpenGL rendering on Win32
161 *
162 * Revision 6.41  2000/01/07 19:37:06  thiessen
163 * fix minor OpenGL problems
164 *
165 * Revision 6.40  2000/01/07 16:28:36  thiessen
166 * fixed broken header conflicts
167 *
168 * Revision 6.39  2000/01/07 00:22:45  thiessen
169 * fixes for LessTif and OpenGL X visual selection
170 *
171 * Revision 6.38  2000/01/06 17:41:53  kans
172 * Mac complained about True and False, changed to TRUE and FALSE
173 *
174 * Revision 6.37  2000/01/06 17:23:36  thiessen
175 * various OpenGL improvements
176 *
177 * Revision 6.36  2000/01/06 00:04:41  lewisg
178 * selection bug fixes, update message outbound, animation APIs moved to vibrant
179 *
180 * Revision 6.35  2000/01/03 14:41:10  thiessen
181 * fixed selection pointer inaccuracy
182 *
183 * Revision 6.34  1999/12/17 20:25:01  thiessen
184 * put in preliminary PNG output (for Cn3D)
185 *
186 * Revision 6.33  1999/12/15 20:02:49  thiessen
187 * added missing prototype
188 *
189 * Revision 6.32  1999/12/15 19:18:48  thiessen
190 * bug fix for previous revision
191 *
192 * Revision 6.30  1999/12/08 22:57:59  thiessen
193 * added quality settings for OpenGL rendering
194 *
195 * Revision 6.29  1999/12/07 19:18:58  thiessen
196 * fixed font color problem in OpenGL on SGI
197 *
198 * Revision 6.28  1999/12/07 15:46:17  thiessen
199 * fonts working in OpenGL on Mac
200 *
201 * Revision 6.27  1999/12/06 14:43:59  thiessen
202 * made OpenGL font selection work in X/Motif
203 *
204 * Revision 6.26  1999/12/03 15:55:01  thiessen
205 * added font styles and prettified OpenGL label panel
206 *
207 * Revision 6.25  1999/12/02 23:56:52  thiessen
208 * added font selection for OpenGL/Win32 labels
209 *
210 * Revision 6.24  1999/11/23 21:18:04  thiessen
211 * fixed Mac prototype problem
212 *
213 * Revision 6.23  1999/11/23 19:24:16  thiessen
214 * better solution to OpenGL render rect setting on Mac
215 *
216 * Revision 6.22  1999/11/23 18:41:34  thiessen
217 * fixed Mac OpenGL render rect bug
218 *
219 * Revision 6.21  1999/11/19 18:07:23  thiessen
220 * added label capability for OpenGL version on Mac and Motif
221 *
222 * Revision 6.20  1999/11/16 14:30:28  thiessen
223 * avoid white-blanking of OpenGL window on redraw
224 *
225 * Revision 6.19  1999/11/14 19:26:29  thiessen
226 * fixed broken select mode in OpenGL
227 *
228 * Revision 6.18  1999/11/10 17:17:27  thiessen
229 * fixed diffuse/ambient coloring in 8-bit opengl
230 *
231 * Revision 6.17  1999/11/10 15:25:38  thiessen
232 * partial fix for 8-bit OpenGL coloring
233 *
234 * Revision 6.16  1999/11/09 14:36:59  lewisg
235 * NT faults on write of const array
236 *
237 * Revision 6.15  1999/11/08 20:43:05  thiessen
238 * added much more thorough (but optional) GL error checking; see #define DEBUG_GL
239 *
240 * Revision 6.14  1999/11/08 19:46:30  thiessen
241 * fixed OpenGL transformation bug; also added check for GL error flag
242 *
243 * Revision 6.13  1999/11/08 16:43:20  thiessen
244 * major rearrangement of OpenGL color/material/lighting; also added 3-d (thick) brick
245 *
246 * Revision 6.12  1999/11/03 17:00:39  thiessen
247 * added capped cylinders for 'helix' object
248 *
249 * Revision 6.11  1999/10/31 22:39:34  thiessen
250 * added wifreframe worm capability to viewer3d
251 *
252 * Revision 6.10  1999/10/15 17:37:43  thiessen
253 * put in splined 'worm' model for virtual BB
254 *
255 * Revision 6.9  1999/10/04 18:05:34  thiessen
256 * fix minor glX problem with Motif
257 *
258 * Revision 6.8  1999/10/04 14:27:16  thiessen
259 * hacked partial compatibility with Mesa OpenGL implementation - does *not* work when rendering inside vibrant window in Motif
260 *
261 * Revision 6.7  1999/09/27 18:28:29  thiessen
262 * Made 24-bit and doublebuffered OpenGL modes work on Mac; also should select
263 * mode like X
264 *
265 * Revision 6.6  1999/09/27 16:29:29  thiessen
266 * added OpenGL buffer swap for X
267 *
268 * Revision 6.5  1999/09/22 14:22:49  lewisg
269 * fixed forward declaration of TOGL_Layers to compile on SGI
270 *
271 * Revision 6.4  1999/09/21 13:45:31  thiessen
272 * port of Lewis's OpenGL code to X/Motif
273 *
274 * Revision 6.3  1999/09/20 20:12:56  lewisg
275 * change typedefs for a colorcell, add triangle generator, fix incorrect return values
276 *
277 * Revision 6.2  1999/06/14 23:15:11  lewisg
278 * moved useful helper functions out of the ifdef
279 *
280 * Revision 6.1  1999/04/06 14:23:28  lewisg
281 * add opengl replacement for viewer3d
282 *
283 *
284 */
285 
286 #ifdef _OPENGL
287 
288 #if defined(WIN32)              /* braindead windows dependency */
289 #include <windows.h>
290 
291 #elif defined(macintosh)
292 #include <agl.h>
293 #include <fonts.h>
294 # if !defined(OS_UNIX_DARWIN)
295 #include "MoreCarbonAccessors.h"
296 #endif
297 
298 #elif defined(WIN_MOTIF)
299 #include <GL/glx.h>
300 #include <X11/Xlib.h>
301 #ifdef Status   /* avoid name conflict from Xlib */
302 #undef Status
303 #endif
304 
305 #endif
306 
307 /*
308 *  Include the GL dependencies.  GL has its own typedef's for basic types, just like the toolkit.
309 *  If you get warnings about type mismatch, this should be investigated.  The GL typedef's can't
310 *  be included in general toolkit code because of the windows.h dependency for WIN32 which
311 *  causes all sorts of name collisions.
312 */
313 
314 #if defined(macintosh)
315 #include <gl.h>
316 #include <glu.h>
317 #else
318 #include <GL/gl.h>
319 #include <GL/glu.h>
320 #endif
321 
322 #ifdef _PNG
323 #include <png.h> /* must go berore ncbi headers */
324 #ifndef png_jmpbuf
325 #define png_jmpbuf(x) ((x)->jmpbuf)
326 #endif
327 #endif
328 
329 /* from ncbimisc.h */
330 #include <ncbi.h>
331 NLM_EXTERN void LIBCALL Nlm_HeapSort PROTO((VoidPtr base, size_t nel, size_t width,
332                                            int (LIBCALLBACK *cmp) (VoidPtr, VoidPtr) ));
333 
334 #endif                          /* _OPENGL */
335 
336 #include <math.h>
337 #include <shim3d.h>
338 #include <stdio.h>
339 #include <ddvcolor.h>
340 
341 #if defined(_OPENGL) && defined(_PNG)
342 TOGL_Data *Cn3D_GetCurrentOGLData(void); /* in cn3dxprt.c */
343 #endif
344 
345 
346 /* VRML functions */
347 
VRML_ColorToString(Char * pString,DDV_ColorCell * pColor)348 void VRML_ColorToString(Char * pString, DDV_ColorCell * pColor)
349 {
350     sprintf(pString, "%f %f %f", pColor->rgb[0] / 255.0,
351             pColor->rgb[1] / 255.0, pColor->rgb[2] / 255.0);
352 }
353 
VRML_AddSphere3D(DDV_ColorCell * pColor,FloatHi x,FloatHi y,FloatHi z,FloatHi radius)354 void VRML_AddSphere3D(DDV_ColorCell * pColor, FloatHi x, FloatHi y,
355                       FloatHi z, FloatHi radius)
356 {
357     Char szColor[256];
358 
359     printf("Transform {\ntranslation %f %f %f\nchildren [\nShape{\n", x, y,
360            z);
361     printf("appearance Appearance {\nmaterial Material {\n");
362     VRML_ColorToString(szColor, pColor);
363     printf("diffuseColor %s\n}\n}\n", szColor);
364     printf("geometry Sphere {\n radius %f\n}\n", radius);
365     printf("}\n]\n}\n");
366 }
367 
VRML_AddCylinder3D(DDV_ColorCell * pColor,Nlm_FloatHi x1,Nlm_FloatHi y1,Nlm_FloatHi z1,Nlm_FloatHi x2,Nlm_FloatHi y2,Nlm_FloatHi z2,Nlm_FloatHi radius)368 void VRML_AddCylinder3D(DDV_ColorCell * pColor,
369                         Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
370                         Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
371                         Nlm_FloatHi radius)
372 {
373     Char szColor[256];
374     FloatHi Rotate[16], Translate[3], length, *Cross;
375     FloatHi z[3] = { 0.0, 0.0, 1.0 };
376 
377     OGL_CreateCTransform(x1, y1, z1, x2, y2, z2, Rotate, Translate,
378                          &length);
379 
380     Cross = OGL_CrossProduct(&Rotate[8], z);
381     if (Cross == NULL)
382         return;
383 
384     printf("Transform {\nrotation %f %f %f %f", Cross[0], Cross[1],
385            Cross[2], acos(Rotate[10]));
386     printf("\ntranslation %f %f %f\nchildren [\nShape{\n", Translate[0],
387            Translate[1], Translate[2]);
388     printf("appearance Appearance {\nmaterial Material {\n");
389     VRML_ColorToString(szColor, pColor);
390     printf("diffuseColor %s\n}\n}\n", szColor);
391     printf("geometry Cylinder {\n radius %f\nheight %f\ntop FALSE\n}\n",
392            radius, length);
393     printf("}\n]\n}\n");
394 
395     MemFree(Cross);
396 }
397 
398 
399 /* function in common with vrml and opengl */
400 
OGL_CrossProduct(Nlm_FloatHi * v1,Nlm_FloatHi * v2)401 FloatHi *OGL_CrossProduct(Nlm_FloatHi * v1, Nlm_FloatHi * v2)
402 {
403     Nlm_FloatHi *RetVal;
404 
405     if (v1 == NULL || v2 == NULL)
406         return NULL;
407     RetVal = MemNew(3 * sizeof(Nlm_FloatHi));
408     if (RetVal == NULL)
409         return NULL;
410 
411     RetVal[0] = v1[1] * v2[2] - v1[2] * v2[1];
412     RetVal[1] = v1[2] * v2[0] - v1[0] * v2[2];
413     RetVal[2] = v1[0] * v2[1] - v1[1] * v2[0];
414 
415     return RetVal;
416 }
417 
OGL_CreateCTransform(Nlm_FloatHi x1,Nlm_FloatHi y1,Nlm_FloatHi z1,Nlm_FloatHi x2,Nlm_FloatHi y2,Nlm_FloatHi z2,Nlm_FloatHi * Rotate,Nlm_FloatHi * Translate,Nlm_FloatHi * length)418 void OGL_CreateCTransform(Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
419                           Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
420                           Nlm_FloatHi * Rotate, Nlm_FloatHi * Translate,
421                           Nlm_FloatHi * length)
422 {
423     FloatHi a, b, c;            /* the normal z */
424     FloatHi yy2, yy3;           /* the normal y */
425     FloatHi xx1, xx2, xx3;      /* the normal x */
426     Int4 iCount;
427 
428     if (Rotate == NULL || Translate == NULL || length == NULL)
429         return;
430 
431     Translate[0] = (x1 + x2) / 2;
432     Translate[1] = (y1 + y2) / 2;
433     Translate[2] = (z1 + z2) / 2;
434 
435     *length =
436         sqrt(OGL_SQR(x1 - x2) + OGL_SQR(y1 - y2) + OGL_SQR(z1 - z2)) / 2.0;
437 
438     for (iCount = 0; iCount < 16; iCount++)
439         Rotate[iCount] = 0.0;
440     Rotate[15] = 1;             /* identity */
441 
442     /* create the normal z */
443     a = (x1 - Translate[0]) / (*length);
444     b = (y1 - Translate[1]) / (*length);
445     c = (z1 - Translate[2]) / (*length);
446 
447     /* create the normal y */
448 
449     yy2 = sqrt(1.0 / (1.0 + OGL_SQR(b) / OGL_SQR(c)));
450     yy3 = -(b / c) * yy2;
451 
452     /* create the normal x */
453 
454     xx2 =
455         sqrt(1.0 /
456              (pow(c, 4.0) / (OGL_SQR(a) * OGL_SQR(b)) +
457               2.0 * OGL_SQR(c) / OGL_SQR(a)
458               + OGL_SQR(b) / OGL_SQR(a) + 1 + OGL_SQR(c) / OGL_SQR(b)));
459     xx3 = xx2 * c / b;
460     xx1 = (-OGL_SQR(c) / (a * b) - b / a) * xx2;
461 
462 
463     /* now use the normals to make the rotation matrix */
464 
465     Rotate[0] = xx1;
466     Rotate[1] = xx2;
467     Rotate[2] = xx3;
468 
469     Rotate[4] = 0.0;
470     Rotate[5] = yy2;
471     Rotate[6] = yy3;
472 
473     Rotate[8] = a;
474     Rotate[9] = b;
475     Rotate[10] = c;
476 }
477 
478 
479 
480 #ifdef _OPENGL
481 
482 
483 static Nlm_VoidPtr OGL_CurrentName = NULL;
484 
485 
486 
487 /* define this to do (frequent) checking of GL error status - but this is
488    very expensive, so be sure to turn off for production! */
489 /* #define DEBUG_GL 1 */
490 #if defined(_DEBUG) && !defined(DEBUG_GL)
491 #define DEBUG_GL 1
492 #endif
493 
494 /* for now, just print warning if any GL error flag is set */
OGL_CheckForErrors(void)495 static Boolean OGL_CheckForErrors(void)
496 {
497     GLenum errCode;
498     const GLubyte *errString;
499     Boolean hadErrors = FALSE;
500 
501     while ((errCode = glGetError()) != GL_NO_ERROR) {
502         errString = gluErrorString(errCode);
503         Message(MSG_POST, "OpenGL error: %s", errString);
504         hadErrors = TRUE;
505     }
506     return hadErrors;
507 }
508 
509 /*
510 *   Various helper functions used in drawing
511 */
512 
513 typedef struct _TOGL_Layers
514 /* this struct contains the information used to manage the different layers of the display */
515 {
516     GLuint FirstLayer;
517     GLuint LastLayer;
518     GLuint SelectedLayer;
519     Nlm_Boolean IsOn[OGLMAXLAYERS];
520 } TOGL_Layers;
521 
522 
OGL_Normalize(Nlm_FloatHi * v)523 void OGL_Normalize(Nlm_FloatHi * v)
524 /* normalize a vector */
525 {
526     Nlm_FloatHi Length;
527 
528     if (v == NULL)
529         return;
530     Length = sqrt(OGL_SQR(v[0]) + OGL_SQR(v[1]) + OGL_SQR(v[2]));
531     v[0] /= Length;
532     v[1] /= Length;
533     v[2] /= Length;
534 }
535 
536 
OGL_MakeNormal(Nlm_FloatHi * origin,Nlm_FloatHi * v1,Nlm_FloatHi * v2)537 FloatHi *OGL_MakeNormal(Nlm_FloatHi * origin, Nlm_FloatHi * v1,
538                         Nlm_FloatHi * v2)
539 /* creates a normal to the given 3 vertices */
540 {
541     Nlm_FloatHi Vector1[3], Vector2[3], *RetValue;
542 
543     if (origin == NULL || v1 == NULL || v2 == NULL)
544         return NULL;
545     Vector1[0] = v1[0] - origin[0];
546     Vector1[1] = v1[1] - origin[1];
547     Vector1[2] = v1[2] - origin[2];
548 
549     Vector2[0] = v2[0] - origin[0];
550     Vector2[1] = v2[1] - origin[1];
551     Vector2[2] = v2[2] - origin[2];
552 
553     RetValue = OGL_CrossProduct(Vector1, Vector2);
554     OGL_Normalize(RetValue);
555     return RetValue;
556 }
557 
558 
ColorCell2Array(GLfloat * array,DDV_ColorCell * color)559 static void ColorCell2Array(GLfloat * array, DDV_ColorCell * color)
560 /* copies a color cell to a GL array */
561 {
562     if (array == NULL || color == NULL)
563         return;
564     array[0] = (GLfloat) (color->rgb[0] / 255.0);
565     array[1] = (GLfloat) (color->rgb[1] / 255.0);
566     array[2] = (GLfloat) (color->rgb[2] / 255.0);
567 }
568 
569 
570 /* these are used for both matrial colors and light colors */
571 static const GLfloat Color_Off[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
572 static const GLfloat Color_MostlyOff[4] = { 0.05f, 0.05f, 0.05f, 1.0f };
573 static const GLfloat Color_MostlyOn[4] = { 0.95f, 0.95f, 0.95f, 1.0f };
574 static const GLfloat Color_On[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
575 
576 /* cache previous color, to avoid unnecessary calls to glMaterial */
OGL_SetColor(TOGL_Data * OGL_Data,DDV_ColorCell * color,GLenum type,GLfloat alpha)577 void OGL_SetColor(TOGL_Data * OGL_Data, DDV_ColorCell * color, GLenum type,
578                   GLfloat alpha)
579 {
580 #ifdef DEBUG_GL
581     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering SetColor");
582 #endif
583 
584     if (!OGL_Data) return;
585     if (OGL_Data->IndexMode == FALSE ) {
586         static GLfloat pr, pg, pb, pa;
587         static GLenum pt = GL_NONE;
588         static GLfloat rgb[4];
589 
590         if (!color) {
591             glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Color_Off);
592             pt = GL_NONE;
593             return;
594         }
595         ColorCell2Array(rgb, color);
596 
597 #ifdef _DEBUG
598         if (rgb[0] == 0.0 && rgb[1] == 0.0 && rgb[2] == 0.0)
599             Message(MSG_POST, "Warning: OGL_Setcolor request color (0,0,0)");
600         if (alpha == 0.0)
601             Message(MSG_POST, "Warning: OGL_SetColor request alpha 0.0");
602 #endif
603 
604         rgb[3] = alpha;
605         if (rgb[0] != pr || rgb[1] != pg || rgb[2] != pb || rgb[3] != pa || type != pt) {
606             if (type != pt) {
607                 if (type == GL_DIFFUSE) {
608                     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Color_MostlyOff);
609                 } else if (type == GL_AMBIENT) {
610                     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Color_Off);
611                 } else {
612                     printf("don't know how to handle material type %i\n",type);
613                 }
614                 pt = type;
615             }
616             glMaterialfv(GL_FRONT_AND_BACK, type, rgb);
617             if (type == GL_AMBIENT) {
618                 /* this is necessary so that fonts are rendered in correct
619                    color in SGI's OpenGL implementation, and maybe others */
620                 glColor4f(rgb[0], rgb[1], rgb[2], rgb[3]);
621             }
622             pr = rgb[0];
623             pg = rgb[1];
624             pb = rgb[2];
625             pa = rgb[3];
626         }
627 
628     } else { /* color index mode */
629         ValNodePtr PaletteIndex;
630         GLint indx[3];
631         static GLint pi0 = -1, pi1, pi2;
632         static GLint pt;
633 
634         if (!color) {
635             pi0 = -1;
636             return;
637         }
638 
639         PaletteIndex = OGL_SearchPaletteIndex(OGL_Data->PaletteIndex, color);
640         if (!PaletteIndex) {
641             Message(MSG_POST, "Couldn't find color in PaletteIndex!");
642             return;
643         }
644         if (type == GL_DIFFUSE) {
645             indx[0] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->Begin;
646             indx[1] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
647             indx[2] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
648         } else if (type == GL_AMBIENT) {
649             indx[0] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
650             indx[1] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
651             indx[2] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
652         } else {
653             Message(MSG_POST, "don't know how to handle material type %i\n",type);
654         }
655 
656 #ifdef WIN32
657         /* on Windows, need to skip over the first ten static palette colors */
658         indx[0] += 10;
659         indx[1] += 10;
660         indx[2] += 10;
661 #endif
662 
663         if (indx[0] != pi0 || indx[1] != pi1 || indx[2] != pi2 || type != pt) {
664             glMaterialiv(GL_FRONT_AND_BACK, GL_COLOR_INDEXES, indx);
665             pi0 = indx[0];
666             pi1 = indx[1];
667             pi2 = indx[2];
668             pt = type;
669         }
670     }
671 
672 #ifdef DEBUG_GL
673     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving SetColor");
674 #endif
675 }
676 
677 /* only need single once-allocated quadric */
678 static GLUquadricObj *OGL_qobj = NULL;
679 
680 /*
681  *  Functions used to draw various primitives
682  */
683 
OGL_AddQuad3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_FloatHi * v1,Nlm_FloatHi * v2,Nlm_FloatHi * v3,Nlm_FloatHi * v4)684 void OGL_AddQuad3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
685                    Nlm_FloatHi * v1, Nlm_FloatHi * v2, Nlm_FloatHi * v3,
686                    Nlm_FloatHi * v4)
687 /* draws a quadralateral with the 4 given vertices of form double v1[3] */
688 {
689     Nlm_FloatHi *Normal;
690 
691     if (v1 == NULL || v2 == NULL || v3 == NULL || v4 == NULL
692         || OGL_Data == NULL || color == NULL)
693         return;
694 
695     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
696 
697     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);*/
698 
699     glBegin(GL_QUADS);
700     Normal = OGL_MakeNormal(v1, v2, v4);
701     if (Normal != NULL) {
702         glNormal3dv(Normal);
703         MemFree(Normal);
704     }
705     glVertex3dv(v1);
706     glVertex3dv(v2);
707     glVertex3dv(v3);
708     glVertex3dv(v4);
709     glEnd();
710 
711     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);*/
712 }
713 
OGL_AddBrick3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_FloatHi * Nterm,Nlm_FloatHi * Cterm,Nlm_FloatHi * norm,Nlm_FloatHi width,Nlm_FloatHi thickness,Nlm_Boolean doArrow)714 void OGL_AddBrick3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
715                     Nlm_FloatHi * Nterm, Nlm_FloatHi * Cterm,
716                     Nlm_FloatHi * norm, Nlm_FloatHi width,
717                     Nlm_FloatHi thickness, Nlm_Boolean doArrow)
718 {
719     static const double arrowLen = 2.8, arrowWidthProp = 1.6;
720 
721     GLdouble c000[3], c001[3], c010[3], c011[3],
722              c100[3], c101[3], c110[3], c111[3], n[3];
723     Nlm_FloatHi a[3], *h;
724     int i;
725 
726 #ifdef DEBUG_GL
727     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddBrick");
728 #endif
729 
730     if (Nterm == NULL || Cterm == NULL || norm == NULL ||
731         OGL_Data == NULL || color == NULL ||
732         width*thickness == 0.0)
733         return;
734 
735     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
736 
737     /* in this brick's world coordinates, the long axis (N-C direction) is
738        along +Z, with N terminus at Z=0; width is in the X direction, and
739        thickness in Y. Arrowhead at C-terminus, of course. */
740 
741     OGL_Normalize(norm);
742 
743     for (i=0; i<3; i++) {
744         a[i] = Cterm[i] - Nterm[i];
745     }
746     OGL_Normalize(a);
747     h = OGL_CrossProduct(norm, a);
748     if (!h) return;
749 
750     if (doArrow)
751         for (i=0; i<3; i++)
752             Cterm[i] -= a[i] * arrowLen;
753 
754     for (i=0; i<3; i++) {
755         c000[i] = Nterm[i] - h[i]*width/2 - norm[i]*thickness/2;
756         c001[i] = Cterm[i] - h[i]*width/2 - norm[i]*thickness/2;
757         c010[i] = Nterm[i] - h[i]*width/2 + norm[i]*thickness/2;
758         c011[i] = Cterm[i] - h[i]*width/2 + norm[i]*thickness/2;
759         c100[i] = Nterm[i] + h[i]*width/2 - norm[i]*thickness/2;
760         c101[i] = Cterm[i] + h[i]*width/2 - norm[i]*thickness/2;
761         c110[i] = Nterm[i] + h[i]*width/2 + norm[i]*thickness/2;
762         c111[i] = Cterm[i] + h[i]*width/2 + norm[i]*thickness/2;
763     }
764 
765     glBegin(GL_QUADS);
766 
767     for (i=0; i<3; i++) n[i] = norm[i];
768     glNormal3dv(n);
769     glVertex3dv(c010);
770     glVertex3dv(c011);
771     glVertex3dv(c111);
772     glVertex3dv(c110);
773 
774     for (i=0; i<3; i++) n[i] = -norm[i];
775     glNormal3dv(n);
776     glVertex3dv(c000);
777     glVertex3dv(c100);
778     glVertex3dv(c101);
779     glVertex3dv(c001);
780 
781     for (i=0; i<3; i++) n[i] = h[i];
782     glNormal3dv(n);
783     glVertex3dv(c100);
784     glVertex3dv(c110);
785     glVertex3dv(c111);
786     glVertex3dv(c101);
787 
788     for (i=0; i<3; i++) n[i] = -h[i];
789     glNormal3dv(n);
790     glVertex3dv(c000);
791     glVertex3dv(c001);
792     glVertex3dv(c011);
793     glVertex3dv(c010);
794 
795     for (i=0; i<3; i++) n[i] = -a[i];
796     glNormal3dv(n);
797     glVertex3dv(c000);
798     glVertex3dv(c010);
799     glVertex3dv(c110);
800     glVertex3dv(c100);
801 
802     if (!doArrow) {
803         for (i=0; i<3; i++) n[i] = a[i];
804         glNormal3dv(n);
805         glVertex3dv(c001);
806         glVertex3dv(c101);
807         glVertex3dv(c111);
808         glVertex3dv(c011);
809 
810     } else {
811         GLdouble FT[3], LT[3], RT[3], FB[3], LB[3], RB[3];
812         Nlm_FloatHi *nL, *nR;
813 
814         for (i=0; i<3; i++) {
815             FT[i] = Cterm[i] + norm[i]*thickness/2 + a[i]*arrowLen;
816             LT[i] = Cterm[i] + norm[i]*thickness/2 + h[i]*arrowWidthProp*width/2;
817             RT[i] = Cterm[i] + norm[i]*thickness/2 - h[i]*arrowWidthProp*width/2;
818             FB[i] = Cterm[i] - norm[i]*thickness/2 + a[i]*arrowLen;
819             LB[i] = Cterm[i] - norm[i]*thickness/2 + h[i]*arrowWidthProp*width/2;
820             RB[i] = Cterm[i] - norm[i]*thickness/2 - h[i]*arrowWidthProp*width/2;
821         }
822 
823         for (i=0; i<3; i++) n[i] = -a[i];
824         glNormal3dv(n);
825         glVertex3dv(c111);
826         glVertex3dv(LT);
827         glVertex3dv(LB);
828         glVertex3dv(c101);
829 
830         glVertex3dv(c011);
831         glVertex3dv(c001);
832         glVertex3dv(RB);
833         glVertex3dv(RT);
834 
835         for (i=0; i<3; i++) h[i] = FT[i] - LT[i];
836         if (!(nL = OGL_CrossProduct(norm, h))) return;
837         OGL_Normalize(nL);
838         for (i=0; i<3; i++) n[i] = nL[i];
839         glNormal3dv(n);
840         glVertex3dv(FT);
841         glVertex3dv(FB);
842         glVertex3dv(LB);
843         glVertex3dv(LT);
844         MemFree(nL);
845 
846         for (i=0; i<3; i++) h[i] = FT[i] - RT[i];
847         if (!(nR = OGL_CrossProduct(h, norm))) return;
848         OGL_Normalize(nR);
849         for (i=0; i<3; i++) n[i] = nR[i];
850         glNormal3dv(n);
851         glVertex3dv(FT);
852         glVertex3dv(RT);
853         glVertex3dv(RB);
854         glVertex3dv(FB);
855         MemFree(nR);
856 
857         glEnd();
858         glBegin(GL_TRIANGLES);
859 
860         for (i=0; i<3; i++) n[i] = norm[i];
861         glNormal3dv(n);
862         glVertex3dv(FT);
863         glVertex3dv(LT);
864         glVertex3dv(RT);
865 
866         for (i=0; i<3; i++) n[i] = -norm[i];
867         glNormal3dv(n);
868         glVertex3dv(FB);
869         glVertex3dv(RB);
870         glVertex3dv(LB);
871     }
872 
873     glEnd();
874 
875     MemFree(h);
876 
877 #ifdef DEBUG_GL
878     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddBrick");
879 #endif
880 }
881 
OGL_AddTri3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_FloatHi * v1,Nlm_FloatHi * v2,Nlm_FloatHi * v3,Nlm_FloatHi * Normal)882 void OGL_AddTri3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
883                   Nlm_FloatHi * v1, Nlm_FloatHi * v2, Nlm_FloatHi * v3,
884                   Nlm_FloatHi * Normal)
885 /* draws a triangle given 3 vertices of form double v1[3] and the normal */
886 {
887     if (v1 == NULL || v2 == NULL || v3 == NULL || OGL_Data == NULL ||
888         color == NULL)
889         return;
890 
891     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
892 
893     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);*/
894 
895     glBegin(GL_TRIANGLES);
896     if (Normal != NULL)
897         glNormal3dv(Normal);
898     glVertex3dv(v1);
899     glVertex3dv(v2);
900     glVertex3dv(v3);
901     glEnd();
902 
903     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);*/
904 }
905 
906 
OGL_AddCylinder3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_FloatHi x1,Nlm_FloatHi y1,Nlm_FloatHi z1,Nlm_Boolean cap1,Nlm_FloatHi x2,Nlm_FloatHi y2,Nlm_FloatHi z2,Nlm_Boolean cap2,Nlm_FloatHi radius,Nlm_Int4 sides,Nlm_Boolean doArrow)907 void OGL_AddCylinder3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
908                        Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1, Nlm_Boolean cap1,
909                        Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2, Nlm_Boolean cap2,
910                        Nlm_FloatHi radius, Nlm_Int4 sides, Nlm_Boolean doArrow)
911                         /* create a cylinder with given endcaps and radius */
912 {
913     static const double arrowLen = 4.0,
914         arrowWidthPropBase = 1.2, arrowWidthPropTip = 0.4;
915 
916     Nlm_Int4 iCount;
917     GLdouble length;
918 
919 #ifdef DEBUG_GL
920     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddCylinder");
921 #endif
922 
923     if (OGL_Data == NULL || color == NULL)
924         return;
925 
926     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
927 
928     glPushMatrix();
929 
930     length = sqrt(
931         (x2 - x1) * (x2 - x1) +
932         (y2 - y1) * (y2 - y1) +
933         (z2 - z1) * (z2 - z1)
934     );
935     if (length < 0.000001) return;
936 
937     /* to translate into place */
938     glTranslated(x1, y1, z1);
939 
940     /* to rotate from initial position, so bond points right direction;
941        handle special case where both ends share ~same x,y */
942 #define DEGREES(rad) ((rad)*180.0/3.14159265358979323846)
943     if (fabs(y1 - y2) < 0.000001 &&
944         fabs(x2 - x1) < 0.000001) {
945         if (z2 - z1 < 0.0) glRotated(180.0,1.0,0.0,0.0);
946     } else {
947         glRotated(DEGREES(acos((z2 - z1) / length)),
948                   y1 - y2, x2 - x1, 0.0);
949     }
950 
951     if (doArrow) length -= arrowLen;
952     gluCylinder(OGL_qobj, radius, radius, length, sides, 1);
953 
954     if (cap1) {
955         glPushMatrix();
956         glRotated(180.0, 0.0, 1.0, 0.0);
957         gluDisk(OGL_qobj, 0.0, radius, sides, 1);
958         glPopMatrix();
959     }
960     if (doArrow) {
961         glPushMatrix();
962         glTranslated(0.0, 0.0, length);
963         if (arrowWidthPropBase > 1.0) {
964             glPushMatrix();
965             glRotated(180.0, 0.0, 1.0, 0.0);
966             gluDisk(OGL_qobj, 0.0, radius*arrowWidthPropBase, sides, 1);
967             glPopMatrix();
968         }
969         gluCylinder(OGL_qobj, radius*arrowWidthPropBase,
970             radius*arrowWidthPropTip, arrowLen, sides, 10);
971         if (arrowWidthPropTip > 0.0) {
972             glTranslated(0.0, 0.0, arrowLen);
973             gluDisk(OGL_qobj, 0.0, radius*arrowWidthPropTip, sides, 1);
974         }
975         glPopMatrix();
976     } else if (cap2) {
977         glPushMatrix();
978         glTranslated(0.0, 0.0, length);
979         gluDisk(OGL_qobj, 0.0, radius, sides, 1);
980         glPopMatrix();
981     }
982 
983     glPopMatrix();
984 
985 #ifdef DEBUG_GL
986     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddCylinder");
987 #endif
988 }
989 
990 
OGL_AddLine3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_FloatHi x1,Nlm_FloatHi y1,Nlm_FloatHi z1,Nlm_FloatHi x2,Nlm_FloatHi y2,Nlm_FloatHi z2)991 void OGL_AddLine3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
992                    Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
993                    Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2)
994                     /* draw a single line */
995 {
996 #ifdef DEBUG_GL
997     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddLine");
998 #endif
999 
1000     if (OGL_Data == NULL || color == NULL)
1001         return;
1002 
1003     OGL_SetColor(OGL_Data, color, GL_AMBIENT, 1.0);
1004 
1005     glBegin(GL_LINES);
1006     glVertex3d(x1, y1, z1);
1007     glVertex3d(x2, y2, z2);
1008     glEnd();
1009 
1010 #ifdef DEBUG_GL
1011     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddLine");
1012 #endif
1013 }
1014 
1015 
1016 typedef struct _TransparentSphereData {
1017     DDV_ColorCell color;
1018     Nlm_FloatHi x, y, z, radius, alpha, distFromCamera;
1019     Nlm_Int4 slices, stacks;
1020     Nlm_Int1 layer;
1021     Nlm_VoidPtr name;
1022     ValNodePtr transforms;
1023     struct _TransparentSphereData *next;
1024 } TransparentSphereData, PNTR TransparentSphereDataPtr;
1025 
1026 static TransparentSphereDataPtr OGL_transSpheresTail = NULL,
1027                                 OGL_transSpheresHead = NULL,
1028                                 *OGL_transSpheresList = NULL;
1029 
OGL_DistCompareFunc(Nlm_VoidPtr va,Nlm_VoidPtr vb)1030 int LIBCALLBACK OGL_DistCompareFunc(Nlm_VoidPtr va, Nlm_VoidPtr vb)
1031 {
1032     TransparentSphereDataPtr a = *((TransparentSphereDataPtr *) va),
1033                              b = *((TransparentSphereDataPtr *) vb);
1034 
1035     if (a->distFromCamera > b->distFromCamera) return -1;
1036     else if (a->distFromCamera < b->distFromCamera) return 1;
1037 	else return 0;
1038 }
1039 
1040 Nlm_Boolean OGL_GetLayer(TOGL_Data *, Nlm_Int4);
1041 
OGL_RenderTransparentSpheres(TOGL_Data * OGL_Data)1042 static void OGL_RenderTransparentSpheres(TOGL_Data *OGL_Data)
1043 {
1044 #ifdef DEBUG_GL
1045     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_RenderTransparentSpheres");
1046 #endif
1047 
1048     if (OGL_Data && OGL_transSpheresHead) {
1049         TransparentSphereDataPtr sph;
1050         int i, n, iList;
1051         GLdouble m[16];
1052         Nlm_FloatHi x, y, z;
1053         Nlm_Boolean show;
1054 
1055         /* make an array of pointers to sphere data; sort by distance from camera */
1056         for (n=0, sph=OGL_transSpheresHead; sph; n++, sph = sph->next) {
1057 
1058             /* transform model's xyz into GL-frame coordinates */
1059             OGL_PushTransformation(sph->transforms);
1060             glGetDoublev(GL_MODELVIEW_MATRIX, m);
1061             OGL_PopTransformation();
1062 
1063             x = m[0]*sph->x + m[4]*sph->y + m[8]*sph->z + m[12];
1064             y = m[1]*sph->x + m[5]*sph->y + m[9]*sph->z + m[13];
1065             z = m[2]*sph->x + m[6]*sph->y + m[10]*sph->z + m[14];
1066             sph->distFromCamera =
1067                 sqrt((x * x) + (y * y) +
1068                      ((z - OGL_Data->CameraDistance) *
1069                       (z - OGL_Data->CameraDistance)))
1070                 - sph->radius;
1071         }
1072 
1073         if (!OGL_transSpheresList) {
1074             OGL_transSpheresList = (TransparentSphereDataPtr *)
1075                 MemNew(n * sizeof(TransparentSphereDataPtr));
1076             if (!OGL_transSpheresList) return;
1077             for (n=0, sph=OGL_transSpheresHead; sph; n++, sph=sph->next)
1078                 OGL_transSpheresList[n] = sph;
1079         }
1080         Nlm_HeapSort((Nlm_VoidPtr) OGL_transSpheresList, n,
1081                      sizeof(TransparentSphereDataPtr), OGL_DistCompareFunc);
1082 
1083         /* turn on blending */
1084         glEnable(GL_BLEND);
1085         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1086 
1087         /* render the spheres in order, and only for turned-on layers */
1088         for (i = 0; i < n; i++) {
1089             show = FALSE;
1090             if (OGL_Data->Layers->SelectedLayer) {
1091                 if (OGL_Data->Layers->SelectedLayer - OGL_Data->Layers->FirstLayer ==
1092                     OGL_transSpheresList[i]->layer) {
1093                     show = TRUE;
1094                 }
1095             } else {
1096                 for (iList = OGL_Data->Layers->FirstLayer;
1097                      iList <= OGL_Data->Layers->LastLayer;
1098                      iList++) {
1099                     if (iList - OGL_Data->Layers->FirstLayer == OGL_transSpheresList[i]->layer &&
1100                         OGL_GetLayer(OGL_Data, iList - OGL_Data->Layers->FirstLayer)) {
1101                         show = TRUE;
1102                         break;
1103                     }
1104                 }
1105             }
1106             if (show) {
1107                 OGL_LoadName(OGL_transSpheresList[i]->name);
1108                 OGL_SetColor(OGL_Data, &(OGL_transSpheresList[i]->color),
1109                              GL_DIFFUSE, OGL_transSpheresList[i]->alpha);
1110                 OGL_PushTransformation(OGL_transSpheresList[i]->transforms);
1111                 glTranslated(OGL_transSpheresList[i]->x, OGL_transSpheresList[i]->y,
1112                              OGL_transSpheresList[i]->z);
1113                 gluSphere(OGL_qobj, OGL_transSpheresList[i]->radius,
1114                           OGL_transSpheresList[i]->slices, OGL_transSpheresList[i]->stacks);
1115                 OGL_PopTransformation();
1116             }
1117         }
1118 
1119         /* blending back off now */
1120         glDisable(GL_BLEND);
1121     }
1122 
1123 #ifdef DEBUG_GL
1124     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_RenderTransparentSpheres");
1125 #endif
1126 }
1127 
1128 static Nlm_Int1 OGL_currentLayer;
1129 
OGL_AddTransparentSphere(DDV_ColorCell * color,Nlm_FloatHi x,Nlm_FloatHi y,Nlm_FloatHi z,Nlm_FloatHi radius,Nlm_Int4 slices,Nlm_Int4 stacks,Nlm_FloatHi alpha,Nlm_VoidPtr name,ValNodePtr transforms)1130 static void OGL_AddTransparentSphere(DDV_ColorCell * color,
1131                      Nlm_FloatHi x, Nlm_FloatHi y, Nlm_FloatHi z,
1132                      Nlm_FloatHi radius, Nlm_Int4 slices, Nlm_Int4 stacks,
1133                      Nlm_FloatHi alpha, Nlm_VoidPtr name, ValNodePtr transforms)
1134 {
1135     TransparentSphereDataPtr newSphere = (TransparentSphereDataPtr)
1136         MemNew(sizeof(TransparentSphereData));
1137     if (newSphere) {
1138         DDV_CopyColorCell(&(newSphere->color), color);
1139         newSphere->x = x;
1140         newSphere->y = y;
1141         newSphere->z = z;
1142         newSphere->transforms = transforms;
1143         newSphere->radius = radius;
1144         newSphere->slices = slices;
1145         newSphere->stacks = stacks;
1146         newSphere->alpha = alpha;
1147         newSphere->layer = OGL_currentLayer;
1148         newSphere->name = name;
1149         newSphere->next = NULL;
1150         if (OGL_transSpheresTail)
1151             OGL_transSpheresTail = OGL_transSpheresTail->next = newSphere;
1152         else
1153             OGL_transSpheresTail = OGL_transSpheresHead = newSphere;
1154     }
1155 }
1156 
OGL_ClearTransparentSpheres(void)1157 void OGL_ClearTransparentSpheres(void)
1158 {
1159     TransparentSphereDataPtr sph = OGL_transSpheresHead, tmp;
1160 
1161     while (sph) {
1162         tmp = sph->next;
1163         MemFree(sph);
1164         sph = tmp;
1165     }
1166     OGL_transSpheresHead = OGL_transSpheresTail = NULL;
1167     if (OGL_transSpheresList) {
1168         MemFree(OGL_transSpheresList);
1169         OGL_transSpheresList = NULL;
1170     }
1171 }
1172 
1173 
OGL_AddSphere3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_FloatHi x,Nlm_FloatHi y,Nlm_FloatHi z,Nlm_FloatHi radius,Nlm_Int4 slices,Nlm_Int4 stacks,Nlm_FloatHi alpha,ValNodePtr transforms)1174 void OGL_AddSphere3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
1175                      Nlm_FloatHi x, Nlm_FloatHi y, Nlm_FloatHi z,
1176                      Nlm_FloatHi radius, Nlm_Int4 slices, Nlm_Int4 stacks,
1177                      Nlm_FloatHi alpha, ValNodePtr transforms)
1178                       /* draws a sphere */
1179 {
1180 #ifdef DEBUG_GL
1181     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddSphere");
1182 #endif
1183 
1184     if (OGL_Data == NULL || color == NULL)
1185         return;
1186 
1187     if(!OGL_Data->IndexMode && alpha < 1.0) /* no transparency in index mode */
1188         OGL_AddTransparentSphere(color, x, y, z, radius, slices, stacks,
1189                                  alpha, OGL_CurrentName, transforms);
1190     else {
1191         OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
1192         glPushMatrix();
1193         glTranslated(x, y, z);
1194         gluSphere(OGL_qobj, radius, slices, stacks);
1195         glPopMatrix();
1196     }
1197 
1198 #ifdef DEBUG_GL
1199     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddSphere");
1200 #endif
1201 }
1202 
1203 
OGL_AddText3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_CharPtr string,Nlm_FloatHi x,Nlm_FloatHi y,Nlm_FloatHi z,Nlm_Int2 flags)1204 void OGL_AddText3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
1205                    Nlm_CharPtr string, Nlm_FloatHi x, Nlm_FloatHi y,
1206                    Nlm_FloatHi z, Nlm_Int2 flags)
1207 {
1208     Nlm_Int4 Length, i;
1209 
1210     if (OGL_Data == NULL || color == NULL || string == NULL)
1211         return;
1212 
1213     OGL_SetColor(OGL_Data, color, GL_AMBIENT, 1.0);
1214 
1215     glListBase(OGLFONTBASE);
1216 
1217     Length = Nlm_StrLen(string);
1218     glRasterPos3d(x, y, z);
1219     if (flags & OGLTEXT3D_CENTER) {
1220         glBitmap(0, 0, 0.0, 0.0,
1221                  (GLfloat) -0.5 * Length * OGL_Data->SpaceWidth, 0.0,
1222                  NULL);
1223     }
1224     if (flags & OGLTEXT3D_MIDDLE)
1225         glBitmap(0, 0, 0.0, 0.0,
1226                  0.0f, (GLfloat) (-0.5 * OGL_Data->SpaceHeight),
1227                  NULL);
1228 
1229     glCallLists(Length, GL_UNSIGNED_BYTE, string);
1230 
1231     glListBase(0);
1232 }
1233 
1234 
OGL_PushTransformation(ValNodePtr transforms)1235 void OGL_PushTransformation(ValNodePtr transforms)
1236 {
1237     FloatLoPtr pflv;
1238     FloatLoPtr *ppflm;
1239     GLfloat xmat[16];
1240 
1241     glPushMatrix();
1242 
1243     while (transforms) {
1244         if (transforms->choice == 2) { /* Move_translate */
1245             pflv = (FloatLoPtr) transforms->data.ptrvalue;
1246             glTranslatef(pflv[0], pflv[1], pflv[2]);
1247 
1248         } else if (transforms->choice == 1) { /* Move_rotate */
1249             ppflm = (FloatLoPtr *) transforms->data.ptrvalue;
1250             xmat[0]=ppflm[0][0]; xmat[4]=ppflm[1][0]; xmat[8]= ppflm[2][0]; xmat[12]=0;
1251             xmat[1]=ppflm[0][1]; xmat[5]=ppflm[1][1]; xmat[9]= ppflm[2][1]; xmat[13]=0;
1252             xmat[2]=ppflm[0][2]; xmat[6]=ppflm[1][2]; xmat[10]=ppflm[2][2]; xmat[14]=0;
1253             xmat[3]=0;           xmat[7]=0;           xmat[11]=0;           xmat[15]=1;
1254             glMultMatrixf(xmat);
1255         }
1256 
1257         transforms = transforms->next;
1258     }
1259 }
1260 
OGL_PopTransformation(void)1261 void OGL_PopTransformation(void)
1262 {
1263     glPopMatrix();
1264 }
1265 
1266 /*
1267 *   Functions used to manage display lists
1268 */
1269 
OGL_Start(TOGL_Data * OGL_Data,Nlm_Int1 List)1270 void OGL_Start(TOGL_Data * OGL_Data, Nlm_Int1 List)
1271 {
1272 #ifdef DEBUG_GL
1273     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_Start");
1274 #endif
1275 
1276     if (OGL_Data == NULL)
1277         return;
1278 
1279     /* begin a display list */
1280     if (List >= OGLMAXLAYERS)
1281         return;
1282     glNewList(List + OGL_Data->Layers->FirstLayer, GL_COMPILE);
1283     OGL_SetLayer(OGL_Data, List, TRUE);
1284     OGL_currentLayer = List;
1285 
1286     /* clear all color states */
1287     OGL_SetColor(OGL_Data, NULL, GL_NONE, 1.0);
1288 
1289 #ifdef DEBUG_GL
1290     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_Start");
1291 #endif
1292 }
1293 
OGL_End()1294 void OGL_End()
1295 /* end a display list */
1296 {
1297 #ifdef DEBUG_GL
1298     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_End");
1299 #endif
1300     glEndList();
1301 #ifdef DEBUG_GL
1302     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_End");
1303 #endif
1304 }
1305 
1306 
OGL_SetLayers(TOGL_Data * OGL_Data,Nlm_Boolean Status)1307 void OGL_SetLayers(TOGL_Data * OGL_Data, Nlm_Boolean Status)
1308 /* set the status of all the layers */
1309 {
1310     Nlm_Int4 i;
1311 
1312     if (OGL_Data == NULL)
1313         return;
1314     for (i = 0; i < OGLMAXLAYERS; i++)
1315         OGL_Data->Layers->IsOn[i] = Status;
1316     return;
1317 }
1318 
OGL_SetLayer(TOGL_Data * OGL_Data,Nlm_Int4 i,Nlm_Boolean Status)1319 void OGL_SetLayer(TOGL_Data * OGL_Data, Nlm_Int4 i, Nlm_Boolean Status)
1320 /* set the status of a particular layer -- is it on or off? */
1321 {
1322     if (OGL_Data == NULL)
1323         return;
1324     OGL_Data->Layers->IsOn[i] = Status;
1325     return;
1326 }
1327 
OGL_GetLayer(TOGL_Data * OGL_Data,Nlm_Int4 i)1328 Nlm_Boolean OGL_GetLayer(TOGL_Data * OGL_Data, Nlm_Int4 i)
1329 /* return layer status */
1330 {
1331     if (OGL_Data == NULL)
1332         return FALSE;
1333     return OGL_Data->Layers->IsOn[i];
1334 }
1335 
1336 
OGL_SetLayerTop3D(TOGL_Data * OGL_Data,Nlm_Int4 TopLayer)1337 void OGL_SetLayerTop3D(TOGL_Data * OGL_Data, Nlm_Int4 TopLayer)
1338 /* set the highest value layer used */
1339 {
1340     if (OGL_Data == NULL)
1341         return;
1342     OGL_Data->Layers->LastLayer = TopLayer + OGL_Data->Layers->FirstLayer;
1343     return;
1344 }
1345 
OGL_AllLayerOnProc(TOGL_Data * OGL_Data)1346 void OGL_AllLayerOnProc(TOGL_Data * OGL_Data)
1347 /* turn on all used layers */
1348 {
1349     if (OGL_Data == NULL)
1350         return;
1351     OGL_Data->Layers->SelectedLayer = 0;
1352     OGL_SetLayers(OGL_Data, TRUE);
1353     return;
1354 }
1355 
OGL_RewindLayerProc(TOGL_Data * OGL_Data)1356 void OGL_RewindLayerProc(TOGL_Data * OGL_Data)
1357 /* rewind to the first layer */
1358 {
1359     if (OGL_Data == NULL)
1360         return;
1361     OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1362     return;
1363 }
1364 
1365 
OGL_PrevLayerProc(TOGL_Data * OGL_Data)1366 void OGL_PrevLayerProc(TOGL_Data * OGL_Data)
1367 /* go back to the previous layer */
1368 {
1369     if (OGL_Data == NULL)
1370         return;
1371     if (OGL_Data->Layers->SelectedLayer) {
1372         if (OGL_Data->Layers->SelectedLayer > OGL_Data->Layers->FirstLayer)
1373             OGL_Data->Layers->SelectedLayer--;
1374         else
1375             OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->LastLayer;
1376     } else
1377         OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1378 
1379     return;
1380 }
1381 
1382 
OGL_NextLayerProc(TOGL_Data * OGL_Data)1383 void OGL_NextLayerProc(TOGL_Data * OGL_Data)
1384 /* go to the next layer */
1385 {
1386     if (OGL_Data == NULL)
1387         return;
1388     if (OGL_Data->Layers->SelectedLayer) {
1389         if (OGL_Data->Layers->SelectedLayer < OGL_Data->Layers->LastLayer)
1390             OGL_Data->Layers->SelectedLayer++;
1391         else
1392             OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1393     } else
1394         OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1395 
1396     return;
1397 }
1398 
1399 
OGL_Play(TOGL_Data * OGL_Data)1400 void OGL_Play(TOGL_Data * OGL_Data)
1401 /* used to flip through layers in endless loop */
1402 {
1403     if (OGL_Data == NULL)
1404         return;
1405     if (OGL_Data->Layers->SelectedLayer) {
1406         if (OGL_Data->Layers->SelectedLayer < OGL_Data->Layers->LastLayer)
1407             OGL_Data->Layers->SelectedLayer++;
1408         else
1409             OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1410     } else
1411         OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1412 
1413     return;
1414 }
1415 
1416 
1417 /*
1418  *  Color manipulation functions
1419  */
1420 
1421 
OGL_SearchPaletteIndex(ValNodePtr PaletteIndex,DDV_ColorCell * pColorCell)1422 ValNodePtr OGL_SearchPaletteIndex(ValNodePtr PaletteIndex,
1423                                   DDV_ColorCell * pColorCell)
1424 {
1425     if (PaletteIndex == NULL || pColorCell == NULL)
1426         return NULL;
1427     for (; PaletteIndex; PaletteIndex = PaletteIndex->next)
1428         if (((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->
1429             ColorCell.rgb[0] == pColorCell->rgb[0]
1430             && ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->
1431             ColorCell.rgb[1] == pColorCell->rgb[1]
1432             && ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->
1433             ColorCell.rgb[2] == pColorCell->rgb[2]) {
1434             return PaletteIndex;
1435         }
1436     return NULL;
1437 }
1438 
1439 
1440 /*
1441  *  The mouse and rotation/translation/zoom code
1442  */
1443 
MAToOGL(MAPtr ma)1444 static TOGL_Data *MAToOGL(MAPtr ma)
1445 /* extracts OGL_Data out of the extra pointer in the mouse data */
1446 {
1447     return (TOGL_Data *) MA_GetExtra(ma);
1448 }
1449 
1450 
PanelToOGL(Nlm_PaneL panel)1451 static TOGL_Data *PanelToOGL(Nlm_PaneL panel)
1452 /* extract OGL_Data out of the panel data */
1453 {
1454     MAPtr ma;
1455     Nlm_GetPanelExtra(panel, &ma);
1456 
1457     return MAToOGL(ma);
1458 }
1459 
1460 
OGL_DrawViewer3D_CB(Nlm_PaneL panel)1461 static void OGL_DrawViewer3D_CB(Nlm_PaneL panel)
1462 /* callback */
1463 {
1464     OGL_DrawViewer3D(PanelToOGL(panel));
1465 }
1466 
1467 
1468 /*
1469  *  Move
1470  */
OGL_Move3D(TOGL_Data * OGL_Data,Nlm_Int2 dx,Nlm_Int2 dy)1471 static void OGL_Move3D(TOGL_Data * OGL_Data, Nlm_Int2 dx, Nlm_Int2 dy)
1472 {
1473     GLint viewport[4];
1474     Nlm_FloatHi pixelSize;
1475 
1476     glGetIntegerv(GL_VIEWPORT, viewport);
1477 
1478     pixelSize =
1479         tan(OGL_Data->CameraAngle / 2.0) *
1480         2.0 * OGL_Data->CameraDistance /
1481         viewport[3];
1482 
1483     OGL_Data->CameraDirection[0] -= dx * pixelSize;
1484     OGL_Data->CameraDirection[1] += dy * pixelSize;
1485 
1486     OGL_Data->NeedCameraSetup = TRUE;
1487 }
1488 
1489 
1490 /*
1491  *  Zoom
1492  */
1493 extern void Nlm_GetRect (Nlm_GraphiC a, Nlm_RectPtr r);
1494 
OGL_Zoom3D(TOGL_Data * OGL_Data,Nlm_Int2 x1,Nlm_Int2 y1,Nlm_Int2 x2,Nlm_Int2 y2)1495 static void OGL_Zoom3D(TOGL_Data * OGL_Data, Nlm_Int2 x1, Nlm_Int2 y1,
1496                        Nlm_Int2 x2, Nlm_Int2 y2)
1497 {
1498     Nlm_FloatHi zoom;
1499     Nlm_RecT rect;
1500     GLint viewport[4];
1501 
1502     /* translate window coords as passed to this function into coords
1503        relative to OpenGL area (the Panel) coords */
1504     Nlm_GetRect((Nlm_GraphiC)OGL_Data->Panel, &rect);
1505     x1 -= rect.left;
1506     x2 -= rect.left;
1507     y1 -= rect.top;
1508     y2 -= rect.top;
1509 
1510     /* set new camera direction ... */
1511     glGetIntegerv(GL_VIEWPORT, viewport);
1512     OGL_Move3D(OGL_Data,
1513         (viewport[2] - (x1 + x2)) / 2,
1514         (viewport[3] - (y1 + y2)) / 2);
1515 
1516     /* ... and angle. (This assumes zoom box aspect ratio is correct!) */
1517     zoom = ((Nlm_FloatHi) abs(y1 - y2)) / viewport[3];
1518     OGL_Data->CameraAngle = atan(zoom * tan(OGL_Data->CameraAngle));
1519 
1520     OGL_Data->NeedCameraSetup = TRUE;
1521 }
1522 
OGL_ZoomOut(TOGL_Data * OGL_Data)1523 NLM_EXTERN void OGL_ZoomOut(TOGL_Data *OGL_Data)
1524 {
1525     OGL_Data->CameraAngle *= 1.5;
1526     OGL_Data->NeedCameraSetup = TRUE;
1527     OGL_DrawViewer3D(OGL_Data);
1528 }
1529 
OGL_ZoomIn(TOGL_Data * OGL_Data)1530 NLM_EXTERN void OGL_ZoomIn(TOGL_Data *OGL_Data)
1531 {
1532     OGL_Data->CameraAngle /= 1.5;
1533     OGL_Data->NeedCameraSetup = TRUE;
1534     OGL_DrawViewer3D(OGL_Data);
1535 }
1536 
1537 /*
1538  *  Rotation
1539  */
1540 
1541 
1542 typedef enum {
1543     ROTATE_X,
1544     ROTATE_Y,
1545     ROTATE_Z
1546 } OGL_enumRotate3D;
1547 
1548 typedef struct {
1549     OGL_enumRotate3D H;         /* horizontal dragging */
1550     OGL_enumRotate3D V;         /* vertical   dragging */
1551 } OGL_RotatePivots3D, *OGL_RotatePivots3DPtr;
1552 
1553 
OGL_Rotate(TOGL_Data * OGL_Data,Nlm_Int4 dAngle,OGL_enumRotate3D pivot)1554 static void OGL_Rotate(TOGL_Data * OGL_Data, Nlm_Int4 dAngle,
1555                        OGL_enumRotate3D pivot)
1556 {
1557 #ifdef DEBUG_GL
1558     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_Rotate");
1559 #endif
1560 
1561     if (!dAngle)
1562         return;
1563     if (OGL_Data == NULL)
1564         return;
1565 
1566     glLoadIdentity();
1567 
1568     switch (pivot) {
1569     case ROTATE_X:
1570         glRotatef((GLfloat) (dAngle), 1.0f, 0.0f, 0.0f);
1571         break;
1572     case ROTATE_Y:
1573         glRotatef((GLfloat) (dAngle), 0.0f, 1.0f, 0.0f);
1574         break;
1575     case ROTATE_Z:
1576         glRotatef((GLfloat) (dAngle), 0.0f, 0.0f, 1.0f);
1577         break;
1578     }
1579 
1580     glMultMatrixd((GLdouble *) OGL_Data->ModelMatrix);
1581 
1582     glGetDoublev(GL_MODELVIEW_MATRIX, (GLdouble *) OGL_Data->ModelMatrix);
1583 
1584 #ifdef DEBUG_GL
1585     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_Rotate");
1586 #endif
1587 }
1588 
1589 
1590 
OGL_ViewerRotate(Nlm_SlatE panel,Nlm_Int4 delta,OGL_enumRotate3D pivot,Nlm_Boolean adjust_scrollbar,Nlm_Boolean redraw)1591 static void OGL_ViewerRotate(Nlm_SlatE panel, Nlm_Int4 delta,
1592                              OGL_enumRotate3D pivot,
1593                              Nlm_Boolean adjust_scrollbar,
1594                              Nlm_Boolean redraw)
1595 {
1596     TOGL_Data *OGL_Data;
1597 
1598     if (panel == NULL ||
1599         !Nlm_Visible(panel) || !Nlm_AllParentsVisible(panel)) return;
1600 
1601     OGL_Data = PanelToOGL((Nlm_PaneL) panel);
1602 
1603     if (adjust_scrollbar) {     /* adjust the relevant rotation scrollbar, if any */
1604         Nlm_BaR sbar = NULL;
1605         switch (pivot) {
1606         case ROTATE_X:
1607             sbar = Nlm_GetSlateVScrollBar(panel);
1608             break;
1609         case ROTATE_Y:
1610             sbar = Nlm_GetSlateHScrollBar(panel);
1611             break;
1612         case ROTATE_Z:
1613             sbar = OGL_Data->Z_rotate;
1614             break;
1615         }
1616 
1617         if (sbar) {
1618             Nlm_ResetClip();
1619             Nlm_CorrectBarValue(sbar,
1620                                 (Nlm_GetValue(sbar) + delta + 360) % 360);
1621         }
1622     }
1623 
1624     /* the coordination transformation (rotation) */
1625     if (pivot == ROTATE_X)
1626         delta = -delta;
1627     OGL_Rotate(OGL_Data, delta, pivot);
1628 
1629     if (redraw) {               /* Draw the viewer */
1630         OGL_DrawViewer3D(OGL_Data);
1631     }
1632 
1633     /*  Nlm_DiagReset(); */
1634 }
1635 
1636 
1637 
1638 /* scrollbar callbacks */
OGL_ViewerVScrollProc(Nlm_BaR sb,Nlm_SlatE viewer,Nlm_Int2 newval,Nlm_Int2 oldval)1639 static void OGL_ViewerVScrollProc(Nlm_BaR sb, Nlm_SlatE viewer,
1640                                   Nlm_Int2 newval, Nlm_Int2 oldval)
1641 {
1642     OGL_ViewerRotate(viewer, newval - oldval, ROTATE_X, FALSE, TRUE);
1643 }
1644 
OGL_ViewerHScrollProc(Nlm_BaR sb,Nlm_SlatE viewer,Nlm_Int2 newval,Nlm_Int2 oldval)1645 static void OGL_ViewerHScrollProc(Nlm_BaR sb, Nlm_SlatE viewer,
1646                                   Nlm_Int2 newval, Nlm_Int2 oldval)
1647 {
1648     OGL_ViewerRotate(viewer, newval - oldval, ROTATE_Y, FALSE, TRUE);
1649 }
1650 
OGL_ViewerZScrollProc(Nlm_BaR sb,Nlm_GraphiC group,Nlm_Int2 newval,Nlm_Int2 oldval)1651 static void OGL_ViewerZScrollProc(Nlm_BaR sb, Nlm_GraphiC group,
1652                                   Nlm_Int2 newval, Nlm_Int2 oldval)
1653 {
1654     Nlm_SlatE viewer = (Nlm_SlatE) Nlm_GetObjectExtra(sb);
1655     OGL_ViewerRotate(viewer, newval - oldval, ROTATE_Z, FALSE, TRUE);
1656 }
1657 
1658 
1659 
1660 
1661 /*
1662  *  MOUSE EVENT HANDLERS
1663  */
1664 
1665 
1666 
1667 /*
1668  * MOVE
1669  */
1670 
OGL_Move_DrawTrace(MA_TracePtr trace)1671 static void OGL_Move_DrawTrace(MA_TracePtr trace)
1672 {
1673     if (Nlm_EqualPt(trace->start, trace->end))
1674         return;
1675 
1676 #ifdef WIN_MOTIF
1677     Nlm_SetColor(0xf1);
1678 #endif
1679     Nlm_InvertMode();           /* turn on xor */
1680     Nlm_DrawLine(trace->start, trace->end); /* draw to current hdc */
1681     Nlm_CopyMode();             /* turn off xor */
1682 }
1683 
1684 
OGL_Move_PressMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1685 static void OGL_Move_PressMA(MAPtr ma,
1686                              MA_TracePtr trace, Nlm_PoinT point,
1687                              Nlm_VoidPtr extra)
1688 {
1689     trace->start = trace->end = point;
1690 }
1691 
1692 
OGL_Move_DragMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1693 static void OGL_Move_DragMA(MAPtr ma,
1694                             MA_TracePtr trace, Nlm_PoinT point,
1695                             Nlm_VoidPtr extra)
1696 {
1697     OGL_Move_DrawTrace(trace);
1698     trace->end = point;
1699     OGL_Move_DrawTrace(trace);
1700 }
1701 
1702 
OGL_Move_ReleaseMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1703 static void OGL_Move_ReleaseMA(MAPtr ma,
1704                                MA_TracePtr trace, Nlm_PoinT point,
1705                                Nlm_VoidPtr extra)
1706 {
1707     OGL_Move_DrawTrace(trace);
1708     trace->end = point;
1709 
1710     if (Nlm_EqualPt(trace->start, trace->end))
1711         return;
1712 
1713     {                           /* do the move transform */
1714         TOGL_Data *OGL_Data = MAToOGL(ma);
1715 
1716         OGL_Move3D(OGL_Data, (Int2) (trace->end.x - trace->start.x),
1717                    (Int2) (trace->end.y - trace->start.y));
1718         OGL_DrawViewer3D(OGL_Data);
1719 
1720     }
1721 
1722     trace->start = trace->end;
1723 }
1724 
1725 
OLG_Move_CancelMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1726 static void OLG_Move_CancelMA(MAPtr ma,
1727                               MA_TracePtr trace, Nlm_PoinT point,
1728                               Nlm_VoidPtr extra)
1729 {
1730     OGL_Move_DrawTrace(trace);
1731 }
1732 
1733 
1734 /*
1735  * ZOOM
1736  */
1737 
OGL_Zoom_DrawTrace(MA_TracePtr trace)1738 static void OGL_Zoom_DrawTrace(MA_TracePtr trace)
1739 {
1740     Nlm_RecT rubber_box;
1741     if (Nlm_EqualPt(trace->start, trace->end))
1742         return;
1743 
1744 #ifdef WIN_MOTIF
1745     Nlm_SetColor(0xf1);
1746 #endif
1747     Nlm_InvertMode();
1748     Nlm_LoadRect(&rubber_box, trace->start.x, trace->start.y,
1749                  trace->end.x, trace->end.y);
1750     Nlm_FrameRect(&rubber_box); /* draw the frame */
1751     Nlm_CopyMode();
1752 }
1753 
OGL_Zoom_PressMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1754 static void OGL_Zoom_PressMA(MAPtr ma,
1755                              MA_TracePtr trace, Nlm_PoinT point,
1756                              Nlm_VoidPtr extra)
1757 {
1758     trace->start = trace->end = point;
1759 }
1760 
1761 
1762 /* constrain the rubber band shape as it's dragged to match the aspect
1763    ratio of the OpenGL region */
OGL_Zoom_DragMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1764 static void OGL_Zoom_DragMA(MAPtr ma,
1765                             MA_TracePtr trace, Nlm_PoinT point,
1766                             Nlm_VoidPtr extra)
1767 {
1768     TOGL_Data *OGL_Data = MAToOGL(ma);
1769     GLint viewport[4];
1770 
1771     glGetIntegerv(GL_VIEWPORT, viewport);
1772 
1773     OGL_Zoom_DrawTrace(trace);
1774     if (point.y >= trace->start.y)
1775         point.y = trace->start.y +
1776             abs(trace->end.x - trace->start.x) *
1777             viewport[3] / viewport[2];
1778     else
1779         point.y = trace->start.y -
1780             abs(trace->end.x - trace->start.x) *
1781             viewport[3] / viewport[2];
1782     trace->end = point;
1783     OGL_Zoom_DrawTrace(trace);
1784 }
1785 
1786 
OGL_Zoom_ReleaseMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1787 static void OGL_Zoom_ReleaseMA(MAPtr ma,
1788                                MA_TracePtr trace, Nlm_PoinT point,
1789                                Nlm_VoidPtr extra)
1790 {
1791     OGL_Zoom_DrawTrace(trace);
1792 
1793     if (Nlm_EqualPt(trace->start, trace->end))
1794         return;
1795 
1796     {                           /* do the zoom */
1797         TOGL_Data *OGL_Data = MAToOGL(ma);
1798 
1799         OGL_Zoom3D(OGL_Data, trace->start.x, trace->start.y,
1800                    trace->end.x, trace->end.y);
1801         OGL_DrawViewer3D(OGL_Data);
1802 
1803     }
1804 }
1805 
OGL_Zoom_CancelMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1806 static void OGL_Zoom_CancelMA(MAPtr ma,
1807                               MA_TracePtr trace, Nlm_PoinT point,
1808                               Nlm_VoidPtr extra)
1809 {
1810     OGL_Zoom_DrawTrace(trace);
1811 }
1812 
1813 
1814 /*
1815  * ROTATE
1816  */
1817 
1818 
OGL_Rotate_PressMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1819 static void OGL_Rotate_PressMA(MAPtr ma,
1820                                MA_TracePtr trace, Nlm_PoinT point,
1821                                Nlm_VoidPtr extra)
1822 {
1823     trace->start = point;
1824 }
1825 
1826 
1827 
OGL_Rotate_DragMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1828 static void OGL_Rotate_DragMA(MAPtr ma,
1829                               MA_TracePtr trace, Nlm_PoinT point,
1830                               Nlm_VoidPtr extra)
1831 {
1832     TOGL_Data *OGL_Data;
1833     GLint viewport[4];
1834 
1835     OGL_RotatePivots3DPtr pivot;
1836     if (Nlm_EqualPt(trace->start, point))
1837         return;
1838 
1839     OGL_Data = MAToOGL(ma);
1840     pivot = (OGL_RotatePivots3DPtr) extra;
1841 
1842     glGetIntegerv(GL_VIEWPORT, viewport);
1843 
1844     OGL_ViewerRotate((Nlm_SlatE) OGL_Data->Panel,
1845                      (Int4) ((180.0 * (point.x - trace->start.x)) /
1846                              viewport[2]), pivot->H, TRUE, FALSE);
1847 
1848     OGL_ViewerRotate((Nlm_SlatE) OGL_Data->Panel,
1849                      (Int4) ((180.0 * (trace->start.y - point.y)) /
1850                              viewport[3]), pivot->V, TRUE, TRUE);
1851     trace->start = point;
1852 }
1853 
1854 
1855 /*
1856  * RESET
1857  */
1858 
OGL_ResetMA(MAPtr ma,MA_TracePtr trace,Nlm_PoinT point,Nlm_VoidPtr extra)1859 static void OGL_ResetMA(MAPtr ma,
1860                         MA_TracePtr trace, Nlm_PoinT point,
1861                         Nlm_VoidPtr extra)
1862 {
1863     VERIFY(MA_UnsetAll(ma));
1864 }
1865 
1866 
OGL_SetStdMouse(TOGL_Data * OGL_Data,Nlm_enumStdMAOGL action)1867 static Nlm_Boolean OGL_SetStdMouse(TOGL_Data * OGL_Data,
1868                                    Nlm_enumStdMAOGL action)
1869 {
1870     if (OGL_Data == NULL)
1871         return FALSE;
1872     return MA_SetGroup(OGL_Data->ma_std_group[action]);
1873 }
1874 
1875 
1876 
1877 /* Initialize MA for the viewer
1878 */
1879 
OGL_InitializeMA(TOGL_Data * OGL_Data)1880 static Nlm_Boolean OGL_InitializeMA(TOGL_Data * OGL_Data)
1881 {
1882     MAPtr ma = OGL_Data->ma;
1883 
1884     /* rotate */
1885     MActionPtr rotate_press =
1886         MA_AddAction(ma, MK_Normal, MA_Press, OGL_Rotate_PressMA, NULL,
1887                      NULL);
1888 
1889     static OGL_RotatePivots3D RotateDrag_YX = { ROTATE_Y, ROTATE_X };
1890     MActionPtr rotate_drag_YX =
1891         MA_AddAction(ma, MK_Normal, MA_Drag, OGL_Rotate_DragMA,
1892                      &RotateDrag_YX, NULL);
1893     MA_GroupPtr rotate_group_YX = MA_AddGroup(ma, "Rotate_YX",
1894                                               rotate_press, MA_ONLY,
1895                                               rotate_drag_YX, MA_ONLY,
1896                                               NULL);
1897 
1898     static OGL_RotatePivots3D RotateDrag_ZX = { ROTATE_Z, ROTATE_X };
1899     MActionPtr rotate_drag_ZX =
1900         MA_AddAction(ma, MK_Normal, MA_Drag, OGL_Rotate_DragMA,
1901                      &RotateDrag_ZX, NULL);
1902     MA_GroupPtr rotate_group_ZX = MA_AddGroup(ma, "Rotate_ZX",
1903                                               rotate_press, MA_ONLY,
1904                                               rotate_drag_ZX, MA_ONLY,
1905                                               NULL);
1906 
1907     static OGL_RotatePivots3D RotateDrag_YZ = { ROTATE_Y, ROTATE_Z };
1908     MActionPtr rotate_drag_YZ =
1909         MA_AddAction(ma, MK_Normal, MA_Drag, OGL_Rotate_DragMA,
1910                      &RotateDrag_YZ, NULL);
1911     MA_GroupPtr rotate_group_YZ = MA_AddGroup(ma, "Rotate_YZ",
1912                                               rotate_press, MA_ONLY,
1913                                               rotate_drag_YZ, MA_ONLY,
1914                                               NULL);
1915 
1916     /* move */
1917     MActionPtr move_press =
1918         MA_AddAction(ma, MK_Shift, MA_Press, OGL_Move_PressMA, NULL, NULL);
1919     MActionPtr move_drag =
1920         MA_AddAction(ma, MK_Shift, MA_Drag, OGL_Move_DragMA, NULL, NULL);
1921     MActionPtr move_release =
1922         MA_AddAction(ma, MK_Shift, MA_Release, OGL_Move_ReleaseMA, NULL,
1923                      NULL);
1924     MActionPtr move_cancel =
1925         MA_AddAction(ma, MK_Shift, MA_Cancel, OLG_Move_CancelMA, NULL,
1926                      NULL);
1927 
1928     MA_GroupPtr move_group = MA_AddGroup(ma, "Move",
1929                                          move_press, MA_ONLY,
1930                                          move_drag, MA_ONLY,
1931                                          move_release, MA_ONLY,
1932                                          move_cancel, MA_ONLY,
1933                                          NULL);
1934 
1935     /* zoom */
1936     MActionPtr zoom_press =
1937         MA_AddAction(ma, MK_Ctrl, MA_Press, OGL_Zoom_PressMA, NULL, NULL);
1938     MActionPtr zoom_drag =
1939         MA_AddAction(ma, MK_Ctrl, MA_Drag, OGL_Zoom_DragMA, NULL, NULL);
1940     MActionPtr zoom_release =
1941         MA_AddAction(ma, MK_Ctrl, MA_Release, OGL_Zoom_ReleaseMA, NULL,
1942                      NULL);
1943     MActionPtr zoom_cancel =
1944         MA_AddAction(ma, MK_Ctrl, MA_Cancel, OGL_Zoom_CancelMA, NULL,
1945                      NULL);
1946 
1947     MA_GroupPtr zoom_group = MA_AddGroup(ma, "Zoom",
1948                                          zoom_press, MA_ONLY,
1949                                          zoom_drag, MA_ONLY,
1950                                          zoom_release, MA_ONLY,
1951                                          zoom_cancel, MA_ONLY,
1952                                          NULL);
1953 
1954     /* miscellaneous actions */
1955 /*
1956  this is done in the main program.  move it here after deleting viewer3d
1957     MActionPtr bg_hl_dclick =
1958         MA_AddAction(ma, MK_Normal, MA_DClick,  NULL, NULL,
1959         "Highlight-Prim or Background");
1960 */
1961 
1962     /* this group disables all mouse actions when set */
1963     MActionPtr reset_init =
1964         MA_AddAction(ma, MK_Normal, MA_Init, OGL_ResetMA, NULL, NULL);
1965 
1966     MA_GroupPtr reset_group = MA_AddGroup(ma, "No Action",
1967                                           reset_init, MA_SHARED,
1968                                           NULL);
1969 
1970     if (OGL_Data == NULL)
1971         return FALSE;
1972 
1973     { {                         /* "No-Action"s */
1974             int i, j;
1975             for (i = 0; i < MK_Default; i++)
1976                 for (j = 0; j < MA_Init; j++) {
1977                     VERIFY(MA_AddAction(ma, (enumMKey) i, (enumMAction) j,
1978                                         DoNothingMA, NULL, "No Action"));
1979                 }
1980     }
1981     }
1982 
1983     /* register the set of standard 3D-viewer groups */
1984     OGL_Data->ma_std_group[MouseOGL_DoNothing] = reset_group;
1985     OGL_Data->ma_std_group[MouseOGL_RotateYX] = rotate_group_YX;
1986     OGL_Data->ma_std_group[MouseOGL_RotateZX] = rotate_group_ZX;
1987     OGL_Data->ma_std_group[MouseOGL_RotateYZ] = rotate_group_YZ;
1988     OGL_Data->ma_std_group[MouseOGL_Move] = move_group;
1989     OGL_Data->ma_std_group[MouseOGL_Zoom] = zoom_group;
1990 
1991     /* Test, Setup defaults and Link viewer panel to MA */
1992     if (!rotate_press ||
1993         !rotate_drag_YX || !rotate_group_YX ||
1994         !rotate_drag_ZX || !rotate_group_ZX ||
1995         !rotate_drag_YZ || !rotate_group_YZ ||
1996         !move_press || !move_drag || !move_release ||
1997         !move_cancel || !move_group ||
1998         !zoom_press || !zoom_drag || !zoom_release ||
1999         !zoom_cancel || !zoom_group ||
2000 /*        !bg_hl_dclick    ||*/
2001         !reset_group ||
2002         !OGL_SetStdMouse(OGL_Data, MouseOGL_RotateYX) ||
2003         !OGL_SetStdMouse(OGL_Data, MouseOGL_Move) ||
2004         !OGL_SetStdMouse(OGL_Data, MouseOGL_Zoom) ||
2005 /*        !MA_SetAction(bg_hl_dclick, FALSE)  ||*/
2006         !MA_LinkPanel(ma, OGL_Data->Panel)) {
2007         MA_Reset(ma);
2008         return FALSE;
2009     }
2010 
2011     return TRUE;
2012 }
2013 
2014 
2015 /*
2016  *  Doing selection in OpenGL
2017  */
2018 
OGL_Select(TOGL_Data * OGL_Data,Nlm_Boolean SelectMode)2019 void OGL_Select(TOGL_Data * OGL_Data, Nlm_Boolean SelectMode)
2020 {
2021     if (OGL_Data == NULL)
2022         return;
2023     OGL_Data->SelectMode = SelectMode;
2024     return;
2025 }
2026 
2027 
OGL_LoadName(Nlm_VoidPtr PtrValue)2028 void OGL_LoadName(Nlm_VoidPtr PtrValue)
2029 /* load a pointer onto the name stack.  compensate for possible long long */
2030 {
2031     Nlm_Int4 i;
2032 
2033     for (i = 0; i < sizeof(Nlm_VoidPtr) / sizeof(GLuint); i++)
2034         glPopName();
2035 
2036     for (i = 0; i < sizeof(Nlm_VoidPtr) / sizeof(GLuint); i++)
2037         glPushName((GLuint) (((long) PtrValue) >> (i * sizeof(GLuint) * 8))); /* 64 bits? */
2038 
2039     OGL_CurrentName = PtrValue;
2040 }
2041 
2042 
OGL_Hit(TOGL_Data * OGL_Data)2043 Nlm_VoidPtr OGL_Hit(TOGL_Data * OGL_Data)
2044 /* this function looks through the hit stack and extracts the nearest hit */
2045 {
2046     GLuint *Hits, nNames, hit, p = 0, j, ZMin;
2047     long ZMinName = 0;          /* this is a hack, but should work for 64 bits */
2048 
2049     if (OGL_Data == NULL || OGL_Data->SelectBuffer == NULL)
2050         return NULL;
2051 
2052     Hits = (GLuint *) OGL_Data->SelectBuffer;
2053     for (hit=0; hit < OGL_Data->SelectHits; hit++) { /* loop over all hits */
2054         nNames = Hits[p];
2055         p++; /* move to zmin */
2056         /* look for *minimum* depth - that's the top object on the screen */
2057         if (hit == 0 || Hits[p] < ZMin) {
2058             ZMin = Hits[p];
2059             p += 2; /* skip over zmax to name stack */
2060             ZMinName = 0;
2061             /* currently only looks at the top of the name stack */
2062             for (j = 0; j < sizeof(Nlm_VoidPtr) / sizeof(GLuint); j++) {
2063                 ZMinName = ZMinName << (sizeof(GLuint) * 8);
2064                 ZMinName |= Hits[p + j];
2065             }
2066             p += nNames * sizeof(Nlm_VoidPtr) / sizeof(GLuint);
2067         } else
2068             p += nNames * sizeof(Nlm_VoidPtr) / sizeof(GLuint) + 2;
2069     }
2070     return (Nlm_VoidPtr) ZMinName;
2071 }
2072 
2073 
OGL_SetSelectPoint(TOGL_Data * OGL_Data,Nlm_PoinT Point)2074 void OGL_SetSelectPoint(TOGL_Data * OGL_Data, Nlm_PoinT Point)
2075 {
2076     Nlm_RecT rect;
2077 
2078     if (OGL_Data == NULL)
2079         return;
2080 
2081     OGL_Data->SelectPoint.x = Point.x;
2082     OGL_Data->SelectPoint.y = Point.y;
2083 
2084     /* translate window coords as passed to this function into coords
2085        relative to OpenGL area (the Panel) coords */
2086     Nlm_GetRect((Nlm_GraphiC)OGL_Data->Panel, &rect);
2087     OGL_Data->SelectPoint.x -= rect.left;
2088     OGL_Data->SelectPoint.y -= rect.top;
2089 
2090 #ifdef WIN_MSWIN
2091     /* For some reason, windows requires a fudge to get the mouse pointer
2092        to be more accurate */
2093     OGL_Data->SelectPoint.x += 1;
2094     OGL_Data->SelectPoint.y -= 4;
2095 #endif
2096 }
2097 
2098 
2099 /*
2100  *  functions used to create, do, and finish drawing
2101  */
2102 
2103 
OGL_DeleteViewer3D(TOGL_Data * OGL_Data)2104 static void OGL_DeleteViewer3D(TOGL_Data * OGL_Data)
2105 /* delete OGL_Data */
2106 {
2107     if (OGL_Data == NULL)
2108         return;
2109 
2110     MA_Destroy(OGL_Data->ma);
2111 
2112     /* to do: someone else is getting rid of Panel
2113        if ( OGL_Data->Panel )
2114        Nlm_Remove( OGL_Data->Panel );
2115      */
2116 
2117     /* delete the display lists */
2118 #ifndef MESA
2119     /* There's some bug (?) in Mesa that causes a crash here, maybe because
2120        we're trying to delete non-existent lists? Dunno... */
2121     glDeleteLists(OGL_Data->Layers->FirstLayer - 1, OGLMAXLAYERS);
2122 #endif
2123 
2124     /* free items on the heap */
2125     MemFree(OGL_Data->Layers);
2126     MemFree(OGL_Data->ModelMatrix);
2127     MemFree(OGL_Data);
2128 }
2129 
2130 
OGL_ResetViewerProc_CB(Nlm_PaneL panel)2131 static void OGL_ResetViewerProc_CB(Nlm_PaneL panel)
2132 {
2133     TOGL_Data *OGL_Data = PanelToOGL(panel);
2134     if (!OGL_Data)
2135         return;
2136 
2137     OGL_Data = NULL;
2138     OGL_DeleteViewer3D(PanelToOGL(panel));
2139 }
2140 
2141 
2142 typedef struct {
2143 #ifdef WIN_MAC
2144     int familyID;
2145 #else
2146     char *name;
2147 #endif
2148 } OGLFontListItem;
2149 
2150 #define MAXFONTS 1000
2151 static OGLFontListItem OGLFontList[MAXFONTS]; /* should really be dynamic... */
2152 static int nFonts = 0;
2153 
2154 #if defined(WIN32)
Nlm_FindFontCB(ENUMLOGFONTEX * lpelfe,NEWTEXTMETRICEX * lpntme,DWORD FontType,LPARAM lParam)2155 int CALLBACK Nlm_FindFontCB(
2156   ENUMLOGFONTEX *lpelfe,
2157   NEWTEXTMETRICEX *lpntme,
2158   DWORD FontType,
2159   LPARAM lParam )
2160 {
2161     OGLFontList[nFonts].name = strdup(lpelfe->elfLogFont.lfFaceName);
2162     Nlm_PopupItem((Nlm_PopuP)lParam, OGLFontList[nFonts].name);
2163     nFonts++;
2164     if (nFonts == MAXFONTS)
2165         return 0;
2166     else
2167         return 1;
2168 }
2169 
2170 #elif defined(WIN_MOTIF)
2171 static char *standardXFont = "9x15";
2172 #endif
2173 
2174 /* called during app initialization, to set up font family menu */
Nlm_FindAvailableFonts(Nlm_PopuP pupmenu)2175 NLM_EXTERN void Nlm_FindAvailableFonts(Nlm_PopuP pupmenu)
2176 {
2177 #if defined(WIN32)
2178     LOGFONT lf;
2179     HDC hdc = wglGetCurrentDC();
2180     /* to enumerate all styles of all fonts for the ANSI character set */
2181     lf.lfFaceName[0] = '\0';
2182     lf.lfCharSet = ANSI_CHARSET;
2183     lf.lfPitchAndFamily = 0;
2184     EnumFontFamiliesEx((HDC)hdc, (LPLOGFONT)&lf, (FONTENUMPROC)Nlm_FindFontCB,
2185                         (LPARAM)pupmenu, (DWORD)0);
2186 
2187 #elif defined(WIN_MAC)
2188 	int i;
2189 	char name[255];
2190 
2191 #define ADD_FONT(str,ID) do { \
2192                            OGLFontList[nFonts].familyID = (ID); \
2193                            Nlm_PopupItem(pupmenu, (str)); \
2194                            nFonts++; \
2195                            if (nFonts == MAXFONTS) return; \
2196                          } while (0)
2197 
2198 	/* There's gotta be a better way to get a list of
2199 	   available fonts... this takes forever even on a fast mac! */
2200 	for (i=2; i<=16383; i++) {
2201 		if (i==256) i=1024;
2202 		if (!RealFont(i,12)) continue;
2203 		memset(name, '\0', 255);
2204 		GetFontName(i, (unsigned char *)name);
2205 		if (name[0] != '\0') {
2206 		  ADD_FONT(name+1, i); /* for some reason, name starts at 2nd character? */
2207 		}
2208 	}
2209 
2210 #elif defined(WIN_MOTIF)
2211     extern Display *Nlm_currentXDisplay;
2212     char **list, fnd_fam[256], *p;
2213     int i, j, tot;
2214     XFontStruct *xfs;
2215 
2216 #define ADD_FONT(str) do { \
2217                         OGLFontList[nFonts].name = strdup((str)); \
2218                         Nlm_PopupItem(pupmenu, (str)); \
2219                         nFonts++; \
2220                         if (nFonts == MAXFONTS) return; \
2221                       } while (0);
2222 
2223     xfs = XLoadQueryFont(Nlm_currentXDisplay, standardXFont);
2224     if (xfs) {
2225         ADD_FONT(standardXFont);
2226         XFreeFont(Nlm_currentXDisplay, xfs);
2227     }
2228     list = XListFonts(Nlm_currentXDisplay,
2229                       "-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
2230                       100000, &tot);
2231     for (i=0; i<tot; i++) {
2232         strncpy(fnd_fam,list[i]+1,256);
2233         p = strchr(fnd_fam,'-');
2234         if (!p) continue;
2235         p = strchr(p+1,'-');
2236         if (!p) continue;
2237         *p = '\0';
2238         /* use foundry-family pair (i.e., adobe-courier)
2239            as "name" of font to store */
2240         for (j=0; j<nFonts; j++) {
2241             if (strcmp(OGLFontList[j].name,fnd_fam) == 0) break;
2242         }
2243         if (j == nFonts) ADD_FONT(fnd_fam);
2244     }
2245     XFreeFontNames(list);
2246 #endif
2247 }
2248 
SetOGLFont(TOGL_Data * OGL_Data,Nlm_Int2 fontNameIndex,Nlm_Int2 fontSize,Nlm_Boolean isBold,Nlm_Boolean isItalic,Nlm_Boolean isUnderlined)2249 NLM_EXTERN void SetOGLFont(TOGL_Data *OGL_Data, Nlm_Int2 fontNameIndex, Nlm_Int2 fontSize,
2250     Nlm_Boolean isBold, Nlm_Boolean isItalic, Nlm_Boolean isUnderlined)
2251 {
2252     static Nlm_Int2 currentIndex = -1, currentSize = -1;
2253     static Nlm_Boolean currentBold = FALSE, currentItalic = FALSE,
2254                        currentUnderlined = FALSE;
2255 
2256     fontNameIndex--; /* vibrant menus start at one */
2257     if (fontNameIndex == currentIndex && currentSize == fontSize &&
2258         isBold == currentBold && isItalic == currentItalic &&
2259         isUnderlined == currentUnderlined)
2260         return; /* only switch fonts if necessary */
2261     currentIndex = fontNameIndex;
2262     currentSize = fontSize;
2263     currentBold = isBold;
2264     currentItalic = isItalic;
2265     currentUnderlined = isUnderlined;
2266 
2267 #if defined(WIN32)
2268     {
2269         HDC hdc;
2270         HGDIOBJ currentFont;
2271         static HGDIOBJ newFont = NULL;
2272         SIZE TextSize;
2273 
2274         /* delete font used in previous call */
2275         if (newFont) DeleteObject(newFont);
2276 
2277         hdc = wglGetCurrentDC();
2278         newFont =
2279             CreateFont(
2280                 -MulDiv(currentSize, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2281                 0, 0, 0, currentBold ? FW_BOLD : FW_NORMAL,
2282                 currentItalic ? TRUE : FALSE,
2283                 currentUnderlined ? TRUE : FALSE, FALSE, ANSI_CHARSET,
2284                 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
2285                 FF_DONTCARE, (LPCTSTR) OGLFontList[currentIndex].name
2286             );
2287         currentFont = SelectObject(hdc, newFont);
2288         wglUseFontBitmaps(hdc, 0, 255, OGLFONTBASE);
2289 
2290         /* Get the size of an average character */
2291         GetTextExtentPoint32(hdc, "A", 1, &TextSize);
2292         OGL_Data->SpaceWidth = TextSize.cx;
2293         OGL_Data->SpaceHeight = TextSize.cy;
2294 
2295         /* restore previous font */
2296         SelectObject(hdc, currentFont);
2297     }
2298 #elif defined(WIN_MAC)
2299 	{
2300     	FontInfo fInfo;
2301     	GrafPtr currentPort;
2302     	GLint ID = OGLFontList[currentIndex].familyID;
2303     	Style face = 0;
2304 
2305 		if (!RealFont(ID, currentSize))
2306 			return;
2307 
2308 		if (isBold) face |= bold;
2309 		if (isItalic) face |= italic;
2310 		if (isUnderlined) face |= underline;
2311 
2312     	aglUseFont(aglGetCurrentContext(),
2313                    ID, face, currentSize,
2314     	           0, 256, OGLFONTBASE);
2315 
2316     	GetPort(&currentPort); /* store the current port's font info */
2317     	TextFont(ID);
2318     	TextFace(face);
2319     	TextSize(currentSize);
2320         OGL_Data->SpaceWidth = CharWidth('A');
2321         GetFontInfo(&fInfo);
2322         OGL_Data->SpaceHeight = fInfo.ascent + fInfo.descent;
2323         TextFont(GetPortTextFont(currentPort)); /* restore previous font */
2324         TextFace(GetPortTextFace(currentPort));
2325         TextSize(GetPortTextSize(currentPort));
2326     }
2327 
2328 #elif defined(WIN_MOTIF)
2329     {
2330         static XFontStruct *fontInfo = NULL;
2331         extern Display *Nlm_currentXDisplay;
2332         char query[256], **list, *pos;
2333         int nFound, ii, ib, size, diff, mindiff, closest;
2334         static const char
2335             *bold[] = { "bold", "black", NULL },
2336             *unbold[] = { "medium", "regular", NULL }, **boldSel,
2337             *ital[] = { "i", "o", NULL },
2338             *unital[] = { "r", NULL }, **italSel;
2339 
2340         if (fontInfo) XFreeFont(Nlm_currentXDisplay, fontInfo);
2341 
2342         if (strcmp(OGLFontList[currentIndex].name, standardXFont) == 0) {
2343             fontInfo = XLoadQueryFont(Nlm_currentXDisplay, standardXFont);
2344         } else {
2345 
2346             /* first find fonts with right typeface; bold, italic if possible */
2347             boldSel = currentBold ? bold : unbold;
2348             italSel = currentItalic ? ital : unital;
2349             for (ib = 0; boldSel[ib] ; ib++) {
2350                 for (ii = 0; italSel[ii] ; ii++) {
2351                     sprintf(query,"-%s-%s-%s-*-*-*-*-*-*-*-*-*-*",
2352                             OGLFontList[currentIndex].name,
2353                             boldSel[ib], italSel[ii]);
2354                     list = XListFonts(Nlm_currentXDisplay, query, 1000, &nFound);
2355                     if (nFound) break;
2356                 }
2357                 if (nFound) break;
2358             }
2359             if (!nFound) { /* can't find any of this style; use any available */
2360                 sprintf(query,"-%s-*-*-*-*-*-*-*-*-*-*-*-*",
2361                         OGLFontList[currentIndex].name);
2362                 list = XListFonts(Nlm_currentXDisplay, query, 1000, &nFound);
2363             }
2364             /* now find closest pixelsize to that requested */
2365             for (ii = 0; ii < nFound; ii++) {
2366                 /* find start of pixelsize field of font string */
2367                 pos = list[ii];
2368                 for (ib = 0; ib < 6; ib++) pos = strchr(pos+1, '-');
2369                 pos++;
2370                 /* store closest size match to requested */
2371                 size = atoi(pos);
2372                 /* use 2 * menu size setting as pixel size */
2373                 diff = abs(size - currentSize*2);
2374                 if (ii == 0 || diff < mindiff) {
2375                     mindiff = diff;
2376                     closest = ii;
2377                 }
2378             }
2379             fontInfo = XLoadQueryFont(Nlm_currentXDisplay, list[closest]);
2380             XFreeFontNames(list);
2381         }
2382 
2383         if (fontInfo->per_char == NULL) {
2384             OGL_Data->SpaceWidth = fontInfo->max_bounds.width;
2385             OGL_Data->SpaceHeight = fontInfo->max_bounds.ascent +
2386                                     fontInfo->max_bounds.descent;
2387         } else {
2388             XCharStruct *charA = &(fontInfo->per_char['A']);
2389             OGL_Data->SpaceWidth = charA->width;
2390             OGL_Data->SpaceHeight = charA->ascent + charA->descent;
2391         }
2392 
2393         glXUseXFont(fontInfo->fid,
2394                     fontInfo->min_char_or_byte2,
2395                     fontInfo->max_char_or_byte2 -
2396                       fontInfo->min_char_or_byte2 + 1,
2397                     OGLFONTBASE + fontInfo->min_char_or_byte2);
2398     }
2399 #endif
2400 }
2401 
2402 
OGL_CreateViewer(Nlm_GrouP prnt,Uint2Ptr width,Uint2 height,Int4 flags,Nlm_MenU ma_group_menu,Nlm_MenU ma_action_menu,Nlm_MAInitOGLFunc ma_init_func,VoidPtr ma_init_data)2403 TOGL_Data *OGL_CreateViewer(Nlm_GrouP prnt,
2404                             Uint2Ptr width, Uint2 height,
2405                             Int4 flags,
2406                             Nlm_MenU ma_group_menu,
2407                             Nlm_MenU ma_action_menu,
2408                             Nlm_MAInitOGLFunc ma_init_func,
2409                             VoidPtr ma_init_data)
2410                              /* initialize the OpenGL library */
2411 {
2412     TOGL_Data *OGL_Data;
2413     Nlm_Uint2 x_width;
2414 
2415     OGL_Data = (TOGL_Data *) MemNew(sizeof(TOGL_Data));
2416     if (OGL_Data == NULL)
2417         return NULL;
2418 
2419     OGL_Data->ModelMatrix = (Nlm_VoidPtr) MemNew(16 * sizeof(GLdouble));
2420     if (OGL_Data->ModelMatrix == NULL)
2421         return NULL;
2422     OGL_Data->NeedCameraSetup = FALSE;
2423 
2424     OGL_Data->Layers = (TOGL_Layers *) MemNew(sizeof(TOGL_Layers));
2425     if (OGL_Data->Layers == NULL)
2426         return NULL;
2427 
2428     OGL_Data->Layers->SelectedLayer = 0;
2429     OGL_SetLayers(OGL_Data, FALSE); /* null all the layers out */
2430 
2431     OGL_Data->IsPlaying = FALSE; /* animation off */
2432     OGL_Data->Tick = 0.01;
2433     OGL_Data->ParentWindow = Nlm_ParentWindow((Nlm_Handle) prnt);
2434     OGL_Data->PaletteExpanded = NULL;
2435     OGL_Data->PaletteIndex = NULL;
2436 
2437     OGL_Data->SelectMode = FALSE;
2438     OGL_Data->SelectBuffer =
2439         (Nlm_VoidPtr) MemNew(OGLSELECTBUFFER * sizeof(GLuint));
2440     if (OGL_Data->SelectBuffer == NULL)
2441         return NULL;
2442 
2443     OGL_Data->Panel = Nlm_Autonomous3DPanel(prnt,
2444                                             (Int2) * width, (Int2) height,
2445                                             OGL_DrawViewer3D_CB,
2446                                             ((flags & Y_ROTATE_SBAR) ?
2447                                              OGL_ViewerVScrollProc : NULL),
2448                                             ((flags & X_ROTATE_SBAR) ?
2449                                              OGL_ViewerHScrollProc : NULL),
2450                                             sizeof(MAPtr),
2451                                             OGL_ResetViewerProc_CB, NULL,
2452                                             &OGL_Data->IndexMode, &OGL_Data->display,
2453                                             &OGL_Data->visinfo);
2454 
2455 
2456     if (flags & Z_ROTATE_SBAR) {
2457         OGL_Data->Z_rotate =
2458             Nlm_ScrollBar(prnt, 1, 0, OGL_ViewerZScrollProc);
2459         if (!OGL_Data->Z_rotate) {
2460             MemFree(OGL_Data);
2461             if (OGL_Data->Panel)
2462                 Nlm_Remove(OGL_Data->Panel);
2463             return NULL;
2464         }
2465         Nlm_SetObjectExtra(OGL_Data->Z_rotate, OGL_Data->Panel, NULL);
2466     }
2467      {
2468         Nlm_RecT rect;
2469         Nlm_GetPosition(OGL_Data->Panel, &rect);
2470         rect.right = (Int2) (rect.left + *width);
2471         rect.bottom = (Int2) (rect.top + height);
2472         OGL_SetPosition3D(OGL_Data, &rect);
2473         x_width = (Uint2) (rect.right - rect.left);
2474     }
2475 
2476     if (flags & X_ROTATE_SBAR) {
2477         Nlm_BaR sb = Nlm_GetSlateHScrollBar((Nlm_SlatE) OGL_Data->Panel);
2478         Nlm_CorrectBarValue(sb, 0);
2479         Nlm_SetRange(sb, 10, 10, 360);
2480     }
2481     if (flags & Y_ROTATE_SBAR) {
2482         Nlm_BaR sb = Nlm_GetSlateVScrollBar((Nlm_SlatE) OGL_Data->Panel);
2483         Nlm_CorrectBarValue(sb, 0);
2484         Nlm_SetRange(sb, 10, 10, 360);
2485     }
2486     if (flags & Z_ROTATE_SBAR) {
2487         Nlm_SetRange(OGL_Data->Z_rotate, 10, 10, 360);
2488         Nlm_CorrectBarValue(OGL_Data->Z_rotate, 180);
2489     }
2490 
2491     OGL_Data->ma = MA_Create(ma_group_menu, ma_action_menu);
2492     MA_SetExtra(OGL_Data->ma, OGL_Data);
2493 
2494     if (!OGL_InitializeMA(OGL_Data)) {
2495         MemFree(OGL_Data);
2496         if (OGL_Data->Z_rotate)
2497             Nlm_Remove(OGL_Data->Z_rotate);
2498         if (OGL_Data->Panel)
2499             Nlm_Remove(OGL_Data->Panel);
2500         return NULL;
2501     }
2502 
2503     if (ma_init_func && !(*ma_init_func) (OGL_Data->ma, ma_init_data)) {
2504         MemFree(OGL_Data);
2505         if (OGL_Data->Z_rotate)
2506             Nlm_Remove(OGL_Data->Z_rotate);
2507         if (OGL_Data->Panel)
2508             Nlm_Remove(OGL_Data->Panel);
2509         return NULL;
2510     }
2511 
2512     *width = x_width;
2513 
2514     if (!OGL_qobj) {
2515         /* allocate quadric object (once only) */
2516         OGL_qobj = gluNewQuadric();
2517         if (!OGL_qobj) return NULL;
2518         gluQuadricDrawStyle(OGL_qobj, GLU_FILL);
2519         gluQuadricNormals(OGL_qobj, GLU_SMOOTH);
2520         gluQuadricOrientation(OGL_qobj, GLU_OUTSIDE);
2521     }
2522 
2523     return OGL_Data;
2524 }
2525 
OGL_InitializeLists(TOGL_Data * OGL_Data)2526 void OGL_InitializeLists(TOGL_Data * OGL_Data)
2527 {
2528     OGL_Data->Layers->FirstLayer = glGenLists((GLsizei) OGLMAXLAYERS);
2529     OGL_Data->Layers->FirstLayer++; /* avoid weird bug in windows OpenGL */
2530     OGL_Data->Layers->LastLayer = OGL_Data->Layers->FirstLayer;
2531 }
2532 
2533 #ifdef WIN_MAC
2534 static Nlm_Boolean drawingOffscreen = FALSE;
2535 #endif
2536 
OGL_DrawViewer3D(TOGL_Data * OGL_Data)2537 void OGL_DrawViewer3D(TOGL_Data * OGL_Data)
2538 /* does the drawing */
2539 {
2540     GLint Viewport[4];
2541     static GLint prevW = -1, prevH = -1;
2542     GLfloat LightPosition[4], aspect;
2543     Nlm_Int4 TotalColors;
2544     Nlm_Uint4 iList;
2545     int i;
2546 
2547 #ifdef DEBUG_GL
2548     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering DrawViewer3D");
2549 #endif
2550 
2551     if (OGL_Data == NULL) return;
2552 
2553 #ifdef WIN_MOTIF
2554     /* need to know if in color index mode - i.e., after context established */
2555     if (!glXGetCurrentContext()) return;
2556 #endif
2557 
2558 #ifdef WIN_MAC
2559 	if (drawingOffscreen) {
2560         aglDisable(aglGetCurrentContext(), AGL_BUFFER_RECT);
2561     } else {
2562         /* HACK to limit opengl to correct region of the (Cn3D) window */
2563         GLint wrect[4];
2564         Nlm_RecT r;
2565         AGLContext ctx = aglGetCurrentContext();
2566         Nlm_GetRect((Nlm_GraphiC) OGL_Data->Panel, &r);
2567         wrect[0] = r.left;
2568         wrect[1] = r.top - Nlm_hScrollBarHeight  - 4; /* this last column is simply tweaks */
2569         wrect[2] = r.right - r.left + 1          - 2; /* to make window position look nice */
2570         wrect[3] = r.bottom - r.top + 1          - 3;
2571         aglSetInteger(ctx, AGL_BUFFER_RECT, wrect);
2572         aglEnable(ctx, AGL_BUFFER_RECT);
2573     }
2574 #endif
2575 
2576     /* set background color */
2577     if (OGL_Data->IndexMode) {
2578         TotalColors = ValNodeLen(OGL_Data->PaletteExpanded);
2579 #ifdef WIN32
2580         glClearIndex((GLfloat) (TotalColors + 10));
2581 #else
2582         glClearIndex((GLfloat) TotalColors);
2583 #endif
2584 
2585     } else {
2586         glClearColor(1.0*OGL_Data->Background.rgb[0]/255,
2587                      1.0*OGL_Data->Background.rgb[1]/255,
2588                      1.0*OGL_Data->Background.rgb[2]/255, 1.0);
2589     }
2590 
2591     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2592 
2593     glGetIntegerv(GL_VIEWPORT, Viewport);
2594 
2595     /* only do this (expensive) stuff if we have to */
2596     if (Viewport[2] != prevW || Viewport[3] != prevH ||
2597         OGL_Data->SelectMode || OGL_Data->NeedCameraSetup) {
2598 
2599         prevW = Viewport[2];
2600         prevH = Viewport[3];
2601         if (OGL_Data->SelectMode)
2602             OGL_Data->NeedCameraSetup = TRUE; /* will need to redo camera next time */
2603         else
2604             OGL_Data->NeedCameraSetup = FALSE;
2605 
2606         glMatrixMode(GL_PROJECTION);
2607         glLoadIdentity();
2608 
2609         if (OGL_Data->SelectMode) {
2610             glSelectBuffer((GLsizei) OGLSELECTBUFFER,
2611                            (GLuint *) OGL_Data->SelectBuffer);
2612             glRenderMode(GL_SELECT);
2613             glInitNames();
2614             for (i = 0; i < sizeof(Nlm_VoidPtr) / sizeof(GLuint); i++)
2615                 glPushName(0);
2616             gluPickMatrix((GLdouble) (OGL_Data->SelectPoint.x),
2617                           (GLdouble) (Viewport[3] - OGL_Data->SelectPoint.y),
2618                           1.0, 1.0, Viewport);
2619         }
2620 
2621         aspect = ((GLfloat)(Viewport[2])) / Viewport[3];
2622         gluPerspective(OGL_Data->CameraAngle * 180.0/acos(-1), /* viewing angle (degrees) */
2623                        aspect,                                 /* w/h aspect    */
2624                        0.5 * OGL_Data->CameraDistance,         /* near clipping plane */
2625                        1.5 * OGL_Data->CameraDistance);        /* far clipping plane  */
2626         gluLookAt(0.0,0.0,OGL_Data->CameraDistance,  /* the camera position */
2627                   OGL_Data->CameraDirection[0],      /* the "look-at" point */
2628                   OGL_Data->CameraDirection[1],
2629                   0.0,
2630                   0.0,1.0,0.0);                      /* the up direction */
2631 
2632         glMatrixMode(GL_MODELVIEW);
2633         glLoadIdentity();
2634 
2635         /* set up the lighting */
2636         LightPosition[0] = 0.0; /* same as eye */
2637         LightPosition[1] = 0.0;
2638         LightPosition[2] = OGL_Data->CameraDistance;
2639         LightPosition[3] = 0.0;  /* directional light (faster) when 0.0 */
2640         glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
2641 
2642 
2643         if (OGL_Data->IndexMode == FALSE) {
2644             glLightfv(GL_LIGHT0, GL_AMBIENT, Color_Off);
2645             glLightfv(GL_LIGHT0, GL_DIFFUSE, Color_MostlyOn);
2646             glLightfv(GL_LIGHT0, GL_SPECULAR, Color_Off);
2647 
2648             /* clear these material colors */
2649             glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Color_Off);
2650             glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Color_Off);
2651 
2652         } else { /* color index mode */
2653             glLightfv(GL_LIGHT0, GL_AMBIENT, Color_Off);
2654             glLightfv(GL_LIGHT0, GL_DIFFUSE, Color_On);
2655             glLightfv(GL_LIGHT0, GL_SPECULAR, Color_Off);
2656         }
2657 
2658         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Color_On); /* global ambience */
2659         glEnable(GL_LIGHTING);
2660         glEnable(GL_LIGHT0);
2661 
2662         glShadeModel(GL_SMOOTH);
2663         glEnable(GL_DEPTH_TEST);
2664 
2665         /* turn on culling to speed rendering */
2666         glEnable(GL_CULL_FACE);
2667         glCullFace(GL_BACK);
2668         glFrontFace(GL_CCW);
2669     }
2670 
2671     glLoadIdentity();
2672     glMultMatrixd((GLdouble *) OGL_Data->ModelMatrix);
2673 
2674     /* selectively display the layers */
2675     if (OGL_Data->Layers->SelectedLayer) {
2676         glCallList(OGL_Data->Layers->SelectedLayer);
2677     } else {
2678         for (iList = OGL_Data->Layers->FirstLayer;
2679              iList <= OGL_Data->Layers->LastLayer; iList++) {
2680             if (OGL_GetLayer(OGL_Data, iList - OGL_Data->Layers->FirstLayer)) {
2681                 glCallList(iList);
2682             }
2683         }
2684     }
2685 
2686     /* display transparent spheres */
2687     if (!OGL_Data->IndexMode) {
2688         OGL_SetColor(OGL_Data, NULL, GL_NONE, 1.0); /* clear all color states */
2689         OGL_RenderTransparentSpheres(OGL_Data);
2690     }
2691 
2692     OGL_CheckForErrors(); /* check GL error status */
2693 
2694     glFlush();
2695 
2696     if (OGL_Data->SelectMode)
2697         OGL_Data->SelectHits = glRenderMode(GL_RENDER);
2698 
2699     else { /* regular rendering mode */
2700         /* swap when double buffering */
2701 #if defined(WIN32)
2702         wglSwapLayerBuffers(wglGetCurrentDC(), WGL_SWAP_MAIN_PLANE);/**/
2703         /*SwapBuffers(wglGetCurrentDC());*/
2704 #elif defined(WIN_MOTIF)
2705         glXSwapBuffers((Display *) OGL_Data->display, glXGetCurrentDrawable());
2706 #elif defined(WIN_MAC)
2707         aglSwapBuffers(aglGetCurrentContext());
2708 #endif
2709 
2710     }
2711 
2712 #ifdef DEBUG_GL
2713     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving DrawViewer3D");
2714 #endif
2715 }
2716 
2717 
OGL_Reset(TOGL_Data * OGL_Data)2718 void OGL_Reset(TOGL_Data * OGL_Data)
2719 /* reset the transform matrix */
2720 {
2721     Nlm_FloatLo diff;
2722 
2723     if (OGL_Data == NULL)
2724         return;
2725 
2726     if (!OGL_Data->BoundBox.set)
2727         OGL_ClearBoundBox(&(OGL_Data->BoundBox));
2728 
2729     OGL_Data->MaxSize = (Nlm_FloatLo)
2730         fabs(OGL_Data->BoundBox.x[0] - OGL_Data->BoundBox.x[1]);
2731     diff = fabs(OGL_Data->BoundBox.y[0] - OGL_Data->BoundBox.y[1]);
2732     if (diff > OGL_Data->MaxSize)
2733         OGL_Data->MaxSize = diff;
2734     diff = fabs(OGL_Data->BoundBox.z[0] - OGL_Data->BoundBox.z[1]);
2735     if (diff > OGL_Data->MaxSize)
2736         OGL_Data->MaxSize = diff;
2737 
2738     /* set up the initial view point */
2739     OGL_Data->CameraDistance = (GLfloat) 5.0 *OGL_Data->MaxSize;
2740     OGL_Data->CameraDirection[0] = 0.0;
2741     OGL_Data->CameraDirection[1] = 0.0;
2742     OGL_Data->CameraAngle = 2.0 *
2743         atan((fabs(OGL_Data->BoundBox.y[0] - OGL_Data->BoundBox.y[1]) / 2.0) /
2744               OGL_Data->CameraDistance);
2745     OGL_Data->NeedCameraSetup = TRUE;
2746 
2747     /* translate model so origin is at bounding box center */
2748     glMatrixMode(GL_MODELVIEW);
2749     glLoadIdentity();
2750     glTranslatef(
2751         (GLfloat) -((OGL_Data->BoundBox.x[0] + OGL_Data->BoundBox.x[1]) / 2.0),
2752         (GLfloat) -((OGL_Data->BoundBox.y[0] + OGL_Data->BoundBox.y[1]) / 2.0),
2753         (GLfloat) -((OGL_Data->BoundBox.z[0] + OGL_Data->BoundBox.z[1]) / 2.0)
2754     );
2755     glGetDoublev(GL_MODELVIEW_MATRIX, (GLdouble *) OGL_Data->ModelMatrix);
2756 }
2757 
2758 
OGL_SetPosition3D(TOGL_Data * OGL_Data,Nlm_RectPtr rect)2759 Boolean OGL_SetPosition3D(TOGL_Data * OGL_Data, Nlm_RectPtr rect)
2760 /* resizes and positions the 3D window */
2761 {
2762     Nlm_Int4 width = rect->right - rect->left + 1;
2763     Nlm_Int4 height = rect->bottom - rect->top + 1;
2764 
2765     if (OGL_Data == NULL || rect == NULL)
2766         return FALSE;
2767 
2768     if (OGL_Data->Z_rotate) {
2769         rect->top += Nlm_hScrollBarHeight;
2770         height -= Nlm_hScrollBarHeight;
2771     }
2772 
2773     if (Nlm_GetSlateVScrollBar((Nlm_SlatE) OGL_Data->Panel))
2774         width -= Nlm_vScrollBarWidth + 3;
2775     if (Nlm_GetSlateHScrollBar((Nlm_SlatE) OGL_Data->Panel))
2776         height -= Nlm_hScrollBarHeight + 3;
2777 
2778     if (width < 16 || height < 16)
2779         return FALSE;
2780 
2781 #if defined(WIN_MOTIF) && defined(MESA)
2782     if (glXGetCurrentContext())
2783 #endif
2784         glViewport(0, 0, width, height);
2785 
2786     rect->right = (Nlm_Int2) (rect->left + width + 3);
2787     rect->bottom = (Nlm_Int2) (rect->top + height + 3);
2788     if (Nlm_GetSlateVScrollBar((Nlm_SlatE) OGL_Data->Panel))
2789         rect->right += Nlm_vScrollBarWidth;
2790     if (Nlm_GetSlateHScrollBar((Nlm_SlatE) OGL_Data->Panel))
2791         rect->bottom += Nlm_hScrollBarHeight;
2792 
2793     Nlm_SetPosition(OGL_Data->Panel, rect); /* resize the panel */
2794     Nlm_ProcessUpdatesFirst(FALSE);
2795     Nlm_AdjustPrnt(OGL_Data->Panel, rect, FALSE);
2796 
2797     if (OGL_Data->Z_rotate) {   /* if there is a z rotation scroll bar */
2798         rect->bottom = rect->top;
2799         rect->top -= Nlm_hScrollBarHeight;
2800         Nlm_SetPosition(OGL_Data->Z_rotate, rect);
2801     }
2802 
2803     return TRUE;
2804 }
2805 
2806 
OGL_ClearOGL_Data(TOGL_Data * OGL_Data)2807 void OGL_ClearOGL_Data(TOGL_Data * OGL_Data)
2808 /* clear the transforms and bound box in OGL_Data structure */
2809 {
2810     if (OGL_Data == NULL)
2811         return;
2812 
2813     OGL_ClearBoundBox(&(OGL_Data->BoundBox));
2814     OGL_Data->MaxSize = 2.0 * OGL_DEFAULT_SIZE;
2815     OGL_Data->Background.rgb[0] = 0;
2816     OGL_Data->Background.rgb[1] = 0;
2817     OGL_Data->Background.rgb[2] = 0;
2818 }
2819 
2820 
OGL_ClearBoundBox(TOGL_BoundBox * BoundBox)2821 void OGL_ClearBoundBox(TOGL_BoundBox * BoundBox)
2822 /* initialize a bounds box */
2823 {
2824     Nlm_Int4 i;
2825 
2826     if (BoundBox == NULL)
2827         return;
2828     for (i = 0; i < 2; i++) {
2829         BoundBox->x[i] = (Nlm_FloatLo) ((i * 2 - 1) * OGL_DEFAULT_SIZE);
2830         BoundBox->y[i] = (Nlm_FloatLo) ((i * 2 - 1) * OGL_DEFAULT_SIZE);
2831         BoundBox->z[i] = (Nlm_FloatLo) ((i * 2 - 1) * OGL_DEFAULT_SIZE);
2832     }
2833     BoundBox->set = TRUE;       /* we've set up the default values */
2834 }
2835 
2836 /* function to start, stop, and check if animation is playing */
2837 
OGL_IsPlaying(TOGL_Data * pOGL_Data)2838 NLM_EXTERN Nlm_Boolean OGL_IsPlaying(TOGL_Data *pOGL_Data)
2839 {
2840 #ifdef WIN_MAC
2841     return FALSE;
2842 #else
2843     return pOGL_Data->IsPlaying;
2844 #endif
2845 }
2846 
OGL_StopPlaying(TOGL_Data * pOGL_Data)2847 NLM_EXTERN void OGL_StopPlaying(TOGL_Data *pOGL_Data)
2848 {
2849 #ifndef WIN_MAC
2850     pOGL_Data->IsPlaying = FALSE;
2851 #endif
2852 }
2853 
OGL_StartPlaying(TOGL_Data * pOGL_Data)2854 NLM_EXTERN void OGL_StartPlaying(TOGL_Data *pOGL_Data)
2855 {
2856 #ifndef WIN_MAC
2857     Nlm_StopWatchPtr pStopwatch;
2858     Nlm_FloatHi newtime;
2859 
2860     pOGL_Data->IsPlaying = TRUE;
2861     pStopwatch = Nlm_StopWatchNew();
2862     Nlm_StopWatchStart(pStopwatch);
2863     while (!Nlm_QuittingProgram() && OGL_IsPlaying(pOGL_Data)) {
2864         OGL_Play(pOGL_Data);
2865         OGL_DrawViewer3D(pOGL_Data);
2866         if(pOGL_Data->Tick != 0.0L) {
2867             do {
2868                 Nlm_StopWatchStop(pStopwatch);
2869                 newtime = Nlm_GetElapsedTime(pStopwatch);
2870                 if (Nlm_EventAvail()) Nlm_ProcessAnEvent();
2871             } while (newtime < pOGL_Data->Tick);
2872             Nlm_StopWatchStart(pStopwatch);
2873         }
2874         while (Nlm_EventAvail())
2875             Nlm_ProcessAnEvent();
2876     }
2877     Nlm_StopWatchFree(pStopwatch);
2878 #endif
2879 }
2880 
2881 
2882 #ifdef _PNG
2883 
2884 /* callback used by PNG library to report errors */
writepng_error_handler(png_structp png_ptr,png_const_charp msg)2885 static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
2886 {
2887     Message(MSG_ERROR, "PNG Error: %s", msg);
2888 }
2889 
2890 #ifdef WIN_MOTIF
2891 static Nlm_Boolean gotAnXError;
2892 
OGL_XErrorHandler(Display * dpy,XErrorEvent * error)2893 int OGL_XErrorHandler(Display *dpy, XErrorEvent *error)
2894 {
2895     gotAnXError = TRUE;
2896     return 0;
2897 }
2898 #endif /* WIN_MOTIF */
2899 
2900 static Nlm_MonitorPtr row_monitor;
2901 static int nRows;
2902 
write_row_callback(png_structp png_ptr,png_uint_32 row,int pass)2903 void write_row_callback(png_structp png_ptr, png_uint_32 row, int pass)
2904 {
2905     char message[256];
2906     float progress;
2907 
2908     if (nRows < 0) { /* if negative, then we're doing interlacing */
2909         float start, end;
2910         switch (pass + 1) {
2911             case 1: start = 0;  end = 1;  break;
2912             case 2: start = 1;  end = 2;  break;
2913             case 3: start = 2;  end = 3;  break;
2914             case 4: start = 3;  end = 5;  break;
2915             case 5: start = 5;  end = 7;  break;
2916             case 6: start = 7;  end = 11; break;
2917             case 7: start = 11; end = 15; break;
2918         }
2919         progress = 100.0 * (
2920             (start / 15) +
2921             (((float) row) / (-nRows)) * ((end - start) / 15)
2922         );
2923     } else { /* not interlaced */
2924         progress = 100.0 * row / nRows;
2925     }
2926     sprintf(message, "Writing PNG file (%i%%) ...", (int) progress);
2927     Nlm_MonitorStrValue(row_monitor, message);
2928     Nlm_Update();
2929 }
2930 
2931 /* saves current OpenGL context window to PNG file */
Nlm_SaveImagePNG(Nlm_Char * fname)2932 void Nlm_SaveImagePNG(Nlm_Char *fname)
2933 {
2934     GLint viewport[4];
2935     GLboolean isRGBA;
2936     Nlm_Uchar *row = NULL;
2937     FILE *out = NULL;
2938     int i, bytesPerPixel = 3;
2939     png_structp png_ptr = NULL;
2940     png_infop info_ptr = NULL;
2941     Nlm_Boolean doInterlacing = TRUE;
2942     TOGL_Data *OGL_Data = (TOGL_Data *)(Cn3D_GetCurrentOGLData());
2943 
2944 #if defined(WIN_MOTIF)
2945     GLint glSize;
2946     int nAttribs, attribs[20];
2947     XVisualInfo *visinfo;
2948     Nlm_Boolean localVI = FALSE;
2949     Pixmap xPixmap;
2950     GLXContext currentCtx, glCtx = NULL;
2951     GLXPixmap glxPixmap;
2952     GLXDrawable currentXdrw;
2953     extern Display *Nlm_currentXDisplay;
2954     extern int Nlm_currentXScreen;
2955     int (*currentXErrHandler)(Display *, XErrorEvent *);
2956 
2957 #elif defined(WIN_MSWIN)
2958     HDC current_hdc = NULL, hdc = NULL;
2959     HGDIOBJ current_hgdiobj = NULL;
2960     HBITMAP hbm = NULL;
2961     PIXELFORMATDESCRIPTOR pfd;
2962     int nPixelFormat;
2963     HGLRC hglrc = NULL, current_hglrc = NULL;
2964 
2965 #elif defined(WIN_MAC)
2966 	Nlm_Uchar *base = NULL;
2967     GLint attrib[20], glSize;
2968     int na = 0;
2969     AGLPixelFormat fmt = NULL;
2970     AGLContext ctx = NULL, currentCtx;
2971 
2972 #endif
2973 
2974     glGetBooleanv(GL_RGBA_MODE, &isRGBA);
2975     if (!isRGBA) {
2976         /* turn this off till I figure out good way to retrieve colormap */
2977         Message(MSG_ERROR, "8-Bit PNG output currently unavailable");
2978         return;
2979     }
2980 
2981     /* determine the size of the window and allocate (platform-dependent)
2982        off-screen rendering area */
2983     glGetIntegerv(GL_VIEWPORT, viewport);
2984     /*
2985     viewport[2] *= 3;
2986     viewport[3] *= 3;
2987     */
2988 
2989 #if defined(WIN_MOTIF)
2990     currentCtx = glXGetCurrentContext(); /* save current context info */
2991     currentXdrw = glXGetCurrentDrawable();
2992 
2993     currentXErrHandler = XSetErrorHandler(OGL_XErrorHandler);
2994     gotAnXError = FALSE;
2995 
2996     /* first, try to get a non-doublebuffered visual, to economize on memory */
2997     nAttribs = 0;
2998     attribs[nAttribs++] = GLX_USE_GL;
2999     attribs[nAttribs++] = GLX_RGBA;
3000     attribs[nAttribs++] = GLX_RED_SIZE;
3001     glGetIntegerv(GL_RED_BITS, &glSize);
3002     attribs[nAttribs++] = glSize;
3003     attribs[nAttribs++] = GLX_GREEN_SIZE;
3004     attribs[nAttribs++] = glSize;
3005     attribs[nAttribs++] = GLX_BLUE_SIZE;
3006     attribs[nAttribs++] = glSize;
3007     attribs[nAttribs++] = GLX_DEPTH_SIZE;
3008     glGetIntegerv(GL_DEPTH_BITS, &glSize);
3009     attribs[nAttribs++] = glSize;
3010     attribs[nAttribs++] = None;
3011     visinfo = glXChooseVisual((Display *) OGL_Data->display,
3012                               DefaultScreen((Display *) OGL_Data->display),
3013                               attribs);
3014 
3015     /* if that fails, just revert to the one used for the regular window */
3016     if (visinfo)
3017         localVI = TRUE;
3018     else
3019         visinfo = (XVisualInfo *) (OGL_Data->visinfo);
3020 
3021     xPixmap = XCreatePixmap((Display *) OGL_Data->display,
3022         RootWindow((Display *) OGL_Data->display, Nlm_currentXScreen),
3023         viewport[2], viewport[3], visinfo->depth);
3024     glxPixmap = glXCreateGLXPixmap((Display *) OGL_Data->display,
3025         visinfo, xPixmap);
3026     if (gotAnXError) {
3027         Message(MSG_ERROR, "Got an X error creating GLXPixmap context");
3028         goto cleanup;
3029     }
3030     glCtx = glXCreateContext((Display *) OGL_Data->display,
3031         visinfo,
3032         currentCtx, /* share display list with "regular" context */
3033         GL_FALSE);
3034     if (!glCtx || !glXMakeCurrent((Display *) OGL_Data->display,
3035                                   glxPixmap, glCtx)) {
3036         Message(MSG_ERROR, "Failed to make current GLXPixmap rendering context");
3037         goto cleanup;
3038     }
3039 
3040 #elif defined(WIN_MSWIN)
3041     current_hglrc = wglGetCurrentContext(); /* save to restore later */
3042     current_hdc = wglGetCurrentDC();
3043 
3044     /* create bitmap of same color type as current rendering context */
3045     hbm = CreateCompatibleBitmap(current_hdc, viewport[2], viewport[3]);
3046     if (!hbm) {
3047         Message(MSG_ERROR, "Failed to create rendering BITMAP");
3048         goto cleanup;
3049     }
3050     memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
3051     pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
3052     pfd.nVersion = 1;
3053     /* NOT doublebuffered, to save memory */
3054     pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL;
3055     pfd.iPixelType = PFD_TYPE_RGBA;
3056     pfd.cColorBits = GetDeviceCaps(current_hdc, BITSPIXEL);
3057     pfd.iLayerType = PFD_MAIN_PLANE;
3058 
3059     hdc = CreateCompatibleDC(current_hdc);
3060     if (!hdc) {
3061         Message(MSG_ERROR, "CreateCompatibleDC failed");
3062         goto cleanup;
3063     }
3064 
3065     current_hgdiobj = SelectObject(hdc, hbm);
3066     if (!current_hgdiobj) {
3067         Message(MSG_ERROR, "SelectObject failed");
3068         goto cleanup;
3069     }
3070     nPixelFormat = ChoosePixelFormat(hdc, &pfd);
3071     if (!nPixelFormat) {
3072         Message(MSG_ERROR, "ChoosePixelFormat failed: %i", GetLastError());
3073         goto cleanup;
3074     }
3075     if (!SetPixelFormat(hdc, nPixelFormat, &pfd)) {
3076         Message(MSG_ERROR, "SetPixelFormat failed: %i", GetLastError());
3077         goto cleanup;
3078     }
3079     /*DescribePixelFormat(hdc, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);*/
3080     hglrc = wglCreateContext(hdc);
3081     if (!hglrc) {
3082         Message(MSG_ERROR, "wglCreateContext failed: %i", GetLastError());
3083         goto cleanup;
3084     }
3085     /* need to share display lists with regular window */
3086     if (!wglShareLists(current_hglrc, hglrc)) {
3087         Message(MSG_ERROR, "wglShareLists failed: %i", GetLastError());
3088         goto cleanup;
3089     }
3090     if (!wglMakeCurrent(hdc, hglrc)) {
3091         Message(MSG_ERROR, "wglMakeCurrent failed: %i", GetLastError());
3092         goto cleanup;
3093     }
3094 
3095 #elif defined(WIN_MAC)
3096 	currentCtx = aglGetCurrentContext();
3097 
3098 	/* Mac pixels seem to always be 32-bit */
3099 	bytesPerPixel = 4;
3100 
3101 	base = (Nlm_Uchar *) MemNew(viewport[2] * viewport[3] * bytesPerPixel);
3102 	if (!base) {
3103     	Message(MSG_ERROR, "Failed to allocate image buffer");
3104     	goto cleanup;
3105 	}
3106 
3107 	/* create an off-screen rendering context (NOT doublebuffered) */
3108 	attrib[na++] = AGL_OFFSCREEN;
3109 	attrib[na++] = AGL_RGBA;
3110 	glGetIntegerv(GL_RED_BITS, &glSize);
3111     attrib[na++] = AGL_RED_SIZE;
3112     attrib[na++] = glSize;
3113     attrib[na++] = AGL_GREEN_SIZE;
3114     attrib[na++] = glSize;
3115     attrib[na++] = AGL_BLUE_SIZE;
3116     attrib[na++] = glSize;
3117 	glGetIntegerv(GL_DEPTH_BITS, &glSize);
3118     attrib[na++] = AGL_DEPTH_SIZE;
3119     attrib[na++] = glSize;
3120     attrib[na++] = AGL_NONE;
3121 
3122     if ((fmt=aglChoosePixelFormat(NULL, 0, attrib)) == NULL) {
3123     	Message(MSG_ERROR, "aglChoosePixelFormat failed");
3124     	goto cleanup;
3125     }
3126     /* share display lists with current "regular" context */
3127     if ((ctx=aglCreateContext(fmt, currentCtx)) == NULL) {
3128     	Message(MSG_ERROR, "aglCreateContext failed");
3129     	goto cleanup;
3130     }
3131 
3132     /* attach off-screen buffer to this context */
3133     if (!aglSetOffScreen(ctx, viewport[2], viewport[3],
3134                          bytesPerPixel * viewport[2], base)) {
3135     	Message(MSG_ERROR, "aglSetOffScreen failed");
3136     	goto cleanup;
3137 	}
3138     if (!aglSetCurrentContext(ctx)) {
3139     	Message(MSG_ERROR, "aglSetCurrentContext failed");
3140     	goto cleanup;
3141 	}
3142 
3143 #endif
3144 
3145     row = (Nlm_Uchar *) MemNew(viewport[2] * bytesPerPixel);
3146     if (!row) {
3147         Message(MSG_ERROR, "Failed to allocate pixel storage for PNG output");
3148         goto cleanup;
3149     }
3150 
3151     /* open the output file for writing */
3152     out = fopen(fname, "wb");
3153     if (!out) {
3154         Message(MSG_ERROR, "Can't write to file '%s'", fname);
3155         goto cleanup;
3156     }
3157 
3158     /* set up the PNG writing (see libpng.txt) */
3159     png_ptr = png_create_write_struct(
3160         PNG_LIBPNG_VER_STRING, NULL, writepng_error_handler, NULL);
3161     if (!png_ptr) {
3162         Message(MSG_ERROR, "Can't create PNG write structure");
3163         goto cleanup;
3164     }
3165 
3166     info_ptr = png_create_info_struct(png_ptr);
3167     if (!info_ptr) {
3168         Message(MSG_ERROR, "Can't create PNG info structure");
3169         goto cleanup;
3170     }
3171 
3172     if (setjmp(png_jmpbuf(png_ptr))) {
3173         Message(MSG_ERROR, "PNG write failed");
3174         goto cleanup;
3175     }
3176 
3177     png_init_io(png_ptr, out);
3178 
3179     /* sets callback that's called by PNG after each written row */
3180     png_set_write_status_fn(png_ptr, write_row_callback);
3181 
3182     png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
3183 
3184     png_set_IHDR(png_ptr, info_ptr, viewport[2], viewport[3],
3185         8, PNG_COLOR_TYPE_RGB,
3186         doInterlacing ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
3187         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
3188 
3189     png_write_info(png_ptr, info_ptr);
3190 
3191     /*
3192        Redraw the model into the new off-screen context, then use glReadPixels
3193        to retrieve pixel data. It's much easier to use glReadPixels rather than
3194        trying to read directly from the off-screen buffer, since GL can do all
3195        the potentially tricky work of translating from whatever pixel format
3196        the buffer uses into "regular" RGB byte triples.
3197     */
3198     row_monitor = Nlm_MonitorStrNewEx("PNG Progress", 20, FALSE);
3199     Nlm_MonitorStrValue(row_monitor, "Rendering to off-screen buffer...");
3200     Nlm_Update();
3201     OGL_Data->NeedCameraSetup = TRUE;
3202 #ifdef WIN_MAC
3203     drawingOffscreen = TRUE; /* signal redraw not to use agl buffer rect */
3204 #endif
3205     OGL_DrawViewer3D(OGL_Data);
3206 #ifdef WIN_MAC
3207     drawingOffscreen = FALSE;
3208 #endif
3209     glPixelStorei(GL_PACK_ALIGNMENT, 1);
3210 
3211     /*
3212        Write image row by row to avoid having to allocate another full image's
3213        worth of memory. Do multiple passes for interlacing, if necessary. Note
3214        that PNG's rows are stored top down, while GL's are bottom up.
3215     */
3216     nRows = viewport[3];
3217     if (doInterlacing) {
3218         int pass, r;
3219         nRows = -nRows; /* signal to monitor that we're interlacing */
3220         if (png_set_interlace_handling(png_ptr) != 7) {
3221             Message(MSG_ERROR, "confused by unkown PNG interlace scheme");
3222             goto cleanup;
3223         }
3224         for (pass = 1; pass <= 7; pass++) {
3225             for (i = viewport[3] - 1; i >= 0; i--) {
3226                 r = viewport[3] - i - 1;
3227                 /* when interlacing, only certain rows are actually read
3228                    during certain passes - avoid unncessary reads */
3229                 if (
3230                     ((pass == 1 || pass == 2) && (r % 8 == 0)) ||
3231                     ((pass == 3) && ((r - 4) % 8 == 0)) ||
3232                     ((pass == 4) && (r % 4 == 0)) ||
3233                     ((pass == 5) && ((r - 2) % 4 == 0)) ||
3234                     ((pass == 6) && (r % 2 == 0)) ||
3235                     ((pass == 7) && ((r - 1) % 2 == 0))
3236                    )
3237                     glReadPixels(viewport[0], i, viewport[2], 1,
3238                                  GL_RGB, GL_UNSIGNED_BYTE, row);
3239                 /* ... but still have to call this for each row in each pass */
3240                 png_write_row(png_ptr, row);
3241             }
3242         }
3243     } else { /* not interlaced */
3244         for (i = viewport[3] - 1; i >= 0; i--) {
3245             glReadPixels(viewport[0], i, viewport[2], 1,
3246                          GL_RGB, GL_UNSIGNED_BYTE, row);
3247             png_write_row(png_ptr, row);
3248         }
3249     }
3250     Nlm_MonitorFree(row_monitor);
3251     Nlm_Update();
3252 
3253     /* finish up */
3254     png_write_end(png_ptr, info_ptr);
3255 
3256 
3257     /* restore context and clean up */
3258     cleanup:
3259 
3260     if (out) fclose(out);
3261 
3262 #if defined(WIN_MOTIF)
3263     gotAnXError = FALSE;
3264     if (glCtx) {
3265         glXMakeCurrent(Nlm_currentXDisplay, currentXdrw, currentCtx);
3266         glXDestroyContext(Nlm_currentXDisplay, glCtx);
3267     }
3268     glXDestroyGLXPixmap(Nlm_currentXDisplay, glxPixmap);
3269     XFreePixmap(Nlm_currentXDisplay, xPixmap);
3270     if (localVI && visinfo) XFree(visinfo);
3271     if (gotAnXError) {
3272         Message(MSG_ERROR, "Got an X error destroying GLXPixmap context");
3273     }
3274     XSetErrorHandler(currentXErrHandler);
3275 
3276 #elif defined(WIN_MSWIN)
3277     if (current_hdc && current_hglrc) wglMakeCurrent(current_hdc, current_hglrc);
3278     if (hglrc) wglDeleteContext(hglrc);
3279     /*if (current_hgdiobj) SelectObject(hdc, current_hgdiobj);*/
3280     if (hbm) DeleteObject(hbm);
3281     if (hdc) DeleteDC(hdc);
3282 
3283 #elif defined(WIN_MAC)
3284 	aglSetCurrentContext(currentCtx);
3285 	if (ctx) aglDestroyContext(ctx);
3286 	if (fmt) aglDestroyPixelFormat(fmt);
3287 	if (base) MemFree(base);
3288 
3289 #endif
3290 
3291     if (row) MemFree(row);
3292     if (png_ptr) {
3293         if (info_ptr)
3294             png_destroy_write_struct(&png_ptr, &info_ptr);
3295         else
3296             png_destroy_write_struct(&png_ptr, NULL);
3297     }
3298 
3299     /* redraw in case progress meter overwrote part of GL area */
3300     OGL_DrawViewer3D(OGL_Data);
3301 }
3302 
3303 #endif /* _PNG */
3304 
3305 
3306 /* create display list with logo */
OGL_DrawLogo(TOGL_Data * OGL_Data)3307 NLM_EXTERN void OGL_DrawLogo(TOGL_Data *OGL_Data)
3308 {
3309     DDV_ColorCell colorCell;
3310     Uint1 logoColor[3] = { 100, 240, 150 };
3311     int i, n, s, g;
3312 
3313 #define LOGO_SIDES 36
3314     int segments = 180;
3315     GLdouble bigRad = 12.0, height = 24.0,
3316         minRad = 0.1, maxRad = 2.0,
3317         ringPts[LOGO_SIDES * 3], *pRingPts = ringPts,
3318         prevRing[LOGO_SIDES * 3], *pPrevRing = prevRing, *tmp,
3319         ringNorm[LOGO_SIDES * 3], *pRingNorm = ringNorm,
3320         prevNorm[LOGO_SIDES * 3], *pPrevNorm = prevNorm,
3321         PI = acos(-1), length,
3322         startRad, midRad, phase, currentRad, CR[3], H[3], V[3];
3323 
3324     if (!OGL_Data || OGL_Data->IndexMode) return;
3325 
3326     /* set up initial camera */
3327     OGL_Data->CameraDistance = 200.0;
3328     OGL_Data->CameraDirection[0] = 0.0;
3329     OGL_Data->CameraDirection[1] = 0.0;
3330     OGL_Data->CameraAngle = acos(-1) / 14;
3331     OGL_Data->NeedCameraSetup = TRUE;
3332     glMatrixMode(GL_MODELVIEW);
3333     glLoadIdentity();
3334     glGetDoublev(GL_MODELVIEW_MATRIX, (GLdouble *) OGL_Data->ModelMatrix);
3335 
3336     glNewList(1, GL_COMPILE);
3337 
3338     /* create logo */
3339     DDV_SetColorInCell(&colorCell, logoColor);
3340     OGL_SetColor(OGL_Data, &colorCell, GL_DIFFUSE, 1.0);
3341 
3342     for (n = 0; n < 2; n++) { /* helix strand */
3343         if (n == 0) {
3344             startRad = maxRad;
3345             midRad = minRad;
3346             phase = 0;
3347         } else {
3348             startRad = minRad;
3349             midRad = maxRad;
3350             phase = PI;
3351         }
3352         for (g = 0; g <= segments; g++) { /* segment (bottom to top) */
3353 
3354             if (g < segments/2)
3355                 currentRad = startRad + (midRad - startRad) *
3356                     (0.5 - 0.5 * cos(PI * g / (segments/2)));
3357             else
3358                 currentRad = midRad + (startRad - midRad) *
3359                     (0.5 - 0.5 * cos(PI * (g - segments/2) / (segments/2)));
3360 
3361             CR[1] = height * g / segments - height/2;
3362             if (g > 0) phase += PI * 2 / segments;
3363             CR[2] = bigRad * cos(phase);
3364             CR[0] = bigRad * sin(phase);
3365 
3366             /* make a strip around the strand circumference */
3367             for (s = 0; s < LOGO_SIDES; s++) {
3368                 V[0] = CR[0];
3369                 V[2] = CR[2];
3370                 V[1] = 0;
3371                 length = sqrt(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
3372                 for (i = 0; i < 3; i++) V[i] /= length;
3373                 H[0] = H[2] = 0;
3374                 H[1] = 1;
3375                 for (i = 0; i < 3; i++) {
3376                     pRingNorm[3*s + i] = V[i] * cos(PI * 2 * s / LOGO_SIDES) +
3377                                          H[i] * sin(PI * 2 * s / LOGO_SIDES);
3378                     pRingPts[3*s + i] = CR[i] + pRingNorm[3*s + i] * currentRad;
3379                 }
3380             }
3381             if (g > 0) {
3382                 glBegin(GL_TRIANGLE_STRIP);
3383                 for (s = 0; s < LOGO_SIDES; s++) {
3384                     glNormal3d(pPrevNorm[3*s], pPrevNorm[3*s + 1], pPrevNorm[3*s + 2]);
3385                     glVertex3d(pPrevRing[3*s], pPrevRing[3*s + 1], pPrevRing[3*s + 2]);
3386                     glNormal3d(pRingNorm[3*s], pRingNorm[3*s + 1], pRingNorm[3*s + 2]);
3387                     glVertex3d(pRingPts[3*s], pRingPts[3*s + 1], pRingPts[3*s + 2]);
3388                 }
3389                 glNormal3d(pPrevNorm[0], pPrevNorm[1], pPrevNorm[2]);
3390                 glVertex3d(pPrevRing[0], pPrevRing[1], pPrevRing[2]);
3391                 glNormal3d(pRingNorm[0], pRingNorm[1], pRingNorm[2]);
3392                 glVertex3d(pRingPts[0], pRingPts[1], pRingPts[2]);
3393                 glEnd();
3394             }
3395 
3396             /* cap the ends */
3397             glBegin(GL_POLYGON);
3398             if ((g == 0 && n == 0) || (g == segments && n == 1))
3399                 glNormal3d(-1, 0, 0);
3400             else
3401                 glNormal3d(1, 0, 0);
3402             if (g == 0) {
3403                 for (s = 0; s < LOGO_SIDES; s++)
3404                     glVertex3d(pRingPts[3*s], pRingPts[3*s + 1], pRingPts[3*s + 2]);
3405             } else if (g == segments) {
3406                 for (s = LOGO_SIDES - 1; s >= 0; s--)
3407                     glVertex3d(pRingPts[3*s], pRingPts[3*s + 1], pRingPts[3*s + 2]);
3408             }
3409             glEnd();
3410 
3411             /* switch pointers to store previous ring */
3412             tmp = pPrevRing;
3413             pPrevRing = pRingPts;
3414             pRingPts = tmp;
3415             tmp = pPrevNorm;
3416             pPrevNorm = pRingNorm;
3417             pRingNorm = tmp;
3418         }
3419     }
3420 
3421     glEndList();
3422 
3423     OGL_Data->Layers->SelectedLayer = 1; /* fool DrawViewer3D into thinking there's a single structure */
3424     OGL_DrawViewer3D(OGL_Data);
3425 }
3426 
3427 #endif                          /* _OPENGL */
3428 
3429 
3430 /* this function "shared" by both vibrant and OpenGL versions */
3431 #ifdef _OPENGL
3432 /* add a thick splined curve from point 1 *halfway* to point 2 */
Nlm_AddHalfWorm3D(TOGL_Data * OGL_Data,DDV_ColorCell * color,Nlm_FloatHi x0,Nlm_FloatHi y0,Nlm_FloatHi z0,Nlm_FloatHi x1,Nlm_FloatHi y1,Nlm_FloatHi z1,Nlm_FloatHi x2,Nlm_FloatHi y2,Nlm_FloatHi z2,Nlm_FloatHi x3,Nlm_FloatHi y3,Nlm_FloatHi z3,Nlm_Boolean cap1,Nlm_Boolean cap2,Nlm_FloatHi radius,Nlm_Int4 segments,Nlm_Int4 sides)3433 void Nlm_AddHalfWorm3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
3434                        Nlm_FloatHi x0, Nlm_FloatHi y0, Nlm_FloatHi z0,
3435                        Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
3436                        Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
3437                        Nlm_FloatHi x3, Nlm_FloatHi y3, Nlm_FloatHi z3,
3438                        Nlm_Boolean cap1, Nlm_Boolean cap2, Nlm_FloatHi radius,
3439                        Nlm_Int4 segments, Nlm_Int4 sides)
3440 #else
3441 typedef Nlm_FloatLo GLfloat;
3442 typedef Nlm_FloatHi GLdouble;
3443 void Nlm_AddHalfWorm3D(Nlm_Picture3D pic,
3444                        Nlm_Segment3D segment, BigScalar userData,
3445                        Uint1 layer, Uint1 color,
3446                        Nlm_FloatHi x0, Nlm_FloatHi y0, Nlm_FloatHi z0,
3447                        Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
3448                        Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
3449                        Nlm_FloatHi x3, Nlm_FloatHi y3, Nlm_FloatHi z3,
3450                        Nlm_FloatHi radius, Nlm_Int4 segments)
3451 #endif
3452 {
3453     Nlm_Int4 i, j, k, m, offset;
3454     GLdouble len, R1x, R1y, R1z, R2x, R2y, R2z, MG[4][3],
3455         T[4], t, Qtx, Qty, Qtz, px, py, pz,
3456         dQtx, dQty, dQtz, /*ddQtx, ddQty, ddQtz,*/
3457         Hx, Hy, Hz, Vx, Vy, Vz, prevlen,
3458         cosj, sinj, pi = 3.14159265358979323846;
3459     GLdouble *Nx = NULL, *Ny, *Nz, *Cx, *Cy, *Cz,
3460         *pNx, *pNy, *pNz, *pCx, *pCy, *pCz, *tmp, *fblock = NULL;
3461     /*
3462      * The Hermite matrix Mh.
3463      */
3464     static Nlm_FloatHi Mh[4][4] = {
3465         { 2, -2,  1,  1},
3466         {-3,  3, -2, -1},
3467         { 0,  0,  1,  0},
3468         { 1,  0,  0,  0}
3469     };
3470     /*
3471      * Variables that affect the curve shape
3472      *   a=b=0 = Catmull-Rom
3473      */
3474     static Nlm_FloatHi a = -0.8, /* tension    (adjustable)  */
3475      c = 0,                     /* continuity (should be 0) */
3476      b = 0;                     /* bias       (should be 0) */
3477 
3478 #ifdef _OPENGL
3479     if (radius > 0.0) {         /* if line, color assigned in OGL_AddLine3D */
3480         if (OGL_Data == NULL || color == NULL)
3481             return;
3482 
3483         OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
3484 
3485         if (sides % 2)
3486             sides++;         /* must be an even number! */
3487     }
3488 #endif
3489 
3490     /* First, calculate the coordinate points of the center of the worm,
3491      * using the Kochanek-Bartels variant of the Hermite curve.
3492      */
3493     R1x = 0.5 * (1 - a) * (1 + b) * (1 + c) * (x1 - x0) +
3494         0.5 * (1 - a) * (1 - b) * (1 - c) * (x2 - x1);
3495     R1y = 0.5 * (1 - a) * (1 + b) * (1 + c) * (y1 - y0) +
3496         0.5 * (1 - a) * (1 - b) * (1 - c) * (y2 - y1);
3497     R1z = 0.5 * (1 - a) * (1 + b) * (1 + c) * (z1 - z0) +
3498         0.5 * (1 - a) * (1 - b) * (1 - c) * (z2 - z1);
3499     R2x = 0.5 * (1 - a) * (1 + b) * (1 - c) * (x2 - x1) +
3500         0.5 * (1 - a) * (1 - b) * (1 + c) * (x3 - x2);
3501     R2y = 0.5 * (1 - a) * (1 + b) * (1 - c) * (y2 - y1) +
3502         0.5 * (1 - a) * (1 - b) * (1 + c) * (y3 - y2);
3503     R2z = 0.5 * (1 - a) * (1 + b) * (1 - c) * (z2 - z1) +
3504         0.5 * (1 - a) * (1 - b) * (1 + c) * (z3 - z2);
3505     /*
3506      * Multiply MG=Mh.Gh, where Gh = [ P(1) P(2) R(1) R(2) ]. This
3507      * 4x1 matrix of vectors is constant for each segment.
3508      */
3509     for (i = 0; i < 4; i++) {   /* calculate Mh.Gh */
3510         MG[i][0] =
3511             Mh[i][0] * x1 + Mh[i][1] * x2 + Mh[i][2] * R1x +
3512             Mh[i][3] * R2x;
3513         MG[i][1] =
3514             Mh[i][0] * y1 + Mh[i][1] * y2 + Mh[i][2] * R1y +
3515             Mh[i][3] * R2y;
3516         MG[i][2] =
3517             Mh[i][0] * z1 + Mh[i][1] * z2 + Mh[i][2] * R1z +
3518             Mh[i][3] * R2z;
3519     }
3520 
3521     for (i = 0; i <= segments; i++) {
3522 
3523         /* t goes from [0,1] from P(1) to P(2) (and we want to go halfway only),
3524            and the function Q(t) defines the curve of this segment. */
3525         t = (0.5 / segments) * i;
3526         /*
3527            * Q(t)=T.(Mh.Gh), where T = [ t^3 t^2 t 1 ]
3528          */
3529         T[0] = t * t * t;
3530         T[1] = t * t;
3531         T[2] = t;
3532         T[3] = 1;
3533         Qtx = T[0] * MG[0][0] + T[1] * MG[1][0] + T[2] * MG[2][0] + MG[3][0] /* *T[3] */
3534             ;
3535         Qty = T[0] * MG[0][1] + T[1] * MG[1][1] + T[2] * MG[2][1] + MG[3][1] /* *T[3] */
3536             ;
3537         Qtz = T[0] * MG[0][2] + T[1] * MG[1][2] + T[2] * MG[2][2] + MG[3][2] /* *T[3] */
3538             ;
3539 
3540 #ifndef _OPENGL
3541         if (i > 0) {
3542             Int4 iX0, iY0, iZ0, iX1, iY1, iZ1;
3543 
3544 /* number 1000000.0 must be same as VIEWSCALE in algorend.c! */
3545             iX0 = (Int4) (px * 1000000.0);
3546             iY0 = (Int4) (py * 1000000.0);
3547             iZ0 = (Int4) (pz * 1000000.0);
3548             iX1 = (Int4) (Qtx * 1000000.0);
3549             iY1 = (Int4) (Qty * 1000000.0);
3550             iZ1 = (Int4) (Qtz * 1000000.0);
3551             AddLine3D(pic, segment, userData, layer, color,
3552                       iX0, iY0, iZ0, iX1, iY1, iZ1);
3553         }
3554         /* save to use as previous point for connecting points together */
3555         px = Qtx;
3556         py = Qty;
3557         pz = Qtz;
3558 
3559 #else
3560         if (radius == 0.0) {
3561             if (i > 0)
3562                 OGL_AddLine3D(OGL_Data, color, px, py, pz, Qtx, Qty, Qtz);
3563             /* save to use as previous point for connecting points together */
3564             px = Qtx;
3565             py = Qty;
3566             pz = Qtz;
3567 
3568         } else {
3569             /* construct a circle of points centered at and
3570                in a plane normal to the curve at t - these points will
3571                be used to construct the "thick" worm */
3572 
3573             /* allocate single block of storage for two circles of points */
3574             if (!Nx) {
3575                 fblock = Nx = MemNew(sizeof(Nlm_FloatHi) * 12 * sides);
3576                 if (!Nx)
3577                     return;
3578                 Ny = &Nx[sides];
3579                 Nz = &Nx[sides * 2];
3580                 Cx = &Nx[sides * 3];
3581                 Cy = &Nx[sides * 4];
3582                 Cz = &Nx[sides * 5];
3583                 pNx = &Nx[sides * 6];
3584                 pNy = &Nx[sides * 7];
3585                 pNz = &Nx[sides * 8];
3586                 pCx = &Nx[sides * 9];
3587                 pCy = &Nx[sides * 10];
3588                 pCz = &Nx[sides * 11];
3589             }
3590 
3591             /*
3592              * The first derivative of Q(t), d(Q(t))/dt, is the slope
3593              * (tangent) at point Q(t); now T = [ 3t^2 2t 1 0 ]
3594              */
3595             T[0] = t * t * 3;
3596             T[1] = t * 2;
3597             T[2] = 1;
3598             T[3] = 0;
3599             dQtx =
3600                 T[0] * MG[0][0] + T[1] * MG[1][0] +
3601                 MG[2][0] /* *T[2] + T[3]*MG[3][0] */ ;
3602             dQty =
3603                 T[0] * MG[0][1] + T[1] * MG[1][1] +
3604                 MG[2][1] /* *T[2] + T[3]*MG[3][1] */ ;
3605             dQtz =
3606                 T[0] * MG[0][2] + T[1] * MG[1][2] +
3607                 MG[2][2] /* *T[2] + T[3]*MG[3][2] */ ;
3608 
3609             /* use cross prod't of [1,0,0] x normal as horizontal */
3610             Hx = 0.0;
3611             Hy = -dQtz;
3612             Hz = dQty;
3613             len = sqrt(Hy * Hy + Hz * Hz);
3614             if (len < 0.000001) { /* nearly colinear - use [1,0.1,0] instead */
3615                 Hx = 0.1 * dQtz;
3616                 Hy = -dQtz;
3617                 Hz = dQty - 0.1 * dQtx;
3618             }
3619             Hx /= len;
3620             Hy /= len;
3621             Hz /= len;
3622 
3623             /* and a vertical vector = normal x H */
3624             Vx = dQty * Hz - dQtz * Hy;
3625             Vy = dQtz * Hx - dQtx * Hz;
3626             Vz = dQtx * Hy - dQty * Hx;
3627             len = sqrt(Vx * Vx + Vy * Vy + Vz * Vz);
3628             Vx /= len;
3629             Vy /= len;
3630             Vz /= len;
3631 
3632             /* finally, the worm circumference points (C) and normals (N) are
3633                simple trigonomic combinations of H and V */
3634             for (j = 0; j < sides; j++) {
3635                 cosj = cos(2 * pi * j / sides);
3636                 sinj = sin(2 * pi * j / sides);
3637                 Nx[j] = Hx * cosj + Vx * sinj;
3638                 Ny[j] = Hy * cosj + Vy * sinj;
3639                 Nz[j] = Hz * cosj + Vz * sinj;
3640                 Cx[j] = Qtx + Nx[j] * radius;
3641                 Cy[j] = Qty + Ny[j] * radius;
3642                 Cz[j] = Qtz + Nz[j] * radius;
3643             }
3644 
3645             /* figure out which points on the previous circle "match" best
3646                with these, to minimize envelope twisting */
3647             for (m = 0; m < sides; m++) {
3648                 len = 0.0;
3649                 for (j = 0; j < sides; j++) {
3650                     k = j + m;
3651                     if (k >= sides)
3652                         k -= sides;
3653                     len += (Cx[k] - pCx[j]) * (Cx[k] - pCx[j]) +
3654                         (Cy[k] - pCy[j]) * (Cy[k] - pCy[j]) +
3655                         (Cz[k] - pCz[j]) * (Cz[k] - pCz[j]);
3656                 }
3657                 if (m == 0 || len < prevlen) {
3658                     prevlen = len;
3659                     offset = m;
3660                 }
3661             }
3662 
3663             /* create triangles from points along this and previous circle */
3664             if (i > 0) {
3665                 glBegin(GL_TRIANGLE_STRIP);
3666                 for (j = 0; j < sides; j++) {
3667                     k = j + offset;
3668                     if (k >= sides) k -= sides;
3669                     glNormal3d(Nx[k], Ny[k], Nz[k]);
3670                     glVertex3d(Cx[k], Cy[k], Cz[k]);
3671                     glNormal3d(pNx[j], pNy[j], pNz[j]);
3672                     glVertex3d(pCx[j], pCy[j], pCz[j]);
3673                 }
3674                 glNormal3d(Nx[offset], Ny[offset], Nz[offset]);
3675                 glVertex3d(Cx[offset], Cy[offset], Cz[offset]);
3676                 glNormal3d(pNx[0], pNy[0], pNz[0]);
3677                 glVertex3d(pCx[0], pCy[0], pCz[0]);
3678                 glEnd();
3679             }
3680 
3681             /* put caps on the end */
3682             if (cap1 && i == 0) {
3683                 glBegin(GL_POLYGON);
3684                 len = sqrt(dQtx*dQtx + dQty*dQty + dQtz*dQtz);
3685                 dQtx /= len; dQty /= len; dQtz /= len;
3686                 glNormal3d(-dQtx, -dQty, -dQtz);
3687                 for (j = sides - 1; j >= 0; j--) {
3688                     k = j + offset;
3689                     if (k >= sides) k -= sides;
3690                     glVertex3d(Cx[k], Cy[k], Cz[k]);
3691                 }
3692                 glEnd();
3693             }
3694             else if (cap2 && i == segments) {
3695                 glBegin(GL_POLYGON);
3696                 len = sqrt(dQtx*dQtx + dQty*dQty + dQtz*dQtz);
3697                 dQtx /= len; dQty /= len; dQtz /= len;
3698                 glNormal3d(dQtx, dQty, dQtz);
3699                 for (j = 0; j < sides; j++) {
3700                     k = j + offset;
3701                     if (k >= sides) k -= sides;
3702                     glVertex3d(Cx[k], Cy[k], Cz[k]);
3703                 }
3704                 glEnd();
3705             }
3706 
3707             /* store this circle as previous for next round; instead of copying
3708                all values, just swap pointers */
3709 #define SWAPPTR(p1,p2) tmp=(p1); (p1)=(p2); (p2)=tmp
3710             SWAPPTR(Nx, pNx);
3711             SWAPPTR(Ny, pNy);
3712             SWAPPTR(Nz, pNz);
3713             SWAPPTR(Cx, pCx);
3714             SWAPPTR(Cy, pCy);
3715             SWAPPTR(Cz, pCz);
3716         }
3717 #endif
3718 
3719     }
3720     if (fblock)
3721         MemFree(fblock);
3722 }
3723 
3724