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