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 #include"os_predef.h"
19 #include"os_std.h"
20 #include"os_gl.h"
21
22 #include"Base.h"
23 #include"OOMac.h"
24 #include"RepSphere.h"
25 #include"RepSphereImmediate.h"
26 #include"RepSphereGenerate.h"
27 #include"Color.h"
28 #include"Sphere.h"
29 #include"Map.h"
30 #include"Setting.h"
31 #include"main.h"
32 #include"Util.h"
33 #include"Feedback.h"
34 #include "ShaderMgr.h"
35 #include "Scene.h"
36 #include"CGO.h"
37 #include"ObjectMolecule.h"
38 #include "Lex.h"
39
40 #define SPHERE_NORMAL_RANGE 6.f
41 #define SPHERE_NORMAL_RANGE2 (SPHERE_NORMAL_RANGE*SPHERE_NORMAL_RANGE)
42
43 /* defer_builds_mode = 5 : Immediate mode for any sphere_mode
44
45 sphere_mode :
46
47 0) Geometry shaders (quality based on sphere_quality, default 1)
48 1) rectangular points with the same size, that can be changed with sphere_point_size
49 2) rectangles with constant size relative to vdw and scene scale (i.e., changes when zoomed)
50 maxed by a multiple of sphere_point_max_size (set it below 1 to see it influence, 3*pixel_scale max)
51 3) same as 2 but with circles
52 4) no longer available
53 5) Uses the fast ARB Shader that approximates spheres as circles
54 6-8) same as 1-3 but with normals computed from close atoms to mimic nice lighting
55 9) GLSL Shader Spheres (only when shaders are available)
56
57 */
58
59 static
RepSphereFree(RepSphere * I)60 void RepSphereFree(RepSphere * I)
61 {
62 if (I->primitiveCGO == I->renderCGO) {
63 I->primitiveCGO = 0;
64 }
65 CGOFree(I->primitiveCGO);
66 CGOFree(I->renderCGO);
67 CGOFree(I->spheroidCGO);
68 FreeP(I->LastColor);
69 FreeP(I->LastVisib);
70 RepPurge(&I->R);
71 OOFreeP(I);
72 }
73
74 /* MULTI-INSTSANCE TODO: isn't this a conflict? */
75 CShaderPrg *sphereARBShaderPrg = NULL;
76
RenderSphereComputeFog(PyMOLGlobals * G,RenderInfo * info,float * fog_info)77 void RenderSphereComputeFog(PyMOLGlobals *G, RenderInfo *info, float *fog_info)
78 {
79 /* compute -Ze = (Wc) of fog start */
80 float nv[4];
81 nv[3] =
82 (info->front +
83 (info->back - info->front) * SettingGetGlobal_f(G, cSetting_fog_start));
84 /* compute Zc of fog start using std. perspective transformation */
85 nv[2] =
86 (nv[3] * (info->back + info->front) -
87 2 * (info->back * info->front)) / (info->back - info->front);
88 /* compute Zc/Wc to get normalized depth coordinate of fog start */
89 nv[0] = (nv[2] / nv[3]);
90 fog_info[0] = (nv[0] * 0.5) + 0.5;
91
92 fog_info[1] = 1.0F / (1.0 - fog_info[0]); /* effective range of fog */
93
94 }
95
96 #ifndef _PYMOL_NO_RAY
RepSphereRenderRay(PyMOLGlobals * G,RepSphere * I,RenderInfo * info)97 static int RepSphereRenderRay(PyMOLGlobals *G, RepSphere * I, RenderInfo * info)
98 {
99 CRay *ray = info->ray;
100 float alpha = 1.0F -
101 SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_sphere_transparency);
102 if(fabs(alpha - 1.0) < R_SMALL4)
103 alpha = 1.0F;
104 ray->transparentf(1.0 - alpha);
105 if (I->spheroidCGO){
106 CGORenderRay(I->spheroidCGO, ray, info, NULL, NULL, I->R.cs->Setting, I->R.obj->Setting);
107 } else {
108 CGORenderRay(I->primitiveCGO, ray, info, NULL, NULL, I->R.cs->Setting, I->R.obj->Setting);
109 }
110 ray->transparentf(0.0);
111 return true;
112 }
113 #endif
114
RepSphereRenderPick(RepSphere * I,RenderInfo * info,int sphere_mode)115 static void RepSphereRenderPick(RepSphere * I, RenderInfo * info, int sphere_mode)
116 {
117 PyMOLGlobals *G = I->R.G;
118
119 if (!I->renderCGO){
120 // only for sphere_mode 5, where we don't use a renderCGO (yet) ARB: immediate mode GL_QUADS
121 short use_shader = SettingGetGlobal_b(G, cSetting_sphere_use_shader) &&
122 SettingGetGlobal_b(G, cSetting_use_shaders);
123 CGO *convertcgo = CGOSimplify(I->primitiveCGO, 0, 0);
124 CGO *convertcgo2 = CGOCombineBeginEnd(convertcgo, 0);
125 if (use_shader){
126 I->renderCGO = CGOOptimizeToVBONotIndexed(convertcgo2, 0);
127 CGOFree(convertcgo2);
128 } else {
129 I->renderCGO = convertcgo2;
130 }
131 I->renderCGO->use_shader = use_shader;
132 CGOFree(convertcgo);
133 }
134 CGORenderGLPicking(I->renderCGO, info, &I->R.context, I->R.cs->Setting, I->R.obj->Setting);
135 }
136
RepGetSphereMode(PyMOLGlobals * G,RepSphere * I,bool use_shader)137 static int RepGetSphereMode(PyMOLGlobals *G, RepSphere * I, bool use_shader){
138 int sphere_mode = SettingGet_i(G, I->R.cs->Setting,
139 I->R.obj->Setting,
140 cSetting_sphere_mode);
141 if (sphere_mode == 4) // sphere_mode 4 no longer exists, use default
142 sphere_mode = -1;
143 switch (sphere_mode) {
144 case 5:
145 #ifdef _PYMOL_ARB_SHADERS
146 if (!sphereARBShaderPrg && G->HaveGUI && G->ValidContext) {
147 sphereARBShaderPrg = CShaderPrg::NewARB(G, "sphere_arb",
148 G->ShaderMgr->GetShaderSource("sphere_arb_vs.vs"),
149 G->ShaderMgr->GetShaderSource("sphere_arb_fs.fs"));
150 }
151 if (!sphereARBShaderPrg)
152 #endif
153 {
154 PRINTFB(G, FB_ShaderMgr, FB_Warnings)
155 " Warning: ARB shaders (sphere_mode=5) not supported.\n" ENDFB(G);
156 if (!use_shader || !G->ShaderMgr->ShaderPrgExists("sphere")) {
157 sphere_mode = 9;
158 } else {
159 sphere_mode = 0;
160 }
161 }
162 break;
163 case -1:
164 sphere_mode = 9;
165 case 9:
166 if (!use_shader || !G->ShaderMgr->ShaderPrgExists("sphere")) {
167 sphere_mode = 0;
168 }
169 }
170 return sphere_mode;
171 }
172
RepSphereRender(RepSphere * I,RenderInfo * info)173 static void RepSphereRender(RepSphere * I, RenderInfo * info)
174 {
175 CRay *ray = info->ray;
176 auto pick = info->pick;
177 PyMOLGlobals *G = I->R.G;
178 int ok = true;
179 bool use_shader = SettingGetGlobal_b(G, cSetting_sphere_use_shader) &&
180 SettingGetGlobal_b(G, cSetting_use_shaders);
181 if(ray) {
182 #ifndef _PYMOL_NO_RAY
183 ok &= RepSphereRenderRay(G, I, info);
184 #endif
185 return;
186 }
187 int sphere_mode = RepGetSphereMode(G, I, use_shader);
188 if(G->HaveGUI && G->ValidContext) {
189 if(pick) {
190 RepSphereRenderPick(I, info, sphere_mode);
191 } else { /* not pick, render! */
192 if (I->spheroidCGO) {
193 CGORenderGL(I->spheroidCGO, NULL, NULL, NULL, info, &I->R);
194 return;
195 }
196
197 #ifdef _PYMOL_ARB_SHADERS
198 if (sphere_mode == 5){
199 // we need to check sphere_mode 5 here until
200 // we implement the CGO ARB operation, since
201 // picking uses the I->renderCGO
202 RepSphere_Generate_ARB_Spheres(G, I, info);
203 return; // sphere_mode 5 does not use I->renderCGO yet
204 }
205 #endif
206 if (I->renderCGO){
207 if (I->renderCGO->use_shader != use_shader){
208 CGOFree(I->renderCGO);
209 I->renderCGO = 0;
210 } else {
211 CGORenderGL(I->renderCGO, NULL, NULL, NULL, info, &I->R);
212 return;
213 }
214 }
215 // Generate renderCGO for sphere_mode
216 switch (sphere_mode) {
217 case 0: /* memory-efficient sphere rendering */
218 RepSphere_Generate_Triangles(G, I, info);
219 break;
220 case 9: // use GLSL impostor shader
221 RepSphere_Generate_Impostor_Spheres(G, I, info);
222 break;
223 default:
224 // sphere_modes 1,2,3,6,7,8
225 RepSphere_Generate_Point_Sprites(G, I, info, sphere_mode);
226 break;
227 }
228
229 CHECKOK(ok, I->renderCGO);
230 if (!ok){
231 CGOFree(I->renderCGO);
232 I->R.fInvalidate(&I->R, I->R.cs, cRepInvPurge);
233 I->R.cs->Active[cRepSphere] = false;
234 }
235
236 if (I->renderCGO)
237 CGORenderGL(I->renderCGO, NULL, NULL, NULL, info, &I->R);
238 }
239 }
240 }
241
242 static
RepSphereSameVis(RepSphere * I,CoordSet * cs)243 int RepSphereSameVis(RepSphere * I, CoordSet * cs)
244 {
245 bool *lv;
246 int *lc;
247 int a;
248 AtomInfoType *ai;
249 if(I->LastVisib && I->LastColor) {
250 lv = I->LastVisib;
251 lc = I->LastColor;
252
253 for(a = 0; a < cs->NIndex; a++) {
254 ai = cs->getAtomInfo(a);
255 if(*(lv++) != GET_BIT(ai->visRep, cRepSphere)) {
256 return false;
257 }
258 if(*(lc++) != ai->color) {
259 return false;
260 }
261 }
262 } else {
263 return false;
264 }
265 return true;
266 }
267
RepSphereDetermineAtomVisibility(PyMOLGlobals * G,AtomInfoType * ati1,int cartoon_side_chain_helper,int ribbon_side_chain_helper)268 static bool RepSphereDetermineAtomVisibility(PyMOLGlobals *G,
269 AtomInfoType *ati1, int cartoon_side_chain_helper, int ribbon_side_chain_helper)
270 {
271 if (!(ati1->flags & cAtomFlag_polymer))
272 return true;
273
274 bool sc_helper =
275 (GET_BIT(ati1->visRep, cRepCartoon) &&
276 AtomSettingGetWD(G, ati1, cSetting_cartoon_side_chain_helper, cartoon_side_chain_helper)) ||
277 (GET_BIT(ati1->visRep, cRepRibbon) &&
278 AtomSettingGetWD(G, ati1, cSetting_ribbon_side_chain_helper, ribbon_side_chain_helper));
279
280 if (sc_helper) {
281 int prot1 = ati1->protons;
282
283 if(prot1 == cAN_N) {
284 if(ati1->name == G->lex_const.N) {
285 if(ati1->resn != G->lex_const.PRO)
286 return false;
287 }
288 } else if(prot1 == cAN_O) {
289 if(ati1->name == G->lex_const.O)
290 return false;
291 } else if(prot1 == cAN_C) {
292 if(ati1->name == G->lex_const.C)
293 return false;
294 }
295 }
296 return true;
297 }
298
RepSphereAddAtomVisInfoToStoredVC(RepSphere * I,ObjectMolecule * obj,CoordSet * cs,int state,int a1,AtomInfoType * ati1,int a,float sphere_scale,int sphere_color,float transp,int * variable_alpha,float sphere_add)299 static void RepSphereAddAtomVisInfoToStoredVC(RepSphere *I, ObjectMolecule *obj,
300 CoordSet * cs, int state, int a1, AtomInfoType *ati1, int a,
301 float sphere_scale, int sphere_color, float transp,
302 int *variable_alpha, float sphere_add)
303 {
304 PyMOLGlobals *G = cs->G;
305 float at_transp = transp;
306 int c1;
307 float vc[3];
308 const float *vcptr;
309
310 float at_sphere_scale = AtomSettingGetWD(G, ati1, cSetting_sphere_scale, sphere_scale);
311 int at_sphere_color = AtomSettingGetWD(G, ati1, cSetting_sphere_color, sphere_color);
312
313 if(AtomSettingGetIfDefined(G, ati1, cSetting_sphere_transparency, &at_transp))
314 *variable_alpha = true;
315
316 int trans_pick_mode = SettingGet<int>(
317 G, cs->Setting, obj->Setting, cSetting_transparency_picking_mode);
318
319 int pickmode = cPickableAtom;
320 if (trans_pick_mode != cTransparencyPickingModePickable &&
321 at_transp > PICKABLE_THROUGH_CUTOFF) {
322 pickmode = cPickableThrough;
323 } else if (ati1->masked) {
324 pickmode = cPickableNoPick;
325 }
326
327 CGOPickColor(I->primitiveCGO, a1, pickmode);
328
329 if(at_sphere_color == -1)
330 c1 = ati1->color;
331 else
332 c1 = at_sphere_color;
333 const float* v0 = cs->coordPtr(a);
334
335 if(ColorCheckRamped(G, c1)) {
336 ColorGetRamped(G, c1, v0, vc, state);
337 vcptr = vc;
338 } else {
339 vcptr = ColorGet(G, c1); /* save new color */
340 }
341 float alpha = 1.0F - at_transp;
342 CGOAlpha(I->primitiveCGO, alpha);
343 CGOColorv(I->primitiveCGO, vcptr);
344 float radius = obj->AtomInfo[a1].vdw * at_sphere_scale + sphere_add;
345 CGOSphere(I->primitiveCGO, v0, radius);
346 }
347
348 /* This function is extraneous to do every time
349 we need to compute normals for RepSphere */
SphereComputeCutMultiplier(SphereRec * sr)350 static float SphereComputeCutMultiplier(SphereRec *sr){
351 int a;
352 float *dot = sr->dot[0];
353 int n_dot = sr->nDot;
354 float cut_mult = -1.0F;
355 for(a = 1; a < n_dot; a++) {
356 float t_dot = dot_product3f(dot, dot + a * 3);
357 if(cut_mult < t_dot)
358 cut_mult = t_dot;
359 }
360 return cut_mult;
361 }
362
363 /*
364 * for the spheroid implementation, this function sets color
365 * and pickcolor in the CGO given the atom idx
366 *
367 */
RepSphereCGOSetSphereColorAndPick(ObjectMolecule * obj,CoordSet * cs,CGO * cgo,int idx,int state,float transp,int sphere_color)368 static void RepSphereCGOSetSphereColorAndPick(ObjectMolecule *obj, CoordSet * cs, CGO *cgo, int idx, int state, float transp, int sphere_color){
369 PyMOLGlobals *G = obj->G;
370 int a1 = cs->IdxToAtm[idx];
371 float at_transp;
372 AtomInfoType *ati1 = obj->AtomInfo + a1;
373
374 int at_sphere_color = AtomSettingGetWD(G, ati1, cSetting_sphere_color, sphere_color);
375
376 if(AtomSettingGetIfDefined(G, ati1, cSetting_sphere_transparency, &at_transp)) {
377 float alpha = 1.0F - at_transp;
378 CGOAlpha(cgo, alpha);
379 }
380 int c1;
381 if(at_sphere_color == -1)
382 c1 = ati1->color;
383 else
384 c1 = at_sphere_color;
385 if(ColorCheckRamped(G, c1)) {
386 const float* v0 = cs->coordPtr(idx);
387 float color[3];
388 ColorGetRamped(G, c1, v0, color, state);
389 CGOColorv(cgo, color);
390 } else {
391 const float *color = ColorGet(G, c1); /* save new color */
392 CGOColorv(cgo, color);
393 }
394
395 }
396
397 static
RepSphereGeneratespheroidCGO(ObjectMolecule * I,CoordSet * cs,SphereRec * sp,int state)398 CGO *RepSphereGeneratespheroidCGO(ObjectMolecule * I, CoordSet *cs, SphereRec *sp, int state){
399 int idx, a, b, c;
400 int *q, *s;
401 bool ok = true;
402 float spheroid_scale =
403 SettingGet_f(I->G, cs->Setting, I->Setting, cSetting_spheroid_scale);
404 int sphere_color =
405 SettingGet_color(I->G, cs->Setting, I->Setting, cSetting_sphere_color);
406 float transp = SettingGet_f(I->G, cs->Setting, I->Setting, cSetting_sphere_transparency);
407
408 CGO *cgo = CGONew(I->G);
409 for(idx = 0; idx < cs->NIndex; idx++) {
410 float *v0 = &cs->Coord[3 * idx];
411 a = cs->IdxToAtm[idx];
412 q = sp->Sequence;
413 s = sp->StripLen;
414 RepSphereCGOSetSphereColorAndPick(I, cs, cgo, idx, state, transp, sphere_color);
415 for(b = 0; ok && b < sp->NStrip; b++) {
416 float *sphLen = cs->Spheroid.data() + (sp->nDot * a);
417 float *sphNorm = cs->SpheroidNormal.data() + (3 * sp->nDot * a);
418 CGOBegin(cgo, GL_TRIANGLE_STRIP);
419 for(c = 0; c < (*s); c++) {
420 float sphTmp, *sphTmpN = sphNorm + 3 * (*q);
421 CGONormalv(cgo, sphTmpN);
422 sphTmp = (*(sphLen + (*q))) * spheroid_scale;
423 // point
424 CGOVertex(cgo, v0[0] + sphTmp * sp->dot[*q][0],
425 v0[1] + sphTmp * sp->dot[*q][1],
426 v0[2] + sphTmp * sp->dot[*q][2]);
427 q++;
428 }
429 CGOEnd(cgo);
430 s++;
431 ok &= !I->G->Interrupt;
432 }
433 }
434 CGOStop(cgo);
435 if (!ok){
436 CGOFree(cgo);
437 }
438 return cgo;
439 }
440
441 /*
442 * when normals are needed (sphere_mode 6-8),
443 * this function computes the normal for an atom
444 * and pickcolor in the CGO given the atom idx
445 *
446 */
RepSphereSetNormalForSphere(RepSphere * I,MapType * map,float * v_tmp,float * v,float cut_mult,int a,int * active,float * dot,int n_dot)447 static void RepSphereSetNormalForSphere(RepSphere *I, MapType *map, float *v_tmp,
448 float *v, float cut_mult, int a,
449 int *active, float *dot, int n_dot){
450 int h, k, l, b, i, j;
451 float v1[3];
452 int n_dot_active, *da;
453 float *vv;
454 float dst;
455
456 MapLocus(map, v, &h, &k, &l);
457 da = active;
458 for(b = 0; b < n_dot; b++) {
459 *(da++) = b * 3;
460 }
461 n_dot_active = n_dot;
462 i = *(MapEStart(map, h, k, l));
463 if(i) {
464 j = map->EList[i++];
465 while(j >= 0) {
466 if(j != a) {
467 vv = v_tmp + 3 * j;
468 if(within3fret(vv, v, SPHERE_NORMAL_RANGE, SPHERE_NORMAL_RANGE2, v1, &dst)) {
469 float cutoff = dst * cut_mult;
470 b = 0;
471 while(b < n_dot_active) {
472 vv = dot + active[b];
473 if(dot_product3f(v1, vv) > cutoff) {
474 n_dot_active--;
475 active[b] = active[n_dot_active];
476 }
477 b++;
478 }
479 }
480 }
481 j = map->EList[i++];
482 }
483 }
484 float v0[3];
485 if(!n_dot_active) {
486 v0[0] = 0.0F;
487 v0[1] = 0.0F;
488 v0[2] = 1.0F;
489 } else {
490 zero3f(v0);
491 b = 0;
492 while(b < n_dot_active) {
493 vv = dot + active[b];
494 add3f(vv, v0, v0);
495 b++;
496 }
497 normalize3f(v0);
498 CGONormalv(I->primitiveCGO, v0);
499 }
500 }
501
RepSphereNew(CoordSet * cs,int state)502 Rep *RepSphereNew(CoordSet * cs, int state)
503 {
504 PyMOLGlobals *G = cs->G;
505 ObjectMolecule *obj;
506 int ok = true;
507 int a, a1;
508 bool *lv;
509 int *lc;
510 float sphere_scale, sphere_add = 0.f;
511 int sphere_color;
512 int cartoon_side_chain_helper = 0;
513 int ribbon_side_chain_helper = 0;
514 AtomInfoType *ati1;
515 int sphere_mode = 0;
516 bool *marked = NULL;
517 float transp;
518 int variable_alpha = false;
519 short use_shader = SettingGetGlobal_b(G, cSetting_sphere_use_shader) &&
520 SettingGetGlobal_b(G, cSetting_use_shaders);
521 // skip if not visible
522 if(!cs->hasRep(cRepSphereBit))
523 return NULL;
524
525 OOCalloc(G, RepSphere);
526 CHECKOK(ok, I);
527 if (!ok)
528 return NULL;
529 obj = cs->Obj;
530
531 marked = pymol::calloc<bool>(obj->NAtom);
532 CHECKOK(ok, marked);
533 if (ok)
534 RepInit(G, &I->R);
535 I->renderCGO = NULL;
536 I->primitiveCGO = NULL;
537 if (!cs->Spheroid.empty())
538 I->spheroidCGO = RepSphereGeneratespheroidCGO(obj, cs, GetSpheroidSphereRec(G), state);
539
540 if (ok){
541 sphere_mode = SettingGet_i(G, cs->Setting, obj->Setting, cSetting_sphere_mode);
542 if (!use_shader && (sphere_mode == 5 || sphere_mode == 9)){
543 sphere_mode = 0;
544 }
545 }
546 if (ok){
547 sphere_color =
548 SettingGet_color(G, cs->Setting, obj->Setting, cSetting_sphere_color);
549 cartoon_side_chain_helper =
550 SettingGet_b(G, cs->Setting, obj->Setting, cSetting_cartoon_side_chain_helper);
551 ribbon_side_chain_helper =
552 SettingGet_b(G, cs->Setting, obj->Setting, cSetting_ribbon_side_chain_helper);
553 transp = SettingGet_f(G, cs->Setting, obj->Setting, cSetting_sphere_transparency);
554 sphere_scale = SettingGet_f(G, cs->Setting, obj->Setting, cSetting_sphere_scale);
555 }
556
557 if (ok){
558 I->R.fRender = (void (*)(struct Rep *, RenderInfo *)) RepSphereRender;
559 I->R.fFree = (void (*)(struct Rep *)) RepSphereFree;
560 I->R.fSameVis = (int (*)(struct Rep *, struct CoordSet *)) RepSphereSameVis;
561 I->R.obj = (CObject *) obj;
562 I->R.cs = cs;
563 I->R.context.object = obj;
564 I->R.context.state = state;
565 }
566 /* raytracing primitives */
567
568 if (ok){
569 if(SettingGet_i(G, cs->Setting, obj->Setting, cSetting_sphere_solvent)) { /* are we generating a solvent surface? */
570 sphere_add = SettingGet_f(G, cs->Setting, obj->Setting, cSetting_solvent_radius); /* if so, get solvent radius */
571 }
572
573 if(SettingGet_b(G, cs->Setting, obj->Setting, cSetting_pickable)) {
574 I->R.P = pymol::malloc<Pickable>(cs->NIndex + 1);
575 CHECKOK(ok, I->R.P);
576 }
577 }
578 I->primitiveCGO = CGONew(G);
579
580 bool needNormals = (sphere_mode >= 6) && (sphere_mode < 9);
581 int nspheres = 0;
582 if (needNormals){
583 float *v_tmp = VLAlloc(float, 1024);
584 for(a = 0; ok && a < cs->NIndex; a++) {
585 a1 = cs->IdxToAtm[a];
586 ati1 = obj->AtomInfo + a1;
587 /* store temporary visibility information */
588 marked[a1] = GET_BIT(ati1->visRep,cRepSphere) &&
589 RepSphereDetermineAtomVisibility(G, ati1,
590 cartoon_side_chain_helper, ribbon_side_chain_helper);
591 if(marked[a1]) {
592 int cnc = nspheres * 3;
593 nspheres++;
594 VLACheck(v_tmp, float, cnc + 3);
595 copy3f(cs->coordPtr(a), &v_tmp[cnc]);
596 }
597 ok &= !G->Interrupt;
598 }
599 MapType *map = MapNew(G, SPHERE_NORMAL_RANGE, v_tmp, nspheres, NULL);
600 float cut_mult = SphereComputeCutMultiplier(G->Sphere->Sphere[1]);
601 float *dot = G->Sphere->Sphere[1]->dot[0];
602 int n_dot = G->Sphere->Sphere[1]->nDot;
603 int *active = pymol::malloc<int>(2 * n_dot);
604
605 ok &= MapSetupExpress(map);
606 for(a = 0; ok && a < cs->NIndex; a++) {
607 a1 = cs->IdxToAtm[a];
608 if(marked[a1]) {
609 ati1 = obj->AtomInfo + a1;
610 RepSphereSetNormalForSphere(I, map, v_tmp, &v_tmp[a * 3], cut_mult, a, active, dot, n_dot);
611 RepSphereAddAtomVisInfoToStoredVC(I, obj, cs, state, a1, ati1, a, sphere_scale, sphere_color, transp, &variable_alpha, sphere_add);
612 }
613 ok &= !G->Interrupt;
614 }
615 FreeP(active);
616 VLAFreeP(v_tmp);
617 MapFree(map);
618 } else { // no normals necessary
619 for(a = 0; ok && a < cs->NIndex; a++) {
620 a1 = cs->IdxToAtm[a];
621 ati1 = obj->AtomInfo + a1;
622 /* store temporary visibility information */
623 marked[a1] = GET_BIT(ati1->visRep,cRepSphere) &&
624 RepSphereDetermineAtomVisibility(G, ati1,
625 cartoon_side_chain_helper, ribbon_side_chain_helper);
626 if(marked[a1]) {
627 nspheres++;
628 RepSphereAddAtomVisInfoToStoredVC(I, obj, cs, state, a1, ati1, a, sphere_scale, sphere_color, transp, &variable_alpha, sphere_add);
629 }
630 ok &= !G->Interrupt;
631 }
632 }
633 CGOStop(I->primitiveCGO);
634
635 if(ok) {
636 if(!I->LastVisib)
637 I->LastVisib = pymol::malloc<bool>(cs->NIndex);
638 CHECKOK(ok, I->LastVisib);
639 if(ok && !I->LastColor)
640 I->LastColor = pymol::malloc<int>(cs->NIndex);
641 CHECKOK(ok, I->LastColor);
642 if (ok){
643 lv = I->LastVisib;
644 lc = I->LastColor;
645 obj = cs->Obj;
646 const AtomInfoType *ai2 = obj->AtomInfo.data();
647 if(sphere_color == -1){
648 for(a = 0; a < cs->NIndex; a++) {
649 int at = cs->IdxToAtm[a];
650 *(lv++) = marked[at];
651 *(lc++) = (ai2 + at)->color;
652 }
653 } else {
654 for(a = 0; a < cs->NIndex; a++) {
655 *(lv++) = marked[cs->IdxToAtm[a]];
656 *(lc++) = sphere_color;
657 }
658 }
659 }
660 }
661
662 FreeP(marked);
663 if(nspheres == 0 || !ok) {
664 RepSphereFree(I);
665 I = NULL;
666 }
667 return (Rep *) I;
668 }
669