1 
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 #include"os_python.h"
18 
19 #include"os_predef.h"
20 #include"os_std.h"
21 #include"os_gl.h"
22 
23 #include"Base.h"
24 #include"OOMac.h"
25 #include"RepDot.h"
26 #include"Color.h"
27 #include"Sphere.h"
28 #include"Map.h"
29 #include"Setting.h"
30 #include"main.h"
31 #include"ObjectMolecule.h"
32 #include"Scene.h"
33 #include"ShaderMgr.h"
34 #include"CGO.h"
35 
36 static void RepDotRender(RepDot * I, RenderInfo * info);
37 
38 static
RepDotFree(RepDot * I)39 void RepDotFree(RepDot * I)
40 {
41   if (I->shaderCGO){
42     CGOFree(I->shaderCGO);
43     I->shaderCGO = 0;
44   }
45   FreeP(I->VC);
46   FreeP(I->V);
47   FreeP(I->T);
48   FreeP(I->F);
49   FreeP(I->VN);
50   FreeP(I->A);
51   FreeP(I->Atom);
52   OOFreeP(I);
53 }
54 
RepDotCGOGenerate(RepDot * I,RenderInfo * info)55 static int RepDotCGOGenerate(RepDot * I, RenderInfo * info)
56 {
57   PyMOLGlobals *G = I->R.G;
58   float *v = I->V;
59   int c = I->N;
60   int cc = 0;
61   int ok = true;
62   CGO *cgo = NULL;
63 
64   int normals =
65     SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_normals);
66   short dot_as_spheres = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_as_spheres);
67 
68   cgo = CGONew(G);
69   CHECKOK(ok, cgo);
70   if (dot_as_spheres){
71     while(ok && c--) {
72       if(!cc) {             /* load up the current vertex color */
73 	cc = (int) (*(v++));
74 	ok &= CGOColorv(cgo, v);
75 	v += 3;
76       }
77       if(ok && normals)
78 	ok &= CGONormalv(cgo, v);
79       v += 3;
80       if (ok)
81 	ok &= CGOSphere(cgo, v, 1.f);
82       v += 3;
83       cc--;
84     }
85   } else {
86     if (ok)
87       ok &= CGOBegin(cgo, GL_POINTS);
88     while(ok && c--) {
89       if(!cc) {             /* load up the current vertex color */
90 	cc = (int) (*(v++));
91 	ok &= CGOColorv(cgo, v);
92 	v += 3;
93       }
94       if(normals)
95 	CGONormalv(cgo, v);
96       v += 3;
97       if (ok)
98 	ok &= CGOVertexv(cgo, v);
99       v += 3;
100       cc--;
101     }
102     if (ok)
103       ok &= CGOEnd(cgo);
104   }
105   if (ok)
106     ok &= CGOStop(cgo);
107   if (ok) {
108     if (dot_as_spheres){
109       CGO *tmpCGO = CGONew(G), *tmp2CGO = NULL;
110       if (ok) ok &= CGOEnable(tmpCGO, GL_SPHERE_SHADER);
111       if (ok) ok &= CGOEnable(tmpCGO, GL_DOT_LIGHTING);
112       if (ok) ok &= CGOSpecial(tmpCGO, DOT_WIDTH_FOR_DOT_SPHERES);
113       tmp2CGO = CGOOptimizeSpheresToVBONonIndexedNoShader(cgo,
114           CGO_BOUNDING_BOX_SZ + fsizeof<cgo::draw::sphere_buffers>() + 2);
115       if (ok)
116 	ok &= CGOAppendNoStop(tmpCGO, tmp2CGO);
117       CGOFreeWithoutVBOs(tmp2CGO);
118       if (ok) ok &= CGODisable(tmpCGO, GL_SPHERE_SHADER);
119       if (ok) ok &= CGOStop(tmpCGO);
120       I->shaderCGO = tmpCGO;
121     } else {
122       CGO *convertcgo = CGOCombineBeginEnd(cgo, 0), *tmp2CGO = NULL;
123       CGO *tmpCGO = CGONew(G);
124       if (ok) ok &= CGOEnable(tmpCGO, GL_DEFAULT_SHADER);
125       if (ok) ok &= CGOEnable(tmpCGO, GL_DOT_LIGHTING);
126       if (ok) ok &= CGOSpecial(tmpCGO, DOT_WIDTH_FOR_DOTS);
127       CHECKOK(ok, convertcgo);
128       if (ok)
129 	tmp2CGO = CGOOptimizeToVBONotIndexedNoShader(convertcgo, CGO_BOUNDING_BOX_SZ + I->N * 3 + 7);
130       CHECKOK(ok, tmp2CGO);
131       if (ok)
132 	ok &= CGOAppendNoStop(tmpCGO, tmp2CGO);
133       CGOFreeWithoutVBOs(tmp2CGO);
134       CGOFree(convertcgo);
135       if (ok) ok &= CGODisable(tmpCGO, GL_DEFAULT_SHADER);
136       if (ok) ok &= CGOStop(tmpCGO);
137       I->shaderCGO = tmpCGO;
138     }
139   }
140   if (ok){
141     I->shaderCGO->use_shader = true;
142     I->shaderCGO_as_spheres = dot_as_spheres;
143   }
144   CGOFree(cgo);
145 
146   /* now that the shaderCGO is created, we can just call RepDotRender to render it */
147   if (ok)
148     RepDotRender(I, info);
149 
150   return ok;
151 
152 }
153 
RepDotRender(RepDot * I,RenderInfo * info)154 static void RepDotRender(RepDot * I, RenderInfo * info)
155 {
156   CRay *ray = info->ray;
157   auto pick = info->pick;
158   PyMOLGlobals *G = I->R.G;
159   float *v = I->V;
160   int c = I->N;
161   int cc = 0;
162   int ok = true;
163 
164   if(ray) {
165 #ifndef _PYMOL_NO_RAY
166     float radius;
167 
168     if(I->dotSize <= 0.0F) {
169       radius = ray->PixelRadius * I->Width / 1.4142F;
170     } else {
171       radius = I->dotSize;
172     }
173 
174     while(ok && c--) {
175       if(!cc) {                 /* load up the current vertex color */
176         cc = (int) (*(v++));
177         ray->color3fv(v);
178         v += 3;
179       }
180       v += 3;
181       ok &= ray->sphere3fv(v, radius);
182       v += 3;
183       cc--;
184     }
185 #endif
186   } else if(G->HaveGUI && G->ValidContext) {
187     if(pick) {
188     } else { /* else not pick, i.e., when rendering */
189       short use_shader, generate_shader_cgo = 0;
190       int normals =
191         SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_normals);
192       short dot_as_spheres = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_as_spheres);
193 
194       use_shader = SettingGetGlobal_b(G, cSetting_dot_use_shader) &
195                    SettingGetGlobal_b(G, cSetting_use_shaders);
196 
197       if (I->shaderCGO && ((!use_shader || CGOCheckWhetherToFree(G, I->shaderCGO)) ||
198 			   I->shaderCGO_as_spheres!= dot_as_spheres)){
199 	CGOFree(I->shaderCGO);
200 	I->shaderCGO = 0;
201       }
202 
203       if (use_shader){
204 	if (!I->shaderCGO){
205 	  generate_shader_cgo = 1;
206 	  ok &= RepDotCGOGenerate(I, info);
207 	} else {
208 	  const float *color;
209 	  color = ColorGet(G, I->R.obj->Color);
210 	  CGORenderGL(I->shaderCGO, color, NULL, NULL, info, &I->R);
211 	  return; /* should not do any other rendering after shaderCGO has
212 		    been rendered */
213 	}
214       }
215 
216       if (!generate_shader_cgo) {
217 	if(!normals)
218 	  SceneResetNormal(G, true);
219         int lighting =
220           SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_lighting);
221 	if(!lighting) {
222 	  if(!info->line_lighting)
223 	    glDisable(GL_LIGHTING);
224 	}
225 
226 	if(info->width_scale_flag)
227 	  glPointSize(I->Width * info->width_scale);
228 	else
229 	  glPointSize(I->Width);
230 
231         glBegin(GL_POINTS);
232         while(c--) {
233           if(!cc) {             /* load up the current vertex color */
234             cc = (int) (*(v++));
235             glColor3fv(v);
236             v += 3;
237           }
238           if(normals)
239             glNormal3fv(v);
240           v += 3;
241           glVertex3fv(v);
242           v += 3;
243           cc--;
244         }
245         glEnd();
246 
247         if(!lighting)
248           glEnable(GL_LIGHTING);
249       }
250     }
251   }
252   if (!ok){
253     CGOFree(I->shaderCGO);
254     I->R.fInvalidate(&I->R, I->R.cs, cRepInvPurge);
255     I->R.cs->Active[cRepDot] = false;
256   }
257 }
258 
RepDotNew(CoordSet * cs,int state)259 Rep *RepDotNew(CoordSet * cs, int state)
260 {
261   return (RepDotDoNew(cs, cRepDotNormal, state));
262 }
263 
RepDotDoNew(CoordSet * cs,int mode,int state)264 Rep *RepDotDoNew(CoordSet * cs, int mode, int state)
265 {
266 
267   /* this routine does double duty - generating the dot representation,
268      but also acting as our surface area computation routine.
269      Modes: cRepDotNormal,cRepDotAreaType
270    */
271   PyMOLGlobals *G = cs->G;
272   ObjectMolecule *obj;
273   int a, b, flag, h, k, l, i, j, c1;
274   float *v, *vn;
275   const float *vc;
276   float *aa = NULL;
277   int *tp = NULL;
278   int *tf = NULL;
279   float *countPtr = NULL;
280   int colorCnt, lastColor;
281   Vector3f v1;
282   MapType *map = NULL;
283   SphereRec *sp = G->Sphere->Sphere[0];
284   int ds;
285   float max_vdw = MAX_VDW;
286   float solv_rad = 0.0;
287   int inclH = true;
288   int cullByFlag = false;
289   int visFlag;
290   int atm, *ati = NULL;
291   AtomInfoType *ai1, *ai2;
292   int dot_color;
293   int ok = true;
294   OOAlloc(G, RepDot);
295   CHECKOK(ok, I);
296   if (ok)
297     obj = cs->Obj;
298 
299   if (ok){
300     if(mode == cRepDotAreaType) { /* assume all atoms "visible" for area comp. */
301       visFlag = true;
302     } else {
303       visFlag = cs->hasRep(cRepDotBit);
304     }
305   }
306   if(!ok || !visFlag) {
307     OOFreeP(I);
308     return (NULL);              /* skip if no dots are visible */
309   }
310 
311   RepInit(G, &I->R);
312 
313   I->dotSize = SettingGet_f(G, cs->Setting, obj->Setting, cSetting_dot_radius);
314 
315   I->A = NULL;
316   I->T = NULL;
317   I->F = NULL;
318   I->V = NULL;
319   I->VC = NULL;
320   I->VN = NULL;
321   I->Atom = NULL;
322   I->R.fRecolor = NULL;
323   I->shaderCGO = 0;
324 
325   I->Width = SettingGet_f(G, cs->Setting, obj->Setting, cSetting_dot_width);
326   cullByFlag = SettingGet_i(G, cs->Setting, obj->Setting, cSetting_trim_dots);      /* are we using flags 24 & 25 */
327 
328   dot_color = SettingGet_color(G, cs->Setting, obj->Setting, cSetting_dot_color);   /* are we using flags 24 & 25 */
329   inclH = SettingGet_i(G, cs->Setting, obj->Setting, cSetting_dot_hydrogens);       /* are we ignoring hydrogens? */
330   if(SettingGet_b(G, cs->Setting, obj->Setting, cSetting_dot_solvent)) {    /* are we generating a solvent surface? */
331     solv_rad = SettingGet_f(G, cs->Setting, obj->Setting, cSetting_solvent_radius); /* if so, get solvent radius */
332   }
333 
334   /* get current dot sampling */
335   ds = SettingGet_i(G, cs->Setting, obj->Setting, cSetting_dot_density);
336 
337   max_vdw += solv_rad;
338 
339 
340 /* Note: significantly affects the accuracy of our area comp. */
341   if(ds < 0)
342     ds = 0;
343   if(ds > 4)
344     ds = 4;
345   sp = G->Sphere->Sphere[ds];
346 
347   I->R.fRender = (void (*)(struct Rep *, RenderInfo * info)) RepDotRender;
348   I->R.fFree = (void (*)(struct Rep *)) RepDotFree;
349   I->R.obj = (CObject *) obj;
350   I->R.cs = cs;
351 
352   I->V = pymol::malloc<float>(cs->NIndex * sp->nDot * 10);
353   CHECKOK(ok, I->V);
354 
355   if(ok && mode == cRepDotAreaType) { /* in area mode, we need to export save addl. info
356                                  * such as the normal vectors, the partial area,
357                                  * the originating atom, etc. */
358     I->A = pymol::malloc<float>(cs->NIndex * sp->nDot);
359     CHECKOK(ok, I->A);
360     if (ok)
361       I->T = pymol::malloc<int>(cs->NIndex * sp->nDot);
362     CHECKOK(ok, I->T);
363     if (ok)
364       I->F = pymol::malloc<int>(cs->NIndex * sp->nDot);
365     CHECKOK(ok, I->F);
366     if (ok)
367       I->VN = pymol::malloc<float>(cs->NIndex * sp->nDot * 3);
368     CHECKOK(ok, I->VN);
369     if (ok)
370       I->Atom = pymol::malloc<int>(cs->NIndex * sp->nDot);
371     CHECKOK(ok, I->Atom);
372     if (ok){
373       aa = I->A;
374       tp = I->T;
375       tf = I->F;
376       ati = I->Atom;
377       inclH = true;
378       cullByFlag = true;
379     }
380   }
381   vn = I->VN;
382   I->N = 0;
383   lastColor = -1;
384   colorCnt = 0;
385   if (ok)
386     map = MapNew(G, max_vdw, cs->Coord, cs->NIndex, NULL);
387   CHECKOK(ok, map);
388   v = I->V;
389   if(ok && map) {
390     ok &= MapSetupExpress(map);
391     for(a = 0; ok && a < cs->NIndex; a++) {
392       atm = cs->IdxToAtm[a];
393       ai1 = obj->AtomInfo + atm;
394       if((ai1->visRep & cRepDotBit) || mode == cRepDotAreaType)
395         if((inclH || (!ai1->isHydrogen())) &&
396            ((!cullByFlag) || (!(ai1->flags & cAtomFlag_exfoliate)))) {
397           c1 = AtomSettingGetWD(G, ai1, cSetting_dot_color, dot_color);
398 
399           /* If we are culling, flag 24 controls which atoms
400              will have dot surfaces generated for them.
401            */
402           if(c1 == -1) {
403             c1 = ai1->color;
404           }
405           const float* v0 = cs->coordPtr(a);
406           const float vdw = ai1->vdw + solv_rad;
407           for(b = 0; b < sp->nDot; b++) {
408             v1[0] = v0[0] + vdw * sp->dot[b][0];
409             v1[1] = v0[1] + vdw * sp->dot[b][1];
410             v1[2] = v0[2] + vdw * sp->dot[b][2];
411 
412             MapLocus(map, v1, &h, &k, &l);
413 
414             flag = true;
415 
416             i = *(MapEStart(map, h, k, l));
417             if(i) {
418               j = map->EList[i++];
419               while(j >= 0) {
420                 ai2 = obj->AtomInfo + cs->IdxToAtm[j];
421                 if((inclH || (!(ai2->isHydrogen()))) &&
422                    ((!cullByFlag) || (!(ai2->flags & cAtomFlag_ignore))))
423                   /* If we are cullilng, flag 25 controls which atoms
424                      are considered "present" in the surface area
425                      calculation (i.e. able to occlude surface) */
426                   if(j != a)
427                     if(within3f(cs->coordPtr(j), v1, ai2->vdw + solv_rad)) {
428                       flag = false;
429                       break;
430                     }
431                 j = map->EList[i++];
432               }
433             }
434             if(flag) {
435               switch (mode) {
436               case cRepDotNormal:
437 
438                 if((lastColor != c1) || ColorCheckRamped(G, c1)) {      /* new color */
439                   if(countPtr)  /* after first pass */
440                     *countPtr = (float) colorCnt;       /* save count */
441                   colorCnt = 1;
442                   countPtr = v++;
443                   vc = ColorGet(G, c1); /* save new color */
444                   lastColor = c1;
445                   if(ColorCheckRamped(G, c1)) {
446                     ColorGetRamped(G, c1, v1, v, state);
447                     v += 3;
448                   } else {
449                     *(v++) = *(vc++);
450                     *(v++) = *(vc++);
451                     *(v++) = *(vc++);
452                   }
453                 } else
454                   colorCnt++;
455                 *(v++) = sp->dot[b][0];
456                 *(v++) = sp->dot[b][1];
457                 *(v++) = sp->dot[b][2];
458                 *(v++) = v1[0];
459                 *(v++) = v1[1];
460                 *(v++) = v1[2];
461                 I->N++;
462                 break;
463               case cRepDotAreaType:
464                 *(v++) = v1[0];
465                 *(v++) = v1[1];
466                 *(v++) = v1[2];
467                 *(aa++) = vdw * vdw * sp->area[b];      /* area */
468                 *(tp++) = ai1->customType;      /* numeric type */
469                 *(tf++) = ai1->flags;   /* flags */
470                 *(vn++) = sp->dot[b][0];
471                 *(vn++) = sp->dot[b][1];
472                 *(vn++) = sp->dot[b][2];
473                 *(ati++) = atm;
474                 I->N++;
475                 break;
476               }
477             }
478           }
479         }
480       ok &= !G->Interrupt;
481     }
482     if(countPtr)
483       *countPtr = (float) colorCnt;     /* save count */
484     MapFree(map);
485   }
486   if (ok)
487     I->V = ReallocForSure(I->V, float, (v - I->V));
488   CHECKOK(ok, I->V);
489   if(ok && mode == cRepDotAreaType) {
490     I->A = ReallocForSure(I->A, float, (aa - I->A));
491     CHECKOK(ok, I->A);
492     if (ok)
493       I->T = ReallocForSure(I->T, int, (tp - I->T));
494     CHECKOK(ok, I->T);
495     if (ok)
496       I->F = ReallocForSure(I->F, int, (tf - I->F));
497     CHECKOK(ok, I->F);
498     if (ok)
499       I->VN = ReallocForSure(I->VN, float, (vn - I->VN));
500     CHECKOK(ok, I->VN);
501     if (ok)
502       I->Atom = ReallocForSure(I->Atom, int, (ati - I->Atom));
503     CHECKOK(ok, I->Atom);
504   }
505   if (!ok){
506     RepDotFree(I);
507     I = NULL;
508   }
509   return (Rep *) I;
510 }
511