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