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"Feedback.h"
24 #include"CGO.h"
25 #include"Base.h"
26 #include"OOMac.h"
27 #include"Setting.h"
28 #include"Sphere.h"
29 #include"PConv.h"
30 #include"GadgetSet.h"
31 #include"VFont.h"
32 #include"P.h"
33 #include"PyMOLGlobals.h"
34 #include"Ray.h"
35 #include"Util.h"
36 #include"Scene.h"
37 #include"ScenePicking.h"
38 #include"Matrix.h"
39 #include"ShaderMgr.h"
40 #include"CoordSet.h"
41 #include"Rep.h"
42 #include"Vector.h"
43 #include"ObjectGadgetRamp.h"
44 #include"Triangle.h"
45 #include "Picking.h"
46 
47 #include "pymol/algorithm.h"
48 
49 #if defined(_PYMOL_IOS) && !defined(_WEBGL)
50 #define ALIGN_VBOS_TO_4_BYTE_ARRAYS
51 #define VAR_FOR_NORMAL  plc
52 #define VAR_FOR_NORMAL_CNT_PLUS   + (cnt / 3)
53 #define VERTEX_NORMAL_SIZE 4
54 #else
55 #define VAR_FOR_NORMAL  pl
56 #define VERTEX_NORMAL_SIZE 3
57 #define VAR_FOR_NORMAL_CNT_PLUS
58 #endif
59 
60 #define VALUES_PER_IMPOSTER_SPACE_COORD 1
61 
62 #if defined(PURE_OPENGL_ES_2)
63 #define VERTICES_PER_SPHERE 6
64 #else
65 #define VERTICES_PER_SPHERE 4
66 #endif
67 
68 #if defined(_PYMOL_IOS) && !defined(_WEBGL)
69 #define NUM_VERTICES_PER_CYLINDER 4
70 #define NUM_TOTAL_VERTICES_PER_CYLINDER 6
71 #else
72 #define NUM_VERTICES_PER_CYLINDER 8
73 #define NUM_TOTAL_VERTICES_PER_CYLINDER 36
74 #endif
75 
76 #ifdef PURE_OPENGL_ES_2
77 #define glVertexAttrib4ubv(loc, data) glVertexAttrib4f(loc, \
78     (data)[0] / 255.f, (data)[1] / 255.f, (data)[2] / 255.f, (data)[3] / 255.f);
79 #endif
80 
81 #include <cassert>
82 #include <iostream>
83 #include <algorithm>
84 
85 using namespace std;
86 
87 #define MAX_INDICES_FOR_IOS 65536
88 
89 const float g_ones4f[4] = {1.f, 1.f, 1.f, 1.f};
90 
91 template <typename T>
CLAMPVALUE(T val,T minimum,T maximum)92 inline T CLAMPVALUE(T val, T minimum, T maximum) {
93   return
94     (val < minimum) ? minimum :
95     (val > maximum) ? maximum : val;
96 }
97 
98 #if defined(_PYMOL_IOS) && !defined(_WEBGL)
99 extern "C" void firePyMOLLimitationWarning();
100 #define CHECK_GL_ERROR_OK(printstr)			\
101   if ((err = glGetError())!=0 || I->G->Interrupt != 0){		\
102       if (err)        \
103 	PRINTFB(I->G, FB_CGO, FB_Errors) printstr, err ENDFB(I->G);	   \
104   }
105 #else
106 #define CHECK_GL_ERROR_OK(printstr)	\
107   if ((err = glGetError()) != 0){						\
108      PRINTFB(I->G, FB_CGO, FB_Errors) printstr, err ENDFB(I->G);	   \
109   }
110 #endif
111 
112 #define WARN_UNEXPECTED_OPERATION(G, op)                                       \
113   PRINTFB(G, FB_CGO, FB_Warnings)                                              \
114   " %s-Warning: unexpected op=0x%x (line %d)\n", __func__, op, __LINE__ ENDFB(G)
115 
116 // like g_return_val_if_fail from glib
117 #define RETURN_VAL_IF_FAIL(expr, val)                                          \
118   {                                                                            \
119     if (!(expr))                                                               \
120       return (val);                                                            \
121   }
122 
123 struct _CCGORenderer {
124   PyMOLGlobals *G;
125   RenderInfo *info;
126   Rep *rep;
127   const float *color;
128   float alpha;
129   short sphere_quality;
130   bool isPicking;
pick_pass_CCGORenderer131   unsigned pick_pass() const { return info->pick->m_pass; }
132   bool use_shader; // OpenGL 1.4+, e.g., glEnableVertexAttribArray() (on) vs. glEnableClientState() (off)
133   bool debug;
134   CSetting *set1, *set2;
135 };
136 
137 static
set_current_pick_color(CGO * cgo,unsigned int idx,int bnd)138 void set_current_pick_color(
139     CGO * cgo,
140     unsigned int idx,
141     int bnd)
142 {
143   if (cgo) {
144     cgo->current_pick_color_index = idx;
145     cgo->current_pick_color_bond = bnd;
146   }
147 }
148 
149 /**
150  * Assign a new pick color index for {context, index, bond} (only in the
151  * first pass) and convert that to an RGBA color for the current picking pass.
152  *
153  * @param[out] cgo Set the "current pick color" in this CGO
154  * @param[in,out] pickmgr Pick color manager
155  * @param[out] color RGBA pick color to use in this pass
156  * @param[in] context Object state identifier
157  * @param[in] index Primary index
158  * @param[in] bond Secondary index
159  */
AssignNewPickColor(CGO * cgo,PickColorManager * pickmgr,unsigned char * color,const PickContext * context,unsigned int index,int bond)160 void AssignNewPickColor(CGO* cgo, PickColorManager* pickmgr,
161     unsigned char* color, const PickContext* context, unsigned int index,
162     int bond)
163 {
164   set_current_pick_color(cgo, index, bond);
165   pickmgr->colorNext(color, context, index, bond);
166 }
167 
168 static
CGOConvertDebugMode(int debug,int modeArg)169 int CGOConvertDebugMode(int debug, int modeArg){
170   int mode = modeArg;
171   if (debug==1){
172     switch (mode){
173     case GL_TRIANGLES:
174       mode = GL_LINES;
175       break;
176     case GL_TRIANGLE_STRIP:
177       mode = GL_LINE_STRIP;
178       break;
179     case GL_TRIANGLE_FAN:
180       mode = GL_LINES;
181       break;
182     }
183   } else {
184     mode = GL_POINTS;
185   }
186   return mode;
187 }
188 
189 
CGORendererInit(PyMOLGlobals * G)190 int CGORendererInit(PyMOLGlobals * G)
191 {
192   CCGORenderer *I = NULL;
193 
194   I = (G->CGORenderer = pymol::calloc<CCGORenderer>(1));
195   if(I) {
196     I->G = G;
197     I->isPicking = false;
198     I->alpha = 1.0F;
199     return 1;
200   } else
201     return 0;
202 }
203 
CGORendererFree(PyMOLGlobals * G)204 void CGORendererFree(PyMOLGlobals * G)
205 {
206   FreeP(G->CGORenderer);
207 }
208 
209 int CGO_sz[] = {
210   CGO_NULL_SZ,
211   CGO_NULL_SZ,
212   CGO_BEGIN_SZ,
213   CGO_END_SZ,
214 
215   CGO_VERTEX_SZ,
216   CGO_NORMAL_SZ,
217   CGO_COLOR_SZ,
218   CGO_SPHERE_SZ,
219 
220   CGO_TRIANGLE_SZ,
221   fsizeof<cgo::draw::cylinder>(),
222   CGO_LINEWIDTH_SZ,
223   CGO_WIDTHSCALE_SZ,
224 
225   CGO_ENABLE_SZ,
226   CGO_DISABLE_SZ,
227   fsizeof<cgo::draw::sausage>(),
228   fsizeof<cgo::draw::custom_cylinder>(),
229 
230   CGO_DOTWIDTH_SZ,
231   CGO_ALPHA_TRIANGLE_SZ,
232   CGO_ELLIPSOID_SZ,
233   CGO_FONT_SZ,
234 
235   CGO_FONT_SCALE_SZ,
236   CGO_FONT_VERTEX_SZ,
237   CGO_FONT_AXES_SZ,
238   CGO_CHAR_SZ,
239 
240   CGO_INDENT_SZ,
241   CGO_ALPHA_SZ,
242   CGO_QUADRIC_SZ,
243   CGO_CONE_SZ,
244 
245   fsizeof<cgo::draw::arrays>(),
246   CGO_NULL_SZ,
247   CGO_RESET_NORMAL_SZ,
248   CGO_PICK_COLOR_SZ,
249 
250   CGO_NULL_SZ, // CGO_DRAW_BUFFERS_SZ no longer used
251   fsizeof<cgo::draw::buffers_indexed>(),
252   CGO_BOUNDING_BOX_SZ,
253   fsizeof<cgo::draw::buffers_not_indexed>(),
254   CGO_SPECIAL_SZ,
255   fsizeof<cgo::draw::cylinder_buffers>(),
256   fsizeof<cgo::draw::shadercylinder>(),
257   fsizeof<cgo::draw::shadercylinder2ndcolor>(),
258   fsizeof<cgo::draw::sphere_buffers>(),
259   CGO_ACCESSIBILITY_SZ,
260   CGO_DRAW_TEXTURE_SZ,
261   fsizeof<cgo::draw::textures>(),
262   fsizeof<cgo::draw::screen_textures>(),
263   CGO_TEX_COORD_SZ,
264   fsizeof<cgo::draw::label>(),
265   fsizeof<cgo::draw::labels>(),
266   CGO_DRAW_CONNECTOR_SZ,
267   fsizeof<cgo::draw::connectors>(),
268   CGO_DRAW_TRILINES_SZ,  CGO_UNIFORM3F_SZ,
269   CGO_SPECIAL_WITH_ARG_SZ,
270   fsizeof<cgo::draw::line>(),
271   fsizeof<cgo::draw::splitline>(),
272   fsizeof<cgo::draw::custom>(),
273   fsizeof<cgo::draw::vertex_attribute_3f>(),
274   fsizeof<cgo::draw::vertex_attribute_4ub>(),
275   fsizeof<cgo::draw::vertex_attribute_1f>(),
276   fsizeof<cgo::draw::mask_attribute_if_picking>(),
277   fsizeof<cgo::draw::bind_vbo_for_picking>(),
278   CGO_VERTEX_BEGIN_LINE_STRIP_SZ,  CGO_INTERPOLATED_SZ,  CGO_VERTEX_CROSS_SZ,
279   fsizeof<cgo::draw::vertex_attribute_4ub_if_picking>(),
280   fsizeof<cgo::draw::custom_cylinder_alpha>(),
281   CGO_NULL_SZ
282 };
283 
284 /**
285  * Get the number of elements in `CGO_sz`
286  */
CGO_sz_size()287 size_t CGO_sz_size()
288 {
289   return sizeof(CGO_sz) / sizeof(*CGO_sz);
290 }
291 
292 // I think CGO rendering functions should not modify CGO's, so the
293 // data pointer should be const. Current exception: `pickcolorsset`
294 #define CGO_OP_DATA_CONST const
295 typedef CGO_OP_DATA_CONST float* const* CGO_op_data;
296 typedef void CGO_op(CCGORenderer * I, CGO_op_data);
297 typedef CGO_op *CGO_op_fn;
298 
299 static float *CGO_add(CGO * I, unsigned c);
300 static float *CGO_size(CGO * I, int sz);
301 static int CGOSimpleCylinder(CGO * I, const float *v1, const float *v2, const float tube_size, const float *c1,
302                              const float *c2, float a1, const float a2, const bool interp, const int cap1, const int cap2,
303                              const Pickable *pickcolor2 = nullptr, const bool stick_round_nub = false);
304 template<typename CylinderT>
305 static int CGOSimpleCylinder(CGO * I, const CylinderT &cyl, const float a1, const float a2, const bool interp, const int cap1,
306                              const int cap2, const Pickable *pickcolor2 = nullptr, const bool stick_round_nub = false);
307 static int CGOSimpleEllipsoid(CGO * I, const float *v, float vdw, const float *n0, const float *n1,
308 			      const float *n2);
309 static int CGOSimpleQuadric(CGO * I, const float *v, float vdw, const float *q);
310 static int CGOSimpleSphere(CGO * I, const float *v, float vdw, short sphere_quality);
311 static int CGOSimpleCone(CGO * I, const float *v1, const float *v2, float r1, float r2, const float *c1,
312 			 const float *c2, int cap1, int cap2);
313 
314 
315 /*
316  * Inverse function of CGOArrayFromPyListInPlace
317  *
318  * I: (input) Primitive CGO (may contain CGO_DRAW_ARRAYS)
319  *
320  * Return: All-float Python list primitive CGO
321  */
CGOArrayAsPyList(const CGO * I)322 static PyObject *CGOArrayAsPyList(const CGO * I)
323 {
324   std::vector<float> flat;
325   flat.reserve(I->c);
326 
327   for (auto it = I->begin(); !it.is_stop(); ++it) {
328     auto op = it.op_code();
329     auto pc = it.data();
330     auto sz = CGO_sz[op];
331 
332     flat.push_back(op);
333 
334     switch (op) {
335     case CGO_BEGIN:
336     case CGO_ENABLE:
337     case CGO_DISABLE:
338     case CGO_SPECIAL:
339       // first member int
340       flat.push_back(*reinterpret_cast<const int*>(pc));
341       ++pc;
342       --sz;
343       break;
344     case CGO_DRAW_ARRAYS:
345       {
346         auto sp = reinterpret_cast<const cgo::draw::arrays*>(pc);
347         flat.push_back(sp->mode);
348         flat.push_back(sp->arraybits);
349         flat.push_back(sp->narrays); // (redundant)
350         flat.push_back(sp->nverts);
351         pc = sp->get_data();
352         sz = sp->get_data_length();
353       }
354     }
355 
356     // float members
357     for(; sz; --sz) {
358       flat.push_back(*(pc++));
359     }
360   }
361 
362   return PConvToPyObject(flat);
363 }
364 
CGOAsPyList(CGO * I)365 PyObject *CGOAsPyList(CGO * I)
366 {
367   PyObject *result;
368   result = PyList_New(2);
369   PyObject *list = CGOArrayAsPyList(I);
370   PyList_SetItem(result, 0, PyInt_FromLong(PyList_Size(list)));
371   PyList_SetItem(result, 1, list);
372   return (result);
373 }
374 
CPythonVal_PyFloat_AsDouble_From_List(void * G,PyObject * list,size_t i)375 static float CPythonVal_PyFloat_AsDouble_From_List(void * G, PyObject * list, size_t i) {
376   float out;
377   PConvPyFloatToFloat(PyList_GetItem(list, i), &out);
378   return out;
379 }
380 
381 /*
382  * Inverse function of CGOArrayAsPyList
383  *
384  * list: (input) All-float Python list primitive CGO (may contain CGO_DRAW_ARRAYS)
385  * I: (output) empty CGO
386  */
CGOArrayFromPyListInPlace(PyObject * list,CGO * I)387 static int CGOArrayFromPyListInPlace(PyObject * list, CGO * I)
388 {
389   // sanity check
390   if (!list || !PyList_Check(list))
391     return false;
392 
393   auto G = I->G;
394 
395 #define GET_FLOAT(i) ((float) CPythonVal_PyFloat_AsDouble_From_List(I->G, list, i))
396 #define GET_INT(i)   ((int)   CPythonVal_PyFloat_AsDouble_From_List(I->G, list, i))
397 
398   for (int i = 0, l = PyList_Size(list); i < l;) {
399     unsigned op = GET_INT(i++);
400     ok_assert(1, op < CGO_sz_size());
401     int sz = CGO_sz[op];
402     float * fdata = I->add_to_buffer(sz + 1);
403     CGO_write_int(fdata, op);
404 
405     switch (op) {
406     case CGO_STOP:
407       // don't increment size for null terminator
408       I->c -= 1;
409       return true;
410     case CGO_BEGIN:
411       I->has_begin_end = true;
412     case CGO_ENABLE:
413     case CGO_DISABLE:
414     case CGO_SPECIAL:
415       // first member int
416       ok_assert(1, i < l);
417       CGO_write_int(fdata, GET_INT(i++));
418       sz--;
419       break;
420     case CGO_DRAW_ARRAYS:
421       {
422         // has abstract superclass, need to be constructed!
423         ok_assert(1, i + 3 < l);
424         auto sp = new (fdata) cgo::draw::arrays(
425             GET_INT(i),
426             GET_INT(i + 1),
427             GET_INT(i + 3));
428 
429         // sanity check
430         int narrays_check = GET_INT(i + 2);
431         if (sp->narrays != narrays_check) {
432           PRINTFB(I->G, FB_CGO, FB_Warnings)
433             " CGO-Warning: narrays mismatch: %d != %d\n",
434             sp->narrays, narrays_check ENDFB(I->G);
435         }
436 
437         // data
438         sz = sp->get_data_length();
439         sp->floatdata = fdata = I->allocate_in_data_heap(sz);
440 
441         i += 4;
442       }
443       break;
444     }
445 
446     // float members
447     for(; sz; --sz) {
448       ok_assert(1, i < l);
449       *(fdata++) = GET_FLOAT(i++);
450     }
451   }
452 
453 #undef GET_FLOAT
454 #undef GET_INT
455 
456   return true;
457 
458 ok_except1:
459   PRINTFB(G, FB_CGO, FB_Errors) " %s-Error: Corrupt data\n", __func__ ENDFB(G);
460   return false;
461 }
462 
CGONewFromPyList(PyMOLGlobals * G,PyObject * list,int version,bool shouldCombine)463 CGO *CGONewFromPyList(PyMOLGlobals * G, PyObject * list, int version, bool shouldCombine)
464 {
465   int ok = true;
466   auto I = CGONew(G);
467   if(ok)
468     ok = (list != NULL);
469   if(ok)
470     ok = PyList_Check(list);
471   /* TO ENABLE BACKWARDS COMPATIBILITY...
472      Always check ll when adding new PyList_GetItem's */
473   if((version > 0) && (version <= 86)) {
474     if(ok)
475       ok = PConvFromPyListItem(G, list, 0, I->c);
476     if(ok)
477       VLACheck(I->op, float, I->c);
478     if(ok)
479       ok = PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 1), I->op, I->c);
480   } else {
481     if(ok)
482       ok = CGOArrayFromPyListInPlace(PyList_GetItem(list, 1), I);
483   }
484   if(!ok) {
485     CGOFree(I);
486   }
487   {
488     CGO *cgo = NULL;
489     if (shouldCombine && I && I->has_begin_end){
490       cgo = CGOCombineBeginEnd(I, 0);
491       CGOFree(I);
492     } else {
493       cgo = I;
494     }
495     return cgo;
496   }
497 }
498 
CGO(PyMOLGlobals * G,int size)499 CGO::CGO(PyMOLGlobals* G, int size)
500     : G(G)
501 {
502   op = VLACalloc(float, size + 32);
503   cgo_shader_ub_color = SettingGet<bool>(G, cSetting_cgo_shader_ub_color);
504   cgo_shader_ub_normal = SettingGet<bool>(G, cSetting_cgo_shader_ub_normal);
505 }
506 
CGOSetUseShader(CGO * I,int use_shader)507 void CGOSetUseShader(CGO *I, int use_shader){
508   I->use_shader = use_shader;
509   if (use_shader){
510     I->cgo_shader_ub_color = SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color);
511     I->cgo_shader_ub_normal = SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal);
512   } else {
513     I->cgo_shader_ub_color = 0;
514     I->cgo_shader_ub_normal = 0;
515   }
516 }
CGOReset(CGO * I)517 void CGOReset(CGO * I)
518 {
519   I->c = 0;
520   I->z_flag = false;
521   I->alpha = 1.f;
522   I->has_begin_end = false;
523   I->has_draw_buffers = false;
524   I->has_draw_cylinder_buffers = false;
525   I->normal[0] = 0.f; I->normal[1] = 0.f; I->normal[2] = 1.f;
526   I->color[0] = 0.f; I->color[1] = 0.f; I->color[2] = 1.f;
527   I->pickColor[0] = 0; I->pickColor[1] = 0; I->pickColor[2] = 0; I->pickColor[3] = 255;
528   I->current_accessibility = 1.f;
529 }
530 
CGOFree(CGO * & I,bool withVBOs)531 void CGOFree(CGO * &I, bool withVBOs)
532 {
533   if(I) {
534     if (!withVBOs) {
535       I->has_draw_buffers = false;
536     }
537     DeleteP(I);
538   }
539 }
540 
~CGO()541 CGO::~CGO()
542 {
543   if (has_draw_buffers) {
544     CGOFreeVBOs(this);
545   }
546   FreeP(i_start);
547   VLAFreeP(op);
548 }
549 
CGO_add(CGO * I,unsigned c)550 static float *CGO_add(CGO * I, unsigned c)
551 {
552   float *at;
553   VLACheck(I->op, float, I->c + c);
554   if (!I->op){
555     return NULL;
556   }
557   at = I->op + I->c;
558   I->c += c;
559   return (at);
560 }
561 
CGO_size(CGO * I,int sz)562 static float *CGO_size(CGO * I, int sz)
563 {
564   float *at;
565   VLASize(I->op, float, sz);
566   if (!I->op){
567       return NULL;
568   }
569   at = I->op + I->c;
570   I->c = sz;
571   return (at);
572 }
573 
574 
575 /*===== Object Creation Routines =======*/
576 
CGOFromFloatArray(CGO * I,const float * src,int len)577 int CGOFromFloatArray(CGO * I, const float *src, int len)
578 {
579   int iarg;
580   int ok;
581   int all_ok = true;
582   int bad_entry = 0;
583   int sz;
584   int a;
585   int cc = 0;
586   float val;
587   float *pc, *save_pc, *tf;
588   VLACheck(I->op, float, I->c + len + 32);
589   save_pc = I->op + I->c;
590   while(len-- > 0) {
591     cc++;
592     const auto op = static_cast<unsigned>(*(src++));
593 
594     if (op >= CGO_sz_size()) {
595       bad_entry = cc;
596       break;
597     }
598 
599     sz = CGO_sz[op];
600     if(len < sz)
601       break;                    /* discard short instruction */
602     len -= sz;
603     pc = save_pc;
604     CGO_write_int(pc, op);
605     ok = true;
606     for(a = 0; a < sz; a++) {
607       cc++;
608       val = *(src++);
609       if((FLT_MAX - val) > 0.0F) {      /* make sure we have a real float */
610         *(pc++) = val;
611       } else {
612         *(pc++) = 0.0;
613         ok = false;
614       }
615     }
616     if(ok) {
617       switch (op) {
618       case CGO_END:
619       case CGO_VERTEX:
620       case CGO_BEGIN:
621 	I->has_begin_end = true;
622       }
623       switch (op) {             /* now convert any instructions with int arguments */
624       case CGO_BEGIN:
625       case CGO_ENABLE:
626       case CGO_DISABLE:
627       case CGO_SPECIAL:
628         tf = save_pc + 1;
629         iarg = (int) *(tf);
630         CGO_write_int(tf, iarg);
631         break;
632       }
633       save_pc = pc;
634       I->c += sz + 1;
635     } else {                    /* discard illegal instructions */
636       if(all_ok)
637         bad_entry = cc;
638       all_ok = false;
639     }
640   }
641   return (bad_entry);
642 }
643 
CGOBegin(CGO * I,int mode)644 int CGOBegin(CGO * I, int mode)
645 {
646   float *pc = CGO_add(I, CGO_BEGIN_SZ + 1);
647   if (!pc)
648     return false;
649   CGO_write_int(pc, CGO_BEGIN);
650   CGO_write_int(pc, mode);
651   I->has_begin_end = true;
652   I->texture[0] = 0.f;
653   I->texture[1] = 0.f;
654   return true;
655 }
656 
CGOEnd(CGO * I)657 int CGOEnd(CGO * I)
658 {
659   float *pc = CGO_add(I, CGO_END_SZ + 1);
660   if (!pc)
661     return false;
662   CGO_write_int(pc, CGO_END);
663   I->has_begin_end = true;
664   return true;
665 }
666 
CGOEnable(CGO * I,int mode)667 int CGOEnable(CGO * I, int mode)
668 {
669   float *pc = CGO_add(I, CGO_ENABLE_SZ + 1);
670   if (!pc)
671     return false;
672   CGO_write_int(pc, CGO_ENABLE);
673   CGO_write_int(pc, mode);
674   return true;
675 }
676 
CGODisable(CGO * I,int mode)677 int CGODisable(CGO * I, int mode)
678 {
679   float *pc = CGO_add(I, CGO_DISABLE_SZ + 1);
680   if (!pc)
681     return false;
682   CGO_write_int(pc, CGO_DISABLE);
683   CGO_write_int(pc, mode);
684   return true;
685 }
686 
CGOLinewidth(CGO * I,float v)687 int CGOLinewidth(CGO * I, float v)
688 {
689   float *pc = CGO_add(I, CGO_LINEWIDTH_SZ + 1);
690   if (!pc)
691     return false;
692   CGO_write_int(pc, CGO_LINEWIDTH);
693   *(pc++) = v;
694   return true;
695 }
696 
697 /*
698  * implements special-case operations inside a CGO
699  *
700  * v: lookup value defined for each special operation (see CGO.h)
701  */
CGOSpecial(CGO * I,int v)702 int CGOSpecial(CGO * I, int v)
703 {
704   float *pc = CGO_add(I, CGO_SPECIAL_SZ + 1);
705   if (!pc)
706     return false;
707   CGO_write_int(pc, CGO_SPECIAL);
708   CGO_write_int(pc, v);
709   return true;
710 }
711 
712 /*
713  * implements special-case operations with an argument
714  * inside a CGO
715  *
716  * v: lookup value defined for each special operation (see CGO.h)
717  * argval : argument value
718  */
CGOSpecialWithArg(CGO * I,int v,float argval)719 int CGOSpecialWithArg(CGO * I, int v, float argval)
720 {
721   float *pc = CGO_add(I, CGO_SPECIAL_WITH_ARG_SZ + 1);
722   if (!pc)
723     return false;
724   CGO_write_int(pc, CGO_SPECIAL_WITH_ARG);
725   CGO_write_int(pc, v);
726   *pc = argval;
727   return true;
728 }
729 
CGODotwidth(CGO * I,float v)730 int CGODotwidth(CGO * I, float v)
731 {
732   float *pc = CGO_add(I, CGO_DOTWIDTH_SZ + 1);
733   if (!pc)
734     return false;
735   CGO_write_int(pc, CGO_DOTWIDTH);
736   *(pc++) = v;
737   return true;
738 }
739 
740 /* CGOUniform3f - specifies a 3f uniform variable and
741    its value.  This function returns the offset of where
742    these values are stored inside the CGO float array
743    so that they can be accessed and changed from outside
744    the CGO.
745 
746  */
CGOUniform3f(CGO * I,int uniform_id,const float * value)747 int CGOUniform3f(CGO *I, int uniform_id, const float *value){
748   float *pc = CGO_add(I, CGO_UNIFORM3F_SZ + 1);
749   if (!pc)
750     return 0;
751   CGO_write_int(pc, CGO_UNIFORM3F);
752   CGO_write_int(pc, uniform_id);
753   copy3f(value, pc);
754   return pc - I->op;
755 }
756 
CGOBoundingBox(CGO * I,const float * min,const float * max)757 int CGOBoundingBox(CGO *I, const float *min, const float *max){
758   float *pc = CGO_add(I, CGO_BOUNDING_BOX_SZ + 1);
759   if (!pc)
760     return false;
761   CGO_write_int(pc, CGO_BOUNDING_BOX);
762   *(pc++) = *(min);
763   *(pc++) = *(min+1);
764   *(pc++) = *(min+2);
765   *(pc++) = *(max);
766   *(pc++) = *(max+1);
767   *(pc++) = *(max+2);
768   return true;
769 }
770 
CGOAccessibility(CGO * I,float a)771 int CGOAccessibility(CGO * I, float a)
772 {
773   float *pc = CGO_add(I, CGO_ACCESSIBILITY_SZ + 1);
774   if (!pc)
775     return false;
776   CGO_write_int(pc, CGO_ACCESSIBILITY);
777   *(pc++) = a;
778   return true;
779 }
780 
CGODrawTexture(CGO * I,int texture_id,float * worldPos,float * screenMin,float * screenMax,float * textExtent)781 int CGODrawTexture(CGO *I, int texture_id, float *worldPos, float *screenMin, float *screenMax, float *textExtent)
782 {
783   float *pc = CGO_add(I, CGO_DRAW_TEXTURE_SZ + 1);
784   if (!pc)
785     return false;
786   CGO_write_int(pc, CGO_DRAW_TEXTURE);
787   *(pc++) = worldPos[0];
788   *(pc++) = worldPos[1];
789   *(pc++) = worldPos[2];
790   *(pc++) = screenMin[0];
791   *(pc++) = screenMin[1];
792   *(pc++) = screenMin[2];
793   *(pc++) = screenMax[0];
794   *(pc++) = screenMax[1];
795   *(pc++) = screenMax[2];
796   *(pc++) = textExtent[0];
797   *(pc++) = textExtent[1];
798   *(pc++) = textExtent[2];
799   *(pc++) = textExtent[3];
800   return true;
801 }
802 
CGODrawConnector(CGO * I,float * targetPt3d,float * labelCenterPt3d,float text_width,float text_height,float * indentFactor,float * screenWorldOffset,float * connectorColor,short relativeMode,int draw_flags,float bkgrd_transp,float * bkgrd_color,float rel_ext_length,float connectorWidth)803 int CGODrawConnector(CGO *I, float *targetPt3d, float *labelCenterPt3d, float text_width, float text_height, float *indentFactor, float *screenWorldOffset, float *connectorColor, short relativeMode, int draw_flags, float bkgrd_transp, float *bkgrd_color, float rel_ext_length, float connectorWidth)
804 {
805   float *pc = CGO_add(I, CGO_DRAW_CONNECTOR_SZ + 1);
806   if (!pc)
807     return false;
808   CGO_write_int(pc, CGO_DRAW_CONNECTOR);
809   *(pc++) = targetPt3d[0];
810   *(pc++) = targetPt3d[1];
811   *(pc++) = targetPt3d[2];
812   *(pc++) = labelCenterPt3d[0];
813   *(pc++) = labelCenterPt3d[1];
814   *(pc++) = labelCenterPt3d[2];
815   *(pc++) = indentFactor[0];
816   *(pc++) = indentFactor[1];
817   *(pc++) = rel_ext_length; /* place for ext_length relative to height (i.e., text_height which is total height */
818   *(pc++) = screenWorldOffset[0];
819   *(pc++) = screenWorldOffset[1];
820   *(pc++) = screenWorldOffset[2];
821   *(pc++) = text_width;
822   *(pc++) = text_height;
823   *(pc++) = connectorColor[0];
824   *(pc++) = connectorColor[1];
825   *(pc++) = connectorColor[2];
826   *(pc++) = (float)relativeMode;
827   *(pc++) = (float)draw_flags;
828   *(pc++) = bkgrd_color[0];
829   *(pc++) = bkgrd_color[1];
830   *(pc++) = bkgrd_color[2];
831   *(pc++) = bkgrd_transp;
832   *(pc++) = connectorWidth; // place for label_connector_width
833   return true;
834 }
835 
CGODrawLabel(CGO * I,int texture_id,float * targetPos,float * worldPos,float * screenWorldOffset,float * screenMin,float * screenMax,float * textExtent,short relativeMode)836 int CGODrawLabel(CGO *I, int texture_id, float *targetPos, float *worldPos, float *screenWorldOffset, float *screenMin, float *screenMax, float *textExtent, short relativeMode)
837 {
838   float *pc = CGO_add(I, CGO_DRAW_LABEL_SZ + 1);
839   if (!pc)
840     return false;
841   CGO_write_int(pc, CGO_DRAW_LABEL);
842   *(pc++) = worldPos[0];
843   *(pc++) = worldPos[1];
844   *(pc++) = worldPos[2];
845   *(pc++) = screenWorldOffset[0];
846   *(pc++) = screenWorldOffset[1];
847   *(pc++) = screenWorldOffset[2];
848   *(pc++) = screenMin[0];
849   *(pc++) = screenMin[1];
850   *(pc++) = screenMin[2];
851   *(pc++) = screenMax[0];
852   *(pc++) = screenMax[1];
853   *(pc++) = screenMax[2];
854   *(pc++) = textExtent[0];
855   *(pc++) = textExtent[1];
856   *(pc++) = textExtent[2];
857   *(pc++) = textExtent[3];
858   *(pc++) = (float)relativeMode;
859   *(pc++) = targetPos[0];
860   *(pc++) = targetPos[1];
861   *(pc++) = targetPos[2];
862   return true;
863 }
864 
CGOConev(CGO * I,const float * p1,const float * p2,float r1,float r2,const float * c1,const float * c2,float cap1,float cap2)865 int CGOConev(CGO * I,
866     const float *p1,
867     const float *p2, float r1, float r2,
868     const float *c1,
869     const float *c2,
870               float cap1, float cap2)
871 {
872   float *pc = CGO_add(I, CGO_CONE_SZ + 1);
873   if (!pc)
874     return false;
875   CGO_write_int(pc, CGO_CONE);
876   *(pc++) = *(p1++);
877   *(pc++) = *(p1++);
878   *(pc++) = *(p1++);
879   *(pc++) = *(p2++);
880   *(pc++) = *(p2++);
881   *(pc++) = *(p2++);
882   *(pc++) = r1;
883   *(pc++) = r2;
884   *(pc++) = *(c1++);
885   *(pc++) = *(c1++);
886   *(pc++) = *(c1++);
887   *(pc++) = *(c2++);
888   *(pc++) = *(c2++);
889   *(pc++) = *(c2++);
890   *(pc++) = cap1;
891   *(pc++) = cap2;
892   return true;
893 }
894 
CGOPickColor(CGO * I,unsigned int index,int bond)895 int CGOPickColor(CGO * I, unsigned int index, int bond)
896 {
897   // check if uchar is -1 since extrude does this for masked atoms
898   if (index == (unsigned int)-1){
899     bond = cPickableNoPick;
900   }
901   if (I->current_pick_color_index==index &&
902       I->current_pick_color_bond==bond)
903     return true;
904   float *pc = CGO_add(I, CGO_PICK_COLOR_SZ + 1);
905   if (!pc)
906     return false;
907   CGO_write_int(pc, CGO_PICK_COLOR);
908   CGO_write_uint(pc, index);
909   CGO_write_int(pc, bond);
910   I->current_pick_color_index = index;
911   I->current_pick_color_bond = bond;
912   return true;
913 }
914 
CGOAlpha(CGO * I,float alpha)915 int CGOAlpha(CGO * I, float alpha)
916 {
917   float *pc = CGO_add(I, CGO_ALPHA_SZ + 1);
918   if (!pc)
919     return false;
920   CGO_write_int(pc, CGO_ALPHA);
921   *(pc++) = alpha;
922   I->alpha = alpha;
923   return true;
924 }
925 
CGOSphere(CGO * I,const float * v1,float r)926 int CGOSphere(CGO * I, const float *v1, float r)
927 {
928   float *pc = CGO_add(I, CGO_SPHERE_SZ + 1);
929   if (!pc)
930     return false;
931   CGO_write_int(pc, CGO_SPHERE);
932   *(pc++) = *(v1++);
933   *(pc++) = *(v1++);
934   *(pc++) = *(v1++);
935   *(pc++) = r;
936   return true;
937 }
938 
CGOEllipsoid(CGO * I,const float * v1,float r,const float * n1,const float * n2,const float * n3)939 int CGOEllipsoid(CGO * I, const float *v1, float r,
940     const float *n1,
941     const float *n2,
942     const float *n3)
943 {
944   float *pc = CGO_add(I, CGO_ELLIPSOID_SZ + 1);
945   if (!pc)
946     return false;
947   CGO_write_int(pc, CGO_ELLIPSOID);
948 
949   *(pc++) = *(v1++);
950   *(pc++) = *(v1++);
951   *(pc++) = *(v1++);
952   *(pc++) = r;
953   *(pc++) = *(n1++);
954   *(pc++) = *(n1++);
955   *(pc++) = *(n1++);
956   *(pc++) = *(n2++);
957   *(pc++) = *(n2++);
958   *(pc++) = *(n2++);
959   *(pc++) = *(n3++);
960   *(pc++) = *(n3++);
961   *(pc++) = *(n3++);
962   return true;
963 }
964 
CGOQuadric(CGO * I,const float * v,float r,const float * q)965 int CGOQuadric(CGO * I, const float *v, float r, const float *q)
966 {
967   float *pc = CGO_add(I, CGO_QUADRIC_SZ + 1);
968   if (!pc)
969     return false;
970   CGO_write_int(pc, CGO_QUADRIC);
971 
972   *(pc++) = *(v++);
973   *(pc++) = *(v++);
974   *(pc++) = *(v++);
975   *(pc++) = r;
976 
977   *(pc++) = *(q++);
978   *(pc++) = *(q++);
979   *(pc++) = *(q++);
980   *(pc++) = *(q++);
981   *(pc++) = *(q++);
982 
983   *(pc++) = *(q++);
984   *(pc++) = *(q++);
985   *(pc++) = *(q++);
986   *(pc++) = *(q++);
987   *(pc++) = *(q++);
988   return true;
989 }
990 
CGOSetZVector(CGO * I,float z0,float z1,float z2)991 void CGOSetZVector(CGO * I, float z0, float z1, float z2)
992 {
993   I->z_flag = true;
994   I->z_vector[0] = z0;
995   I->z_vector[1] = z1;
996   I->z_vector[2] = z2;
997   I->z_min = FLT_MAX;
998   I->z_max = -FLT_MAX;
999 }
1000 
1001 const static float one_third = 1.0F / 3.0F;
1002 const static float _0 = 0.0F;
1003 
CGOAlphaTriangle(CGO * I,const float * v1,const float * v2,const float * v3,const float * n1,const float * n2,const float * n3,const float * c1,const float * c2,const float * c3,float a1,float a2,float a3,int reverse)1004 int CGOAlphaTriangle(CGO * I,
1005                       const float *v1, const float *v2, const float *v3,
1006                       const float *n1, const float *n2, const float *n3,
1007                       const float *c1, const float *c2, const float *c3,
1008                       float a1, float a2, float a3, int reverse)
1009 {
1010   if(v1 && v2 && v3) {
1011     float *pc = CGO_add(I, CGO_ALPHA_TRIANGLE_SZ + 1);
1012     float z = _0;
1013     if (!pc)
1014       return false;
1015     CGO_write_int(pc, CGO_ALPHA_TRIANGLE);
1016     CGO_write_int(pc, 0);  // this is the place for the next triangle in the bin
1017     *(pc++) = (v1[0] + v2[0] + v3[0]) * one_third;
1018     *(pc++) = (v1[1] + v2[1] + v3[1]) * one_third;
1019     *(pc++) = (v1[2] + v2[2] + v3[2]) * one_third;
1020     if(I->z_flag) {
1021       float *zv = I->z_vector;
1022       z = pc[-3] * zv[0] + pc[-2] * zv[1] + pc[-1] * zv[2];
1023       if(z > I->z_max)
1024         I->z_max = z;
1025       if(z < I->z_min)
1026         I->z_min = z;
1027     }
1028     *(pc++) = z;
1029 
1030     if(reverse) {
1031       *(pc++) = *(v2++);        /* vertices @ +5 */
1032       *(pc++) = *(v2++);
1033       *(pc++) = *(v2++);
1034       *(pc++) = *(v1++);
1035       *(pc++) = *(v1++);
1036       *(pc++) = *(v1++);
1037     } else {
1038       *(pc++) = *(v1++);        /* vertices @ +5 */
1039       *(pc++) = *(v1++);
1040       *(pc++) = *(v1++);
1041       *(pc++) = *(v2++);
1042       *(pc++) = *(v2++);
1043       *(pc++) = *(v2++);
1044     }
1045 
1046     *(pc++) = *(v3++);
1047     *(pc++) = *(v3++);
1048     *(pc++) = *(v3++);
1049 
1050     if(reverse) {
1051       *(pc++) = *(n2++);        /* normals @ +14 */
1052       *(pc++) = *(n2++);
1053       *(pc++) = *(n2++);
1054       *(pc++) = *(n1++);
1055       *(pc++) = *(n1++);
1056       *(pc++) = *(n1++);
1057     } else {
1058       *(pc++) = *(n1++);        /* normals @ +14 */
1059       *(pc++) = *(n1++);
1060       *(pc++) = *(n1++);
1061       *(pc++) = *(n2++);
1062       *(pc++) = *(n2++);
1063       *(pc++) = *(n2++);
1064     }
1065     *(pc++) = *(n3++);
1066     *(pc++) = *(n3++);
1067     *(pc++) = *(n3++);
1068 
1069     if(reverse) {
1070       *(pc++) = *(c2++);        /* colors @ +23 */
1071       *(pc++) = *(c2++);
1072       *(pc++) = *(c2++);
1073       *(pc++) = a2;
1074       *(pc++) = *(c1++);
1075       *(pc++) = *(c1++);
1076       *(pc++) = *(c1++);
1077       *(pc++) = a1;
1078     } else {
1079       *(pc++) = *(c1++);        /* colors @ +23 */
1080       *(pc++) = *(c1++);
1081       *(pc++) = *(c1++);
1082       *(pc++) = a1;
1083       *(pc++) = *(c2++);
1084       *(pc++) = *(c2++);
1085       *(pc++) = *(c2++);
1086       *(pc++) = a2;
1087     }
1088     *(pc++) = *(c3++);
1089     *(pc++) = *(c3++);
1090     *(pc++) = *(c3++);
1091     *(pc++) = a3;
1092   }
1093   return true;
1094 }
1095 
CGOVertex(CGO * I,float v1,float v2,float v3)1096 int CGOVertex(CGO * I, float v1, float v2, float v3)
1097 {
1098   float *pc = CGO_add(I, CGO_VERTEX_SZ + 1);
1099   if (!pc)
1100     return false;
1101   CGO_write_int(pc, CGO_VERTEX);
1102   *(pc++) = v1;
1103   *(pc++) = v2;
1104   *(pc++) = v3;
1105   return true;
1106 }
1107 
CGOVertexv(CGO * I,const float * v)1108 int CGOVertexv(CGO * I, const float *v)
1109 {
1110   float *pc = CGO_add(I, CGO_VERTEX_SZ + 1);
1111   if (!pc)
1112     return false;
1113   CGO_write_int(pc, CGO_VERTEX);
1114   *(pc++) = *(v++);
1115   *(pc++) = *(v++);
1116   *(pc++) = *(v++);
1117   return true;
1118 }
1119 
CGOVertexBeginLineStripv(CGO * I,const float * v)1120 int CGOVertexBeginLineStripv(CGO * I, const float *v)
1121 {
1122   float *pc = CGO_add(I, CGO_VERTEX_BEGIN_LINE_STRIP_SZ + 1);
1123   if (!pc)
1124     return false;
1125   CGO_write_int(pc, CGO_VERTEX_BEGIN_LINE_STRIP);
1126   *(pc++) = *(v++);
1127   *(pc++) = *(v++);
1128   *(pc++) = *(v++);
1129   return true;
1130 }
1131 
CGOVertexCrossv(CGO * I,const float * v)1132 int CGOVertexCrossv(CGO * I, const float *v)
1133 {
1134   float *pc = CGO_add(I, CGO_VERTEX_CROSS_SZ + 1);
1135   if (!pc)
1136     return false;
1137   CGO_write_int(pc, CGO_VERTEX_CROSS);
1138   *(pc++) = *(v++);
1139   *(pc++) = *(v++);
1140   *(pc++) = *(v++);
1141   return true;
1142 }
CGOInterpolated(CGO * I,const bool interp)1143 int CGOInterpolated(CGO * I, const bool interp)
1144 {
1145   float *pc = CGO_add(I, CGO_INTERPOLATED_SZ + 1);
1146   if (!pc)
1147     return false;
1148   CGO_write_int(pc, CGO_INTERPOLATED);
1149   *(pc++) = interp ? 1.f : 0.f;
1150   I->interpolated = interp;
1151   return true;
1152 }
1153 
CGOColor(CGO * I,float v1,float v2,float v3)1154 int CGOColor(CGO * I, float v1, float v2, float v3)
1155 {
1156   float *pc = CGO_add(I, CGO_COLOR_SZ + 1);
1157   if (!pc)
1158     return false;
1159   CGO_write_int(pc, CGO_COLOR);
1160   *(pc++) = v1;
1161   *(pc++) = v2;
1162   *(pc++) = v3;
1163   I->color[0] = v1;
1164   I->color[1] = v2;
1165   I->color[2] = v3;
1166   return true;
1167 }
1168 
CGOColorv(CGO * I,const float * v)1169 int CGOColorv(CGO * I, const float *v)
1170 {
1171   return CGOColor(I, v[0], v[1], v[2]);
1172 }
1173 
CGOTexCoord2f(CGO * I,float v1,float v2)1174 int CGOTexCoord2f(CGO * I, float v1, float v2){
1175   float *pc = CGO_add(I, CGO_TEX_COORD_SZ + 1);
1176   if (!pc)
1177     return false;
1178   CGO_write_int(pc, CGO_TEX_COORD);
1179   *(pc++) = v1;
1180   *(pc++) = v2;
1181   I->texture[0] = v1;
1182   I->texture[1] = v2;
1183   return true;
1184 }
1185 
CGONormal(CGO * I,float v1,float v2,float v3)1186 int CGONormal(CGO * I, float v1, float v2, float v3)
1187 {
1188   float *pc = CGO_add(I, CGO_NORMAL_SZ + 1);
1189   if (!pc)
1190     return false;
1191   CGO_write_int(pc, CGO_NORMAL);
1192   *(pc++) = v1;
1193   *(pc++) = v2;
1194   *(pc++) = v3;
1195   I->normal[0] = v1;
1196   I->normal[1] = v2;
1197   I->normal[2] = v3;
1198   return true;
1199 }
1200 
CGOResetNormal(CGO * I,int mode)1201 int CGOResetNormal(CGO * I, int mode)
1202 {
1203   float *pc = CGO_add(I, CGO_RESET_NORMAL_SZ + 1);
1204   if (!pc)
1205     return false;
1206   CGO_write_int(pc, CGO_RESET_NORMAL);
1207   CGO_write_int(pc, mode);
1208   SceneGetResetNormal(I->G, I->normal, mode);
1209   return true;
1210 }
1211 
CGOFontVertexv(CGO * I,const float * v)1212 int CGOFontVertexv(CGO * I, const float *v)
1213 {
1214   float *pc = CGO_add(I, CGO_FONT_VERTEX_SZ + 1);
1215   if (!pc)
1216     return false;
1217   CGO_write_int(pc, CGO_FONT_VERTEX);
1218   *(pc++) = *(v++);
1219   *(pc++) = *(v++);
1220   *(pc++) = *(v++);
1221   return true;
1222 }
1223 
CGOFontVertex(CGO * I,float x,float y,float z)1224 int CGOFontVertex(CGO * I, float x, float y, float z)
1225 {
1226   float *pc = CGO_add(I, CGO_FONT_VERTEX_SZ + 1);
1227   if (!pc)
1228     return false;
1229   CGO_write_int(pc, CGO_FONT_VERTEX);
1230   *(pc++) = x;
1231   *(pc++) = y;
1232   *(pc++) = z;
1233   return true;
1234 }
1235 
CGOFontScale(CGO * I,float v1,float v2)1236 int CGOFontScale(CGO * I, float v1, float v2)
1237 {
1238   float *pc = CGO_add(I, CGO_FONT_SCALE_SZ + 1);
1239   if (!pc)
1240     return false;
1241   CGO_write_int(pc, CGO_FONT_SCALE);
1242   *(pc++) = v1;
1243   *(pc++) = v2;
1244   return true;
1245 }
1246 
CGOChar(CGO * I,char c)1247 int CGOChar(CGO * I, char c)
1248 {
1249   float *pc = CGO_add(I, CGO_CHAR_SZ + 1);
1250   if (!pc)
1251     return false;
1252   CGO_write_int(pc, CGO_CHAR);
1253   *(pc++) = (float) c;
1254   return true;
1255 }
1256 
CGOIndent(CGO * I,char c,float dir)1257 int CGOIndent(CGO * I, char c, float dir)
1258 {
1259   float *pc = CGO_add(I, CGO_INDENT_SZ + 1);
1260   if (!pc)
1261     return false;
1262   CGO_write_int(pc, CGO_INDENT);
1263   *(pc++) = (float) c;
1264   *(pc++) = dir;
1265   return true;
1266 }
1267 
CGOWrite(CGO * I,const char * str)1268 int CGOWrite(CGO * I, const char *str)
1269 {
1270   float *pc;
1271 
1272   while(*str) {
1273     pc = CGO_add(I, CGO_CHAR_SZ + 1);
1274     if (!pc)
1275       return false;
1276     CGO_write_int(pc, CGO_CHAR);
1277     *(pc++) = (float) *(str++);
1278   }
1279   return true;
1280 }
1281 
CGOWriteLeft(CGO * I,const char * str)1282 int CGOWriteLeft(CGO * I, const char *str)
1283 {
1284   float *pc;
1285   const char *s = str;
1286   while(*s) {
1287     pc = CGO_add(I, CGO_INDENT_SZ + 1);
1288     if (!pc)
1289       return false;
1290     CGO_write_int(pc, CGO_INDENT);
1291     *(pc++) = (float) *(s++);
1292     *(pc++) = -1.0F;
1293   }
1294   s = str;
1295   while(*s) {
1296     pc = CGO_add(I, CGO_CHAR_SZ + 1);
1297     if (!pc)
1298       return false;
1299     CGO_write_int(pc, CGO_CHAR);
1300     *(pc++) = (float) *(s++);
1301   }
1302   return true;
1303 }
1304 
CGOWriteIndent(CGO * I,const char * str,float indent)1305 int CGOWriteIndent(CGO * I, const char *str, float indent)
1306 {
1307   float *pc;
1308   const char *s = str;
1309   while(*s) {
1310     pc = CGO_add(I, CGO_INDENT_SZ + 1);
1311     if (!pc)
1312       return false;
1313     CGO_write_int(pc, CGO_INDENT);
1314     *(pc++) = (float) *(s++);
1315     *(pc++) = indent;
1316   }
1317   s = str;
1318   while(*s) {
1319     pc = CGO_add(I, CGO_CHAR_SZ + 1);
1320     if (!pc)
1321       return false;
1322     CGO_write_int(pc, CGO_CHAR);
1323     *(pc++) = (float) *(s++);
1324   }
1325   return true;
1326 }
1327 
CGONormalv(CGO * I,const float * v)1328 int CGONormalv(CGO * I, const float *v)
1329 {
1330   float *pc = CGO_add(I, CGO_NORMAL_SZ + 1);
1331   if (!pc)
1332     return false;
1333   CGO_write_int(pc, CGO_NORMAL);
1334   *(pc++) = *(v++);
1335   *(pc++) = *(v++);
1336   *(pc++) = *(v++);
1337   return true;
1338 }
1339 
1340 /*
1341  * Add a null terminator to the CGO buffer, but don't increment
1342  * the size variable (CGO::c).
1343  */
CGOStop(CGO * I)1344 int CGOStop(CGO * I)
1345 {
1346 #define CGO_STOP_ZEROS 1
1347 
1348   float *pc = CGO_size(I, I->c + CGO_STOP_ZEROS);
1349   if (!pc)
1350     return false;
1351   UtilZeroMem(pc, sizeof(float) * CGO_STOP_ZEROS);
1352   I->c -= CGO_STOP_ZEROS;
1353   return true;
1354 }
1355 
CGOCheckComplex(CGO * I)1356 int CGOCheckComplex(CGO * I)
1357 {
1358   int fc = 0;
1359   const SphereRec* sp = I->G->Sphere->Sphere[1];
1360 
1361   /* stick_quality needs to match *every* CGO? */
1362   auto nEdge = SettingGet<int>(I->G, cSetting_stick_quality);
1363 
1364   for (auto it = I->begin(); !it.is_stop(); ++it) {
1365     auto pc = it.data();
1366     int op = it.op_code();
1367 
1368     switch (op) {
1369     case CGO_CYLINDER:
1370     case CGO_CONE:
1371     case CGO_SAUSAGE:
1372     case CGO_CUSTOM_CYLINDER:
1373     case CGO_CUSTOM_CYLINDER_ALPHA:
1374       fc += 3 * (3 + (nEdge + 1) * 9) + 9;
1375       break;
1376     case CGO_ELLIPSOID:
1377     case CGO_QUADRIC:
1378     case CGO_SPHERE:
1379       fc += (sp->NVertTot * 6) + (sp->NStrip * 3) + 3;
1380       break;
1381     case CGO_DRAW_ARRAYS:
1382       {
1383         cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
1384 	fc += sp->nverts;
1385       }
1386       break;
1387     case CGO_DRAW_BUFFERS_INDEXED:
1388       {
1389         cgo::draw::buffers_indexed * sp = reinterpret_cast<decltype(sp)>(pc);
1390 	switch(sp->mode){
1391 	case GL_TRIANGLES:
1392 	  fc += sp->nindices / 3;
1393 	  break;
1394 	case GL_LINES:
1395 	  fc += sp->nindices / 2;
1396 	  break;
1397 	}
1398       }
1399       break;
1400     case CGO_DRAW_BUFFERS_NOT_INDEXED:
1401       {
1402         cgo::draw::buffers_not_indexed * sp = reinterpret_cast<decltype(sp)>(pc);
1403 	switch(sp->mode){
1404 	case GL_TRIANGLES:
1405 	  fc += sp->nverts / 3;
1406 	  break;
1407 	case GL_LINES:
1408 	  fc += sp->nverts / 2;
1409 	  break;
1410 	}
1411       }
1412       break;
1413     case CGO_DRAW_SPHERE_BUFFERS:
1414       {
1415         cgo::draw::sphere_buffers * sp = reinterpret_cast<decltype(sp)>(pc);
1416         fc += sp->num_spheres * VERTICES_PER_SPHERE;
1417       }
1418       break;
1419     case CGO_DRAW_CYLINDER_BUFFERS:
1420       {
1421         cgo::draw::cylinder_buffers * sp = reinterpret_cast<decltype(sp)>(pc);
1422         fc += sp->num_cyl * NUM_VERTICES_PER_CYLINDER;
1423       }
1424       break;
1425     }
1426   }
1427   return (fc);
1428 }
1429 
CGOPreloadFonts(CGO * I)1430 int CGOPreloadFonts(CGO * I)
1431 {
1432   int ok = true;
1433   int font_seen = false;
1434   int font_id;
1435 
1436   auto blocked = PAutoBlock(I->G);
1437 
1438   for (auto it = I->begin(); !it.is_stop(); ++it) {
1439     const auto op = it.op_code();
1440 
1441     switch (op) {
1442     case CGO_FONT:
1443       ok = ok && (VFontLoad(I->G, 1.0, 1, 1, true));
1444       font_seen = true;
1445       break;
1446     case CGO_CHAR:
1447       if(!font_seen) {
1448         font_id = VFontLoad(I->G, 1.0, 1, 1, true);
1449         ok = ok && font_id;
1450         font_seen = true;
1451       }
1452       break;
1453     }
1454   }
1455   if(blocked)
1456     PUnblock(I->G);
1457   return (ok);
1458 }
1459 
CGOCheckForText(CGO * I)1460 int CGOCheckForText(CGO * I)
1461 {
1462   int fc = 0;
1463 
1464   for (auto it = I->begin(); !it.is_stop(); ++it) {
1465     const auto op = it.op_code();
1466 
1467     switch (op) {
1468     case CGO_FONT:
1469     case CGO_FONT_AXES:
1470     case CGO_FONT_SCALE:
1471       fc++;
1472       break;
1473     case CGO_INDENT:
1474     case CGO_FONT_VERTEX:
1475       fc++;
1476       break;
1477     case CGO_CHAR:
1478       fc += 3 + 2 * 3 * 10;     /* est 10 lines per char */
1479       break;
1480     }
1481   }
1482   PRINTFD(I->G, FB_CGO)
1483     " CGOCheckForText-Debug: %d\n", fc ENDFD;
1484 
1485   return (fc);
1486 }
1487 
CGODrawText(const CGO * I,int est,float * camera)1488 CGO *CGODrawText(const CGO * I, int est, float *camera)
1489 {                               /* assumes blocked intepreter */
1490   CGO *cgo;
1491   int font_id = 0;
1492   char text[2] = " ";
1493   float pos[] = { 0.0F, 0.0F, 0.0F };
1494   float axes[] = { 1.0F, 0.0F, 0.0F,
1495     0.0F, 1.0F, 0.0F,
1496     0.0F, 0.0F, 1.0F
1497   };
1498   float scale[2] = { 1.0, 1.0 };
1499 
1500   cgo = CGONewSized(I->G, I->c + est);
1501 
1502   for (auto it = I->begin(); !it.is_stop(); ++it) {
1503     const auto op = it.op_code();
1504     const auto pc = it.data();
1505 
1506     switch (op) {
1507     case CGO_FONT:
1508       break;
1509     case CGO_FONT_AXES:
1510       break;
1511     case CGO_FONT_SCALE:
1512       scale[0] = pc[0];
1513       scale[1] = pc[1];
1514       break;
1515     case CGO_FONT_VERTEX:
1516       copy3f(pc, pos);
1517       break;
1518     case CGO_INDENT:
1519       text[0] = (unsigned char) *pc;
1520       VFontIndent(I->G, font_id, text, pos, scale, axes, pc[1]);
1521       break;
1522     case CGO_CHAR:
1523       if(!font_id) {
1524         font_id = VFontLoad(I->G, 1.0, 1, 1, false);
1525       }
1526       text[0] = (unsigned char) *pc;
1527       VFontWriteToCGO(I->G, font_id, cgo, text, pos, scale, axes, cgo->color);
1528       break;
1529     case CGO_COLOR:
1530       cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
1531     default:
1532       cgo->add_to_cgo(op, pc);
1533     }
1534   }
1535   CGOStop(cgo);
1536   if (cgo && cgo->has_begin_end){
1537     /* this is mainly for VFontWriteToCGO() that still creates CGOBegin/CGOEnd */
1538     if(cgo && cgo->has_begin_end){
1539       CGO *convertcgo = NULL;
1540       convertcgo = CGOCombineBeginEnd(cgo, 0);
1541       CGOFree(cgo);
1542       cgo = convertcgo;
1543     }
1544   }
1545   return (cgo);
1546 }
1547 
1548 static
CGOAddVertexToDrawArrays(CGO * cgo,int pl,int plc,int pla,const float * vertex,short notHaveValue,float * vertexVals,float * normalVals,float * colorVals,float * pickColorVals,float * accessibilityVals)1549 void CGOAddVertexToDrawArrays(CGO *cgo, int pl, int plc, int pla, const float *vertex,
1550                               short notHaveValue, float *vertexVals, float *normalVals,
1551                               float *colorVals, float *pickColorVals, float *accessibilityVals){
1552   float *tmp_ptr;
1553   if (notHaveValue & CGO_NORMAL_ARRAY){
1554     if (pl){
1555       tmp_ptr = &normalVals[pl-3];
1556       copy3f(tmp_ptr, &normalVals[pl]);
1557     } else {
1558       copy3f(cgo->normal, &normalVals[pl]);
1559     }
1560   }
1561   if (notHaveValue & CGO_COLOR_ARRAY){
1562     if (plc){
1563       tmp_ptr = &colorVals[plc-4];
1564       copy4f(tmp_ptr, &colorVals[plc]);
1565     } else {
1566       copy3f(&colorVals[plc], cgo->color);
1567       colorVals[plc+3] = cgo->alpha;
1568     }
1569   }
1570   if (pickColorVals){
1571     CGO_put_uint(pickColorVals + pla * 2, cgo->current_pick_color_index);
1572     CGO_put_int(pickColorVals + pla * 2 + 1, cgo->current_pick_color_bond);
1573   }
1574   if (accessibilityVals){
1575     accessibilityVals[pla] = cgo->current_accessibility;
1576   }
1577   copy3f(vertex, &vertexVals[pl]);
1578 }
1579 
CGOCombineBeginEnd(CGO ** I,bool do_not_split_lines)1580 bool CGOCombineBeginEnd(CGO ** I, bool do_not_split_lines) {
1581   CGO *cgo = CGOCombineBeginEnd(*I, 0, do_not_split_lines);
1582   CGOFree(*I);
1583   *I = cgo;
1584   return (cgo != NULL);
1585 }
1586 
CGOCombineBeginEnd(const CGO * I,int est,bool do_not_split_lines)1587 CGO *CGOCombineBeginEnd(const CGO * I, int est, bool do_not_split_lines)
1588 {
1589   CGO *cgo;
1590 
1591   int ok = true;
1592   if (!I)
1593       return NULL;
1594   cgo = CGONewSized(I->G, 0);
1595   ok &= cgo ? true : false;
1596 
1597   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
1598     auto pc = it.data();
1599     int op = it.op_code();
1600 
1601     switch (op) {
1602     case CGO_END:
1603     case CGO_VERTEX:
1604       PRINTFB(I->G, FB_CGO, FB_Warnings)
1605         " CGOCombineBeginEnd: op=0x%02x encountered without CGO_BEGIN\n", op
1606         ENDFB(I->G);
1607       break;
1608     case CGO_BEGIN:
1609       {
1610 	float firstColor[3], firstAlpha;
1611 	char hasFirstColor = 0, hasFirstAlpha = 0;
1612 	int nverts = 0, damode = CGO_VERTEX_ARRAY, err = 0;
1613 
1614         // read int argument of the BEGIN operation
1615         int mode = CGO_get_int(it.data());
1616         ++it;
1617 
1618         // we want to iterate twice over the BEGIN/END block
1619         auto it2 = it;
1620 
1621         // first iteration over BEGIN/END block (consumes 'it')
1622         for (; !err && it != CGO_END; ++it) {
1623           auto pc = it.data();
1624           switch (it.op_code()) {
1625 	  case CGO_DRAW_ARRAYS:
1626 	  case CGO_STOP:
1627 	    PRINTFB(I->G, FB_CGO, FB_Errors)
1628 	      " CGO-Error: CGOCombineBeginEnd: invalid op=0x%02x inside BEGIN/END\n",
1629 	      it.op_code() ENDFB(I->G);
1630 	    err = true;
1631 	    continue;
1632 	  case CGO_NORMAL:
1633 	    damode |= CGO_NORMAL_ARRAY;
1634 	    break;
1635 	  case CGO_COLOR:
1636 	    if (!nverts){
1637 	      hasFirstColor = 1;
1638 	      copy3f(pc, firstColor);
1639 	    } else {
1640 	      hasFirstColor = 0;
1641 	      damode |= CGO_COLOR_ARRAY;
1642 	    }
1643 	    copy3f(pc, cgo->color);
1644 	    break;
1645 	  case CGO_PICK_COLOR:
1646 	    damode |= CGO_PICK_COLOR_ARRAY;
1647 	    break;
1648 	  case CGO_ACCESSIBILITY:
1649 	    damode |= CGO_ACCESSIBILITY_ARRAY;
1650 	    break;
1651 	  case CGO_VERTEX:
1652 	    nverts++;
1653 	    break;
1654 	  case CGO_ALPHA:
1655 	    cgo->alpha = *pc;
1656 	    if (!nverts){
1657 	      hasFirstAlpha = 1;
1658 	      firstAlpha = cgo->alpha;
1659 	    } else {
1660 	      hasFirstAlpha = 0;
1661 	      damode |= CGO_COLOR_ARRAY;
1662 	    }
1663             break;
1664           case CGO_LINE:
1665             nverts+=2;
1666             break;
1667           case CGO_SPLITLINE:
1668             {
1669               auto splitline = reinterpret_cast<const cgo::draw::splitline *>(pc);
1670               if (do_not_split_lines || (splitline->flags & cgo::draw::splitline::equal_colors)){
1671                 nverts+=2;
1672               } else {
1673                 nverts+=4;
1674               }
1675             }
1676             break;
1677 	  }
1678 	}
1679 	if (nverts>0 && !err){
1680 	  int pl = 0, plc = 0, pla = 0;
1681 	  float *vertexVals;
1682 	  float *normalVals = 0, *colorVals = 0, *nxtVals = 0, *pickColorVals = 0, *accessibilityVals = 0;
1683 	  short notHaveValue = 0, nxtn = 3;
1684 	  if (hasFirstAlpha || hasFirstColor){
1685 	    if (hasFirstAlpha){
1686 	      CGOAlpha(cgo, firstAlpha);
1687 	    }
1688 	    if (hasFirstColor){
1689 	      CGOColorv(cgo, firstColor);
1690 	    }
1691 	  }
1692 	  nxtVals = vertexVals = cgo->add<cgo::draw::arrays>(mode, damode, nverts);
1693 	  ok &= vertexVals ? true : false;
1694 	  if (!ok)
1695 	    continue;
1696 	  if (damode & CGO_NORMAL_ARRAY){
1697 	    nxtVals = normalVals = vertexVals + (nxtn*nverts);
1698 	    nxtn = 3;
1699 	  }
1700 	  if (damode & CGO_COLOR_ARRAY){
1701 	    nxtVals = colorVals = nxtVals + (nxtn*nverts);
1702 	    nxtn = 4;
1703 	  }
1704 	  if (damode & CGO_PICK_COLOR_ARRAY){
1705 	    nxtVals = nxtVals + (nxtn*nverts);
1706 	    pickColorVals = nxtVals + nverts;
1707 	    nxtn = 3;
1708 	  }
1709 	  if (damode & CGO_ACCESSIBILITY_ARRAY){
1710 	    nxtVals = nxtVals + (nxtn*nverts);
1711 	    accessibilityVals = nxtVals;
1712 	    nxtn = 1;
1713 	  }
1714 	  notHaveValue = damode;
1715 
1716           // second iteration (with copy of iterator, doesn't consume 'it')
1717           for (; ok && it2 != CGO_END; ++it2) {
1718             auto pc = it2.data();
1719             switch (it2.op_code()) {
1720 	    case CGO_NORMAL:
1721 	      copy3f(pc, &normalVals[pl]);
1722 	      notHaveValue &= ~CGO_NORMAL_ARRAY;
1723 	      break;
1724 	    case CGO_COLOR:
1725 	      if (colorVals){
1726 		copy3f(pc, &colorVals[plc]);
1727 		colorVals[plc+3] = cgo->alpha;
1728 		notHaveValue &= ~CGO_COLOR_ARRAY;
1729 	      }
1730 	      copy3f(pc, cgo->color);
1731 	      break;
1732 	    case CGO_PICK_COLOR:
1733 	      cgo->current_pick_color_index = CGO_get_uint(pc);
1734 	      cgo->current_pick_color_bond = CGO_get_int(pc + 1);
1735 	      notHaveValue &= ~CGO_PICK_COLOR_ARRAY;
1736 	      break;
1737 	    case CGO_ACCESSIBILITY:
1738 	      cgo->current_accessibility = pc[0];
1739 	      break;
1740 	    case CGO_SPLITLINE:
1741               {
1742                 auto splitline = reinterpret_cast<const cgo::draw::splitline *>(pc);
1743                 float color2[] = { CONVERT_COLOR_VALUE(splitline->color2[0]),
1744                                    CONVERT_COLOR_VALUE(splitline->color2[1]),
1745                                    CONVERT_COLOR_VALUE(splitline->color2[2]) };
1746                 if (do_not_split_lines || (splitline->flags & cgo::draw::splitline::equal_colors)){
1747                   CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex1, notHaveValue, vertexVals,
1748                                            normalVals, colorVals, pickColorVals, accessibilityVals);
1749                   pl+=3; plc+=4; pla++;
1750                   notHaveValue = damode;
1751 
1752                   if (!(splitline->flags & cgo::draw::splitline::equal_colors)){
1753                     if (colorVals){
1754                       copy3f(color2, &colorVals[plc]);
1755                       colorVals[plc+3] = cgo->alpha;
1756                       notHaveValue = notHaveValue & ~CGO_COLOR_ARRAY;
1757                     }
1758                     copy3f(color2, cgo->color);
1759                   }
1760                   if (pickColorVals){
1761                     cgo->current_pick_color_index = splitline->index;
1762                     cgo->current_pick_color_bond = splitline->bond;
1763                     notHaveValue = notHaveValue & ~CGO_PICK_COLOR_ARRAY;
1764                   }
1765                   CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex2, notHaveValue, vertexVals,
1766                                            normalVals, colorVals, pickColorVals, accessibilityVals);
1767                   pl+=3; plc+=4; pla++;
1768                   notHaveValue = damode;
1769                 } else {
1770                   float mid[3];
1771                   add3f(splitline->vertex1, splitline->vertex2, mid);
1772                   mult3f(mid, .5f, mid);
1773                   CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex1, notHaveValue, vertexVals,
1774                                            normalVals, colorVals, pickColorVals, accessibilityVals);
1775                   notHaveValue = damode;
1776                   pl+=3; plc+=4; pla++;
1777                   CGOAddVertexToDrawArrays(cgo, pl, plc, pla, mid, notHaveValue, vertexVals,
1778                                            normalVals, colorVals, pickColorVals, accessibilityVals);
1779                   pl+=3; plc+=4; pla++;
1780                   if (colorVals){
1781                     copy3f(color2, &colorVals[plc]);
1782                     colorVals[plc+3] = cgo->alpha;
1783                     notHaveValue = notHaveValue & ~CGO_COLOR_ARRAY;
1784                   }
1785                   copy3f(color2, cgo->color);
1786                   if (pickColorVals){
1787                     cgo->current_pick_color_index = splitline->index;
1788                     cgo->current_pick_color_bond = splitline->bond;
1789                     notHaveValue = notHaveValue & ~CGO_PICK_COLOR_ARRAY;
1790                   }
1791                   CGOAddVertexToDrawArrays(cgo, pl, plc, pla, mid, notHaveValue, vertexVals,
1792                                            normalVals, colorVals, pickColorVals, accessibilityVals);
1793                   notHaveValue = damode;
1794                   pl+=3; plc+=4; pla++;
1795                   CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex2, notHaveValue, vertexVals,
1796                                            normalVals, colorVals, pickColorVals, accessibilityVals);
1797                   pl+=3; plc+=4; pla++;
1798                   notHaveValue = damode;
1799                 }
1800               }
1801               break;
1802 	    case CGO_LINE:
1803               {
1804                 auto line = reinterpret_cast<const cgo::draw::line *>(pc);
1805                 CGOAddVertexToDrawArrays(cgo, pl, plc, pla, line->vertex1, notHaveValue, vertexVals,
1806                                          normalVals, colorVals, pickColorVals, accessibilityVals);
1807                 pl+=3; plc+=4; pla++;
1808                 notHaveValue = damode;
1809                 CGOAddVertexToDrawArrays(cgo, pl, plc, pla, line->vertex2, notHaveValue, vertexVals,
1810                                          normalVals, colorVals, pickColorVals, accessibilityVals);
1811                 pl+=3; plc+=4; pla++;
1812               }
1813 	      break;
1814 	    case CGO_VERTEX:
1815               CGOAddVertexToDrawArrays(cgo, pl, plc, pla, pc, notHaveValue, vertexVals,
1816                                        normalVals, colorVals, pickColorVals, accessibilityVals);
1817 	      pl+=3; plc+=4; pla++;
1818 	      notHaveValue = damode;
1819 	      break;
1820 	    case CGO_ALPHA:
1821 	      // in case we're before CGO_COLOR
1822 	      cgo->alpha = *pc;
1823 	      if (colorVals) {
1824 		// in case we're after CGO_COLOR
1825 		colorVals[plc + 3] = *pc;
1826 	      }
1827 	      break;
1828 	    }
1829 	  }
1830 	}
1831       }
1832       break;
1833     case CGO_PICK_COLOR:
1834       cgo->current_pick_color_index = CGO_get_uint(pc);
1835       cgo->current_pick_color_bond = CGO_get_int(pc + 1);
1836       cgo->add_to_cgo(op, pc);
1837       break;
1838     case CGO_ALPHA:
1839       cgo->alpha = *pc;
1840     default:
1841       cgo->add_to_cgo(op, pc);
1842     }
1843   }
1844   if (ok){
1845     ok &= CGOStop(cgo);
1846     if (ok){
1847       cgo->use_shader = I->use_shader;
1848       if (cgo->use_shader){
1849 	cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
1850 	cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
1851       }
1852     }
1853   }
1854   if (!ok){
1855     CGOFree(cgo);
1856   }
1857   return (cgo);
1858 }
1859 
1860 /**
1861  * Release all shader resources from this CGO
1862  */
CGOFreeVBOs(CGO * I)1863 void CGOFreeVBOs(CGO * I) {
1864   constexpr bool freevbos = true;
1865 
1866   for (auto it = I->begin(); !it.is_stop(); ++it) {
1867     const auto op = it.op_code();
1868 
1869     switch (op) {
1870     case CGO_DRAW_TRILINES:
1871     {
1872       unsigned buf = it.cast<cgo::draw::trilines>()->buffer;
1873       if (freevbos)
1874         I->G->ShaderMgr->AddVBOToFree(buf);
1875     }
1876     break;
1877     case CGO_DRAW_CUSTOM:
1878     {
1879       auto sp = it.cast<cgo::draw::custom>();
1880       if (freevbos) {
1881         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1882         I->G->ShaderMgr->freeGPUBuffer(sp->iboid);
1883         I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
1884       }
1885     }
1886     break;
1887     case CGO_DRAW_SPHERE_BUFFERS:
1888     {
1889       auto sp = it.cast<cgo::draw::sphere_buffers>();
1890       if (freevbos) {
1891         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1892         I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
1893       }
1894     }
1895     break;
1896     case CGO_DRAW_LABELS:
1897     {
1898       auto sp = it.cast<cgo::draw::labels>();
1899       if (freevbos) {
1900         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1901         I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
1902       }
1903     }
1904     break;
1905     case CGO_DRAW_TEXTURES:
1906     {
1907       auto sp = it.cast<cgo::draw::textures>();
1908       if (freevbos)
1909         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1910     }
1911     break;
1912     case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
1913     {
1914       auto sp = it.cast<cgo::draw::screen_textures>();
1915       if (freevbos)
1916         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1917     }
1918     break;
1919     case CGO_DRAW_CYLINDER_BUFFERS:
1920     {
1921       auto sp = it.cast<cgo::draw::cylinder_buffers>();
1922       if (freevbos) {
1923         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1924         I->G->ShaderMgr->freeGPUBuffer(sp->iboid);
1925         I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
1926       }
1927     }
1928     break;
1929     case CGO_DRAW_BUFFERS_NOT_INDEXED:
1930     {
1931       auto sp = it.cast<cgo::draw::buffers_not_indexed>();
1932       if (freevbos) {
1933         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1934         I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
1935       }
1936     }
1937     break;
1938     case CGO_DRAW_BUFFERS_INDEXED:
1939     {
1940       auto sp = it.cast<cgo::draw::buffers_indexed>();
1941       if (freevbos) {
1942         I->G->ShaderMgr->freeGPUBuffers({ sp->vboid, sp->iboid, sp->pickvboid });
1943       }
1944     }
1945     break;
1946     case CGO_DRAW_CONNECTORS:
1947     {
1948       auto sp = it.cast<cgo::draw::connectors>();
1949       if (freevbos)
1950         I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
1951     }
1952     break;
1953     }
1954   }
1955 }
1956 
1957 #define set_min_max(mn, mx, pt) {		\
1958   if (mn[0]>*pt) mn[0] = *pt; \
1959   if (mn[1]>*(pt+1)) mn[1] = *(pt+1);		\
1960   if (mn[2]>*(pt+2)) mn[2] = *(pt+2);		\
1961   if (mx[0]<*pt) mx[0] = *pt; \
1962   if (mx[1]<*(pt+1)) mx[1] = *(pt+1);		\
1963   if (mx[2]<*(pt+2)) mx[2] = *(pt+2);}
1964 
1965 
1966 static void CGOCountNumVertices(const CGO *I, int *num_total_vertices, int *num_total_indexes,
1967 			 int *num_total_vertices_lines, int *num_total_indexes_lines,
1968 			 int *num_total_vertices_points);
1969 
CGOCountNumVerticesDEBUG(const CGO * I)1970 void CGOCountNumVerticesDEBUG(const CGO *I){
1971   int num_total_vertices=0, num_total_indexes=0, num_total_vertices_lines=0, num_total_indexes_lines=0, num_total_vertices_points=0;
1972   CGOCountNumVertices(I, &num_total_vertices, &num_total_indexes, &num_total_vertices_lines, &num_total_indexes_lines, &num_total_vertices_points);
1973   printf("CGOCountNumVerticesDEBUG: num_total_vertices=%d num_total_indexes=%d num_total_vertices_lines=%d num_total_indexes_lines=%d num_total_vertices_points=%d\n", num_total_vertices, num_total_indexes, num_total_vertices_lines, num_total_indexes_lines, num_total_vertices_points);
1974 
1975 }
CGOCountNumVertices(const CGO * I,int * num_total_vertices,int * num_total_indexes,int * num_total_vertices_lines,int * num_total_indexes_lines,int * num_total_vertices_points)1976 static void CGOCountNumVertices(const CGO *I, int *num_total_vertices, int *num_total_indexes,
1977 			 int *num_total_vertices_lines, int *num_total_indexes_lines,
1978 			 int *num_total_vertices_points){
1979   int verts_skipped = 0;
1980   short err = 0;
1981 
1982   for (auto it = I->begin(); !it.is_stop(); ++it) {
1983     const auto op = it.op_code();
1984 
1985     err = 0;
1986     switch (op) {
1987     case CGO_DRAW_ARRAYS:
1988       {
1989         const auto sp = it.cast<cgo::draw::arrays>();
1990 	short shouldCompress = false, shouldCompressLines = false, shouldCompressPoints = false;
1991 	switch(sp->mode){
1992 	case GL_TRIANGLE_FAN:
1993 	case GL_TRIANGLE_STRIP:
1994 	case GL_TRIANGLES:
1995 	  shouldCompress = true;
1996 	  break;
1997 	case GL_LINES:
1998 	case GL_LINE_STRIP:
1999 	case GL_LINE_LOOP:
2000 	  shouldCompressLines = true;
2001 	  break;
2002 	case GL_POINTS:
2003 	  shouldCompressPoints = true;
2004 	  break;
2005 	default:
2006 	  break;
2007 	}
2008 	if (!shouldCompress && !shouldCompressLines && !shouldCompressPoints){
2009 	  verts_skipped += sp->nverts;
2010 	} else if (shouldCompressLines) {
2011 	  *num_total_vertices_lines += sp->nverts;
2012 	  switch(sp->mode){
2013 	  case GL_LINE_LOOP:
2014 	    *num_total_indexes_lines += 2 * sp->nverts;
2015 	    break;
2016 	  case GL_LINE_STRIP:
2017 	    *num_total_indexes_lines += 2 * (sp->nverts - 1);
2018 	    break;
2019 	  case GL_LINES:
2020 	    *num_total_indexes_lines += sp->nverts;
2021 	    break;
2022 	  }
2023 	} else if (shouldCompress){
2024 	  *num_total_vertices += sp->nverts;
2025 	  switch(sp->mode){
2026 	  case GL_TRIANGLE_FAN:
2027 	    *num_total_indexes += 3 * (sp->nverts - 2);
2028 	    break;
2029 	  case GL_TRIANGLE_STRIP:
2030 	    *num_total_indexes += 3 * (sp->nverts - 2);
2031 	    break;
2032 	  case GL_TRIANGLES:
2033 	    *num_total_indexes += sp->nverts;
2034 	    break;
2035 	  }
2036 	} else if (shouldCompressPoints){
2037 	  *num_total_vertices_points += sp->nverts;
2038 	}
2039       }
2040 	break;
2041     case CGO_END:
2042       if (!err){
2043 	PRINTFB(I->G, FB_CGO, FB_Warnings) " CGOCountNumVertices: CGO_END encountered, should call CGOCombineBeginEnd before CGOCountNumVertices\n" ENDFB(I->G);
2044 	err = true;
2045       }
2046     case CGO_VERTEX:
2047       if (!err){
2048 	PRINTFB(I->G, FB_CGO, FB_Warnings) " CGOCountNumVertices: CGO_VERTEX encountered, should call CGOCombineBeginEnd before CGOCountNumVertices\n" ENDFB(I->G);
2049 	err = true;
2050       }
2051     case CGO_BEGIN:
2052       if (!err){
2053 	PRINTFB(I->G, FB_CGO, FB_Warnings) " CGOCountNumVertices: CGO_BEGIN encountered, should call CGOCombineBeginEnd before CGOCountNumVertices\n" ENDFB(I->G);
2054 	err = true;
2055       }
2056     default:
2057       break;
2058     }
2059   }
2060 }
2061 
2062 /**
2063  * @param I Primitive CGO
2064  * @return number of vertices, or -1 on error
2065  */
CGOCountNumVerticesForScreen(const CGO * I)2066 static int CGOCountNumVerticesForScreen(const CGO* I)
2067 {
2068   auto G = I->G;
2069   constexpr int MODE_INVALID = -1;
2070   int begin_mode = MODE_INVALID;
2071   int num_total_indexes = 0;
2072   int nverts = 0;
2073 
2074   for (auto it = I->begin(); !it.is_stop(); ++it) {
2075     const auto op = it.op_code();
2076 
2077     // sanity check: non-primitive operations
2078     switch (op) {
2079     case CGO_DRAW_ARRAYS:
2080       WARN_UNEXPECTED_OPERATION(G, op);
2081       return -1;
2082     }
2083 
2084     if (begin_mode == MODE_INVALID) {
2085       switch (op) {
2086       case CGO_BEGIN:
2087         begin_mode = it.cast<cgo::draw::begin>()->mode;
2088         break;
2089       case CGO_VERTEX:
2090       case CGO_END:
2091         WARN_UNEXPECTED_OPERATION(G, op);
2092         return -1;
2093       }
2094     } else {
2095       switch (op) {
2096       case CGO_BEGIN:
2097         WARN_UNEXPECTED_OPERATION(G, op);
2098         return -1;
2099       case CGO_VERTEX:
2100         ++nverts;
2101         break;
2102       case CGO_END:
2103         switch (begin_mode) {
2104         case GL_TRIANGLE_FAN:
2105         case GL_TRIANGLE_STRIP:
2106           num_total_indexes += 3 * (nverts - 2);
2107           break;
2108         case GL_TRIANGLES:
2109           num_total_indexes += nverts;
2110           break;
2111         default:
2112           // not implemented
2113           assert(false);
2114         }
2115         begin_mode = MODE_INVALID;
2116         nverts = 0;
2117         break;
2118       }
2119     }
2120   }
2121 
2122   return num_total_indexes;
2123 }
2124 
2125 static
SetVertexValuesForVBO(PyMOLGlobals * G,CGO * cgo,int pl,int plc,int cnt,int incr,float * vertexValsDA,float * normalValsDA,float * colorValsDA,float * pickColorValsDA,float * vertexVals,uchar * normalValsC,float * normalVals,uchar * colorValsUC,float * colorVals,float * pickColorVals,float * accessibilityVals=NULL,float * accessibilityValsDA=NULL)2126 void SetVertexValuesForVBO(PyMOLGlobals * G, CGO *cgo, int pl, int plc, int cnt, int incr,
2127                            float *vertexValsDA, float *normalValsDA,
2128                            float *colorValsDA, float *pickColorValsDA,
2129 			   float *vertexVals, uchar *normalValsC,
2130                            float *normalVals, uchar *colorValsUC, float *colorVals,
2131                            float *pickColorVals,
2132                            float *accessibilityVals=NULL, float *accessibilityValsDA=NULL){
2133   int pl2 = pl + 1, pl3 = pl + 2;
2134   int pln1 = VAR_FOR_NORMAL, pln2 = VAR_FOR_NORMAL + 1, pln3 = VAR_FOR_NORMAL + 2;
2135   int plc2 = plc + 1, plc3 = plc + 2, plc4 = plc + 3;
2136   int c, c2, c3;
2137   int cc, cc2, cc3, cc4;
2138   int pcc = incr * 2, pcco = cnt * 2;
2139   c = cnt * 3; c2 = c + 1; c3 = c + 2;
2140   cc = cnt * 4; cc2 = cc + 1; cc3 = cc + 2; cc4 = cc + 3;
2141   vertexVals[pl] = vertexValsDA[c]; vertexVals[pl2] = vertexValsDA[c2]; vertexVals[pl3] = vertexValsDA[c3];
2142 
2143     if (normalValsC){
2144       if (normalValsDA){
2145 	normalValsC[pln1] = CLIP_NORMAL_VALUE(normalValsDA[c]); normalValsC[pln2] = CLIP_NORMAL_VALUE(normalValsDA[c2]); normalValsC[pln3] = CLIP_NORMAL_VALUE(normalValsDA[c3]);
2146       } else {
2147 	normalValsC[pln1] = CLIP_NORMAL_VALUE(cgo->normal[0]); normalValsC[pln2] = CLIP_NORMAL_VALUE(cgo->normal[1]); normalValsC[pln3] = CLIP_NORMAL_VALUE(cgo->normal[2]);
2148       }
2149 
2150 #ifdef ALIGN_VBOS_TO_4_BYTE_ARRAYS
2151       normalValsC[pln3+1] = 127;
2152 #endif
2153   } else {
2154     if (normalValsDA){
2155 	normalVals[pln1] = normalValsDA[c]; normalVals[pln2] = normalValsDA[c2]; normalVals[pln3] = normalValsDA[c3];
2156       } else {
2157 	normalVals[pln1] = cgo->normal[0]; normalVals[pln2] = cgo->normal[1]; normalVals[pln3] = cgo->normal[2];
2158       }
2159   }
2160 
2161   if (colorValsUC){
2162     if (colorValsDA){
2163       colorValsUC[plc] = CLIP_COLOR_VALUE(colorValsDA[cc]); colorValsUC[plc2] = CLIP_COLOR_VALUE(colorValsDA[cc2]);
2164       colorValsUC[plc3] = CLIP_COLOR_VALUE(colorValsDA[cc3]); colorValsUC[plc4] = CLIP_COLOR_VALUE(colorValsDA[cc4]);
2165     } else {
2166       colorValsUC[plc] = CLIP_COLOR_VALUE(cgo->color[0]); colorValsUC[plc2] = CLIP_COLOR_VALUE(cgo->color[1]);
2167       colorValsUC[plc3] = CLIP_COLOR_VALUE(cgo->color[2]); colorValsUC[plc4] = CLIP_COLOR_VALUE(cgo->alpha);
2168     }
2169   } else {
2170     if (colorValsDA){
2171       colorVals[plc] = colorValsDA[cc]; colorVals[plc2] = colorValsDA[cc2];
2172       colorVals[plc3] = colorValsDA[cc3]; colorVals[plc4] = colorValsDA[cc4];
2173     } else {
2174       colorVals[plc] = cgo->color[0]; colorVals[plc2] = cgo->color[1];
2175       colorVals[plc3] = cgo->color[2]; colorVals[plc4] = cgo->alpha;
2176     }
2177   }
2178   if (pickColorValsDA){
2179     cgo->current_pick_color_index = CGO_get_uint(pickColorValsDA + pcco);
2180     cgo->current_pick_color_bond = CGO_get_int(pickColorValsDA + pcco + 1);
2181   }
2182   CGO_put_uint(pickColorVals + pcc, cgo->current_pick_color_index);
2183   CGO_put_int(pickColorVals + pcc + 1, cgo->current_pick_color_bond);
2184   if (accessibilityValsDA){
2185     accessibilityVals[pl/3] = accessibilityValsDA[cnt];
2186   }
2187 }
2188 
OptimizePointsToVBO(const CGO * I,CGO * cgo,int num_total_vertices_points,float * min,float * max,short * has_draw_buffer,bool addshaders)2189 static int OptimizePointsToVBO(const CGO *I, CGO *cgo, int num_total_vertices_points, float *min, float *max, short *has_draw_buffer, bool addshaders){
2190   float *vertexVals = 0, *colorVals = 0, *normalVals = 0;
2191   float *pickColorVals;
2192   int pl = 0, plc = 0, idxpl = 0, vpl = 0, tot, nxtn;
2193   uchar *colorValsUC = 0;
2194   uchar *normalValsC = 0;
2195   bool has_normals = false, has_colors = false;
2196   int ok = true;
2197 
2198   cgo->alpha = 1.f;
2199   cgo->color[0] = 1.f; cgo->color[1] = 1.f; cgo->color[2] = 1.f;
2200 
2201   tot = num_total_vertices_points * (3 * 5) ;
2202   //    tot = num_total_indexes * (3 * 3 + 2) ;
2203   /* NOTE/TODO: Not sure why 3*5 needs to be used, but 3*3+2, which is the
2204      correct length, crashes in glBufferData */
2205   vertexVals = pymol::malloc<float>(tot);
2206   CHECKOK(ok, vertexVals);
2207   if (!ok){
2208     PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: OptimizePointsToVBO() vertexVals could not be allocated\n" ENDFB(I->G);
2209     return 0;
2210   }
2211   normalVals = vertexVals + 3 * num_total_vertices_points;
2212   nxtn = 3;
2213   if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
2214     normalValsC = (uchar*) normalVals;
2215     nxtn = 1;
2216   }
2217   colorVals = normalVals + nxtn * num_total_vertices_points;
2218   if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
2219     colorValsUC = (uchar*) colorVals;
2220     nxtn = 1;
2221   } else {
2222     nxtn = 4;
2223   }
2224   pickColorVals = (colorVals + nxtn * num_total_vertices_points);
2225 
2226   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
2227     const auto op = it.op_code();
2228     const auto pc = it.data();
2229 
2230     switch (op) {
2231     case CGO_BOUNDING_BOX:
2232     case CGO_DRAW_SPHERE_BUFFERS:
2233     case CGO_DRAW_LABELS:
2234     case CGO_DRAW_TEXTURES:
2235     case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
2236     case CGO_DRAW_CYLINDER_BUFFERS:
2237     case CGO_DRAW_BUFFERS_NOT_INDEXED:
2238     case CGO_DRAW_BUFFERS_INDEXED:
2239       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: OptimizePointsToVBO used with unsupported CGO ops" ENDFB(I->G);
2240       return 0;
2241       break;
2242     case CGO_NORMAL:
2243       cgo->normal[0] = *pc; cgo->normal[1] = *(pc + 1); cgo->normal[2] = *(pc + 2);
2244       has_normals = true;
2245       break;
2246     case CGO_COLOR:
2247       cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
2248       has_colors = true;
2249       break;
2250     case CGO_ALPHA:
2251       cgo->alpha = *pc;
2252       break;
2253     case CGO_PICK_COLOR:
2254       cgo->current_pick_color_index = CGO_get_uint(pc);
2255       cgo->current_pick_color_bond = CGO_get_int(pc + 1);
2256       break;
2257     case CGO_DRAW_ARRAYS:
2258       {
2259         const auto sp = it.cast<cgo::draw::arrays>();
2260 	short shouldCompress = false;
2261 	switch(sp->mode){
2262 	case GL_POINTS:
2263 	  shouldCompress = true;
2264 	default:
2265 	  break;
2266 	}
2267 	/*	TODO : DO WE NEED TO COMPENSATE FOR THIS? if (!has_normals && arrays & CGO_NORMAL_ARRAY){
2268 	  arrays = arrays ^ CGO_NORMAL_ARRAY;
2269 	  narrays -= 1;
2270 	  }*/
2271 	if (shouldCompress){
2272 	  int cnt, nxtn = 3 ,incr=0;
2273 	  float *vertexValsDA = NULL, *nxtVals = NULL, *colorValsDA = NULL, *normalValsDA = NULL;
2274 	  float *pickColorValsDA = NULL, *pickColorValsTMP;
2275 
2276 	  nxtVals = vertexValsDA = sp->floatdata;
2277 
2278 	  for (cnt=0; cnt<sp->nverts*3; cnt+=3){
2279 	    set_min_max(min, max, &vertexValsDA[cnt]);
2280 	  }
2281 	  if (sp->arraybits & CGO_NORMAL_ARRAY){
2282 	    has_normals = true;
2283 	    nxtVals = normalValsDA = vertexValsDA + (nxtn*sp->nverts);
2284 	  }
2285 	  if (sp->arraybits & CGO_COLOR_ARRAY){
2286 	    has_colors = true;
2287 	    nxtVals = colorValsDA = nxtVals + (nxtn*sp->nverts);
2288 	    nxtn = 4;
2289 	  }
2290 	  if (sp->arraybits & CGO_PICK_COLOR_ARRAY){
2291 	    nxtVals = nxtVals + (nxtn*sp->nverts);
2292 	    pickColorValsDA = nxtVals + sp->nverts;
2293 	    nxtn = 3;
2294 	  }
2295 	  pickColorValsTMP = pickColorVals + (idxpl * 2);
2296 	  switch (sp->mode){
2297 	  case GL_POINTS:
2298 	    for (cnt = 0; cnt < sp->nverts; cnt++){
2299 	      SetVertexValuesForVBO(I->G, cgo, pl, plc, cnt, incr++,
2300 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2301 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2302 				    pickColorValsTMP);
2303 	      idxpl++; pl += 3; plc += 4;
2304 	    }
2305 	    break;
2306 	  }
2307 	  vpl += sp->nverts;
2308         }
2309       }
2310       break;
2311     }
2312     ok &= !I->G->Interrupt;
2313   }
2314   if (ok){
2315     short arrays = CGO_VERTEX_ARRAY | CGO_PICK_COLOR_ARRAY;
2316     short nsz = 12;
2317     GLenum ntp = GL_FLOAT;
2318     bool nnorm = GL_FALSE;
2319     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
2320       nsz = 3;
2321       ntp = GL_BYTE;
2322       nnorm = GL_TRUE;
2323     }
2324 
2325     short csz = 4;
2326     GLenum ctp = GL_FLOAT;
2327     bool cnorm = GL_FALSE;
2328     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
2329       csz = 1;
2330       ctp = GL_UNSIGNED_BYTE;
2331       cnorm = GL_TRUE;
2332     }
2333 
2334     VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>();
2335 
2336     BufferDataDesc bufData =
2337       { { "a_Vertex", GL_FLOAT, 3, sizeof(float) * num_total_vertices_points * 3, vertexVals, GL_FALSE } };
2338 
2339     if (has_normals){
2340       bufData.push_back( { "a_Normal", ntp,      3, (size_t)(num_total_vertices_points * nsz), normalVals, nnorm } );
2341     }
2342     if (has_colors){
2343       bufData.push_back( { "a_Color",  ctp,      4, sizeof(float) * num_total_vertices_points * csz, colorVals, cnorm } );
2344     }
2345     ok = vbo->bufferData(std::move(bufData));
2346 
2347     if (ok && has_colors){
2348       arrays |= CGO_COLOR_ARRAY;
2349     }
2350 
2351     size_t vboid = vbo->get_hash_id();
2352 
2353     if (ok){
2354       float *newPickColorVals ;
2355       if (addshaders)
2356 	CGOEnable(cgo, GL_DEFAULT_SHADER);
2357       newPickColorVals = cgo->add<cgo::draw::buffers_not_indexed>(GL_POINTS, arrays, num_total_vertices_points, vboid);
2358       CHECKOK(ok, newPickColorVals);
2359       if (ok && addshaders)
2360 	ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
2361       if (!newPickColorVals)
2362 	I->G->ShaderMgr->freeGPUBuffer(vboid);
2363       if (ok)
2364 	memcpy(newPickColorVals + num_total_vertices_points, pickColorVals, num_total_vertices_points * 2 * sizeof(float));
2365       *has_draw_buffer = true;
2366     } else {
2367       I->G->ShaderMgr->freeGPUBuffer(vboid);
2368     }
2369   }
2370   FreeP(vertexVals);
2371   return ok;
2372   /* END GL_POINTS */
2373   //    printf("num_total_vertices_points=%d\n", num_total_vertices_points);
2374 }
2375 
2376 static
FixPickColorsForLine(float * pick1,float * pick2)2377 void FixPickColorsForLine(float *pick1, float *pick2){
2378   unsigned int p1 = CGO_get_uint(pick1);
2379   unsigned int p2 = CGO_get_uint(pick2);
2380   int b1 = CGO_get_int(pick1 + 1);
2381   int b2 = CGO_get_int(pick2 + 1);
2382   if (p1 != p2 || b1 != b2){
2383     // if the pick colors are different, then pick the first one
2384     CGO_put_uint(pick1, p2);
2385     CGO_put_int(pick1 + 1, b2);
2386   }
2387 }
2388 
2389 static
FixPickColorsForTriangle(float * pick1,float * pick2,float * pick3)2390 void FixPickColorsForTriangle(float *pick1, float *pick2, float *pick3){
2391   unsigned int p1 = CGO_get_uint(pick1);
2392   unsigned int p2 = CGO_get_uint(pick2);
2393   unsigned int p3 = CGO_get_uint(pick3);
2394   int b1 = CGO_get_int(pick1 + 1);
2395   int b2 = CGO_get_int(pick2 + 1);
2396   int b3 = CGO_get_int(pick3 + 1);
2397   if (p1 != p2 || p1 != p3 || p2 != p3 ||
2398       b1 != b2 || b1 != b3 || b2 != b3){
2399     // right now, if the pick colors are different, then pick majority, otherwise, pick first one
2400     if (p1 == p2 && b1 == b2){
2401       CGO_put_uint(pick3, p1);
2402       CGO_put_int(pick3 + 1, b1);
2403     } else if (p1 == p3 && b1 == b3){
2404       CGO_put_uint(pick2, p1);
2405       CGO_put_int(pick2 + 1, b1);
2406     } else if (p2 == p3 && b2 == b3){
2407       CGO_put_uint(pick1, p2);
2408       CGO_put_int(pick1 + 1, b2);
2409     } else {
2410       CGO_put_uint(pick2, p1);
2411       CGO_put_int(pick2 + 1, b1);
2412       CGO_put_uint(pick3, p1);
2413       CGO_put_int(pick3 + 1, b1);
2414     }
2415   }
2416 }
2417 
CGOProcessCGOtoArrays(const CGO * I,CGO * cgo,CGO * addtocgo,float * min,float * max,int * ambient_occlusion,float * vertexVals,float * normalVals,uchar * normalValsC,float * colorVals,uchar * colorValsUC,float * pickColorVals,float * accessibilityVals,bool & has_normals,bool & has_colors,bool & has_accessibility)2418 static bool CGOProcessCGOtoArrays(const CGO* I, CGO* cgo, CGO* addtocgo,
2419     float* min, float* max, int* ambient_occlusion, float* vertexVals,
2420     float* normalVals, uchar* normalValsC, float* colorVals, uchar* colorValsUC,
2421     float* pickColorVals, float* accessibilityVals, bool& has_normals,
2422     bool& has_colors, bool& has_accessibility)
2423 {
2424   auto G = I->G;
2425   int idxpl = 0;
2426   int pl = 0, plc = 0, vpl = 0;
2427   int ok = true;
2428 
2429   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
2430     const auto op = it.op_code();
2431     const auto pc = it.data();
2432 
2433     switch (op) {
2434     case CGO_BOUNDING_BOX:
2435       {
2436 	const float *newpc = pc;
2437 	if (addtocgo)
2438           addtocgo->add_to_cgo(op, newpc);
2439       }
2440       break;
2441     case CGO_DRAW_SPHERE_BUFFERS:
2442     case CGO_DRAW_LABELS:
2443     case CGO_DRAW_TEXTURES:
2444     case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
2445     case CGO_DRAW_CYLINDER_BUFFERS:
2446     case CGO_DRAW_BUFFERS_NOT_INDEXED:
2447     case CGO_DRAW_BUFFERS_INDEXED:
2448     {
2449       const float * newpc = pc;
2450       if (addtocgo)
2451         addtocgo->add_to_cgo(op, newpc);
2452     }
2453       break;
2454     case CGO_NORMAL:
2455       cgo->normal[0] = *pc; cgo->normal[1] = *(pc + 1); cgo->normal[2] = *(pc + 2);
2456       has_normals = true;
2457       break;
2458     case CGO_COLOR:
2459       cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
2460       has_colors = true;
2461       break;
2462     case CGO_ALPHA:
2463       cgo->alpha = *pc;
2464       break;
2465     case CGO_ACCESSIBILITY:
2466       cgo->current_accessibility = pc[0];
2467       has_accessibility = true;
2468       break;
2469     case CGO_PICK_COLOR:
2470       cgo->current_pick_color_index = CGO_get_uint(pc);
2471       cgo->current_pick_color_bond = CGO_get_int(pc + 1);
2472       break;
2473     case CGO_DRAW_ARRAYS:
2474       {
2475         const auto sp = it.cast<cgo::draw::arrays>();
2476 	short shouldCompress = false;
2477 	switch(sp->mode){
2478 	case GL_TRIANGLE_FAN:
2479 	case GL_TRIANGLE_STRIP:
2480 	case GL_TRIANGLES:
2481 	  shouldCompress = true;
2482 	default:
2483 	  break;
2484 	}
2485 	if (shouldCompress){
2486 	  int cnt, nxtn = 3,incr=0;
2487 	  float *vertexValsDA = NULL, *nxtVals = NULL, *colorValsDA = NULL, *normalValsDA = NULL, *accessibilityValsDA = NULL;
2488 	  float *pickColorValsDA = NULL, *pickColorValsTMP;
2489 	  nxtVals = vertexValsDA = sp->floatdata;
2490 
2491 	  for (cnt=0; cnt<sp->nverts*3; cnt+=3){
2492 	    set_min_max(min, max, &vertexValsDA[cnt]);
2493 	  }
2494 	  if (sp->arraybits & CGO_NORMAL_ARRAY){
2495 	    nxtVals = normalValsDA = vertexValsDA + (nxtn*sp->nverts);
2496             has_normals = true;
2497 	  }
2498 
2499 	  if (sp->arraybits & CGO_COLOR_ARRAY){
2500 	    nxtVals = colorValsDA = nxtVals + (nxtn*sp->nverts);
2501 	    nxtn = 4;
2502             has_colors = true;
2503 	  }
2504 	  if (sp->arraybits & CGO_PICK_COLOR_ARRAY){
2505 	    nxtVals = nxtVals + (nxtn*sp->nverts);
2506 	    pickColorValsDA = nxtVals + sp->nverts;
2507 	    nxtn = 3;
2508 	  }
2509 	  pickColorValsTMP = pickColorVals + (idxpl * 2);
2510 	  if (sp->arraybits & CGO_ACCESSIBILITY_ARRAY){
2511 	    if (!(*ambient_occlusion) && incr){
2512 	      for (cnt=0; cnt<incr;cnt++){
2513 		/* if ambient_occlusion, need to fill in the array */
2514 		accessibilityVals[cnt] = 1.f;
2515 	      }
2516 	    }
2517 	    (*ambient_occlusion) = 1;
2518 	    accessibilityValsDA = nxtVals + nxtn*sp->nverts;
2519             has_accessibility = true;
2520 	  } else {
2521 	    if (*ambient_occlusion){
2522 	      for (cnt=incr; cnt<incr+sp->nverts;cnt++){
2523 		/* if ambient_occlusion, need to fill in the array */
2524 		accessibilityVals[cnt] = 1.f;
2525 	      }
2526 	    }
2527 	  }
2528 	  switch (sp->mode){
2529 	  case GL_TRIANGLES:
2530 	    for (cnt = 0; ok && cnt < sp->nverts; cnt++){
2531 	      SetVertexValuesForVBO(G, cgo, pl, plc, cnt, incr++,
2532 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2533 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2534 				    pickColorValsTMP, accessibilityVals, accessibilityValsDA);
2535               if (incr && (incr % 3) == 0){
2536                 FixPickColorsForTriangle(pickColorValsTMP + (incr-3) * 2,
2537                                          pickColorValsTMP + (incr-2) * 2,
2538                                          pickColorValsTMP + (incr-1) * 2);
2539               }
2540 	      idxpl++; pl += 3; plc += 4;
2541 	      ok &= !G->Interrupt;
2542 	    }
2543 	    break;
2544 	  case GL_TRIANGLE_STRIP:
2545 	    {
2546 	      short flip = 0;
2547 	      for (cnt = 2; ok && cnt < sp->nverts; cnt++){
2548 		SetVertexValuesForVBO(G, cgo, pl, plc, cnt - (flip ? 0 : 2), incr++,
2549 				      vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2550 				      vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2551 				      pickColorValsTMP, accessibilityVals, accessibilityValsDA);
2552 		idxpl++; pl += 3; plc += 4;
2553 		SetVertexValuesForVBO(G, cgo, pl, plc, cnt-1, incr++,
2554 				      vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2555 				      vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2556 				      pickColorValsTMP, accessibilityVals, accessibilityValsDA);
2557 		idxpl++; pl += 3; plc += 4;
2558 		SetVertexValuesForVBO(G, cgo, pl, plc, cnt - (flip ? 2 : 0) , incr++,
2559 				      vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2560 				      vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2561 				      pickColorValsTMP, accessibilityVals, accessibilityValsDA);
2562                 FixPickColorsForTriangle(pickColorValsTMP + (incr-3) * 2,
2563                                          pickColorValsTMP + (incr-2) * 2,
2564                                          pickColorValsTMP + (incr-1) * 2);
2565 		idxpl++; pl += 3; plc += 4;
2566 		ok &= !G->Interrupt;
2567 		flip = !flip;
2568 	      }
2569 	    }
2570 	    break;
2571 	  case GL_TRIANGLE_FAN:
2572 	    for (cnt = 2; ok && cnt < sp->nverts; cnt++){
2573 	      SetVertexValuesForVBO(G, cgo, pl, plc, 0, incr++,
2574 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2575 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2576 				    pickColorValsTMP, accessibilityVals, accessibilityValsDA);
2577 	      idxpl++; pl += 3; plc += 4;
2578 	      SetVertexValuesForVBO(G, cgo, pl, plc, cnt - 1, incr++,
2579 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2580 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2581 				    pickColorValsTMP, accessibilityVals, accessibilityValsDA);
2582 	      idxpl++; pl += 3; plc += 4;
2583 	      SetVertexValuesForVBO(G, cgo, pl, plc, cnt, incr++,
2584 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
2585 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
2586 				    pickColorValsTMP, accessibilityVals, accessibilityValsDA);
2587               FixPickColorsForTriangle(pickColorValsTMP + (incr-3) * 2,
2588                                        pickColorValsTMP + (incr-2) * 2,
2589                                        pickColorValsTMP + (incr-1) * 2);
2590 	      idxpl++; pl += 3; plc += 4;
2591 	      ok &= !G->Interrupt;
2592 	    }
2593 	    break;
2594 	  }
2595 	  vpl += sp->nverts;
2596 	}
2597       }
2598       break;
2599     }
2600     ok &= !G->Interrupt;
2601   }
2602   ok &= !G->Interrupt;
2603   return ok;
2604 }
2605 
CGOProcessScreenCGOtoArrays(PyMOLGlobals * G,CGO * cgo,float * vertexVals,float * texcoordVals,float * colorVals,uchar * colorValsUC)2606 static bool CGOProcessScreenCGOtoArrays(PyMOLGlobals* G, CGO* cgo,
2607     float* vertexVals, float* texcoordVals, float* colorVals,
2608     uchar* colorValsUC)
2609 {
2610   int pl = 0;
2611   cgo->alpha = 1.f;
2612 
2613   for (auto it = cgo->begin(); !it.is_stop(); ++it) {
2614     const auto op = it.op_code();
2615     const auto pc = it.data();
2616 
2617     switch (op) {
2618     case CGO_BOUNDING_BOX:
2619     case CGO_DRAW_SPHERE_BUFFERS:
2620     case CGO_DRAW_LABELS:
2621     case CGO_DRAW_TEXTURES:
2622     case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
2623     case CGO_DRAW_CYLINDER_BUFFERS:
2624     case CGO_DRAW_BUFFERS_NOT_INDEXED:
2625     case CGO_DRAW_BUFFERS_INDEXED:
2626     case CGO_DRAW_ARRAYS:
2627     case CGO_ACCESSIBILITY:
2628       WARN_UNEXPECTED_OPERATION(G, op);
2629       return false;
2630     case CGO_NORMAL:
2631       cgo->normal[0] = *pc; cgo->normal[1] = *(pc + 1); cgo->normal[2] = *(pc + 2);
2632       break;
2633     case CGO_TEX_COORD:
2634       cgo->texture[0] = *pc; cgo->texture[1] = *(pc + 1);
2635       break;
2636     case CGO_COLOR:
2637       cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
2638       break;
2639     case CGO_ALPHA:
2640       cgo->alpha = *pc;
2641       break;
2642     case CGO_PICK_COLOR:
2643       cgo->current_pick_color_index = CGO_get_uint(pc);
2644       cgo->current_pick_color_bond = CGO_get_int(pc + 1);
2645       break;
2646     case CGO_BEGIN:
2647       {
2648 	int mode = it.cast<cgo::draw::begin>()->mode;
2649 	int nverts = 0, ipl = 0;
2650 	cgo->texture[0] = cgo->texture[1] = 0.f;
2651 
2652         for (++it;; ++it) {
2653           if (it.is_stop()) {
2654             WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
2655             return false;
2656           }
2657 
2658           const auto op = it.op_code();
2659           if (op == CGO_END) {
2660             break;
2661           }
2662 
2663           const auto pc = it.data();
2664 
2665 	  switch (op) {
2666 	  case CGO_TEX_COORD:
2667 	    cgo->texture[0] = *pc; cgo->texture[1] = *(pc + 1);
2668 	    break;
2669 	  case CGO_COLOR:
2670 	    cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
2671 	    break;
2672 	  case CGO_ALPHA:
2673 	    cgo->alpha = *pc;
2674 	    break;
2675 	  case CGO_VERTEX:
2676 	    {
2677 	      switch (mode){
2678 	      case GL_TRIANGLES:
2679 		{
2680 		  int vpl = pl * 3, tpl = pl * 2, cpl = pl * 4;
2681 		  vertexVals[vpl] = *pc;
2682 		  vertexVals[vpl + 1] = *(pc + 1);
2683 		  vertexVals[vpl + 2] = *(pc + 2);
2684 		  texcoordVals[tpl] = cgo->texture[0];
2685 		  texcoordVals[tpl+1] = cgo->texture[1];
2686 		  if (colorValsUC){
2687 		    colorValsUC[cpl] = CLIP_COLOR_VALUE(cgo->color[0]);
2688 		    colorValsUC[cpl+1] = CLIP_COLOR_VALUE(cgo->color[1]);
2689 		    colorValsUC[cpl+2] = CLIP_COLOR_VALUE(cgo->color[2]);
2690 		    colorValsUC[cpl+3] = CLIP_COLOR_VALUE(cgo->alpha);
2691 		  } else {
2692 		    colorVals[cpl] = cgo->color[0];
2693 		    colorVals[cpl+1] = cgo->color[1];
2694 		    colorVals[cpl+2] = cgo->color[2];
2695 		    colorVals[cpl+3] = cgo->alpha;
2696 		  }
2697 		  pl++;
2698 		}
2699 		break;
2700 	      case GL_TRIANGLE_STRIP:
2701 		{
2702 		  int vpl = pl * 3, tpl = pl * 2, cpl = pl * 4;
2703 		  if (ipl < 3){
2704 		    vertexVals[vpl] = *pc; vertexVals[vpl + 1] = *(pc + 1); vertexVals[vpl + 2] = *(pc + 2);
2705 		    texcoordVals[tpl] = cgo->texture[0]; texcoordVals[tpl+1] = cgo->texture[1];
2706 		    if (colorValsUC){
2707 		      colorValsUC[cpl] = CLIP_COLOR_VALUE(cgo->color[0]); colorValsUC[cpl+1] = CLIP_COLOR_VALUE(cgo->color[1]);
2708 		      colorValsUC[cpl+2] = CLIP_COLOR_VALUE(cgo->color[2]); colorValsUC[cpl+3] = CLIP_COLOR_VALUE(cgo->alpha);
2709 		    } else {
2710 		      colorVals[cpl] = cgo->color[0]; colorVals[cpl+1] = cgo->color[1];
2711 		      colorVals[cpl+2] = cgo->color[2]; colorVals[cpl+3] = cgo->alpha;
2712 		    }
2713 		    pl++; ipl++;
2714 		  } else {
2715 		    vertexVals[vpl] = vertexVals[vpl-6]; vertexVals[vpl + 1] = vertexVals[vpl-5]; vertexVals[vpl + 2] = vertexVals[vpl-4];
2716 		    texcoordVals[tpl] = texcoordVals[tpl-4]; texcoordVals[tpl+1] = texcoordVals[tpl-3];
2717 		    if (colorValsUC){
2718 		      colorValsUC[cpl] = colorValsUC[cpl-8]; colorValsUC[cpl+1] = colorValsUC[cpl-7];
2719 		      colorValsUC[cpl+2] = colorValsUC[cpl-6]; colorValsUC[cpl+3] = colorValsUC[cpl-5];
2720 		    } else {
2721 		      colorVals[cpl] = colorVals[cpl-8]; colorVals[cpl+1] = colorVals[cpl-7];
2722 		      colorVals[cpl+2] = colorVals[cpl-6]; colorVals[cpl+3] = colorVals[cpl-5];
2723 		    }
2724 		    pl++; vpl+=3; tpl+=2; cpl+=4; ipl++;
2725 		    vertexVals[vpl] = vertexVals[vpl-6]; vertexVals[vpl + 1] = vertexVals[vpl-5]; vertexVals[vpl + 2] = vertexVals[vpl-4];
2726 		    texcoordVals[tpl] = texcoordVals[tpl-4]; texcoordVals[tpl+1] = texcoordVals[tpl-3];
2727 		    if (colorValsUC){
2728 		      colorValsUC[cpl] = colorValsUC[cpl-8]; colorValsUC[cpl+1] = colorValsUC[cpl-7];
2729 		      colorValsUC[cpl+2] = colorValsUC[cpl-6]; colorValsUC[cpl+3] = colorValsUC[cpl-5];
2730 		    } else {
2731 		      colorVals[cpl] = colorVals[cpl-8]; colorVals[cpl+1] = colorVals[cpl-7];
2732 		      colorVals[cpl+2] = colorVals[cpl-6]; colorVals[cpl+3] = colorVals[cpl-5];
2733 		    }
2734 		    pl++; vpl+=3; tpl+=2; cpl+=4; ipl++;
2735 		    vertexVals[vpl] = *pc; vertexVals[vpl + 1] = *(pc + 1); vertexVals[vpl + 2] = *(pc + 2);
2736 		    texcoordVals[tpl] = cgo->texture[0]; texcoordVals[tpl+1] = cgo->texture[1];
2737 		    if (colorValsUC){
2738 		      colorValsUC[cpl] = CLIP_COLOR_VALUE(cgo->color[0]); colorValsUC[cpl+1] = CLIP_COLOR_VALUE(cgo->color[1]);
2739 		      colorValsUC[cpl+2] = CLIP_COLOR_VALUE(cgo->color[2]); colorValsUC[cpl+3] = CLIP_COLOR_VALUE(cgo->alpha);
2740 		    } else {
2741 		      colorVals[cpl] = cgo->color[0]; colorVals[cpl+1] = cgo->color[1];
2742 		      colorVals[cpl+2] = cgo->color[2]; colorVals[cpl+3] = cgo->alpha;
2743 		    }
2744 		    pl++; ipl++;
2745 		  }
2746 		}
2747 		break;
2748 	      case GL_TRIANGLE_FAN:
2749 	      default:
2750 		printf("CGOProcessScreenCGOtoArrays: WARNING: mode=%d not implemented yet GL_LINES=%d GL_LINE_STRIP=%d GL_LINE_LOOP=%d\n", mode, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP);
2751 		break;
2752 	      }
2753 	      nverts++;
2754 	    }
2755 	  }
2756 	}
2757       }
2758       break;
2759     }
2760   }
2761   return true;
2762 }
2763 
CGOOptimizeToVBONotIndexed(CGO ** I)2764 bool CGOOptimizeToVBONotIndexed(CGO ** I) {
2765   CGO *cgo = CGOOptimizeToVBONotIndexed(*I, 0, true, NULL);
2766   CGOFree(*I);
2767   *I = cgo;
2768   return (cgo != NULL);
2769 }
2770 
CGOOptimizeToVBONotIndexed(const CGO * I,int est,bool addshaders,float ** returnedData)2771 CGO *CGOOptimizeToVBONotIndexed(const CGO * I, int est, bool addshaders, float **returnedData)
2772 {
2773   CGO *cgo;
2774   int num_total_vertices = 0, num_total_indexes = 0, num_total_vertices_lines = 0, num_total_indexes_lines = 0,
2775     num_total_vertices_points = 0;
2776   short has_draw_buffer = false;
2777   float min[3] = { FLT_MAX, FLT_MAX, FLT_MAX }, max[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
2778   int ambient_occlusion = 0;
2779   int ok = true;
2780   cgo = CGONewSized(I->G, 0);
2781 
2782   CGOCountNumVertices(I, &num_total_vertices, &num_total_indexes,
2783                          &num_total_vertices_lines, &num_total_indexes_lines,
2784                          &num_total_vertices_points);
2785   if (num_total_vertices_points>0){
2786     if (!OptimizePointsToVBO(I, cgo, num_total_vertices_points, min, max, &has_draw_buffer, addshaders)){
2787       CGOFree(cgo);
2788       return NULL;
2789     }
2790   }
2791   if (num_total_indexes>0){
2792     float *vertexVals = 0, *colorVals = 0, *normalVals;
2793     float *pickColorVals, *accessibilityVals = 0;
2794     int tot, nxtn;
2795     uchar *colorValsUC = 0;
2796     uchar *normalValsC = 0;
2797 
2798     cgo->alpha = 1.f;
2799     cgo->color[0] = 1.f; cgo->color[1] = 1.f; cgo->color[2] = 1.f;
2800 
2801     tot = num_total_indexes * (3 * 6) ;
2802     //    tot = num_total_indexes * (3 * 3 + 2) ;
2803     /* NOTE/TODO: Not sure why 3*5 needs to be used, but 3*3+2, which is the
2804        correct length, crashes in glBufferData */
2805     /* before allocating anything, we should check to make sure that we have enough memory on IOS,
2806        otherwise we should just fail */
2807     vertexVals = pymol::malloc<float>(tot);
2808     if (!vertexVals){
2809       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBONotIndexed() vertexVals could not be allocated\n" ENDFB(I->G);
2810       CGOFree(cgo);
2811       return (NULL);
2812     }
2813     normalVals = vertexVals + 3 * num_total_indexes;
2814     nxtn = 3;
2815     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
2816       normalValsC = (uchar*) normalVals;
2817       nxtn = 1;
2818     }
2819     colorVals = normalVals + nxtn * num_total_indexes;
2820     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
2821       colorValsUC = (uchar*) colorVals;
2822       nxtn = 1;
2823     } else {
2824       nxtn = 4;
2825     }
2826     pickColorVals = (colorVals + nxtn * num_total_indexes);
2827     nxtn = 3;
2828     accessibilityVals = pickColorVals + nxtn * num_total_indexes;
2829 
2830     bool has_normals = false, has_colors = false, has_accessibility = false;
2831     ok = CGOProcessCGOtoArrays(I, cgo, cgo, min, max, &ambient_occlusion,
2832         vertexVals, normalVals, normalValsC, colorVals, colorValsUC,
2833         pickColorVals, accessibilityVals, has_normals, has_colors,
2834         has_accessibility);
2835     if (!ok){
2836       if (!I->G->Interrupt)
2837 	PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOProcessCGOtoArrays() could not allocate enough memory\n" ENDFB(I->G);
2838       FreeP(vertexVals);
2839       CGOFree(cgo);
2840       return (NULL);
2841     }
2842     if (ok){
2843       short nsz = VERTEX_NORMAL_SIZE * 4;
2844       GLenum ntp = GL_FLOAT;
2845       bool nnorm = GL_FALSE;
2846       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
2847         nsz = VERTEX_NORMAL_SIZE;
2848         ntp = GL_BYTE;
2849         nnorm = GL_TRUE;
2850       }
2851 
2852       short csz = 4;
2853       GLenum ctp = GL_FLOAT;
2854       bool cnorm = GL_FALSE;
2855       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
2856         csz = 1;
2857         ctp = GL_UNSIGNED_BYTE;
2858         cnorm = GL_TRUE;
2859       }
2860 
2861       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL);
2862       BufferDataDesc bufData =
2863         { { "a_Vertex", GL_FLOAT, 3, sizeof(float) * num_total_indexes * 3, vertexVals, GL_FALSE } };
2864       if (has_normals){
2865         bufData.push_back( { "a_Normal", ntp,      VERTEX_NORMAL_SIZE, (size_t)(num_total_indexes * nsz), normalVals, nnorm } );
2866       }
2867       if (has_colors){
2868         bufData.push_back( { "a_Color",  ctp,      4, sizeof(float) * num_total_indexes * csz, colorVals, cnorm } );
2869       }
2870       if (has_accessibility){
2871         bufData.push_back( { "a_Accessibility", GL_FLOAT, 1, sizeof(float) * num_total_indexes, accessibilityVals, GL_FALSE } );
2872       }
2873       ok = vbo->bufferData(std::move(bufData));
2874 
2875       size_t vboid = vbo->get_hash_id();
2876       // picking VBO: generate a buffer twice the size needed, for each picking pass
2877       VertexBuffer * pickvbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL, GL_DYNAMIC_DRAW);
2878       ok = pickvbo->bufferData({
2879           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes, 0, GL_TRUE ),
2880           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes, 0, GL_TRUE )
2881         });
2882       size_t pickvboid = pickvbo->get_hash_id();
2883 
2884       if (ok){
2885 	float *newPickColorVals ;
2886 	int arrays = CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY | CGO_COLOR_ARRAY | CGO_PICK_COLOR_ARRAY;
2887 	if (ambient_occlusion){
2888 	  arrays |= CGO_ACCESSIBILITY_ARRAY;
2889 	}
2890 	if (addshaders)
2891 	  CGOEnable(cgo, GL_DEFAULT_SHADER_WITH_SETTINGS);
2892 	newPickColorVals = cgo->add<cgo::draw::buffers_not_indexed>(GL_TRIANGLES, arrays, num_total_indexes, vboid, pickvboid);
2893 	if (ok && addshaders)
2894 	  ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
2895 	CHECKOK(ok, newPickColorVals);
2896 	if (!newPickColorVals){
2897 	  I->G->ShaderMgr->freeGPUBuffer(pickvboid);
2898           I->G->ShaderMgr->freeGPUBuffer(vboid);
2899 	}
2900 	if (!ok){
2901 	  PRINTFB(I->G, FB_CGO, FB_Errors) "CGOOptimizeToVBONotIndexedWithReturnedData: ERROR: CGODrawBuffersNotIndexed() could not allocate enough memory\n" ENDFB(I->G);
2902 	  FreeP(vertexVals);
2903 	  CGOFree(cgo);
2904 	  return (NULL);
2905 	}
2906 	memcpy(newPickColorVals + num_total_indexes, pickColorVals, num_total_indexes * 2 * sizeof(float));
2907 	has_draw_buffer = true;
2908       } else {
2909         I->G->ShaderMgr->freeGPUBuffer(vboid);
2910         I->G->ShaderMgr->freeGPUBuffer(pickvboid);
2911       }
2912     }
2913     if (ok && returnedData){
2914       returnedData[0] = vertexVals;
2915     } else {
2916       FreeP(vertexVals);
2917     }
2918   }
2919   if (ok && num_total_indexes_lines>0){
2920     bool has_color = false, has_normals = false;
2921     float *vertexVals = 0, *colorVals = 0, *normalVals;
2922     float *pickColorVals;
2923     int pl = 0, plc = 0, idxpl = 0, vpl = 0, tot, nxtn;
2924     uchar *colorValsUC = 0;
2925     uchar *normalValsC = 0;
2926 
2927     cgo->alpha = 1.f;
2928     cgo->color[0] = 1.f; cgo->color[1] = 1.f; cgo->color[2] = 1.f;
2929 
2930     tot = num_total_indexes_lines * (3 * 5) ;
2931     //    tot = num_total_indexes * (3 * 3 + 2) ;
2932     /* NOTE/TODO: Not sure why 3*5 needs to be used, but 3*3+2, which is the
2933        correct length, crashes in glBufferData */
2934     vertexVals = pymol::malloc<float>(tot);
2935     if (!vertexVals){
2936       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBONotIndexed() vertexVals could not be allocated\n" ENDFB(I->G);
2937       CGOFree(cgo);
2938       return (NULL);
2939     }
2940     normalVals = vertexVals + 3 * num_total_indexes_lines;
2941     nxtn = 3;
2942     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
2943       normalValsC = (uchar*) normalVals;
2944       nxtn = 1;
2945     }
2946 
2947     colorVals = normalVals + nxtn * num_total_indexes_lines;
2948     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
2949       colorValsUC = (uchar*) colorVals;
2950       nxtn = 1;
2951     } else {
2952       nxtn = 4;
2953     }
2954     pickColorVals = (colorVals + nxtn * num_total_indexes_lines);
2955 
2956     for (auto it = I->begin(); !it.is_stop(); ++it) {
2957       auto pc = it.data();
2958       const auto op = it.op_code();
2959 
2960       switch (op) {
2961       case CGO_SPECIAL:
2962       case CGO_RESET_NORMAL:
2963 	{
2964 	  const float *newpc = pc;
2965           cgo->add_to_cgo(op, newpc);
2966 	}
2967 	break;
2968       case CGO_NORMAL:
2969         has_normals = true;
2970 	cgo->normal[0] = *pc; cgo->normal[1] = *(pc + 1); cgo->normal[2] = *(pc + 2);
2971 	break;
2972       case CGO_COLOR:
2973         has_color = true;
2974 	cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
2975 	break;
2976       case CGO_ACCESSIBILITY:
2977 	cgo->current_accessibility = pc[0];
2978 	break;
2979       case CGO_ALPHA:
2980 	cgo->alpha = *pc;
2981 	break;
2982       case CGO_PICK_COLOR:
2983 	cgo->current_pick_color_index = CGO_get_uint(pc);
2984 	cgo->current_pick_color_bond = CGO_get_int(pc + 1);
2985 	break;
2986       case CGO_DRAW_ARRAYS:
2987       {
2988         auto sp = it.cast<cgo::draw::arrays>();
2989         short shouldCompress = false;
2990 	switch(sp->mode){
2991 	case GL_LINE_LOOP:
2992 	case GL_LINE_STRIP:
2993 	case GL_LINES:
2994 	  shouldCompress = true;
2995 	default:
2996 	  break;
2997 	}
2998 
2999 	if (shouldCompress){
3000 	  int cnt, nxtn = 3, incr = 0;
3001 	  float *vertexValsDA = NULL, *nxtVals = NULL, *colorValsDA = NULL, *normalValsDA = NULL;
3002 	  float *pickColorValsDA = NULL, *pickColorValsTMP;
3003 
3004 	  nxtVals = vertexValsDA = sp->floatdata;
3005 
3006 	  for (cnt=0; cnt<sp->nverts*3; cnt+=3){
3007 	    set_min_max(min, max, &vertexValsDA[cnt]);
3008 	  }
3009 	  if (sp->arraybits & CGO_NORMAL_ARRAY){
3010             has_normals = true;
3011 	    nxtVals = normalValsDA = vertexValsDA + (nxtn*sp->nverts);
3012 	  }
3013 
3014 	  if (sp->arraybits & CGO_COLOR_ARRAY){
3015             has_color = true;
3016 	    nxtVals = colorValsDA = nxtVals + (nxtn*sp->nverts);
3017 	    nxtn = 4;
3018 	  }
3019 	  if (sp->arraybits & CGO_PICK_COLOR_ARRAY){
3020 	    nxtVals = nxtVals + (nxtn*sp->nverts);
3021 	    pickColorValsDA = nxtVals + sp->nverts;
3022 	    nxtn = 3;
3023 	  }
3024 	  pickColorValsTMP = pickColorVals + (idxpl * 2);
3025 	  switch (sp->mode){
3026 	  case GL_LINES:
3027 	    for (cnt = 0; cnt < sp->nverts; cnt++){
3028 	      SetVertexValuesForVBO(I->G, cgo, pl, plc, cnt, incr++,
3029 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
3030 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
3031 				    pickColorValsTMP);
3032               if (incr && (incr % 2) == 0){
3033                 FixPickColorsForLine(pickColorValsTMP + (incr-2) * 2,
3034                                      pickColorValsTMP + (incr-1) * 2);
3035               }
3036 	      idxpl++; pl += 3; plc += 4;
3037 	    }
3038 	    break;
3039 	  case GL_LINE_STRIP:
3040 	    for (cnt = 1; cnt < sp->nverts; cnt++){
3041 	      SetVertexValuesForVBO(I->G, cgo, pl, plc, cnt-1, incr++,
3042 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
3043 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
3044 				    pickColorValsTMP);
3045 	      idxpl++; pl += 3; plc += 4;
3046 	      SetVertexValuesForVBO(I->G, cgo, pl, plc, cnt, incr++,
3047 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
3048 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
3049 				    pickColorValsTMP);
3050               FixPickColorsForLine(pickColorValsTMP + (incr-2) * 2,
3051                                    pickColorValsTMP + (incr-1) * 2);
3052 	      idxpl++; pl += 3; plc += 4;
3053 	    }
3054 	    break;
3055 	  case GL_LINE_LOOP:
3056 	    for (cnt = 1; cnt < sp->nverts; cnt++){
3057 	      SetVertexValuesForVBO(I->G, cgo, pl, plc, cnt-1, incr++,
3058 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
3059 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
3060 				    pickColorValsTMP);
3061 	      idxpl++; pl += 3; plc += 4;
3062 	      SetVertexValuesForVBO(I->G, cgo, pl, plc, cnt, incr++,
3063 				    vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
3064 				    vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
3065 				    pickColorValsTMP);
3066               FixPickColorsForLine(pickColorValsTMP + (incr-2) * 2,
3067                                    pickColorValsTMP + (incr-1) * 2);
3068 	      idxpl++; pl += 3; plc += 4;
3069 	    }
3070 	    SetVertexValuesForVBO(I->G, cgo, pl, plc, 0, incr++,
3071 				  vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
3072 				  vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
3073 				  pickColorValsTMP);
3074 	    idxpl++; pl += 3; plc += 4;
3075 	    SetVertexValuesForVBO(I->G, cgo, pl, plc, sp->nverts-1, incr++,
3076 				  vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
3077 				  vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
3078 				  pickColorValsTMP);
3079             FixPickColorsForLine(pickColorValsTMP + (incr-2) * 2,
3080                                  pickColorValsTMP + (incr-1) * 2);
3081 	    idxpl++; pl += 3; plc += 4;
3082 	    break;
3083 	  }
3084 
3085 	  //	  pl += 3 * nverts;
3086 	  //	  plc += 4 * nverts;
3087 	  vpl += sp->nverts;
3088 	}
3089       }
3090 	break;
3091       }
3092     }
3093     {
3094       short nsz = VERTEX_NORMAL_SIZE * 4;
3095       GLenum ntp = GL_FLOAT;
3096       bool nnorm = GL_FALSE;
3097       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
3098         nsz = VERTEX_NORMAL_SIZE;
3099         ntp = GL_BYTE;
3100         nnorm = GL_TRUE;
3101       }
3102 
3103       short csz = 4;
3104       GLenum ctp = GL_FLOAT;
3105       bool cnorm = GL_FALSE;
3106       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
3107         csz = 1;
3108         ctp = GL_UNSIGNED_BYTE;
3109         cnorm = GL_TRUE;
3110       }
3111 
3112       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL);
3113       BufferDataDesc bufData =
3114         { { "a_Vertex", GL_FLOAT, 3, sizeof(float) * num_total_indexes_lines * 3, vertexVals, GL_FALSE } };
3115 
3116       if (has_normals){
3117         bufData.push_back( { "a_Normal", ntp,      VERTEX_NORMAL_SIZE, (size_t)(num_total_indexes_lines * nsz), normalVals, nnorm } );
3118       }
3119       if (has_color){
3120         bufData.push_back( { "a_Color",  ctp,      4, sizeof(float) * num_total_indexes_lines * csz, colorVals, cnorm } );
3121       }
3122       ok = vbo->bufferData(std::move(bufData));
3123       size_t vboid = vbo->get_hash_id();
3124 
3125       // picking VBO: generate a buffer twice the size needed, for each picking pass
3126       VertexBuffer * pickvbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL, GL_DYNAMIC_DRAW);
3127       ok &= pickvbo->bufferData({
3128           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes_lines, 0, GL_TRUE ),
3129           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes_lines, 0, GL_TRUE )
3130         });
3131       size_t pickvboid = pickvbo->get_hash_id();
3132 
3133       if (ok){
3134 	float *newPickColorVals ;
3135 	if (addshaders)
3136 	  CGOEnable(cgo, GL_DEFAULT_SHADER_WITH_SETTINGS);
3137 	CGODisable(cgo, GL_SHADER_LIGHTING);
3138 	newPickColorVals = cgo->add<cgo::draw::buffers_not_indexed>(GL_LINES, CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY | CGO_COLOR_ARRAY | CGO_PICK_COLOR_ARRAY, num_total_indexes_lines, vboid, pickvboid);
3139 	if (ok && addshaders)
3140 	  ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
3141 	CHECKOK(ok, newPickColorVals);
3142 	if (!ok){
3143 	  PRINTFB(I->G, FB_CGO, FB_Errors) "CGOOptimizeToVBONotIndexedWithReturnedData: ERROR: CGODrawBuffersNotIndexed() could not allocate enough memory\n" ENDFB(I->G);
3144 	  FreeP(vertexVals);
3145 	  CGOFree(cgo);
3146 	  if (!newPickColorVals) {
3147 	    I->G->ShaderMgr->freeGPUBuffer(pickvboid);
3148             I->G->ShaderMgr->freeGPUBuffer(vboid);
3149           }
3150 	  return (NULL);
3151 	}
3152 	memcpy(newPickColorVals + num_total_indexes_lines, pickColorVals, num_total_indexes_lines * 2 * sizeof(float));
3153 	has_draw_buffer = true;
3154       } else {
3155         I->G->ShaderMgr->freeGPUBuffer(pickvboid);
3156         I->G->ShaderMgr->freeGPUBuffer(vboid);
3157       }
3158     }
3159     if (ok && returnedData){
3160       returnedData[1] = vertexVals;
3161     } else {
3162       FreeP(vertexVals);
3163     }
3164   }
3165 
3166   if (ok && (num_total_vertices>0 || num_total_vertices_lines>0 || num_total_vertices_points>0)){
3167     ok &= CGOBoundingBox(cgo, min, max);
3168   }
3169 
3170   if (ok)
3171     ok &= CGOStop(cgo);
3172   if (has_draw_buffer){
3173     cgo->has_draw_buffers = true;
3174   }
3175   cgo->use_shader = I->use_shader;
3176   if (cgo->use_shader){
3177     cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
3178     cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
3179   }
3180   if (!ok){
3181     CGOFree(cgo);
3182   }
3183   return (cgo);
3184 }
3185 
CGOOptimizeToVBOIndexed(CGO * I,int est,const float * color,bool addshaders,bool embedTransparencyInfo)3186 CGO *CGOOptimizeToVBOIndexed(CGO * I, int est,
3187     const float *color, bool addshaders, bool embedTransparencyInfo)
3188 {
3189   CGO *cgo;
3190 
3191   int num_total_vertices = 0, num_total_indexes = 0, num_total_vertices_lines = 0, num_total_indexes_lines = 0,
3192     num_total_vertices_points = 0;
3193   short has_draw_buffer = false;
3194   float min[3] = { FLT_MAX, FLT_MAX, FLT_MAX }, max[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
3195   int ok = true;
3196 
3197   CGOCountNumVertices(I, &num_total_vertices, &num_total_indexes,
3198 		      &num_total_vertices_lines, &num_total_indexes_lines,
3199 		      &num_total_vertices_points);
3200 
3201   cgo = CGONewSized(I->G, I->c + est);
3202   CHECKOK(ok, cgo);
3203   if (ok){
3204     if (color){
3205       cgo->color[0] = color[0]; cgo->color[1] = color[1]; cgo->color[2] = color[2];
3206       cgo->alpha = color[3];
3207     } else {
3208       cgo->color[0] = 1.f; cgo->color[1] = 1.f; cgo->color[2] = 1.f;
3209       cgo->alpha = 1.f;
3210     }
3211   }
3212 
3213 #if defined(_PYMOL_IOS) && !defined(_WEBGL)
3214   if (num_total_indexes > MAX_INDICES_FOR_IOS || num_total_indexes_lines > MAX_INDICES_FOR_IOS){
3215     PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBOIndexed() VBO Memory Limitation: The requested \n       operation requires a larger buffer than PyMOL currently allows. \n       The operation has not entirely completed successfully.\n" ENDFB(I->G);
3216     firePyMOLLimitationWarning();
3217     CGOFree(cgo);
3218     return NULL;
3219   }
3220 #endif
3221 
3222   if (num_total_vertices_points>0){
3223     /* This does not need to be indexed (for now) */
3224     if (!OptimizePointsToVBO(I, cgo, num_total_vertices_points, min, max, &has_draw_buffer, addshaders)){
3225       CGOFree(cgo);
3226       return NULL;
3227     }
3228   }
3229 
3230   if (num_total_vertices>0){
3231     float *vertexVals = 0, *colorVals = 0, *normalVals, *accessibilityVals = 0;
3232     float *pickColorVals;
3233     GL_C_INT_TYPE *vertexIndices;
3234     short vertexIndicesAllocated = 0;
3235     int pl = 0, plc = 0, idxpl = 0, vpl = 0, tot, nxtn;
3236     uchar *colorValsUC = 0;
3237     uchar *normalValsC = 0;
3238     short ambient_occlusion = 0;
3239     float *sumarray = NULL;
3240     int n_data = 0;
3241 
3242     if (embedTransparencyInfo){
3243       int n_tri = num_total_indexes / 3;
3244       int bytes_to_allocate = 2 * num_total_indexes * sizeof(GL_C_INT_TYPE) + // vertexIndicesOriginal, vertexIndices
3245 	3 * num_total_indexes * sizeof(float) +  // 3 * for sum
3246 	n_tri * sizeof(float) + 2 * n_tri * sizeof(int) + 256 * sizeof(int);    // z_value (float * n_tri), ix (n_tri * int), sort_mem ((n_tri + 256) * int)
3247       // round to 4 byte words for the length of the CGO
3248       n_data = bytes_to_allocate / 4 + (((bytes_to_allocate % 4) == 0) ? 0 : 1) ;
3249     }
3250     vertexIndices = pymol::calloc<GL_C_INT_TYPE>(num_total_indexes);
3251     vertexIndicesAllocated = 1;
3252     if (!vertexIndices){
3253       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBOIndexed() vertexIndices could not be allocated\n" ENDFB(I->G);
3254       CGOFree(cgo);
3255       return (NULL);
3256     }
3257     tot = num_total_vertices * (3 * 6) ;
3258     //    tot = num_total_vertices * (3 * 3 + 2) ;
3259     /* NOTE/TODO: Not sure why 3*5 needs to be used, but 3*3+2, which is the
3260        correct length, crashes in glBufferData */
3261     vertexVals = pymol::malloc<float>(tot);
3262     if (!vertexVals){
3263       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBOIndexed() vertexVals could not be allocated\n" ENDFB(I->G);
3264       CGOFree(cgo);
3265       return (NULL);
3266     }
3267     normalVals = vertexVals + 3 * num_total_vertices;
3268     nxtn = 3;
3269     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
3270       normalValsC = (uchar*) normalVals;
3271       nxtn = 1;
3272     }
3273 
3274     colorVals = normalVals + nxtn * num_total_vertices;
3275     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
3276       colorValsUC = (uchar*) colorVals;
3277       nxtn = 1;
3278     } else {
3279       nxtn = 4;
3280     }
3281     pickColorVals = (colorVals + nxtn * num_total_vertices);
3282     accessibilityVals = pickColorVals + 3 * num_total_vertices;
3283 
3284     for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
3285       const auto pc = it.data();
3286       const auto op = it.op_code();
3287 
3288       switch (op) {
3289       case CGO_NORMAL:
3290 	cgo->normal[0] = *pc; cgo->normal[1] = *(pc + 1); cgo->normal[2] = *(pc + 2);
3291 	break;
3292       case CGO_COLOR:
3293 	cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
3294 	break;
3295       case CGO_ALPHA:
3296 	cgo->alpha = *pc;
3297  	break;
3298       case CGO_ACCESSIBILITY:
3299 	cgo->current_accessibility = *pc;
3300 	break;
3301       case CGO_PICK_COLOR:
3302 	cgo->current_pick_color_index = CGO_get_uint(pc);
3303 	cgo->current_pick_color_bond = CGO_get_int(pc + 1);
3304 	break;
3305       case CGO_DRAW_ARRAYS:
3306 	{
3307         auto sp = it.cast<cgo::draw::arrays>();
3308 	short shouldCompress = false;
3309 	switch(sp->mode){
3310 	case GL_TRIANGLE_FAN:
3311 	case GL_TRIANGLE_STRIP:
3312 	case GL_TRIANGLES:
3313 	  shouldCompress = true;
3314 	default:
3315 	  break;
3316 	}
3317 	if (shouldCompress){
3318 	  int cnt, nxtn = 3;
3319 	  float *vertexValsDA = 0, *nxtVals = 0, *colorValsDA = 0, *normalValsDA, *accessibilityValsDA;
3320 	  float *pickColorValsDA, *pickColorValsTMP, *accessibilityValsTMP;
3321 
3322 	  nxtVals = vertexValsDA = sp->floatdata;
3323 	  for (cnt=0; cnt<sp->nverts*3; cnt+=3){
3324 	    set_min_max(min, max, &vertexValsDA[cnt]);
3325 	  }
3326 	  for (cnt=0; cnt<sp->nverts*3; cnt++){
3327 	    vertexVals[pl + cnt] = vertexValsDA[cnt];
3328 	  }
3329 	  if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
3330 	    if (sp->arraybits & CGO_NORMAL_ARRAY){
3331 	      nxtVals = normalValsDA = vertexValsDA + (nxtn*sp->nverts);
3332 	      for (cnt=0; cnt<sp->nverts*3; cnt++){
3333 		normalValsC[VAR_FOR_NORMAL + cnt VAR_FOR_NORMAL_CNT_PLUS] = CLIP_NORMAL_VALUE(normalValsDA[cnt]);
3334 	      }
3335 	    } else {
3336 	      uchar norm[3] = { CLIP_NORMAL_VALUE(cgo->normal[0]), CLIP_NORMAL_VALUE(cgo->normal[1]), CLIP_NORMAL_VALUE(cgo->normal[2]) };
3337 	      for (cnt=0; cnt<sp->nverts*3; cnt++){
3338 		normalValsC[VAR_FOR_NORMAL + cnt VAR_FOR_NORMAL_CNT_PLUS] = norm[cnt%3];
3339 	      }
3340 	    }
3341 	  } else {
3342 	    if (sp->arraybits & CGO_NORMAL_ARRAY){
3343 	      nxtVals = normalValsDA = vertexValsDA + (nxtn*sp->nverts);
3344 	      for (cnt=0; cnt<sp->nverts*3; cnt++){
3345 		normalVals[pl + cnt] = normalValsDA[cnt];
3346 	      }
3347 	    } else {
3348 	      for (cnt=0; cnt<sp->nverts*3; cnt++){
3349 		normalVals[pl + cnt] = cgo->normal[cnt%3];
3350 	      }
3351 	    }
3352 	  }
3353 	  nxtn = 3;
3354 	  if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
3355 	    if (sp->arraybits & CGO_COLOR_ARRAY){
3356 	      nxtVals = colorValsDA = nxtVals + (nxtn*sp->nverts);
3357 	      for (cnt=0; cnt<sp->nverts*4; cnt+=4){
3358 		colorValsUC[plc + cnt] = CLIP_COLOR_VALUE(colorValsDA[cnt]);
3359 		colorValsUC[plc + cnt + 1] = CLIP_COLOR_VALUE(colorValsDA[cnt+1]);
3360 		colorValsUC[plc + cnt + 2] = CLIP_COLOR_VALUE(colorValsDA[cnt+2]);
3361 		colorValsUC[plc + cnt + 3] = CLIP_COLOR_VALUE(colorValsDA[cnt+3]);
3362 	      }
3363 	      nxtn = 4;
3364 	    } else {
3365 	      uchar col[4] = { CLIP_COLOR_VALUE(cgo->color[0]), CLIP_COLOR_VALUE(cgo->color[1]), CLIP_COLOR_VALUE(cgo->color[2]), CLIP_COLOR_VALUE(cgo->alpha) };
3366 	      for (cnt=0; cnt<sp->nverts*4; cnt++){
3367 		colorValsUC[plc + cnt] = col[cnt%4];
3368 	      }
3369 	    }
3370 	  } else {
3371 	    if (sp->arraybits & CGO_COLOR_ARRAY){
3372 	      nxtVals = colorValsDA = nxtVals + (nxtn*sp->nverts);
3373 	      for (cnt=0; cnt<sp->nverts*4; cnt+=4){
3374 		colorVals[plc + cnt] = colorValsDA[cnt];
3375 		colorVals[plc + cnt + 1] = colorValsDA[cnt+1];
3376 		colorVals[plc + cnt + 2] = colorValsDA[cnt+2];
3377 		colorVals[plc + cnt + 3] = colorValsDA[cnt+3];
3378 	      }
3379 	      nxtn = 4;
3380 	    } else {
3381 	      float col[4] = { cgo->color[0], cgo->color[1], cgo->color[2], cgo->alpha };
3382 	      for (cnt=0; cnt<sp->nverts*4; cnt++){
3383 		colorVals[plc + cnt] = col[cnt%4];
3384 	      }
3385 	    }
3386 	  }
3387 	  if (sp->arraybits & CGO_PICK_COLOR_ARRAY){
3388 	    nxtVals = nxtVals + (nxtn*sp->nverts);
3389 	    pickColorValsDA = nxtVals + sp->nverts;
3390 	    pickColorValsTMP = pickColorVals + (vpl * 2);
3391 	    for (cnt=0; cnt<sp->nverts; cnt++){
3392 	      CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
3393 	      CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
3394 	    }
3395 	    nxtn = 3;
3396 	  } else {
3397 	    pickColorValsTMP = pickColorVals + (vpl * 2);
3398 	    for (cnt=0; cnt<sp->nverts; cnt++){
3399 	      CGO_put_uint(pickColorValsTMP++, cgo->current_pick_color_index);
3400 	      CGO_put_int(pickColorValsTMP++, cgo->current_pick_color_bond);
3401 	    }
3402 	  }
3403 	  if (sp->arraybits & CGO_ACCESSIBILITY_ARRAY){
3404 	    if (!ambient_occlusion){
3405 	      for (cnt=0; cnt<vpl; cnt++){
3406 		accessibilityVals[cnt] = 1.f;
3407 	      }
3408 	    }
3409 	    ambient_occlusion = 1;
3410 	    nxtVals = nxtVals + (nxtn*sp->nverts);
3411 	    accessibilityValsDA = nxtVals;
3412 	    accessibilityValsTMP = accessibilityVals + vpl;
3413 	    for (cnt=0; cnt<sp->nverts; cnt++){
3414 	      accessibilityValsTMP[cnt] = accessibilityValsDA[cnt];
3415 	    }
3416 	  } else {
3417 	    if (ambient_occlusion){
3418 	      accessibilityValsTMP = accessibilityVals + vpl;
3419 	      for (cnt=0; cnt<sp->nverts; cnt++){
3420 		accessibilityValsTMP[cnt] = 1.f;
3421 	      }
3422 	    }
3423 	  }
3424 	  switch (sp->mode){
3425 	  case GL_TRIANGLES:
3426 	    for (cnt = 0; cnt < sp->nverts; cnt++){
3427 	      vertexIndices[idxpl++] = vpl + cnt;
3428 	    }
3429 	    break;
3430 	  case GL_TRIANGLE_STRIP:
3431 	    {
3432 	      short flip = 0;
3433 	      for (cnt = 2; cnt < sp->nverts; cnt++){
3434 		vertexIndices[idxpl++] = vpl + cnt - (flip ? 0 : 2);
3435 		vertexIndices[idxpl++] = vpl + cnt - 1;
3436 		vertexIndices[idxpl++] = vpl + cnt - (flip ? 2 : 0);
3437 		flip = !flip;
3438 	      }
3439 	    }
3440 	    break;
3441 	  case GL_TRIANGLE_FAN:
3442 	    for (cnt = 2; cnt < sp->nverts; cnt++){
3443 	      vertexIndices[idxpl++] = vpl;
3444 	      vertexIndices[idxpl++] = vpl + cnt - 1;
3445 	      vertexIndices[idxpl++] = vpl + cnt;
3446 	    }
3447 	    break;
3448 	  }
3449 	  pl += 3 * sp->nverts;
3450 	  plc += 4 * sp->nverts;
3451 	  vpl += sp->nverts;
3452 	}
3453 	}
3454 	break;
3455       default:
3456 	break;
3457       }
3458       ok &= !I->G->Interrupt;
3459     }
3460     if (sumarray){
3461       for (idxpl = 0; idxpl < num_total_indexes; idxpl+=3){
3462 	add3f(&vertexVals[3 * vertexIndices[idxpl]], &vertexVals[3 * vertexIndices[idxpl+1]], sumarray);
3463 	add3f(&vertexVals[3 * vertexIndices[idxpl+2]], sumarray, sumarray);
3464 	sumarray += 3;
3465       }
3466     }
3467     if (ok) {
3468       short nsz = VERTEX_NORMAL_SIZE * 4;
3469       GLenum ntp = GL_FLOAT;
3470       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
3471         nsz = VERTEX_NORMAL_SIZE;
3472         ntp = GL_BYTE;
3473       }
3474 
3475       short csz = 4;
3476       GLenum ctp = GL_FLOAT;
3477       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
3478         csz = 1;
3479         ctp = GL_UNSIGNED_BYTE;
3480       }
3481 
3482       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>();
3483       ok &= vbo->bufferData({
3484           BufferDesc( "a_Vertex",        GL_FLOAT, 3, sizeof(float) * num_total_vertices * 3, vertexVals, GL_FALSE ),
3485           BufferDesc( "a_Normal",        ntp,      VERTEX_NORMAL_SIZE, num_total_vertices * nsz, normalVals, GL_FALSE ),
3486           BufferDesc( "a_Color",         ctp,      4, sizeof(float) * num_total_vertices * csz, colorVals, GL_TRUE ),
3487           BufferDesc( "a_Accessibility", GL_FLOAT, 1, sizeof(float) * num_total_vertices, accessibilityVals, GL_FALSE )
3488         });
3489 
3490       IndexBuffer * ibo = I->G->ShaderMgr->newGPUBuffer<IndexBuffer>();
3491       ok &= ibo->bufferData({
3492           BufferDesc( GL_UNSIGNED_INT, sizeof(GL_C_INT_TYPE) * num_total_indexes, vertexIndices )
3493         });
3494 
3495       size_t vboid = vbo->get_hash_id();
3496       size_t iboid = ibo->get_hash_id();
3497 
3498       VertexBuffer * pickvbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL, GL_DYNAMIC_DRAW);
3499       ok &= pickvbo->bufferData({
3500           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes, 0, GL_TRUE ),
3501           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes, 0, GL_TRUE )
3502         });
3503       size_t pickvboid = pickvbo->get_hash_id();
3504 
3505       if (ok) {
3506 	float *newPickColorVals ;
3507 	int arrays = CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY | CGO_COLOR_ARRAY | CGO_PICK_COLOR_ARRAY;
3508 	if (ambient_occlusion){
3509 	  arrays |= CGO_ACCESSIBILITY_ARRAY;
3510 	}
3511 	if (addshaders)
3512 	  CGOEnable(cgo, GL_DEFAULT_SHADER);
3513 	newPickColorVals = cgo->add<cgo::draw::buffers_indexed>(GL_TRIANGLES, arrays, num_total_indexes, num_total_vertices, vboid, iboid, n_data, pickvboid);
3514 	if (embedTransparencyInfo){
3515 	  int n_tri = num_total_indexes/3;
3516 	  float *sumarray;
3517 	  float *sum = sumarray = newPickColorVals + num_total_vertices*3;
3518 	  float *z_value = sum + (num_total_indexes*3);
3519 	  int *ix = (int *)z_value + n_tri;
3520 	  int *sort_mem = ix + n_tri;
3521 	  GL_C_INT_TYPE *vertexIndicesOriginalTI = (GL_C_INT_TYPE *)(sort_mem + n_tri + 256);
3522 
3523 	  for (idxpl = 0; idxpl < num_total_indexes; idxpl+=3){
3524 	    add3f(&vertexVals[3 * vertexIndices[idxpl]], &vertexVals[3 * vertexIndices[idxpl+1]], sumarray);
3525 	    add3f(&vertexVals[3 * vertexIndices[idxpl+2]], sumarray, sumarray);
3526 	    sumarray += 3;
3527 	  }
3528 	  memcpy(vertexIndicesOriginalTI, vertexIndices, sizeof(GL_C_INT_TYPE) * num_total_indexes);
3529 	}
3530 
3531 	if (addshaders && ok)
3532 	  ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
3533 	CHECKOK(ok, newPickColorVals);
3534 	if (!newPickColorVals){
3535           I->G->ShaderMgr->freeGPUBuffer(pickvboid);
3536           I->G->ShaderMgr->freeGPUBuffer(vboid);
3537           I->G->ShaderMgr->freeGPUBuffer(iboid);
3538 	}
3539 	if (ok)
3540 	  memcpy(newPickColorVals + num_total_vertices, pickColorVals, num_total_vertices * 2 * sizeof(float));
3541 	has_draw_buffer = true;
3542       } else {
3543         I->G->ShaderMgr->freeGPUBuffer(pickvboid);
3544         I->G->ShaderMgr->freeGPUBuffer(vboid);
3545         I->G->ShaderMgr->freeGPUBuffer(iboid);
3546       }
3547     }
3548     if (vertexIndicesAllocated)
3549       FreeP(vertexIndices);
3550     FreeP(vertexVals);
3551   }
3552   if (ok && num_total_vertices_lines>0){
3553     float *vertexVals = 0, *colorVals = 0, *normalVals = NULL, *nxtVals;
3554     float *pickColorVals;
3555     GL_C_INT_TYPE *vertexIndexes;
3556     uchar *colorValsUC = 0;
3557     uchar *normalValsC = 0;
3558     int pl = 0, plc = 0, idxpl = 0, vpl = 0, tot, sz;
3559     bool hasNormals = 0;
3560 
3561     hasNormals = !CGOHasAnyLineVerticesWithoutNormals(I);
3562     vertexIndexes = pymol::malloc<GL_C_INT_TYPE>(num_total_indexes_lines);
3563     if (!vertexIndexes){
3564       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBOIndexed() vertexIndexes could not be allocated\n" ENDFB(I->G);
3565       CGOFree(cgo);
3566       return (NULL);
3567     }
3568     tot = num_total_vertices_lines * (3 * 5) ;
3569     //    tot = num_total_vertices * (3 * 3 + 2) ;
3570     /* NOTE/TODO: Not sure why 3*5 needs to be used, but 3*3+2, which is the
3571        correct length, crashes in glBufferData */
3572     vertexVals = pymol::malloc<float>(tot);
3573     if (!vertexVals){
3574       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBOIndexed() vertexVals could not be allocated\n" ENDFB(I->G);
3575       CGOFree(cgo);
3576       return (NULL);
3577     }
3578     nxtVals = vertexVals + 3 * num_total_vertices_lines;
3579     sz = 3;
3580     if (hasNormals){
3581       normalVals = nxtVals;
3582       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
3583 	normalValsC = (uchar*) normalVals;
3584 	sz = 1;
3585       }
3586     }
3587     colorVals = nxtVals + sz * num_total_vertices_lines;
3588     if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
3589       colorValsUC = (uchar*) colorVals;
3590       sz = 1;
3591     } else {
3592       sz = 4;
3593     }
3594     pickColorVals = (colorVals + sz * num_total_vertices_lines);
3595 
3596     for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
3597       const auto pc = it.data();
3598       const auto op = it.op_code();
3599 
3600       switch (op) {
3601       case CGO_NORMAL:
3602 	cgo->normal[0] = *pc; cgo->normal[1] = *(pc + 1); cgo->normal[2] = *(pc + 2);
3603 	break;
3604       case CGO_COLOR:
3605 	cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
3606 	break;
3607       case CGO_ACCESSIBILITY:
3608 	cgo->current_accessibility = *pc;
3609 	break;
3610       case CGO_ALPHA:
3611 	cgo->alpha = *pc;
3612 	break;
3613       case CGO_PICK_COLOR:
3614 	cgo->current_pick_color_index = CGO_get_uint(pc);
3615 	cgo->current_pick_color_bond = CGO_get_int(pc + 1);
3616 	break;
3617       case CGO_DRAW_ARRAYS:
3618 	{
3619         auto sp = it.cast<cgo::draw::arrays>();
3620 	short shouldCompress = false;
3621 	switch(sp->mode){
3622 	case GL_LINE_LOOP:
3623 	case GL_LINE_STRIP:
3624 	case GL_LINES:
3625 	  shouldCompress = true;
3626 	default:
3627 	  break;
3628 	}
3629 	if (shouldCompress){
3630 	  int cnt, nxtn = 3;
3631 	  float *vertexValsDA = 0, *nxtVals2 = 0, *colorValsDA = 0, *normalValsDA = 0;
3632 	  float *pickColorValsDA = 0, *pickColorValsTMP;
3633 
3634 	  nxtVals2 = vertexValsDA = sp->floatdata;
3635 	  for (cnt=0; cnt<sp->nverts*3; cnt+=3){
3636 	    set_min_max(min, max, &vertexValsDA[cnt]);
3637 	  }
3638 	  for (cnt=0; cnt<sp->nverts*3; cnt++){
3639 	    vertexVals[pl + cnt] = vertexValsDA[cnt];
3640 	  }
3641 	  if (normalVals){
3642 	    if (sp->arraybits & CGO_NORMAL_ARRAY){
3643 	      if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
3644 		nxtVals2 = normalValsDA = nxtVals2 + (nxtn*sp->nverts);
3645 		for (cnt=0; cnt<sp->nverts*3; cnt++){
3646 		  normalValsC[VAR_FOR_NORMAL + cnt VAR_FOR_NORMAL_CNT_PLUS] = CLIP_NORMAL_VALUE(normalValsDA[cnt]);
3647 		}
3648 	      } else {
3649 		nxtVals2 = normalValsDA = nxtVals2 + (nxtn*sp->nverts);
3650 		for (cnt=0; cnt<sp->nverts*3; cnt++){
3651 		  normalVals[VAR_FOR_NORMAL + cnt] = normalValsDA[cnt];
3652 		}
3653 	      }
3654 	    }
3655 	  }
3656 	  if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
3657 	    if (sp->arraybits & CGO_COLOR_ARRAY){
3658 	      nxtVals2 = colorValsDA = nxtVals2 + (nxtn*sp->nverts);
3659 	      for (cnt=0; cnt<sp->nverts*4; cnt++){
3660 		colorValsUC[plc + cnt] = CLIP_COLOR_VALUE(colorValsDA[cnt]);
3661 	      }
3662 	      nxtn = 4;
3663 	    } else {
3664 	      uchar col[4] = { CLIP_COLOR_VALUE(cgo->color[0]), CLIP_COLOR_VALUE(cgo->color[1]), CLIP_COLOR_VALUE(cgo->color[2]), CLIP_COLOR_VALUE(cgo->alpha) };
3665 	      for (cnt=0; cnt<sp->nverts*4; cnt++){
3666 		colorValsUC[plc + cnt] = col[cnt%4];
3667 	      }
3668 	    }
3669 	  } else {
3670 	    if (sp->arraybits & CGO_COLOR_ARRAY){
3671 	      nxtVals2 = colorValsDA = nxtVals2 + (nxtn*sp->nverts);
3672 	      for (cnt=0; cnt<sp->nverts*4; cnt++){
3673 		colorVals[plc + cnt] = colorValsDA[cnt];
3674 	      }
3675 	      nxtn = 4;
3676 	    } else {
3677 	      float col[4] = { cgo->color[0], cgo->color[1], cgo->color[2], cgo->alpha };
3678 	      for (cnt=0; cnt<sp->nverts*4; cnt++){
3679 		colorVals[plc + cnt] = col[cnt%4];
3680 	      }
3681 	    }
3682 	  }
3683 	  if (sp->arraybits & CGO_PICK_COLOR_ARRAY){
3684 	    nxtVals2 = nxtVals2 + (nxtn*sp->nverts);
3685 	    pickColorValsDA = nxtVals2 + sp->nverts;
3686 	    pickColorValsTMP = pickColorVals + (vpl * 2);
3687 	    for (cnt=0; cnt<sp->nverts; cnt++){
3688 	      CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
3689 	      CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
3690 	    }
3691 	    nxtn = 3;
3692 	  } else {
3693 	    pickColorValsTMP = pickColorVals + (vpl * 2);
3694 	    for (cnt=0; cnt<sp->nverts; cnt++){
3695 	      CGO_put_uint(pickColorValsTMP++, cgo->current_pick_color_index);
3696 	      CGO_put_int(pickColorValsTMP++, cgo->current_pick_color_bond);
3697 	    }
3698 	  }
3699 	  if (idxpl + sp->nverts > num_total_indexes_lines){
3700 	    PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeToVBOIndexed() num_total_indexes_lines=%d mode=%d nverts=%d idxpl=%d\n", num_total_indexes_lines, sp->mode, sp->nverts, idxpl ENDFB(I->G);
3701 	  }
3702 	  switch (sp->mode){
3703 	  case GL_LINES:
3704 	    for (cnt = 0; cnt < sp->nverts; cnt++){
3705 	      vertexIndexes[idxpl++] = vpl + cnt;
3706 	    }
3707 	    break;
3708 	  case GL_LINE_STRIP:
3709 	    for (cnt = 1; cnt < sp->nverts; cnt++){
3710 	      vertexIndexes[idxpl++] = vpl + cnt - 1;
3711 	      vertexIndexes[idxpl++] = vpl + cnt;
3712 	    }
3713 	    break;
3714 	  case GL_LINE_LOOP:
3715 	    for (cnt = 1; cnt < sp->nverts; cnt++){
3716 	      vertexIndexes[idxpl++] = vpl + cnt - 1;
3717 	      vertexIndexes[idxpl++] = vpl + cnt;
3718 	    }
3719 	    vertexIndexes[idxpl++] = vpl;
3720 	    vertexIndexes[idxpl++] = vpl + sp->nverts - 1;
3721 	    break;
3722 	  }
3723 
3724 	  pl += 3 * sp->nverts;
3725 	  plc += 4 * sp->nverts;
3726 	  vpl += sp->nverts;
3727 	}
3728 	}
3729 	break;
3730       case CGO_SPECIAL:
3731 	CGOSpecial(cgo, CGO_get_int(pc));
3732       }
3733       ok &= !I->G->Interrupt;
3734     }
3735     if (ok) {
3736       short nsz = VERTEX_NORMAL_SIZE * 4;
3737       GLenum ntp = GL_FLOAT;
3738       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)){
3739         nsz = VERTEX_NORMAL_SIZE;
3740         ntp = GL_BYTE;
3741       }
3742 
3743       short csz = 4;
3744       GLenum ctp = GL_FLOAT;
3745       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
3746         csz = 1;
3747         ctp = GL_UNSIGNED_BYTE;
3748       }
3749 
3750       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>();
3751       ok &= vbo->bufferData({
3752           BufferDesc( "a_Vertex",        GL_FLOAT, 3, sizeof(float) * num_total_vertices_lines * 3, vertexVals, GL_FALSE ),
3753           BufferDesc( "a_Normal",        ntp,      VERTEX_NORMAL_SIZE, num_total_vertices_lines * nsz, normalVals, GL_FALSE ),
3754           BufferDesc( "a_Color",         ctp,      4, sizeof(float) * num_total_vertices_lines * csz, colorVals, GL_TRUE )
3755         });
3756 
3757       IndexBuffer * ibo = I->G->ShaderMgr->newGPUBuffer<IndexBuffer>();
3758       ok &= ibo->bufferData({
3759           BufferDesc( GL_UNSIGNED_INT, sizeof(GL_C_INT_TYPE) * num_total_indexes_lines, vertexIndexes )
3760         });
3761 
3762       size_t vboid = vbo->get_hash_id();
3763       size_t iboid = ibo->get_hash_id();
3764 
3765       VertexBuffer * pickvbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL, GL_DYNAMIC_DRAW);
3766       ok &= pickvbo->bufferData({
3767           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes, 0, GL_TRUE ),
3768           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * num_total_indexes, 0, GL_TRUE )
3769         });
3770       size_t pickvboid = pickvbo->get_hash_id();
3771 
3772       if (ok){
3773 	float *newPickColorVals ;
3774 	if (addshaders){
3775 	  CGOEnable(cgo, GL_DEFAULT_SHADER);
3776 	  CGODisable(cgo, GL_SHADER_LIGHTING);
3777 	}
3778 	newPickColorVals = cgo->add<cgo::draw::buffers_indexed>(GL_LINES,
3779                                                                 CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY |
3780                                                                 CGO_COLOR_ARRAY | CGO_PICK_COLOR_ARRAY,
3781                                                                 num_total_indexes_lines,
3782                                                                 num_total_vertices_lines, vboid, iboid, 0, pickvboid);
3783 	CHECKOK(ok, newPickColorVals);
3784 	if (addshaders && ok)
3785 	  ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
3786 	if (!newPickColorVals) {
3787           I->G->ShaderMgr->freeGPUBuffer(pickvboid);
3788           I->G->ShaderMgr->freeGPUBuffer(vboid);
3789           I->G->ShaderMgr->freeGPUBuffer(iboid);
3790         }
3791 	if (ok)
3792 	  memcpy(newPickColorVals + num_total_vertices_lines,
3793 		 pickColorVals, num_total_vertices_lines * 2 * sizeof(float));
3794 	has_draw_buffer = true;
3795       } else {
3796         I->G->ShaderMgr->freeGPUBuffer(pickvboid);
3797         I->G->ShaderMgr->freeGPUBuffer(vboid);
3798         I->G->ShaderMgr->freeGPUBuffer(iboid);
3799       }
3800     }
3801     FreeP(vertexIndexes);
3802     FreeP(vertexVals);
3803   }
3804   if (ok && (num_total_vertices>0 || num_total_vertices_lines>0)){
3805     ok &= CGOBoundingBox(cgo, min, max);
3806   }
3807 
3808   if (ok)
3809     ok &= CGOStop(cgo);
3810   if (ok){
3811     if (has_draw_buffer){
3812       cgo->has_draw_buffers = true;
3813     }
3814     cgo->use_shader = I->use_shader;
3815     if (cgo->use_shader){
3816       cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
3817       cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
3818     }
3819   }
3820   if (!ok){
3821     CGOFree(cgo);
3822   }
3823   return (cgo);
3824 }
3825 
CGOOptimizeSpheresToVBONonIndexed(const CGO * I,int est,bool addshaders,CGO * leftOverCGO)3826 CGO *CGOOptimizeSpheresToVBONonIndexed(const CGO * I, int est, bool addshaders, CGO *leftOverCGO)
3827 {
3828   CGO *cgo = NULL;
3829 
3830   int rightup_flags[4] = { 0, 1, 3, 2 };
3831   int num_total_spheres = 0;
3832   short has_draw_buffer = false;
3833   float min[3] = { FLT_MAX, FLT_MAX, FLT_MAX }, max[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
3834   int vv, total_vert = 0, total_spheres = 0;
3835   int ok = true;
3836 
3837   num_total_spheres = CGOCountNumberOfOperationsOfType(I, CGO_SPHERE);
3838   if (num_total_spheres>0) {
3839     float *vertVals = 0;
3840     GLubyte *rightUpFlagValsUB = 0;
3841     float *rightUpFlagVals = 0;
3842     GLubyte *colorValsUB = 0;
3843     int tot = VERTICES_PER_SPHERE * 4 * num_total_spheres;
3844     float *org_vertVals = NULL;
3845     GLubyte *org_colorValsUB = NULL;
3846     int *org_pickcolorVals = NULL, *pickcolorVals = NULL;
3847     GLubyte *org_rightUpFlagValsUB = NULL;
3848     float *org_rightUpFlagVals = NULL;
3849     float min_alpha;
3850     short cgo_shader_ub_flags;
3851     bool copyNormalToLeftOver, copyColorToLeftOver, copyPickColorToLeftOver, copyAlphaToLeftOver ;
3852     bool has_picking = CGOHasOperationsOfType(I, CGO_PICK_COLOR);
3853 
3854     cgo = CGONewSized(I->G, I->c + est);
3855 
3856     cgo_shader_ub_flags = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_flags);
3857 
3858     org_vertVals = vertVals = pymol::malloc<float>(tot);
3859     if (!org_vertVals){
3860       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeSpheresToVBONonIndexed() org_vertVals could not be allocated\n" ENDFB(I->G);
3861       CGOFree(cgo);
3862       return (NULL);
3863     }
3864 
3865     org_colorValsUB = colorValsUB = pymol::malloc<GLubyte>(tot);
3866     if (!org_colorValsUB){
3867       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeSpheresToVBONonIndexed() org_colorValsUB could not be allocated\n" ENDFB(I->G);
3868       FreeP(org_vertVals);
3869       CGOFree(cgo);
3870       return (NULL);
3871     }
3872 
3873     if (cgo_shader_ub_flags){
3874       org_rightUpFlagValsUB = rightUpFlagValsUB = pymol::malloc<GLubyte>(VALUES_PER_IMPOSTER_SPACE_COORD * VERTICES_PER_SPHERE * num_total_spheres);
3875       if (!org_rightUpFlagValsUB){
3876 	PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeSpheresToVBONonIndexed() org_rightUpFlagValsUB could not be allocated\n" ENDFB(I->G);
3877 	FreeP(org_colorValsUB);	FreeP(org_vertVals);
3878 	CGOFree(cgo);
3879 	return (NULL);
3880       }
3881     } else {
3882       org_rightUpFlagVals = rightUpFlagVals = pymol::malloc<float>(VALUES_PER_IMPOSTER_SPACE_COORD * VERTICES_PER_SPHERE * num_total_spheres);
3883       if (!org_rightUpFlagVals){
3884 	PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeSpheresToVBONonIndexed() org_rightUpFlagVals could not be allocated\n" ENDFB(I->G);
3885 	FreeP(org_colorValsUB);	FreeP(org_vertVals);
3886 	CGOFree(cgo);
3887 	return (NULL);
3888       }
3889     }
3890     if (has_picking){
3891       // atom/bond info for picking, 2 ints for each sphere
3892       org_pickcolorVals = pickcolorVals = pymol::malloc<int>(num_total_spheres * 2 * 4);
3893     }
3894 
3895     cgo->alpha = 1.f;
3896     min_alpha = 1.f;
3897     copyNormalToLeftOver = copyColorToLeftOver = copyPickColorToLeftOver = copyAlphaToLeftOver = 0;
3898 
3899     for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
3900       const auto op = it.op_code();
3901       const auto pc = it.data();
3902 
3903       switch (op) {
3904       case CGO_NORMAL:
3905         cgo->normal[0] = *pc; cgo->normal[1] = *(pc + 1); cgo->normal[2] = *(pc + 2);
3906 	copyNormalToLeftOver = true;
3907       break;
3908       case CGO_COLOR:
3909         cgo->color[0] = *pc; cgo->color[1] = *(pc + 1); cgo->color[2] = *(pc + 2);
3910 	copyColorToLeftOver = true;
3911       break;
3912       case CGO_ALPHA:
3913         cgo->alpha = *pc;
3914         if (cgo->alpha < min_alpha) min_alpha = cgo->alpha;
3915 	copyAlphaToLeftOver = true;
3916 	break;
3917       case CGO_PICK_COLOR:
3918 	cgo->current_pick_color_index = CGO_get_uint(pc);
3919 	cgo->current_pick_color_bond = CGO_get_int(pc + 1);
3920 	copyPickColorToLeftOver = true;
3921 	break;
3922       case CGO_SPHERE:
3923 	for (vv=0; vv<VERTICES_PER_SPHERE; vv++) { // generate eight vertices of a bounding box for each cylinder
3924 	  vertVals[0] = *(pc);
3925 	  vertVals[1] = *(pc+1);
3926 	  vertVals[2] = *(pc+2);
3927 	  vertVals[3] = *(pc+3);
3928 	  set_min_max(min, max, vertVals);
3929 	  if (cgo_shader_ub_flags){
3930 	    rightUpFlagValsUB[0] = rightup_flags[vv];
3931 	    rightUpFlagValsUB++;
3932 	  } else {
3933 	    rightUpFlagVals[0] = rightup_flags[vv];
3934 	    rightUpFlagVals++;
3935 	  }
3936           colorValsUB[0] = CLIP_COLOR_VALUE(cgo->color[0]);
3937           colorValsUB[1] = CLIP_COLOR_VALUE(cgo->color[1]);
3938           colorValsUB[2] = CLIP_COLOR_VALUE(cgo->color[2]);
3939           colorValsUB[3] = CLIP_COLOR_VALUE(cgo->alpha);
3940           colorValsUB += 4;
3941 	  vertVals += 4;
3942 	  total_vert++;
3943 	}
3944         if (has_picking){
3945           *(pickcolorVals++) = cgo->current_pick_color_index;
3946           *(pickcolorVals++) = cgo->current_pick_color_bond;
3947         }
3948 	total_spheres++;
3949 	break;
3950       case CGO_DRAW_BUFFERS_INDEXED:
3951       case CGO_DRAW_BUFFERS_NOT_INDEXED:
3952 	PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeSpheresToVBONonIndexed() CGO_DRAW_BUFFERS_INDEXED or CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n", op ENDFB(I->G);
3953 	break;
3954       case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
3955 	PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeCylindersToVBO() CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS encountered op=0x%X\n", op ENDFB(I->G);
3956 	break;
3957       case CGO_DRAW_LABELS:
3958 	PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeCylindersToVBO() CGO_DRAW_LABELS encountered op=0x%X\n", op ENDFB(I->G);
3959 	break;
3960       case CGO_DRAW_TEXTURES:
3961 	PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeCylindersToVBO() CGO_DRAW_TEXTURES encountered op=0x%X\n", op ENDFB(I->G);
3962 	break;
3963       case CGO_DRAW_ARRAYS:
3964       default:
3965         if (!leftOverCGO)
3966           break;
3967 	if (copyAlphaToLeftOver){
3968           copyAlphaToLeftOver = false;
3969 	  CGOAlpha(leftOverCGO, cgo->alpha);
3970 	}
3971 	if (copyColorToLeftOver){
3972           copyColorToLeftOver = false;
3973 	  CGOColor(leftOverCGO, cgo->color[0],  cgo->color[1],  cgo->color[2] );
3974 	}
3975 	if (copyNormalToLeftOver){
3976 	  CGONormalv(leftOverCGO, cgo->normal );
3977 	}
3978 	if (copyPickColorToLeftOver){
3979           copyPickColorToLeftOver = false;
3980 	  CGOPickColor(leftOverCGO, cgo->current_pick_color_index, cgo->current_pick_color_bond);
3981 	}
3982         leftOverCGO->add_to_cgo(op, pc);
3983       }
3984 #ifndef _WEBGL
3985       ok &= !I->G->Interrupt;
3986 #endif
3987     }
3988     if (ok && total_spheres > 0) {
3989       GLenum rtp = GL_FLOAT;
3990       short rsz  = sizeof(float);
3991       void * radiusptr = (void *)org_rightUpFlagVals;
3992       if (cgo_shader_ub_flags) {
3993         rtp = GL_UNSIGNED_BYTE;
3994         rsz = sizeof(GLubyte);
3995         radiusptr = (void *)org_rightUpFlagValsUB;
3996       }
3997 
3998       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>();
3999       ok &= vbo->bufferData({
4000           BufferDesc( "a_vertex_radius", GL_FLOAT, 4, sizeof(float) * total_vert * 4, org_vertVals, GL_FALSE ),
4001           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * total_vert, org_colorValsUB, GL_TRUE ),
4002           BufferDesc( "a_rightUpFlags", rtp, VALUES_PER_IMPOSTER_SPACE_COORD, rsz * total_vert * VALUES_PER_IMPOSTER_SPACE_COORD, radiusptr, GL_FALSE )
4003         });
4004       size_t vboid = vbo->get_hash_id();
4005 
4006       VertexBuffer * pickvbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL, GL_DYNAMIC_DRAW);
4007       ok &= pickvbo->bufferData({
4008           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, 0, GL_TRUE ),
4009           BufferDesc( "a_Color", GL_UNSIGNED_BYTE, 4, sizeof(float) * total_vert, GL_TRUE )
4010         }, 0, sizeof(float) * total_vert * 2, 0);
4011       size_t pickvboid = pickvbo->get_hash_id();
4012 
4013       has_draw_buffer = true;
4014 
4015       auto freebuffers = [vboid, pickvboid, I]() {
4016         I->G->ShaderMgr->freeGPUBuffer(vboid);
4017         I->G->ShaderMgr->freeGPUBuffer(pickvboid);
4018       };
4019       if (ok){
4020         int *pickcolor_data;
4021 	if (addshaders)
4022 	  CGOEnable(cgo, GL_SPHERE_SHADER);
4023 	pickcolor_data = (int*)cgo->add<cgo::draw::sphere_buffers>(total_spheres, (cgo_shader_ub_flags ? 3 : 1), vboid, pickvboid); // always cgo_shader_ub_color
4024         CHECKOK(ok, pickcolor_data);
4025         if (ok && has_picking){
4026           memcpy(pickcolor_data, org_pickcolorVals, num_total_spheres * 2 * 4);
4027         }
4028 	if (ok && addshaders)
4029 	  ok &= CGODisable(cgo, GL_SPHERE_SHADER);
4030 	if (!ok){
4031 	  freebuffers();
4032 	}
4033       } else {
4034         freebuffers();
4035       }
4036     }
4037 
4038     FreeP(org_pickcolorVals);
4039     FreeP(org_vertVals);
4040     FreeP(org_colorValsUB);
4041     if (cgo_shader_ub_flags){
4042       FreeP(org_rightUpFlagValsUB);
4043     } else {
4044       FreeP(org_rightUpFlagVals);
4045     }
4046 
4047     if (ok && num_total_spheres>0){
4048       ok &= CGOBoundingBox(cgo, min, max);
4049     }
4050 
4051     if (ok)
4052       ok &= CGOStop(cgo);
4053 
4054     if (ok){
4055       if (has_draw_buffer){
4056 	cgo->has_draw_buffers = true;
4057 	cgo->has_draw_sphere_buffers = true;
4058       }
4059       cgo->use_shader = I->use_shader;
4060       if (cgo->use_shader){
4061 	cgo->cgo_shader_ub_color = true;
4062 	cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
4063       }
4064     }
4065   }
4066   if (!ok){
4067     CGOFree(cgo);
4068   }
4069   return (cgo);
4070 }
4071 
4072 /*
4073  * converts a CGO that has primitives into pure geometry,
4074  *    and converts CGO_BEGIN/CGO_END blocks into CGO_DRAW_ARRAYS
4075  *    operations, similar to what CGOCombineBeginEnd() does.
4076  *
4077  * I:               input CGO
4078  * est:             initial size of the newly allocated CGO array
4079  *                     that is returned by this function
4080  * sphere_quality:  the quality of the spheres generated by this function
4081  *                     (if -1, defaults to cgo_sphere_quality)
4082  * stick_round_nub: if true, a round cap is generated, otherwise, it generates
4083  *                  the old "pointed" caps
4084  */
CGOSimplify(const CGO * I,int est,short sphere_quality,bool stick_round_nub)4085 CGO *CGOSimplify(const CGO * I, int est, short sphere_quality, bool stick_round_nub)
4086 {
4087   auto G = I->G;
4088   int ok = true;
4089   if (sphere_quality < 0){
4090     sphere_quality = SettingGet_i(I->G, NULL, NULL, cSetting_cgo_sphere_quality);
4091   }
4092 
4093   std::unique_ptr<CGO> cgo_managed(CGONew(G, I->c + est));
4094   auto* const cgo = cgo_managed.get();
4095   RETURN_VAL_IF_FAIL(cgo, nullptr);
4096 
4097   for (auto it = I->begin(); !it.is_stop(); ++it) {
4098     const auto op = it.op_code();
4099     const auto pc = it.data();
4100 
4101     switch (op) {
4102     case CGO_COLOR:
4103       copy3f(pc, cgo->color);
4104       CGOColorv(cgo, pc);
4105       break;
4106     case CGO_PICK_COLOR:
4107       CGOPickColor(cgo, CGO_get_uint(pc), CGO_get_int(pc + 1));
4108       break;
4109     case CGO_SHADER_CYLINDER:
4110       {
4111 	float v2[3];
4112         int cap = CGO_get_int(pc + 7);
4113         int fcap = (cap & 1) ? ((cap & cCylShaderCap1RoundBit) ? 2 : 1) : 0;
4114         int bcap = (cap & 2) ? ((cap & cCylShaderCap2RoundBit) ? 2 : 1) : 0;
4115 	add3f(pc, pc + 3, v2);
4116         ok &= CGOSimpleCylinder(cgo, pc, v2, *(pc + 6), 0, 0, cgo->alpha, cgo->alpha, (cap & cCylShaderInterpColor),
4117                                 fcap, bcap, nullptr, stick_round_nub);
4118       }
4119       break;
4120     case CGO_SHADER_CYLINDER_WITH_2ND_COLOR:
4121       {
4122         auto cyl = it.cast<cgo::draw::shadercylinder2ndcolor>();
4123 	float v1[3];
4124         int cap = cyl->cap;
4125         int fcap = (cap & 1) ? ((cap & cCylShaderCap1RoundBit) ? 2 : 1) : 0;
4126         int bcap = (cap & 2) ? ((cap & cCylShaderCap2RoundBit) ? 2 : 1) : 0;
4127         Pickable pickcolor2 = { cyl->pick_color_index, cyl->pick_color_bond };
4128         float color1[3] = { cgo->color[0], cgo->color[1], cgo->color[2] };
4129 	add3f(pc, pc + 3, v1);
4130         float mid[3];
4131         mult3f(cyl->axis, .5f, mid);
4132         add3f(cyl->origin, mid, mid);
4133         float alpha2 = cyl->alpha >= 0.f ? cyl->alpha : cgo->alpha;
4134         if (cap & cCylShaderInterpColor){
4135           ok &= CGOSimpleCylinder(cgo, cyl->origin, v1, cyl->tube_size, color1, cyl->color2, cgo->alpha, alpha2, true, bcap, fcap, &pickcolor2, stick_round_nub);
4136         } else {
4137           ok &= CGOColorv(cgo, color1);
4138           ok &= CGOSimpleCylinder(cgo, cyl->origin, mid, cyl->tube_size, color1, NULL, cgo->alpha, alpha2, false, fcap, 0, nullptr, stick_round_nub);
4139           ok &= CGOColorv(cgo, cyl->color2);
4140           ok &= CGOPickColor(cgo, pickcolor2.index, pickcolor2.bond);
4141           ok &= CGOSimpleCylinder(cgo, mid, v1, cyl->tube_size, cyl->color2, NULL, cgo->alpha, alpha2, false, 0, bcap, nullptr, stick_round_nub);
4142         }
4143       }
4144       break;
4145     case CGO_CYLINDER:
4146       {
4147         auto cyl = it.cast<cgo::draw::cylinder>();
4148         ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true, 1, 1, nullptr, stick_round_nub);
4149       }
4150       break;
4151     case CGO_CONE:
4152       ok &= CGOSimpleCone(cgo, pc, pc + 3, *(pc + 6), *(pc + 7), pc + 8, pc + 11,
4153                     (int) *(pc + 14), (int) *(pc + 15));
4154       break;
4155     case CGO_SAUSAGE:
4156       ok &= CGOSimpleCylinder(cgo, pc, pc + 3, *(pc + 6), pc + 7, pc + 10, cgo->alpha, cgo->alpha, true, 2, 2, nullptr, stick_round_nub);
4157       break;
4158     case CGO_CUSTOM_CYLINDER:
4159       {
4160         auto cyl = it.cast<cgo::draw::custom_cylinder>();
4161         ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true, cyl->cap1, cyl->cap2, nullptr, stick_round_nub);
4162       }
4163       break;
4164     case CGO_CUSTOM_CYLINDER_ALPHA:
4165       {
4166         auto cyl = it.cast<cgo::draw::custom_cylinder_alpha>();
4167         ok &= CGOSimpleCylinder(cgo, *cyl, cyl->color1[3], cyl->color2[3], true, cyl->cap1, cyl->cap2, nullptr, stick_round_nub);
4168       }
4169       break;
4170     case CGO_SPHERE:
4171       ok &= CGOSimpleSphere(cgo, pc, *(pc + 3), sphere_quality);
4172       break;
4173     case CGO_ELLIPSOID:
4174       ok &= CGOSimpleEllipsoid(cgo, pc, *(pc + 3), pc + 4, pc + 7, pc + 10);
4175       break;
4176     case CGO_QUADRIC:
4177       ok &= CGOSimpleQuadric(cgo, pc, *(pc + 3), pc + 4);
4178       break;
4179     case CGO_DRAW_BUFFERS_INDEXED:
4180     case CGO_DRAW_BUFFERS_NOT_INDEXED:
4181     case CGO_DRAW_SPHERE_BUFFERS:
4182     case CGO_DRAW_CYLINDER_BUFFERS:
4183     case CGO_DRAW_LABELS:
4184     case CGO_DRAW_TEXTURES:
4185     case CGO_END:
4186     case CGO_VERTEX:
4187       WARN_UNEXPECTED_OPERATION(G, op);
4188       return nullptr;
4189     case CGO_BEGIN:
4190       {
4191         float firstColor[3], firstAlpha;
4192 	char hasFirstColor = 0, hasFirstAlpha = 0;
4193 	int nverts = 0, damode = CGO_VERTEX_ARRAY, err = 0;
4194 	int mode = it.cast<cgo::draw::begin>()->mode;
4195 
4196         // remember for a second iteration
4197         auto it2 = it;
4198 
4199         for (++it;; ++it) {
4200           if (it.is_stop()) {
4201             WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
4202             return nullptr;
4203           }
4204 
4205           const auto op = it.op_code();
4206           if (op == CGO_END) {
4207             break;
4208           }
4209 
4210           const auto pc = it.data();
4211 
4212 	  switch (op) {
4213 	  case CGO_DRAW_ARRAYS:
4214             WARN_UNEXPECTED_OPERATION(G, op);
4215             return nullptr;
4216 	  case CGO_NORMAL:
4217 	    damode |= CGO_NORMAL_ARRAY;
4218 	    break;
4219 	  case CGO_COLOR:
4220 	    if (!nverts){
4221 	      hasFirstColor = 1;
4222 	      firstColor[0] = pc[0]; firstColor[1] = pc[1]; firstColor[2] = pc[2];
4223 	    } else {
4224 	      hasFirstColor = 0;
4225 	      damode |= CGO_COLOR_ARRAY;
4226 	    }
4227 	    break;
4228 	  case CGO_PICK_COLOR:
4229 	    damode |= CGO_PICK_COLOR_ARRAY;
4230 	    break;
4231 	  case CGO_ACCESSIBILITY:
4232 	    damode |= CGO_ACCESSIBILITY_ARRAY;
4233 	    break;
4234 	  case CGO_VERTEX:
4235 	    nverts++;
4236 	    break;
4237 	  case CGO_ALPHA:
4238 	    cgo->alpha = *pc;
4239 	    if (!nverts){
4240 	      hasFirstAlpha = 1;
4241 	      firstAlpha = cgo->alpha;
4242 	    } else {
4243 	      hasFirstAlpha = 0;
4244 	      damode |= CGO_COLOR_ARRAY;
4245 	    }
4246 	  }
4247 	}
4248 
4249 	if (nverts>0 && !err){
4250 	  int pl = 0, plc = 0, pla = 0;
4251 	  float *vertexVals, *tmp_ptr;
4252 	  float *normalVals = 0, *colorVals = 0, *nxtVals = 0, *pickColorVals = 0, *accessibilityVals = 0;
4253 	  short notHaveValue = 0, nxtn = 3;
4254 	  if (hasFirstAlpha || hasFirstColor){
4255 	    if (hasFirstAlpha){
4256 	      CGOAlpha(cgo, firstAlpha);
4257 	    }
4258 	    if (hasFirstColor){
4259 	      CGOColorv(cgo, firstColor);
4260 	    }
4261 	  }
4262 	  nxtVals = vertexVals = cgo->add<cgo::draw::arrays>(mode, damode, nverts);
4263           RETURN_VAL_IF_FAIL(vertexVals, nullptr);
4264 	  if (damode & CGO_NORMAL_ARRAY){
4265 	    nxtVals = normalVals = vertexVals + (nxtn*nverts);
4266 	  }
4267 	  if (damode & CGO_COLOR_ARRAY){
4268 	    nxtVals = colorVals = nxtVals + (nxtn*nverts);
4269 	    nxtn = 4;
4270 	  }
4271 	  if (damode & CGO_PICK_COLOR_ARRAY){
4272 	    nxtVals = nxtVals + (nxtn*nverts);
4273 	    pickColorVals = nxtVals + nverts;
4274 	    nxtn = 3;
4275 	  }
4276 	  if (damode & CGO_ACCESSIBILITY_ARRAY){
4277 	    nxtVals = nxtVals + (nxtn*nverts);
4278 	    accessibilityVals = nxtVals;
4279 	    nxtn = 1;
4280 	  }
4281 	  notHaveValue = damode;
4282 	  bool skiptoend = false;
4283 
4284           // second iteration
4285           for (++it2; !skiptoend; ++it2) {
4286             const auto op = it2.op_code();
4287             if (op == CGO_END) {
4288               break;
4289             }
4290 
4291             const auto pc = it2.data();
4292 
4293 	    switch (op) {
4294 	    case CGO_NORMAL:
4295 	      normalVals[pl] = pc[0]; normalVals[pl+1] = pc[1]; normalVals[pl+2] = pc[2];
4296 	      notHaveValue &= ~CGO_NORMAL_ARRAY;
4297 	      break;
4298 	    case CGO_COLOR:
4299 	      if (colorVals){
4300 		colorVals[plc] = pc[0]; colorVals[plc+1] = pc[1];
4301 		colorVals[plc+2] = pc[2]; colorVals[plc+3] = cgo->alpha;
4302 		notHaveValue &= ~CGO_COLOR_ARRAY;
4303 	      }
4304 	      break;
4305 	    case CGO_PICK_COLOR:
4306               CGOPickColor(cgo, CGO_get_uint(pc), CGO_get_int(pc + 1));
4307 	      notHaveValue &= ~CGO_PICK_COLOR_ARRAY;
4308 	      break;
4309 	    case CGO_ACCESSIBILITY:
4310 	      cgo->current_accessibility = pc[0];
4311 	      break;
4312 	    case CGO_VERTEX:
4313 	      if (notHaveValue & CGO_NORMAL_ARRAY){
4314 		if (pl){
4315 		  tmp_ptr = &normalVals[pl-3];
4316 		  normalVals[pl] = tmp_ptr[0]; normalVals[pl+1] = tmp_ptr[1]; normalVals[pl+2] = tmp_ptr[2];
4317 		} else {
4318 		  copy3f(cgo->normal, &normalVals[pl]);
4319 		}
4320 	      }
4321 	      if (notHaveValue & CGO_COLOR_ARRAY){
4322 		if (plc){
4323 		  tmp_ptr = &colorVals[plc-4];
4324 		  colorVals[plc] = tmp_ptr[0]; colorVals[plc+1] = tmp_ptr[1];
4325 		  colorVals[plc+2] = tmp_ptr[2];	colorVals[plc+3] = tmp_ptr[3];
4326 		} else {
4327 		  copy3f(cgo->color, &colorVals[plc]);
4328 		  colorVals[plc+3] = cgo->alpha;
4329 		}
4330 	      }
4331 	      if (pickColorVals){
4332 		CGO_put_uint(pickColorVals + pla * 2, cgo->current_pick_color_index);
4333 		CGO_put_int(pickColorVals + pla * 2 + 1, cgo->current_pick_color_bond);
4334 	      }
4335 	      if (accessibilityVals){
4336 		accessibilityVals[pla] = cgo->current_accessibility;
4337 	      }
4338 	      vertexVals[pl++] = pc[0]; vertexVals[pl++] = pc[1]; vertexVals[pl++] = pc[2];
4339 	      plc += 4;
4340 	      pla++;
4341 	      if (pla >= nverts) // anything past the last vertex is ignored
4342 		skiptoend = true;
4343 	      notHaveValue = damode;
4344 	      break;
4345 	    case CGO_ALPHA:
4346 	      // in case we're before CGO_COLOR
4347 	      cgo->alpha = *pc;
4348 	      if (colorVals) {
4349 		// in case we're after CGO_COLOR
4350 		colorVals[plc + 3] = *pc;
4351 	      }
4352 	      break;
4353 	    }
4354 	  }
4355 	}
4356       }
4357       break;
4358     case CGO_ALPHA:
4359       cgo->alpha = *pc;
4360     default:
4361       cgo->add_to_cgo(op, pc);
4362     }
4363 
4364     if (G->Interrupt) {
4365       return nullptr;
4366     }
4367 
4368     RETURN_VAL_IF_FAIL(ok, nullptr);
4369   }
4370 
4371   CGOStop(cgo);
4372   return cgo_managed.release();
4373 }
4374 
4375 /*
4376  * converts a CGO that has primitives into pure geomtry, just like CGOSimplify
4377  *    but without converting the CGO_BEGIN/CGO_END blocks.
4378  *
4379  */
CGOSimplifyNoCompress(const CGO * I,int est,short sphere_quality,bool stick_round_nub)4380 CGO *CGOSimplifyNoCompress(const CGO * I, int est, short sphere_quality, bool stick_round_nub)
4381 {
4382   CGO *cgo;
4383 
4384   int ok = true;
4385   if (sphere_quality < 0){
4386     sphere_quality = SettingGet_i(I->G, NULL, NULL, cSetting_cgo_sphere_quality);
4387   }
4388 
4389   cgo = CGONewSized(I->G, I->c + est);
4390   CHECKOK(ok, cgo);
4391 
4392   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
4393     const auto op = it.op_code();
4394     const auto pc = it.data();
4395 
4396     switch (op) {
4397     case CGO_PICK_COLOR:
4398       CGOPickColor(cgo, CGO_get_uint(pc), CGO_get_int(pc + 1));
4399       break;
4400     case CGO_SHADER_CYLINDER:
4401       {
4402 	float v2[3];
4403         int cap = CGO_get_int(pc + 7);
4404         int fcap = (cap & 1) ? ((cap & cCylShaderCap1RoundBit) ? 2 : 1) : 0;
4405         int bcap = (cap & 2) ? ((cap & cCylShaderCap2RoundBit) ? 2 : 1) : 0;
4406 	add3f(pc, pc + 3, v2);
4407         ok &= CGOSimpleCylinder(cgo, pc, v2, *(pc + 6), 0, 0, cgo->alpha, cgo->alpha, (cap & cCylShaderInterpColor),
4408                                 fcap, bcap, nullptr, stick_round_nub);
4409       }
4410       break;
4411     case CGO_SHADER_CYLINDER_WITH_2ND_COLOR:
4412       {
4413         auto cyl = it.cast<cgo::draw::shadercylinder2ndcolor>();
4414 	float v1[3];
4415         int cap = cyl->cap;
4416         int fcap = (cap & 1) ? ((cap & cCylShaderCap1RoundBit) ? 2 : 1) : 0;
4417         int bcap = (cap & 2) ? ((cap & cCylShaderCap2RoundBit) ? 2 : 1) : 0;
4418         Pickable pickcolor2 = { cyl->pick_color_index, cyl->pick_color_bond };
4419         float color1[3] = { cgo->color[0], cgo->color[1], cgo->color[2] };
4420 	add3f(cyl->origin, cyl->axis, v1);
4421         float mid[3];
4422         mult3f(cyl->axis, .5f, mid);
4423         add3f(cyl->origin, mid, mid);
4424         float alpha2 = cyl->alpha >= 0.f ? cyl->alpha : cgo->alpha;
4425         if (cap & cCylShaderInterpColor){
4426           ok &= CGOSimpleCylinder(cgo, cyl->origin, v1, cyl->tube_size, color1, cyl->color2, cgo->alpha, alpha2, true, bcap, fcap, &pickcolor2, stick_round_nub);
4427         } else {
4428           ok &= CGOColorv(cgo, color1);
4429           ok &= CGOSimpleCylinder(cgo, cyl->origin, mid, cyl->tube_size, color1, NULL, cgo->alpha, alpha2, false, fcap, 0, NULL, stick_round_nub);
4430           ok &= CGOColorv(cgo, cyl->color2);
4431           ok &= CGOPickColor(cgo, pickcolor2.index, pickcolor2.bond);
4432           ok &= CGOSimpleCylinder(cgo, mid, v1, cyl->tube_size, cyl->color2, NULL, cgo->alpha, alpha2, false, 0, bcap, NULL, stick_round_nub);
4433         }
4434       }
4435       break;
4436     case CGO_CYLINDER:
4437       {
4438         auto cyl = it.cast<cgo::draw::cylinder>();
4439         ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true, 1, 1, nullptr, stick_round_nub);
4440       }
4441       break;
4442     case CGO_CONE:
4443       ok &= CGOSimpleCone(cgo, pc, pc + 3, *(pc + 6), *(pc + 7), pc + 8, pc + 11,
4444                     (int) *(pc + 14), (int) *(pc + 15));
4445       break;
4446     case CGO_SAUSAGE:
4447       ok &= CGOSimpleCylinder(cgo, pc, pc + 3, *(pc + 6), pc + 7, pc + 10, cgo->alpha, cgo->alpha, true, 2, 2, nullptr, stick_round_nub);
4448       break;
4449     case CGO_CUSTOM_CYLINDER:
4450       {
4451         auto cyl = it.cast<cgo::draw::custom_cylinder>();
4452         ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true, cyl->cap1, cyl->cap2, nullptr, stick_round_nub);
4453       }
4454       break;
4455     case CGO_CUSTOM_CYLINDER_ALPHA:
4456       {
4457         auto cyl = it.cast<cgo::draw::custom_cylinder_alpha>();
4458         ok &= CGOSimpleCylinder(cgo, *cyl, cyl->color1[3], cyl->color2[3], true, cyl->cap1, cyl->cap2, nullptr, stick_round_nub);
4459       }
4460       break;
4461     case CGO_SPHERE:
4462       ok &= CGOSimpleSphere(cgo, pc, *(pc + 3), sphere_quality);
4463       break;
4464     case CGO_ELLIPSOID:
4465       ok &= CGOSimpleEllipsoid(cgo, pc, *(pc + 3), pc + 4, pc + 7, pc + 10);
4466       break;
4467     case CGO_QUADRIC:
4468       ok &= CGOSimpleQuadric(cgo, pc, *(pc + 3), pc + 4);
4469       break;
4470     case CGO_DRAW_BUFFERS_INDEXED:
4471 	PRINTFB(I->G, FB_CGO, FB_Errors) "CGOSimplifyNoCompress-Error: CGO_DRAW_BUFFERS_INDEXED encountered\n" ENDFB(I->G);
4472       break;
4473     case CGO_DRAW_BUFFERS_NOT_INDEXED:
4474 	PRINTFB(I->G, FB_CGO, FB_Errors) "CGOSimplifyNoCompress-Error: CGO_DRAW_BUFFERS_NOT_INDEXED encountered\n" ENDFB(I->G);
4475       break;
4476     case CGO_DRAW_SPHERE_BUFFERS:
4477         PRINTFB(I->G, FB_CGO, FB_Errors) "CGOSimplifyNoCompress-Error: CGO_DRAW_SPHERE_BUFFERS encountered\n" ENDFB(I->G);
4478       break;
4479     case CGO_DRAW_CYLINDER_BUFFERS:
4480         PRINTFB(I->G, FB_CGO, FB_Errors) "CGOSimplifyNoCompress-Error: CGO_DRAW_CYLINDER_BUFFERS encountered\n" ENDFB(I->G);
4481       break;
4482     case CGO_DRAW_LABELS:
4483 	PRINTFB(I->G, FB_CGO, FB_Errors) "CGOSimplifyNoCompress-Error: CGO_DRAW_LABELS encountered\n" ENDFB(I->G);
4484       break;
4485     case CGO_DRAW_TEXTURES:
4486 	PRINTFB(I->G, FB_CGO, FB_Errors) "CGOSimplifyNoCompress-Error: CGO_DRAW_TEXTURES encountered\n" ENDFB(I->G);
4487       break;
4488     case CGO_BEGIN:
4489       cgo->has_begin_end = true;
4490     default:
4491       cgo->add_to_cgo(op, pc);
4492     }
4493     ok &= !I->G->Interrupt;
4494   }
4495   if (ok){
4496     ok &= CGOStop(cgo);
4497   }
4498   if (!ok){
4499     CGOFree(cgo);
4500   }
4501   return (cgo);
4502 }
4503 
4504 
CGOOptimizeTextures(const CGO * I,int est)4505 CGO *CGOOptimizeTextures(const CGO * I, int est)
4506 {
4507   CGO *cgo = NULL;
4508   int num_total_textures;
4509   int ok = true;
4510   num_total_textures = CGOCountNumberOfOperationsOfType(I, CGO_DRAW_TEXTURE);
4511   //  printf("CGOOptimizeTextures: num_total_textures=%d\n", num_total_textures);
4512   if (num_total_textures){
4513     float *worldPos, *screenValues, *textExtents, *pickColorVals;
4514     int place3 = 0, place2 = 0;
4515     worldPos = pymol::malloc<float>(num_total_textures * 18);
4516     if (!worldPos){
4517       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeTextures() worldPos could not be allocated\n" ENDFB(I->G);
4518       return NULL;
4519     }
4520     screenValues = pymol::malloc<float>(num_total_textures * 18);
4521     if (!screenValues){
4522       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeTextures() screenValues could not be allocated\n" ENDFB(I->G);
4523       FreeP(worldPos);
4524       return NULL;
4525     }
4526     textExtents = pymol::malloc<float>(num_total_textures * 12);
4527     if (!textExtents){
4528       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeTextures() textExtents could not be allocated\n" ENDFB(I->G);
4529       FreeP(screenValues);
4530       FreeP(worldPos);
4531       return NULL;
4532     }
4533     pickColorVals = pymol::malloc<float>(num_total_textures * 12); /* pick index and bond */
4534     if (!pickColorVals){
4535       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeTextures() pickColorVals could not be allocated\n" ENDFB(I->G);
4536       FreeP(textExtents);
4537       FreeP(screenValues);
4538       FreeP(worldPos);
4539       return NULL;
4540     }
4541 
4542     cgo = CGONewSized(I->G, 0);
4543 
4544     for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
4545       const auto op = it.op_code();
4546       const auto pc = it.data();
4547 
4548       switch (op) {
4549       case CGO_PICK_COLOR:
4550 	cgo->current_pick_color_index = CGO_get_uint(pc);
4551 	cgo->current_pick_color_bond = CGO_get_int(pc + 1);
4552 	break;
4553       case CGO_DRAW_BUFFERS_INDEXED:
4554       case CGO_DRAW_BUFFERS_NOT_INDEXED:
4555 	PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeTextures() CGO_DRAW_BUFFERS_INDEXED or CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n", op ENDFB(I->G);
4556 	break;
4557       case CGO_DRAW_TEXTURE:
4558 	{
4559 	  float screenMin[3], screenMax[3], textExtent[4];
4560 	  copy3f(pc, &worldPos[place3]);
4561 	  copy3f(pc, &worldPos[place3+3]);
4562 	  copy3f(pc, &worldPos[place3+6]);
4563 	  copy3f(pc, &worldPos[place3+9]);
4564 	  copy3f(pc, &worldPos[place3+12]);
4565 	  copy3f(pc, &worldPos[place3+15]);
4566 	  copy3f(pc + 3, screenMin);
4567 	  copy3f(pc + 6, screenMax);
4568 	  copy4f(pc + 9, textExtent);
4569 	  copy3f(screenMin, &screenValues[place3]);
4570 	  copy3f(screenMin, &screenValues[place3+3]);
4571 	  copy3f(screenMin, &screenValues[place3+6]);
4572 	  copy3f(screenMin, &screenValues[place3+9]);
4573 	  copy3f(screenMin, &screenValues[place3+12]);
4574 	  copy3f(screenMax, &screenValues[place3+15]);
4575 	  screenValues[place3+4] = screenMax[1];
4576 	  screenValues[place3+6] = screenMax[0];
4577 	  screenValues[place3+10] = screenMax[1];
4578 	  screenValues[place3+12] = screenMax[0];
4579 	  screenValues[place3+17] = screenMin[2];
4580 	  place3 += 18;
4581 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4582 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4583 	  textExtents[place2++] = textExtent[0]; textExtents[place2++] = textExtent[1];
4584 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4585 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4586 	  textExtents[place2++] = textExtent[0]; textExtents[place2++] = textExtent[3];
4587 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4588 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4589 	  textExtents[place2++] = textExtent[2]; textExtents[place2++] = textExtent[1];
4590 	  CGO_put_int(pickColorVals + place2, cgo->current_pick_color_index);
4591 	  CGO_put_uint(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4592 	  textExtents[place2++] = textExtent[0]; textExtents[place2++] = textExtent[3];
4593 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4594 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4595 	  textExtents[place2++] = textExtent[2]; textExtents[place2++] = textExtent[1];
4596 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4597 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4598 	  textExtents[place2++] = textExtent[2]; textExtents[place2++] = textExtent[3];
4599 	}
4600 	break;
4601       }
4602       ok &= !I->G->Interrupt;
4603     }
4604     if (ok) {
4605       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL);
4606       ok &= vbo->bufferData({
4607           BufferDesc( "attr_worldpos", GL_FLOAT, 3, sizeof(float) * num_total_textures * 18, worldPos, GL_FALSE ),
4608           BufferDesc( "attr_screenoffset", GL_FLOAT, 3, sizeof(float) * num_total_textures * 18, screenValues, GL_FALSE ),
4609           BufferDesc( "attr_texcoords", GL_FLOAT, 3, sizeof(float) * num_total_textures * 18, textExtents, GL_FALSE )
4610         });
4611       size_t vboid = vbo->get_hash_id();
4612 
4613       if (ok) {
4614 	float *pickArray = cgo->add<cgo::draw::textures>(num_total_textures, vboid);
4615 	CHECKOK(ok, pickArray);
4616 	if (!pickArray)
4617 	  I->G->ShaderMgr->freeGPUBuffer(vboid);
4618 	if (ok)
4619 	  memcpy(pickArray + num_total_textures * 6, pickColorVals, num_total_textures * 12 * sizeof(float));
4620 	if (ok)
4621 	  ok &= CGOStop(cgo);
4622       } else {
4623 	I->G->ShaderMgr->freeGPUBuffer(vboid);
4624       }
4625       if (!ok){
4626 	CGOFree(cgo);
4627       }
4628     }
4629     FreeP(worldPos);
4630     FreeP(screenValues);
4631     FreeP(textExtents);
4632     FreeP(pickColorVals);
4633   }
4634   return cgo;
4635 }
4636 
CGOConvertToLabelShader(const CGO * I,CGO * addTo)4637 CGO *CGOConvertToLabelShader(const CGO *I, CGO * addTo){
4638   /* Lines that pass in two vertices per line */
4639   PyMOLGlobals *G = I->G;
4640 
4641   AttribDataOp world_pos_op =
4642     { { CGO_DRAW_LABEL,       1, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::label, world_pos),           0 } };
4643   AttribDataOp screen_offset_op =
4644     { { CGO_DRAW_LABEL,       2, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::label, screen_world_offset), 0 } };
4645   AttribDataOp screen_min_op =
4646     { { CGO_DRAW_LABEL,       3, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::label, screen_min),          0 } };
4647   AttribDataOp screen_max_op =
4648     { { CGO_DRAW_LABEL,       4, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::label, screen_max),          0 } };
4649   AttribDataOp text_extent_op =
4650     { { CGO_DRAW_LABEL,       5, FLOAT2_TO_FLOAT2, offsetof(cgo::draw::label, text_extent),         0 } };
4651   AttribDataOp relative_mode_op =
4652     { { CGO_DRAW_LABEL,       6, FLOAT_TO_FLOAT,   offsetof(cgo::draw::label, relative_mode),       0 } };
4653   AttribDataOp target_pos_op =
4654     { { CGO_DRAW_LABEL,       7, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::label, target_pos),          6 } };
4655 
4656   AttribDataDesc attrDesc = { { "attr_worldpos",          GL_FLOAT, 3, GL_FALSE, world_pos_op },
4657                               { "attr_targetpos",         GL_FLOAT, 3, GL_FALSE, target_pos_op },
4658                               { "attr_screenoffset",      GL_FLOAT, 3, GL_FALSE, screen_min_op },
4659                               { "attr_texcoords",         GL_FLOAT, 2, GL_FALSE, text_extent_op },
4660                               { "attr_screenworldoffset", GL_FLOAT, 3, GL_FALSE, screen_offset_op },
4661                               { "attr_relative_mode",     GL_FLOAT, 1, GL_FALSE, relative_mode_op } };
4662 
4663   auto ComputeScreenValues = [](void * varData, const float * pc, void * screenData, int idx) {
4664     auto sp = reinterpret_cast<const cgo::draw::label *>(pc);
4665     const auto& smin = sp->screen_min;
4666     const auto& smax = sp->screen_max;
4667     float * v = reinterpret_cast<float *>(varData);
4668     switch (idx) {
4669     case 0:
4670     v[0] = smin[0]; v[1] = smin[1]; v[2] = smin[2];
4671     break;
4672     case 1:
4673     v[0] = smin[0]; v[1] = smax[1]; v[2] = smin[2];
4674     break;
4675     case 2:
4676     v[0] = smax[0]; v[1] = smin[1]; v[2] = smin[2];
4677     break;
4678     case 3:
4679     v[0] = smin[0]; v[1] = smax[1]; v[2] = smin[2];
4680     break;
4681     case 4:
4682     v[0] = smax[0]; v[1] = smin[1]; v[2] = smin[2];
4683     break;
4684     case 5:
4685     v[0] = smax[0]; v[1] = smax[1]; v[2] = smin[2];
4686     break;
4687     };
4688   };
4689 
4690   auto ComputeTexCoords = [](void * varData, const float * pc, void * discard, int idx) {
4691     auto sp = reinterpret_cast<const cgo::draw::label *>(pc);
4692     float * v = reinterpret_cast<float *>(varData);
4693     const auto& te = sp->text_extent;
4694     static struct { int x, y; } const idxs[6] = {
4695       { 0, 1 },
4696       { 0, 3 },
4697       { 2, 1 },
4698       { 0, 3 },
4699       { 2, 1 },
4700       { 2, 3 }
4701     };
4702     v[0] = te[idxs[idx].x];
4703     v[1] = te[idxs[idx].y];
4704   };
4705 
4706   attrDesc[1].attrOps[0].funcDataConversions.push_back({ ComputeScreenValues, nullptr, "attr_screenoffset" });
4707   attrDesc[1].attrOps[0].funcDataConversions.push_back({ ComputeTexCoords, nullptr, "attr_texcoords" });
4708 
4709   uchar pickdata[4] = { 0, 0, 0, 0 };
4710   addTo->add<cgo::draw::vertex_attribute_4ub>(G->ShaderMgr->GetAttributeUID("attr_pickcolor"), pickdata);
4711 
4712   AttribDataOp pickOp = { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 } };
4713   AttribDataDesc pickDesc = { { "attr_pickcolor", GL_UNSIGNED_BYTE, 4, GL_TRUE, pickOp } };
4714   return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, VertexBuffer::INTERLEAVED, true);
4715 }
4716 
CGOOptimizeLabels(const CGO * I,int est,bool addshaders)4717 CGO *CGOOptimizeLabels(const CGO * I, int est, bool addshaders)
4718 {
4719   CGO *cgo = NULL;
4720   int num_total_labels;
4721   int ok = true;
4722   num_total_labels = CGOCountNumberOfOperationsOfType(I, CGO_DRAW_LABEL);
4723   if (num_total_labels){
4724     float *targetPos, *worldPos, *screenValues, *screenWorldValues, *textExtents, *pickColorVals;
4725     float *relativeMode;
4726     int place3 = 0, place2 = 0, place = 0;
4727     worldPos = pymol::malloc<float>(num_total_labels * 6 * 17);
4728     if (!worldPos){
4729       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeLabels() worldPos could not be allocated\n" ENDFB(I->G);
4730       return NULL;
4731     }
4732     screenValues = worldPos + (num_total_labels * 18);
4733     targetPos = screenValues + (num_total_labels * 18);
4734     screenWorldValues = targetPos + (num_total_labels * 18);
4735     textExtents = screenWorldValues + (num_total_labels * 18);
4736     pickColorVals = textExtents + (num_total_labels * 12); /* pick index and bond */
4737     relativeMode = (float *)(pickColorVals + (num_total_labels * 12));
4738     cgo = CGONewSized(I->G, 0);
4739 
4740     for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
4741       const auto op = it.op_code();
4742       const auto pc = it.data();
4743 
4744       switch (op) {
4745       case CGO_PICK_COLOR:
4746 	cgo->current_pick_color_index = CGO_get_uint(pc);
4747 	cgo->current_pick_color_bond = CGO_get_int(pc + 1);
4748 	break;
4749       case CGO_DRAW_BUFFERS_INDEXED:
4750       case CGO_DRAW_BUFFERS_NOT_INDEXED:
4751 	PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeLabels() CGO_DRAW_BUFFERS_INDEXED or CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n", op ENDFB(I->G);
4752 	break;
4753       case CGO_DRAW_LABEL:
4754 	{
4755 	  float screenWorldOffset[3], screenMin[3], screenMax[3], textExtent[4];
4756 	  copy3f(pc, &worldPos[place3]);
4757 	  copy3f(pc, &worldPos[place3+3]);
4758 	  copy3f(pc, &worldPos[place3+6]);
4759 	  copy3f(pc, &worldPos[place3+9]);
4760 	  copy3f(pc, &worldPos[place3+12]);
4761 	  copy3f(pc, &worldPos[place3+15]);
4762 	  copy3f(pc + 3, screenWorldOffset);
4763 	  copy3f(pc + 6, screenMin);
4764 	  copy3f(pc + 9, screenMax);
4765 	  copy4f(pc + 12, textExtent);
4766 	  copy3f(screenWorldOffset, &screenWorldValues[place3]);
4767 	  copy3f(&screenWorldValues[place3], &screenWorldValues[place3+3]);
4768 	  copy3f(&screenWorldValues[place3], &screenWorldValues[place3+6]);
4769 	  copy3f(&screenWorldValues[place3], &screenWorldValues[place3+9]);
4770 	  copy3f(&screenWorldValues[place3], &screenWorldValues[place3+12]);
4771 	  copy3f(&screenWorldValues[place3], &screenWorldValues[place3+15]);
4772 	  copy3f(screenMin, &screenValues[place3]);
4773 	  copy3f(screenMin, &screenValues[place3+3]);
4774 	  copy3f(screenMin, &screenValues[place3+6]);
4775 	  copy3f(screenMin, &screenValues[place3+9]);
4776 	  copy3f(screenMin, &screenValues[place3+12]);
4777 	  copy3f(screenMax, &screenValues[place3+15]);
4778 	  screenValues[place3+4] = screenMax[1];
4779 	  screenValues[place3+6] = screenMax[0];
4780 	  screenValues[place3+10] = screenMax[1];
4781 	  screenValues[place3+12] = screenMax[0];
4782 	  screenValues[place3+17] = screenMin[2];
4783 	  copy3f(pc + 17, &targetPos[place3]);
4784 	  copy3f(pc + 17, &targetPos[place3+3]);
4785 	  copy3f(pc + 17, &targetPos[place3+6]);
4786 	  copy3f(pc + 17, &targetPos[place3+9]);
4787 	  copy3f(pc + 17, &targetPos[place3+12]);
4788 	  copy3f(pc + 17, &targetPos[place3+15]);
4789 	  place3 += 18;
4790 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4791 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4792 	  textExtents[place2++] = textExtent[0]; textExtents[place2++] = textExtent[1];
4793 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4794 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4795 	  textExtents[place2++] = textExtent[0]; textExtents[place2++] = textExtent[3];
4796 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4797 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4798 	  textExtents[place2++] = textExtent[2]; textExtents[place2++] = textExtent[1];
4799 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4800 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4801 	  textExtents[place2++] = textExtent[0]; textExtents[place2++] = textExtent[3];
4802 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4803 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4804 	  textExtents[place2++] = textExtent[2]; textExtents[place2++] = textExtent[1];
4805 	  CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
4806 	  CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
4807 	  textExtents[place2++] = textExtent[2]; textExtents[place2++] = textExtent[3];
4808 	  {
4809 	    uchar rM = (uchar)*(pc + 16);
4810 	    relativeMode[place++] = rM;
4811 	    relativeMode[place++] = rM;
4812 	    relativeMode[place++] = rM;
4813 	    relativeMode[place++] = rM;
4814 	    relativeMode[place++] = rM;
4815 	    relativeMode[place++] = rM;
4816 	  }
4817 	}
4818 	break;
4819       }
4820       ok &= !I->G->Interrupt;
4821     }
4822     if (ok) {
4823       // Static Vertex Data
4824       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL);
4825       ok &= vbo->bufferData({
4826           BufferDesc( "attr_worldpos", GL_FLOAT, 3, sizeof(float)*18*num_total_labels, worldPos,          GL_FALSE ),
4827           BufferDesc( "attr_targetpos", GL_FLOAT, 3, sizeof(float)*18*num_total_labels, targetPos,         GL_FALSE ),
4828           BufferDesc( "attr_screenoffset", GL_FLOAT, 3, sizeof(float)*18*num_total_labels, screenValues,      GL_FALSE ),
4829           BufferDesc( "attr_texcoords", GL_FLOAT, 2, sizeof(float)*12*num_total_labels, textExtents,       GL_FALSE ),
4830           BufferDesc( "attr_screenworldoffset", GL_FLOAT, 3, sizeof(float)*18*num_total_labels, screenWorldValues, GL_FALSE ),
4831           BufferDesc( "attr_relative_mode", GL_FLOAT, 1, sizeof(float)*6*num_total_labels,  relativeMode,      GL_FALSE )
4832         });
4833       size_t vboid = vbo->get_hash_id();
4834 
4835       VertexBuffer * pickvbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL, GL_DYNAMIC_DRAW);
4836       ok &= pickvbo->bufferData({
4837           BufferDesc( "attr_pickcolor", GL_UNSIGNED_BYTE, VERTEX_COLOR_SIZE, 0, GL_TRUE ),
4838           BufferDesc( "attr_pickcolor", GL_UNSIGNED_BYTE, VERTEX_COLOR_SIZE, sizeof(float) * num_total_labels * 6, GL_TRUE )
4839         }, 0, sizeof(float) * num_total_labels * 12, 0);
4840       size_t pickvboid = pickvbo->get_hash_id();
4841 
4842       auto freebuffers = [vboid, pickvboid, I]() {
4843         I->G->ShaderMgr->freeGPUBuffer(vboid);
4844         I->G->ShaderMgr->freeGPUBuffer(pickvboid);
4845       };
4846 
4847       if (ok) {
4848 	float *pickArray = NULL;
4849 	if (addshaders){
4850 	  CGOEnable(cgo, GL_LABEL_SHADER);
4851 	}
4852 	pickArray = cgo->add<cgo::draw::labels>(num_total_labels, vboid, pickvboid);
4853 	if (addshaders){
4854 	  CGODisable(cgo, GL_LABEL_SHADER);
4855 	}
4856 	CHECKOK(ok, pickArray);
4857 	if (!pickArray) {
4858           freebuffers();
4859         }
4860 	if (ok)
4861 	  memcpy(pickArray + num_total_labels * 6, pickColorVals, num_total_labels * 12 * sizeof(float));
4862 	if (ok)
4863 	  ok &= CGOStop(cgo);
4864       } else {
4865         freebuffers();
4866       }
4867       if (!ok){
4868 	CGOFree(cgo);
4869       }
4870     }
4871     FreeP(worldPos);
4872   }
4873   return cgo;
4874 }
4875 
CGOOptimizeConnectors(const CGO * I,int est)4876 CGO *CGOOptimizeConnectors(const CGO * I, int est)
4877 {
4878   CGO *cgo = NULL;
4879   int num_total_connectors;
4880   int ok = true;
4881   int use_geometry_shaders = SettingGetGlobal_b(I->G, cSetting_use_geometry_shaders);
4882   int factor = (use_geometry_shaders ? 1 : 4);
4883   num_total_connectors = CGOCountNumberOfOperationsOfType(I, CGO_DRAW_CONNECTOR);
4884 
4885   if (num_total_connectors){
4886     float *targetPt3d, *labelCenterPt3d, *indentFactor, *screenWorldOffset, *connectorColor, *textSize;
4887     float *bkgrdColor, *relExtLength, *connectorWidth;
4888     uchar *relativeMode, *drawBkgrd;
4889     uchar *isCenterPt = NULL;
4890     int place3 = 0, place2 = 0, place = 0;
4891     targetPt3d = pymol::calloc<float>(num_total_connectors * 20 * factor); /* too much, relativeMode only needs 1 byte per vertex, instead of 1 float */
4892     if (!targetPt3d){
4893       PRINTFB(I->G, FB_CGO, FB_Errors) "ERROR: CGOOptimizeConnectors() could not be allocated\n" ENDFB(I->G);
4894       return NULL;
4895     }
4896     labelCenterPt3d = targetPt3d + (num_total_connectors*3 * factor);
4897     indentFactor = labelCenterPt3d + (num_total_connectors*3 * factor);
4898     screenWorldOffset = indentFactor + (num_total_connectors*2 * factor);
4899     connectorColor = screenWorldOffset + (num_total_connectors*3 * factor);
4900     textSize = connectorColor + (num_total_connectors*factor);
4901     relativeMode = (uchar *)(textSize + (num_total_connectors*2*factor));
4902     drawBkgrd = (uchar *)(relativeMode + (num_total_connectors*factor));
4903     bkgrdColor = (float*)(drawBkgrd + (num_total_connectors*factor));
4904     relExtLength = (float*)(bkgrdColor + (num_total_connectors*factor));
4905     connectorWidth = (float*)(relExtLength + (num_total_connectors*factor));
4906     if (!use_geometry_shaders)
4907       isCenterPt = (uchar *)(connectorWidth + (num_total_connectors*factor));
4908     else
4909       isCenterPt = nullptr;
4910     cgo = CGONewSized(I->G, 0);
4911 
4912     for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
4913       const auto op = it.op_code();
4914       const auto pc = it.data();
4915 
4916       switch (op) {
4917       case CGO_PICK_COLOR:
4918 	cgo->current_pick_color_index = CGO_get_uint(pc);
4919 	cgo->current_pick_color_bond = CGO_get_int(pc + 1);
4920 	break;
4921       case CGO_DRAW_BUFFERS_INDEXED:
4922       case CGO_DRAW_BUFFERS_NOT_INDEXED:
4923 	PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeConnectors() CGO_DRAW_BUFFERS_INDEXED or CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n", op ENDFB(I->G);
4924 	break;
4925       case CGO_DRAW_CONNECTOR:
4926 	{
4927 	  uchar *uc;
4928 	  int f;
4929 	  if (!use_geometry_shaders){
4930 	      isCenterPt[place] = 0; isCenterPt[place+1] = 2; isCenterPt[place+2] = 2; isCenterPt[place+3] = 1;
4931 	  }
4932 	  copy3f(pc, &targetPt3d[place3]);
4933 	  copy3f(pc + 3, &labelCenterPt3d[place3]);
4934 	  copy2f(pc + 6, &indentFactor[place2]);
4935 	  copy3f(pc + 9, &screenWorldOffset[place3]);
4936 	  copy2f(pc + 12, &textSize[place2]);
4937 	  relativeMode[place] = (uchar)(int)pc[17];
4938 	  drawBkgrd[place] = (uchar)(int)pc[18];
4939 	  uc = (uchar *)&bkgrdColor[place];
4940 	  uc[0] = CLIP_COLOR_VALUE(*(pc+19));
4941 	  uc[1] = CLIP_COLOR_VALUE(*(pc+20));
4942 	  uc[2] = CLIP_COLOR_VALUE(*(pc+21));
4943 	  uc[3] = CLIP_COLOR_VALUE(*(pc+22));
4944 	  uc = (uchar *)&connectorColor[place];
4945 	  uc[0] = CLIP_COLOR_VALUE(*(pc+14));
4946 	  uc[1] = CLIP_COLOR_VALUE(*(pc+15));
4947 	  uc[2] = CLIP_COLOR_VALUE(*(pc+16));
4948 	  uc[3] = 255;
4949 	  relExtLength[place] = *(pc + 8);
4950 	  connectorWidth[place] = *(pc + 23);
4951 	  place3 += 3; place2 += 2; place += 1;
4952 	  for (f=1;f<factor; f++){
4953 	    copy3f(pc, &targetPt3d[place3]);
4954 	    copy3f(pc + 3, &labelCenterPt3d[place3]);
4955 	    copy2f(pc + 6, &indentFactor[place2]);
4956 	    copy3f(pc + 9, &screenWorldOffset[place3]);
4957 	    copy2f(pc + 12, &textSize[place2]);
4958 	    relativeMode[place] = (uchar)(int)pc[17];
4959 	    drawBkgrd[place] = (uchar)(int)pc[18];
4960 	    relExtLength[place] = *(pc + 8);
4961 	    connectorWidth[place] = *(pc + 23);
4962 	    uc = (uchar *)&bkgrdColor[place];
4963 	    uc[0] = uc[-4];
4964 	    uc[1] = uc[-3];
4965 	    uc[2] = uc[-2];
4966 	    uc[3] = uc[-1];
4967 	    uc = (uchar *)&connectorColor[place];
4968 	    uc[0] = uc[-4];
4969 	    uc[1] = uc[-3];
4970 	    uc[2] = uc[-2];
4971 	    uc[3] = uc[-1];
4972 	    place3 += 3; place2 += 2; place += 1;
4973 	  }
4974 	}
4975 	break;
4976       }
4977       ok &= !I->G->Interrupt;
4978     }
4979     if (ok) {
4980       const size_t quant = factor * num_total_connectors;
4981       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>();
4982       ok = vbo->bufferData({
4983           BufferDesc( "a_target_pt3d",       GL_FLOAT, 3, sizeof(float) * 3 * quant,     targetPt3d,        GL_FALSE ),
4984           BufferDesc( "a_center_pt3d",       GL_FLOAT, 3, sizeof(float) * 3 * quant,     labelCenterPt3d,   GL_FALSE ),
4985           BufferDesc( "a_indentFactor",      GL_FLOAT, 2, sizeof(float) * 2 * quant,     indentFactor,      GL_FALSE ),
4986           BufferDesc( "a_screenWorldOffset", GL_FLOAT, 3, sizeof(float) * 3 * quant,     screenWorldOffset, GL_FALSE ),
4987           BufferDesc( "a_textSize",          GL_FLOAT, 2, sizeof(float) * 2 * quant,     textSize,          GL_FALSE ),
4988           BufferDesc( "a_Color",             GL_UNSIGNED_BYTE, 4, sizeof(float) * quant, connectorColor,    GL_TRUE  ),
4989           BufferDesc( "a_relative_mode",     GL_UNSIGNED_BYTE, 1, sizeof(uchar) * quant, relativeMode,      GL_FALSE ),
4990           BufferDesc( "a_draw_flags",        GL_UNSIGNED_BYTE, 1, sizeof(uchar) * quant, drawBkgrd,         GL_FALSE ),
4991           BufferDesc( "a_bkgrd_color",       GL_UNSIGNED_BYTE, 4, sizeof(float) * quant, bkgrdColor,        GL_TRUE  ),
4992           BufferDesc( "a_rel_ext_length",    GL_FLOAT, 1, sizeof(float) * quant,         relExtLength,      GL_FALSE ),
4993           BufferDesc( "a_con_width",         GL_FLOAT, 1, sizeof(float) * quant,         connectorWidth,    GL_FALSE ),
4994           BufferDesc( "a_isCenterPt",        GL_UNSIGNED_BYTE, 1, sizeof(uchar) * quant, isCenterPt,        GL_FALSE )
4995         });
4996       size_t vboid = vbo->get_hash_id();
4997       if (ok) {
4998 	cgo->add<cgo::draw::connectors>(num_total_connectors, vboid);
4999 	if (ok)
5000 	  ok &= CGOStop(cgo);
5001       }
5002       if (!ok){
5003         I->G->ShaderMgr->freeGPUBuffer(vboid);
5004 	CGOFree(cgo);
5005       }
5006     }
5007     FreeP(targetPt3d);
5008   }
5009   {
5010     GLenum err ;
5011     CHECK_GL_ERROR_OK("ERROR: CGOOptimizeConnectors() end returns err=%d\n");
5012   }
5013   return cgo;
5014 }
5015 
5016 
CGOExpandDrawTextures(const CGO * I,int est)5017 CGO *CGOExpandDrawTextures(const CGO * I, int est)
5018 {
5019   CGO *cgo = CGONew(I->G);
5020   int ok = true;
5021 
5022   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
5023     auto pc = it.data();
5024     int op = it.op_code();
5025 
5026     switch (op) {
5027     case CGO_PICK_COLOR:
5028       cgo->current_pick_color_index = CGO_get_uint(pc);
5029       cgo->current_pick_color_bond = CGO_get_int(pc + 1);
5030       break;
5031     case CGO_DRAW_BUFFERS_INDEXED:
5032     case CGO_DRAW_BUFFERS_NOT_INDEXED:
5033       PRINTFB(I->G, FB_CGO, FB_Warnings) "WARNING: CGOOptimizeTextures() CGO_DRAW_BUFFERS_INDEXED or CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n", op ENDFB(I->G);
5034       break;
5035     case CGO_DRAW_TEXTURE:
5036       {
5037 	float screenMin[3], screenMax[3], textExtent[4];
5038 	float alpha = cgo->alpha;
5039 	CGOAlpha(cgo, 0.f);
5040 	CGOColor(cgo, 0.f,0.f,0.f);
5041 	copy3f(pc + 3, screenMin);
5042 	copy3f(pc + 6, screenMax);
5043 	copy4f(pc + 9, textExtent);
5044 	CGOBegin(cgo, GL_TRIANGLES);
5045 	CGOTexCoord2f(cgo, textExtent[0], textExtent[1]);
5046 	CGOVertexv(cgo, screenMin);
5047 	CGOTexCoord2f(cgo, textExtent[0], textExtent[3]);
5048 	CGOVertex(cgo, screenMin[0], screenMax[1], screenMin[2]);
5049 	CGOTexCoord2f(cgo, textExtent[2], textExtent[1]);
5050 	CGOVertex(cgo, screenMax[0], screenMin[1], screenMin[2]);
5051 	CGOTexCoord2f(cgo, textExtent[0], textExtent[3]);
5052 	CGOVertex(cgo, screenMin[0], screenMax[1], screenMin[2]);
5053 	CGOTexCoord2f(cgo, textExtent[2], textExtent[1]);
5054 	CGOVertex(cgo, screenMax[0], screenMin[1], screenMin[2]);
5055 	CGOTexCoord2f(cgo, textExtent[2], textExtent[3]);
5056 	CGOVertex(cgo, screenMax[0], screenMax[1], screenMin[2]);
5057 	CGOEnd(cgo);
5058 	CGOAlpha(cgo, alpha);
5059       }
5060       break;
5061     default:
5062       cgo->add_to_cgo(op, pc);
5063     }
5064     ok &= !I->G->Interrupt;
5065   }
5066   CGOStop(cgo);
5067   return cgo;
5068 }
5069 
5070 /* ======== Raytrace Renderer ======== */
5071 
CGOGetExtent(const CGO * I,float * mn,float * mx)5072 int CGOGetExtent(const CGO * I, float *mn, float *mx)
5073 {
5074   int result = false;
5075 
5076 #define check_extent(v,r) {\
5077     if(!result) {\
5078       mn[0]=((*(v  ))-r); \
5079       mx[0]=((*(v  ))+r);  \
5080       mn[1]=((*(v+1))-r); \
5081       mx[1]=((*(v+1))+r); \
5082       mn[2]=((*(v+2))-r); \
5083       mx[2]=((*(v+2))+r); \
5084       result=true; \
5085   } else {\
5086        if(mn[0]>((*(v    ))-r)) mn[0]=((*(v    ))-r); \
5087        if(mx[0]<((*(v    ))+r)) mx[0]=((*(v    ))+r); \
5088        if(mn[1]>((*((v)+1))-r)) mn[1]=((*((v)+1))-r); \
5089        if(mx[1]<((*((v)+1))+r)) mx[1]=((*((v)+1))+r); \
5090        if(mn[2]>((*((v)+2))-r)) mn[2]=((*((v)+2))-r); \
5091        if(mx[2]<((*((v)+2))+r)) mx[2]=((*((v)+2))+r); }}
5092 
5093 #define check_extent4(v,r) {\
5094     if(!result) {\
5095       mn[0]=((*(v  ))-r); \
5096       mx[0]=((*(v  ))+r);  \
5097       mn[1]=((*(v+1))-r); \
5098       mx[1]=((*(v+1))+r); \
5099       mn[2]=((*(v+2))-r); \
5100       mx[2]=((*(v+2))+r); \
5101       mn[3]=((*(v+3))-r); \
5102       mx[3]=((*(v+3))+r); \
5103       result=true; \
5104   } else {\
5105        if(mn[0]>((*(v    ))-r)) mn[0]=((*(v    ))-r); \
5106        if(mx[0]<((*(v    ))+r)) mx[0]=((*(v    ))+r); \
5107        if(mn[1]>((*((v)+1))-r)) mn[1]=((*((v)+1))-r); \
5108        if(mx[1]<((*((v)+1))+r)) mx[1]=((*((v)+1))+r); \
5109        if(mn[2]>((*((v)+2))-r)) mn[2]=((*((v)+2))-r); \
5110        if(mx[2]<((*((v)+2))+r)) mx[2]=((*((v)+2))+r); \
5111        if(mn[3]>((*((v)+3))-r)) mn[3]=((*((v)+3))-r); \
5112        if(mx[3]<((*((v)+3))+r)) mx[3]=((*((v)+3))+r); }}
5113 
5114   for (auto it = I->begin(); !it.is_stop(); ++it) {
5115     const auto pc = it.data();
5116     const auto op = it.op_code();
5117 
5118     switch (op) {
5119     case CGO_VERTEX:
5120       check_extent(pc, 0);
5121       break;
5122     case CGO_SPHERE:
5123     case CGO_ELLIPSOID:
5124       check_extent(pc, *(pc + 3));
5125       break;
5126     case CGO_CYLINDER:
5127     case CGO_CONE:
5128     case CGO_SAUSAGE:
5129     case CGO_CUSTOM_CYLINDER:
5130     case CGO_CUSTOM_CYLINDER_ALPHA:
5131       check_extent(pc, *(pc + 6));
5132       check_extent(pc + 3, *(pc + 6));
5133       break;
5134     case CGO_TRIANGLE:
5135       check_extent(pc, 0);
5136       check_extent(pc + 3, 0);
5137       check_extent(pc + 6, 0);
5138       break;
5139     case CGO_DRAW_ARRAYS:
5140       {
5141         const cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
5142 	const float *pct = sp->floatdata;
5143 	int pl;
5144 
5145 	if (sp->arraybits & CGO_VERTEX_ARRAY){
5146 	  for (pl = 0; pl < sp->nverts; pl++){
5147 	    check_extent(pct, 0);
5148 	    pct += 3;
5149 	  }
5150 	}
5151 	if (sp->arraybits & CGO_NORMAL_ARRAY){
5152 	  for (pl = 0; pl < sp->nverts; pl++){
5153 	    pct += 3;
5154 	  }
5155 	}
5156 	if (sp->arraybits & CGO_COLOR_ARRAY){
5157 	  for (pl = 0; pl < sp->nverts; pl++){
5158 	    pct += 4;
5159 	  }
5160 	}
5161 	if (sp->arraybits & CGO_PICK_COLOR_ARRAY){
5162 	  for (pl = 0; pl < sp->nverts; pl++){
5163 	    pct += 3;
5164 	  }
5165 	}
5166       }
5167       break;
5168     case CGO_BOUNDING_BOX:
5169       {
5170 	if (!result){
5171 	  mn[0]=(*pc);
5172 	  mn[1]=*(pc+1);
5173 	  mn[2]=*(pc+2);
5174 	  mx[0]=*(pc+3);
5175 	  mx[1]=*(pc+4);
5176 	  mx[2]=*(pc+5);
5177 	  result = true;
5178 	} else {
5179 	  if(mn[0]>*pc) mn[0]=(*pc);
5180 	  if(mn[1]>*(pc+1)) mn[1]=*(pc+1);
5181 	  if(mn[2]>*(pc+2)) mn[2]=*(pc+2);
5182 	  if(mx[0]<*(pc+3)) mx[0]=*(pc+3);
5183 	  if(mx[1]<*(pc+4)) mx[1]=*(pc+4);
5184 	  if(mx[2]<*(pc+5)) mx[2]=*(pc+5);
5185 	}
5186       }
5187     }
5188   }
5189   return (result);
5190 }
5191 
CGOHasNormals(const CGO * I)5192 int CGOHasNormals(const CGO * I)
5193 {
5194   for (auto it = I->begin(); !it.is_stop(); ++it) {
5195     switch (it.op_code()) {
5196     case CGO_NORMAL:
5197     case CGO_SPHERE:
5198     case CGO_ELLIPSOID:
5199     case CGO_CYLINDER:
5200     case CGO_CONE:
5201     case CGO_SAUSAGE:
5202     case CGO_CUSTOM_CYLINDER:
5203     case CGO_CUSTOM_CYLINDER_ALPHA:
5204       return true;
5205     case CGO_DRAW_ARRAYS:
5206       if (it.cast<cgo::draw::arrays>()->arraybits & CGO_NORMAL_ARRAY) {
5207         return true;
5208       }
5209       break;
5210     }
5211   }
5212   return false;
5213 }
5214 
CGOQuadricToEllipsoid(const float * v,float r,const float * q,float * r_el,float * n0,float * n1,float * n2)5215 static int CGOQuadricToEllipsoid(const float *v, float r, const float *q,
5216                                  float *r_el, float *n0, float *n1, float *n2)
5217 {
5218   int ok = false;
5219   double inp_matrix[16];
5220   double e_val[4];
5221   double e_vec[16];
5222   double inverse[16];
5223 
5224   inp_matrix[0] = q[0];
5225   inp_matrix[1] = q[3];
5226   inp_matrix[2] = q[5];
5227   inp_matrix[3] = q[6];
5228   inp_matrix[4] = q[3];
5229   inp_matrix[5] = q[1];
5230   inp_matrix[6] = q[4];
5231   inp_matrix[7] = q[7];
5232   inp_matrix[8] = q[5];
5233   inp_matrix[9] = q[4];
5234   inp_matrix[10] = q[2];
5235   inp_matrix[11] = q[8];
5236   inp_matrix[12] = q[6];
5237   inp_matrix[13] = q[7];
5238   inp_matrix[14] = q[8];
5239   inp_matrix[15] = q[9];
5240 
5241   if(xx_matrix_invert(inverse, inp_matrix, 4)) {
5242 
5243     /* inverse now contains Uij coefficients */
5244     float pradius = sqrt1f(-1 / inverse[15]);
5245     int n_rot;
5246 
5247     if(xx_matrix_jacobi_solve(e_vec, e_val, &n_rot, inverse, 4)) {
5248       float mag[3];
5249       float scale[3];
5250       float mx;
5251       n0[0] = e_vec[0];
5252       n0[1] = e_vec[4];
5253       n0[2] = e_vec[8];
5254       n1[0] = e_vec[1];
5255       n1[1] = e_vec[5];
5256       n1[2] = e_vec[9];
5257       n2[0] = e_vec[2];
5258       n2[1] = e_vec[6];
5259       n2[2] = e_vec[10];
5260 
5261       normalize3f(n0);
5262       normalize3f(n1);
5263       normalize3f(n2);
5264       mag[0] = sqrt1f(e_val[0]);
5265       mag[1] = sqrt1f(e_val[1]);
5266       mag[2] = sqrt1f(e_val[2]);
5267 
5268       mx = mag[0];
5269       if(mx < mag[1])
5270         mx = mag[1];
5271       if(mx < mag[2])
5272         mx = mag[2];
5273 
5274       scale[0] = mag[0] / mx;
5275       scale[1] = mag[1] / mx;
5276       scale[2] = mag[2] / mx;
5277 
5278       scale3f(n0, scale[0], n0);
5279       scale3f(n1, scale[1], n1);
5280       scale3f(n2, scale[2], n2);
5281 
5282       *r_el = mx * pradius;
5283       ok = true;
5284     }
5285   }
5286   return ok;
5287 }
5288 
CGORenderQuadricRay(CRay * ray,float * v,float r,float * q)5289 static int CGORenderQuadricRay(CRay * ray, float *v, float r, float *q)
5290 {
5291   float r_el, n0[3], n1[3], n2[3];
5292   int ok = true;
5293   if(CGOQuadricToEllipsoid(v, r, q, &r_el, n0, n1, n2))
5294     ok &= ray->ellipsoid3fv(v, r_el, n0, n1, n2);
5295   return ok;
5296 }
5297 
5298 
5299 /* ======== Raytrace Renderer ======== */
5300 
CGORenderRay(CGO * I,CRay * ray,RenderInfo * info,const float * color,ObjectGadgetRamp * ramp,CSetting * set1,CSetting * set2)5301 int CGORenderRay(CGO * I, CRay * ray, RenderInfo * info, const float *color, ObjectGadgetRamp *ramp, CSetting * set1, CSetting * set2)
5302 {
5303 #ifdef _PYMOL_NO_RAY
5304   return 0;
5305 #else
5306   int vc = 0;
5307   float linewidth = 1.0F;
5308   float widthscale = 0.15F;
5309   float lineradius, dotradius, dotwidth;
5310   float white[] = { 1.0, 1.0, 1.0 };
5311   float zee[] = { 0.0, 0.0, 1.0 };
5312   int ok = true;
5313   const float *n0 = NULL, *n1 = NULL, *n2 = NULL, *v0 = NULL, *v1 = NULL, *v2 = NULL, *c0 =
5314     NULL, *c1 = NULL, *c2 = NULL;
5315   float rampc0[3], rampc1[3], rampc2[3];
5316   int mode = -1;
5317   /* workaround; multi-state ray-trace bug */
5318   if (!I) {
5319     assert("TODO investigate" && false);
5320     return 0; /* not sure if it should return 0 or 1, 0 - fails, but is it a memory issue? might not be since the arg is NULL */
5321   }
5322 
5323   I->G->CGORenderer->alpha =
5324     1.0F - SettingGet_f(I->G, set1, set2, cSetting_cgo_transparency);
5325 
5326   widthscale = SettingGet_f(I->G, set1, set2, cSetting_cgo_ray_width_scale);
5327 
5328   /*  printf("debug %8.9f\n",SceneGetScreenVertexScale(I->G,zee)); */
5329   linewidth = SettingGet_f(I->G, set1, set2, cSetting_cgo_line_width);
5330   if(linewidth < 0.0F)
5331     linewidth = 1.0F;
5332   lineradius = SettingGet_f(I->G, set1, set2, cSetting_cgo_line_radius);
5333   dotwidth = SettingGet_f(I->G, set1, set2, cSetting_cgo_dot_width);
5334   dotradius = SettingGet_f(I->G, set1, set2, cSetting_cgo_dot_radius);
5335   if(lineradius < 0.0F)
5336     lineradius = linewidth * ray->PixelRadius / 2.0F;
5337   if(dotradius < 0.0F)
5338     dotradius = dotwidth * ray->PixelRadius / 2.0F;
5339   if(widthscale < 0.0F)
5340     widthscale = ray->PixelRadius / 2.0F;
5341   if(color)
5342     c0 = color;
5343   else
5344     c0 = white;
5345   ray->transparentf(1.0F - I->G->CGORenderer->alpha);
5346 
5347   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
5348     const auto pc = it.data();
5349     const auto op = it.op_code();
5350 
5351     switch (op) {
5352     case CGO_BEGIN:
5353       mode = CGO_get_int(pc);
5354       vc = 0;
5355       n0 = zee;
5356       break;
5357     case CGO_END:
5358       switch (mode) {
5359       case GL_LINE_LOOP:
5360         if(vc > 1)
5361           ok &= ray->sausage3fv(v0, v2, lineradius, c0, c2);
5362         break;
5363       }
5364       mode = -1;
5365       break;
5366     case CGO_WIDTHSCALE:
5367       widthscale = *pc;
5368       lineradius = widthscale * linewidth;
5369       dotradius = widthscale * dotwidth;
5370       break;
5371     case CGO_DOTWIDTH:
5372       dotwidth = *pc;
5373       dotradius = widthscale * dotwidth;
5374       break;
5375     case CGO_LINEWIDTH:
5376       linewidth = *pc;
5377       lineradius = widthscale * linewidth;
5378       break;
5379     case CGO_NORMAL:
5380       n0 = pc;
5381       break;
5382     case CGO_SPECIAL_WITH_ARG:
5383       {
5384         float argval = *(pc + 1);
5385         switch (*(int*)pc){
5386         case LINEWIDTH_FOR_LINES:
5387           linewidth = argval;
5388           lineradius = widthscale * linewidth;
5389         }
5390       }
5391       break;
5392     case CGO_SPECIAL:
5393       {
5394         switch ((*(int*)pc)){
5395         case LINEWIDTH_DYNAMIC_WITH_SCALE_RIBBON:
5396           {
5397             float radius = SettingGet_f(I->G, set1, set2, cSetting_ribbon_radius);
5398             if(radius == 0.0F) {
5399               float ribbon_width = SettingGet_f(I->G, set1, set2, cSetting_ribbon_width);
5400               float line_width = SceneGetDynamicLineWidth(info, ribbon_width);
5401               SceneGetDynamicLineWidth(info, line_width);
5402               radius = ray->PixelRadius * line_width / 2.0F;
5403             }
5404             lineradius = radius;
5405           }
5406           break;
5407         case LINEWIDTH_FOR_LINES:
5408           {
5409             float radius = SettingGet_f(I->G, set1, set2, cSetting_line_radius);
5410             if(radius <= 0.0F) {
5411               float line_width = SettingGet_f(I->G, set1, set2, cSetting_line_width);
5412               line_width = SceneGetDynamicLineWidth(info, line_width);
5413               radius = ray->PixelRadius * line_width / 2.0F;
5414             }
5415             lineradius = radius;
5416           }
5417           break;
5418         case CYLINDER_WIDTH_FOR_NONBONDED:
5419         case LINEWIDTH_WITH_SCALE:
5420           {
5421             float line_width = SettingGet_f(I->G, set1, set2, cSetting_line_width);
5422             line_width = SceneGetDynamicLineWidth(info, line_width);
5423             lineradius = widthscale * line_width / 2.f;
5424           }
5425           break;
5426         }
5427       }
5428       break;
5429     case CGO_COLOR:
5430       c0 = pc;
5431       ray->color3fv(c0);
5432       break;
5433     case CGO_ALPHA:
5434       I->G->CGORenderer->alpha = *pc;
5435       ray->transparentf(1.0F - *pc);
5436       break;
5437     case CGO_LINE:
5438       {
5439         auto line = reinterpret_cast<cgo::draw::line *>(pc);
5440         ok &= ray->sausage3fv(line->vertex1, line->vertex2, lineradius, c0, c0);
5441       }
5442       break;
5443     case CGO_SPLITLINE:
5444       {
5445         auto splitline = reinterpret_cast<cgo::draw::splitline *>(pc);
5446         float color2[] = { CONVERT_COLOR_VALUE(splitline->color2[0]),
5447                            CONVERT_COLOR_VALUE(splitline->color2[1]),
5448                            CONVERT_COLOR_VALUE(splitline->color2[2]) };
5449         if (splitline->flags & cgo::draw::splitline::interpolation){
5450           ok &= ray->sausage3fv(splitline->vertex1, splitline->vertex2, lineradius, c0, color2);
5451         } else {
5452           float mid[3];
5453           add3f(splitline->vertex1, splitline->vertex2, mid);
5454           mult3f(mid, .5f, mid);
5455           ok &= ray->customCylinder3fv(splitline->vertex1, mid,
5456                                        lineradius, c0, c0, 2, 0);
5457           ok &= ray->customCylinder3fv(mid, splitline->vertex2,
5458                                        lineradius, color2, color2, 0, 2);
5459         }
5460       }
5461       break;
5462     case CGO_VERTEX_CROSS:
5463       {
5464         float pt1[3], pt2[3];
5465         float nonbonded_size =
5466           SettingGet_f(I->G, set1, set2, cSetting_nonbonded_size);
5467         copy3f(pc, pt1);
5468         copy3f(pc, pt2);
5469         pt1[0] -= nonbonded_size;
5470         pt2[0] += nonbonded_size;
5471         ok &= ray->sausage3fv(pt1, pt2, lineradius, c0, c0);
5472 
5473         copy3f(pc, pt1);
5474         copy3f(pc, pt2);
5475         pt1[1] -= nonbonded_size;
5476         pt2[1] += nonbonded_size;
5477         ok &= ray->sausage3fv(pt1, pt2, lineradius, c0, c0);
5478 
5479         copy3f(pc, pt1);
5480         copy3f(pc, pt2);
5481         pt1[2] -= nonbonded_size;
5482         pt2[2] += nonbonded_size;
5483         ok &= ray->sausage3fv(pt1, pt2, lineradius, c0, c0);
5484       }
5485       break;
5486     case CGO_VERTEX_BEGIN_LINE_STRIP:
5487     case CGO_VERTEX:
5488       v0 = pc;
5489 
5490       if (ramp){
5491 	if (!ObjectGadgetRampInterVertex(ramp, v0, rampc0, -1)){
5492 	  copy3f(white, rampc0);
5493 	}
5494 	c0 = rampc0;
5495       }
5496       switch (mode) {
5497       case GL_POINTS:
5498         ok &= ray->sphere3fv(v0, dotradius);
5499         break;
5500       case GL_LINES:
5501         if(vc & 0x1)
5502           ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
5503         v1 = v0;
5504 	if (!ramp){
5505 	  c1 = c0;
5506 	}
5507         break;
5508       case GL_LINE_STRIP:
5509         if(vc){
5510           ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
5511         }
5512         v1 = v0;
5513 	if (!ramp){
5514 	  c1 = c0;
5515 	}
5516         break;
5517       case GL_LINE_LOOP:
5518         if(vc)
5519           ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
5520         else {
5521           v2 = v0;
5522           c2 = c0;
5523         }
5524         v1 = v0;
5525 	if (!ramp)
5526 	  c1 = c0;
5527         break;
5528       case GL_TRIANGLES:
5529 	if( ((vc + 1) % 3) == 0)
5530           ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
5531         v2 = v1;
5532         n2 = n1;
5533         v1 = v0;
5534         n1 = n0;
5535 	if (!ramp){
5536 	  c2 = c1;
5537 	  c1 = c0;
5538 	}
5539         break;
5540       case GL_TRIANGLE_STRIP:
5541         if(vc > 1)
5542           ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
5543         v2 = v1;
5544         n2 = n1;
5545         v1 = v0;
5546         n1 = n0;
5547 	if (!ramp){
5548 	  c2 = c1;
5549 	  c1 = c0;
5550 	}
5551         break;
5552       case GL_TRIANGLE_FAN:
5553         if(vc > 1)
5554           ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
5555         else if(!vc) {
5556           n2 = n0;
5557           v2 = v0;
5558 	  if (!ramp)
5559 	    c2 = c0;
5560         }
5561         v1 = v0;
5562         n1 = n0;
5563 	if (!ramp)
5564 	  c1 = c0;
5565         break;
5566       }
5567       if (ramp){
5568 	switch (mode){
5569 	case GL_TRIANGLES:
5570 	case GL_TRIANGLE_STRIP:
5571 	case GL_TRIANGLE_FAN:
5572 	  copy3f(rampc1, rampc2);
5573 	  c2 = rampc2;
5574 	case GL_LINES:
5575 	case GL_LINE_STRIP:
5576 	case GL_LINE_LOOP:
5577 	  copy3f(rampc0, rampc1);
5578 	  c1 = rampc1;
5579 	  break;
5580 	}
5581       }
5582       vc++;
5583       break;
5584     case CGO_SPHERE:
5585       ray->color3fv(c0);
5586       ok &= ray->sphere3fv(pc, *(pc + 3));
5587       break;
5588     case CGO_ELLIPSOID:
5589       ray->color3fv(c0);
5590       ok &= ray->ellipsoid3fv(pc, *(pc + 3), pc + 4, pc + 7, pc + 10);
5591       break;
5592     case CGO_QUADRIC:
5593       ray->color3fv(c0);
5594       ok &= CGORenderQuadricRay(ray, pc, *(pc + 3), pc + 4);
5595       break;
5596     case CGO_CONE:
5597       ok &= ray->cone3fv(pc, pc + 3, *(pc + 6), *(pc + 7), pc + 8, pc + 11,
5598 			  (int) *(pc + 14), (int) *(pc + 15));
5599       break;
5600     case CGO_CUSTOM_CYLINDER:
5601       {
5602         auto cyl = reinterpret_cast<cgo::draw::custom_cylinder*>(pc);
5603         ok &= ray->customCylinder3fv(*cyl);
5604       }
5605       break;
5606     case CGO_CUSTOM_CYLINDER_ALPHA:
5607       {
5608         auto cyl = reinterpret_cast<cgo::draw::custom_cylinder_alpha*>(pc);
5609         ok &= ray->customCylinderAlpha3fv(*cyl);
5610       }
5611       break;
5612     case CGO_SHADER_CYLINDER:
5613       {
5614         float p2[3];
5615         int cap = CGO_get_int(pc + 7);
5616         int cap1 = cap & 1 ? ( (cap & cCylShaderCap1RoundBit) ? 2 : 1 ) : 0;
5617         int cap2 = cap & 2 ? ( (cap & cCylShaderCap2RoundBit) ? 2 : 1 ) : 0;
5618         add3f(pc, pc + 3, p2);
5619         ok &= ray->customCylinder3fv(pc, p2, *(pc + 6), ray->CurColor, ray->CurColor,
5620                                      cap1, cap2);
5621       }
5622       break;
5623     case CGO_SHADER_CYLINDER_WITH_2ND_COLOR:
5624       {
5625         auto cyl = reinterpret_cast<cgo::draw::shadercylinder2ndcolor*>(pc);
5626         float v1[3];
5627         int cap = cyl->cap;
5628         int fcap = (cap & 1) ? ((cap & cCylShaderCap1RoundBit) ? 2 : 1) : 0;
5629         int bcap = (cap & 2) ? ((cap & cCylShaderCap2RoundBit) ? 2 : 1) : 0;
5630         int colorinterp = cap & cCylShaderInterpColor;
5631         const float *color1 = c0;
5632         const float *color2 = cyl->color2;
5633         add3f(cyl->origin, cyl->axis, v1);
5634         float alpha1 = I->G->CGORenderer->alpha;
5635         float alpha2 = cyl->alpha >= 0.f ? cyl->alpha : alpha1;
5636         if (colorinterp || equal3f(color1, color2)) {
5637           ok &= ray->customCylinder3fv(pc, v1, cyl->tube_size, color1, color2, fcap, bcap, alpha1, alpha2);
5638         } else {
5639           float mid[3];
5640           mult3f(cyl->axis, .5f, mid);
5641           add3f(cyl->origin, mid, mid);
5642 
5643           ray->color3fv(c0);
5644           ok &= ray->customCylinder3fv(cyl->origin, mid, cyl->tube_size, color1, color1, fcap, 0, alpha1, alpha2);
5645           ray->color3fv(cyl->color2);
5646           ok &= ray->customCylinder3fv(mid, v1, cyl->tube_size, color2, color2, 0, bcap, alpha1, alpha2);
5647         }
5648       }
5649       break;
5650     case CGO_CYLINDER:
5651       {
5652         auto *cyl = reinterpret_cast<cgo::draw::cylinder*>(pc);
5653         ok &= ray->cylinder3fv(*cyl);
5654       }
5655       break;
5656     case CGO_SAUSAGE:
5657       ok &= ray->sausage3fv(pc, pc + 3, *(pc + 6), pc + 7, pc + 10);
5658       break;
5659     case CGO_TRIANGLE:
5660       ok &= ray->triangle3fv(pc, pc + 3, pc + 6, pc + 9, pc + 12, pc + 15, pc + 18,
5661 			      pc + 21, pc + 24);
5662       break;
5663     case CGO_DRAW_ARRAYS:
5664       {
5665         cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
5666 	int mode = sp->mode, arrays = sp->arraybits, narrays = sp->narrays, nverts = sp->nverts, v, pl, plc;
5667 	float *vertexVals = sp->floatdata;
5668 	float *normalVals = 0, *colorVals = 0;
5669         int offset = 0;
5670 	(void)narrays;
5671 	if (arrays & CGO_VERTEX_ARRAY){
5672 	  vertexVals = sp->floatdata;
5673           offset += nverts * 3;
5674 	}
5675 	if (arrays & CGO_NORMAL_ARRAY){
5676 	  normalVals = sp->floatdata + offset;
5677           offset += nverts * 3;
5678 	}
5679 	if (arrays & CGO_COLOR_ARRAY){
5680 	  colorVals = sp->floatdata + offset;
5681           offset += nverts * 4;
5682 	}
5683 	if (arrays & CGO_PICK_COLOR_ARRAY){
5684           offset += nverts * 3;
5685 	}
5686 	vc = 0;
5687 	for (v=0, pl=0, plc=0; ok && v<nverts; v++, pl+=3, plc+=4){
5688 	  if (normalVals){
5689 	    n0 = &normalVals[pl];
5690 	  }
5691 	  if (colorVals){
5692 	    c0 = &colorVals[plc];
5693 	    ray->color3fv(c0);
5694 	    ray->transparentf(1.0f - c0[3]);
5695 	  }
5696 	  if (vertexVals){
5697 	    v0 = &vertexVals[pl];
5698 	  }
5699 	  switch (mode){
5700 	  case GL_POINTS:
5701 	    ok &= ray->sphere3fv(v0, dotradius);
5702 	    break;
5703 	  case GL_LINES:
5704 	    if(vc & 0x1)
5705 	      ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
5706 	    v1 = v0;
5707 	    c1 = c0;
5708 	    break;
5709 	  case GL_LINE_STRIP:
5710 	    if(vc)
5711 	      ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
5712 	    v1 = v0;
5713 	    c1 = c0;
5714 	    break;
5715 	  case GL_LINE_LOOP:
5716 	    if(vc)
5717 	      ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
5718 	    else {
5719 	      v2 = v0;
5720 	      c2 = c0;
5721 	    }
5722 	    v1 = v0;
5723 	    c1 = c0;
5724 	    break;
5725 	  case GL_TRIANGLES:
5726 	    if( ((vc + 1) % 3) == 0)
5727 	      ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
5728 	    v2 = v1;
5729 	    c2 = c1;
5730 	    n2 = n1;
5731 	    v1 = v0;
5732 	    c1 = c0;
5733 	    n1 = n0;
5734 	    break;
5735 	  case GL_TRIANGLE_STRIP:
5736 	    if(vc > 1)
5737 	      ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
5738 	    v2 = v1;
5739 	    c2 = c1;
5740 	    n2 = n1;
5741 	    v1 = v0;
5742 	    c1 = c0;
5743 	    n1 = n0;
5744 	    break;
5745 	  case GL_TRIANGLE_FAN:
5746 	    if(vc > 1)
5747 	      ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
5748 	    else if(!vc) {
5749 	      n2 = n0;
5750 	      v2 = v0;
5751 	      c2 = c0;
5752 	    }
5753 	    v1 = v0;
5754 	    c1 = c0;
5755 	    n1 = n0;
5756 	    break;
5757 	  }
5758 	  vc++;
5759 	}
5760       }
5761       break;
5762     default:
5763       break;
5764     }
5765   }
5766 
5767   if (ok)
5768     ray->transparentf(0.0F);
5769   return ok;
5770 #endif
5771 }
5772 
5773 
5774 /* ======== GL Rendering ======== */
5775 
5776 static int CGO_gl_begin_WARNING_CALLED = false, CGO_gl_end_WARNING_CALLED = false, CGO_gl_vertex_WARNING_CALLED = false;
CGO_gl_begin(CCGORenderer * I,CGO_op_data pc)5777 static void CGO_gl_begin(CCGORenderer * I, CGO_op_data pc){
5778   if (I->use_shader){
5779     if (!CGO_gl_begin_WARNING_CALLED) {
5780       PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_begin() is called but not implemented in OpenGLES\n" ENDFB(I->G);
5781       CGO_gl_begin_WARNING_CALLED = true;
5782     }
5783   } else {
5784     int mode = CGO_get_int(*pc);
5785     if (I->debug)
5786       mode = CGOConvertDebugMode(I->debug, mode);
5787     glBegin(mode);
5788   }
5789 }
CGO_gl_end(CCGORenderer * I,CGO_op_data)5790 static void CGO_gl_end(CCGORenderer * I, CGO_op_data){
5791   if (I->use_shader){
5792     if (!CGO_gl_end_WARNING_CALLED) {
5793       PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_end() is called but not implemented in OpenGLES\n" ENDFB(I->G);
5794       CGO_gl_end_WARNING_CALLED = true;
5795     }
5796   } else {
5797     glEnd();
5798   }
5799 }
CGO_gl_vertex(CCGORenderer * I,CGO_op_data v)5800 static void CGO_gl_vertex(CCGORenderer * I, CGO_op_data v){
5801   if (I->use_shader){
5802   if (!CGO_gl_vertex_WARNING_CALLED) {
5803     PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_vertex() is called but not implemented in OpenGLES\n" ENDFB(I->G);
5804     CGO_gl_vertex_WARNING_CALLED = true;
5805   }
5806   } else {
5807     glVertex3fv(*v);
5808   }
5809 }
5810 
CGO_gl_vertex_cross(CCGORenderer * I,CGO_op_data v)5811 static void CGO_gl_vertex_cross(CCGORenderer * I, CGO_op_data v){
5812 #ifndef PURE_OPENGL_ES_2
5813   if (I->use_shader){
5814 #endif
5815   if (!CGO_gl_vertex_WARNING_CALLED) {
5816     PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_vertex() is called but not implemented in OpenGLES\n" ENDFB(I->G);
5817     CGO_gl_vertex_WARNING_CALLED = true;
5818   }
5819 #ifndef PURE_OPENGL_ES_2
5820   } else {
5821     CSetting * set1 = NULL, * set2 = NULL;
5822     if (I->rep&&I->rep->cs) set1 = I->rep->cs->Setting;
5823     if (I->rep&&I->rep->obj) set2 = I->rep->obj->Setting;
5824     float nonbonded_size =
5825       SettingGet_f(I->G, set1, set2, cSetting_nonbonded_size);
5826     float pt[3];
5827     copy3f(*v, pt);
5828     pt[0] -= nonbonded_size;
5829     glVertex3fv(pt);
5830     pt[0] += 2 * nonbonded_size;
5831     glVertex3fv(pt);
5832     copy3f(*v, pt);
5833     pt[1] -= nonbonded_size;
5834     glVertex3fv(pt);
5835     pt[1] += 2 * nonbonded_size;
5836     glVertex3fv(pt);
5837     copy3f(*v, pt);
5838     pt[2] -= nonbonded_size;
5839     glVertex3fv(pt);
5840     pt[2] += 2 * nonbonded_size;
5841     glVertex3fv(pt);
5842   }
5843 #endif
5844 }
5845 
CGO_gl_line(CCGORenderer * I,CGO_op_data v)5846 static void CGO_gl_line(CCGORenderer * I, CGO_op_data v){
5847 #ifndef PURE_OPENGL_ES_2
5848   if (!I->use_shader){
5849     auto line = reinterpret_cast<const cgo::draw::line *>(*v);
5850     glVertex3fv(line->vertex1);
5851     glVertex3fv(line->vertex2);
5852   }
5853 #endif
5854 }
5855 
CGO_gl_splitline(CCGORenderer * I,CGO_op_data v)5856 static void CGO_gl_splitline(CCGORenderer * I, CGO_op_data v){
5857 #ifndef PURE_OPENGL_ES_2
5858   if (!I->use_shader){
5859     auto splitline = reinterpret_cast<const cgo::draw::splitline *>(*v);
5860     bool interpolation = splitline->flags & cgo::draw::splitline::interpolation;
5861     bool equal_colors = splitline->flags & cgo::draw::splitline::equal_colors;
5862     bool no_split_for_pick = splitline->flags & cgo::draw::splitline::no_split_for_pick;
5863 
5864     if (I->isPicking){
5865       if (no_split_for_pick){
5866         glVertex3fv(splitline->vertex1);
5867         glVertex3fv(splitline->vertex2);
5868       } else {
5869         float h[3];
5870         average3f(splitline->vertex1, splitline->vertex2, h);
5871         glVertex3fv(splitline->vertex1);
5872         glVertex3fv(h);
5873         unsigned char col[4];
5874         AssignNewPickColor(nullptr, I->info->pick, col, &I->rep->context,
5875                            splitline->index, splitline->bond);
5876         glColor4ubv(col);
5877         glVertex3fv(h);
5878         glVertex3fv(splitline->vertex2);
5879       }
5880     } else if (interpolation || equal_colors){
5881       glVertex3fv(splitline->vertex1);
5882       if (!equal_colors)
5883         glColor4ub(splitline->color2[0], splitline->color2[1], splitline->color2[2], CLIP_COLOR_VALUE(I->alpha));
5884       glVertex3fv(splitline->vertex2);
5885     } else {
5886       float h[3];
5887       average3f(splitline->vertex1, splitline->vertex2, h);
5888       glVertex3fv(splitline->vertex1);
5889       glVertex3fv(h);
5890       glColor4ub(splitline->color2[0], splitline->color2[1], splitline->color2[2], CLIP_COLOR_VALUE(I->alpha));
5891       glVertex3fv(h);
5892       glVertex3fv(splitline->vertex2);
5893     }
5894   }
5895 #endif
5896 }
5897 
5898 
CGO_gl_normal(CCGORenderer * I,CGO_op_data varg)5899 static void CGO_gl_normal(CCGORenderer * I, CGO_op_data varg){
5900   const float* v = *varg;
5901   if (I->use_shader){
5902     glVertexAttrib3fv(VERTEX_NORMAL, v);
5903   } else {
5904     glNormal3f(v[0],v[1],v[2]);
5905   }
5906 }
5907 
CGO_gl_draw_arrays(CCGORenderer * I,CGO_op_data pc)5908 static void CGO_gl_draw_arrays(CCGORenderer * I, CGO_op_data pc){
5909   auto sp = reinterpret_cast<const cgo::draw::arrays*>(*pc);
5910   int mode = sp->mode, arrays = sp->arraybits, narrays = sp->narrays, nverts = sp->nverts;
5911   const float* data = sp->floatdata;
5912   (void) narrays;
5913 #ifndef PURE_OPENGL_ES_2
5914   if (I->use_shader){
5915 #endif
5916 
5917   if (arrays & CGO_VERTEX_ARRAY) glEnableVertexAttribArray(VERTEX_POS);
5918   if (arrays & CGO_NORMAL_ARRAY) glEnableVertexAttribArray(VERTEX_NORMAL);
5919   if (I->isPicking){
5920     if (arrays & CGO_PICK_COLOR_ARRAY){
5921       glEnableVertexAttribArray(VERTEX_COLOR);
5922     }
5923   } else {
5924     if (arrays & CGO_COLOR_ARRAY)
5925       glEnableVertexAttribArray(VERTEX_COLOR);
5926   }
5927 
5928   if (arrays & CGO_VERTEX_ARRAY){
5929 #ifdef _WEBGL
5930 #else
5931     glVertexAttribPointer(VERTEX_POS, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, 0, data);
5932 #endif
5933     data += nverts*3;
5934   }
5935   if (arrays & CGO_NORMAL_ARRAY){
5936 #ifdef _WEBGL
5937 #else
5938     glVertexAttribPointer(VERTEX_NORMAL, VERTEX_NORMAL_SIZE, GL_FLOAT, GL_FALSE, 0, data);
5939 #endif
5940     data += nverts*3;
5941   }
5942   if (I->isPicking){
5943     if (arrays & CGO_COLOR_ARRAY){
5944       data += nverts*4;
5945     }
5946     if (arrays & CGO_PICK_COLOR_ARRAY){
5947 #ifdef _WEBGL
5948     glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
5949     glBufferData(GL_ARRAY_BUFFER, nverts * 4, data, GL_STATIC_DRAW);
5950     glVertexAttribPointer(VERTEX_COLOR, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0);
5951 #else
5952       glVertexAttribPointer(VERTEX_COLOR, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_FALSE, 0, data);
5953 #endif
5954       data += nverts*3;
5955     }
5956   } else {
5957     if (arrays & CGO_COLOR_ARRAY){
5958 #ifdef _WEBGL
5959 #else
5960       glVertexAttribPointer(VERTEX_COLOR, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, 0, data);
5961 #endif
5962       data += nverts*4;
5963     }
5964     if (arrays & CGO_PICK_COLOR_ARRAY){
5965       data += nverts*3;
5966     }
5967   }
5968   if (I->debug){
5969     mode = CGOConvertDebugMode(I->debug, mode);
5970   }
5971   glDrawArrays(mode, 0, nverts);
5972 
5973   if (I->isPicking){
5974     if (arrays & CGO_PICK_COLOR_ARRAY){
5975       glDisableVertexAttribArray(VERTEX_COLOR);
5976     }
5977   } else {
5978     if (arrays & CGO_COLOR_ARRAY)
5979       glDisableVertexAttribArray(VERTEX_COLOR);
5980   }
5981   if (arrays & CGO_VERTEX_ARRAY) glDisableVertexAttribArray(VERTEX_POS);
5982   if (arrays & CGO_NORMAL_ARRAY) glDisableVertexAttribArray(VERTEX_NORMAL);
5983 
5984 #ifndef PURE_OPENGL_ES_2
5985   } else {
5986 
5987     int pl, pla, plc;
5988     const float *vertexVals = nullptr;
5989     const float *colorVals = 0, *normalVals = 0, *tmp_ptr;
5990     const uchar *pickColorVals = 0, *tmp_pc_ptr;
5991     float alpha = I->alpha;
5992     if (arrays & CGO_VERTEX_ARRAY){
5993       vertexVals = data;
5994       data += nverts*3;
5995     }
5996     if (arrays & CGO_NORMAL_ARRAY){
5997       normalVals = data;
5998       data += nverts*3;
5999     }
6000     if (I->isPicking){
6001       alpha = 1.f;
6002       if (arrays & CGO_COLOR_ARRAY){
6003 	data += nverts*4;
6004       }
6005       if (arrays & CGO_PICK_COLOR_ARRAY){
6006 	pickColorVals = (uchar*)data;
6007 	data += nverts*3;
6008       }
6009     } else {
6010       if (arrays & CGO_COLOR_ARRAY){
6011 	colorVals = data;
6012 	data += nverts*4;
6013       }
6014       if (arrays & CGO_PICK_COLOR_ARRAY){
6015 	data += nverts*3;
6016       }
6017     }
6018     if (arrays & CGO_ACCESSIBILITY_ARRAY) data += nverts;
6019 
6020     if (I->debug){
6021       mode = CGOConvertDebugMode(I->debug, mode);
6022     }
6023 
6024     glBegin(mode);
6025     for (pl = 0, pla = 0, plc = 0; pl<nverts; pl++, pla+=3, plc+=4){
6026       if (pickColorVals){
6027         tmp_pc_ptr = &pickColorVals[plc]; /* the pick colors are saved with rgba */
6028         glColor4ub(tmp_pc_ptr[0], tmp_pc_ptr[1], tmp_pc_ptr[2], tmp_pc_ptr[3]);
6029       } else {
6030         if (colorVals){
6031           tmp_ptr = &colorVals[plc];
6032           glColor4f(tmp_ptr[0], tmp_ptr[1], tmp_ptr[2], alpha);
6033         }
6034         if (normalVals){
6035           tmp_ptr = &normalVals[pla];
6036           glNormal3fv(&normalVals[pla]);
6037         }
6038       }
6039       if (vertexVals){
6040 	tmp_ptr = &vertexVals[pla];
6041 	glVertex3fv(&vertexVals[pla]);
6042       }
6043     }
6044     glEnd();
6045   }
6046 #endif
6047 }
6048 
6049 static
6050 void TransparentInfoSortIX(PyMOLGlobals * G, float *sum, float *z_value,
6051 			   int *ix, int n_tri, int *sort_mem, int t_mode);
6052 static
6053 void CGOReorderIndicesWithTransparentInfo(PyMOLGlobals * G, int nindices,
6054 					  size_t vbuf, int n_tri, int *ix,
6055 					  GL_C_INT_TYPE *vertexIndicesOriginal,
6056 					  GL_C_INT_TYPE *vertexIndices);
6057 
CGO_gl_draw_buffers_indexed(CCGORenderer * I,CGO_op_data pc)6058 static void CGO_gl_draw_buffers_indexed(CCGORenderer * I, CGO_op_data pc){
6059   auto sp = reinterpret_cast<const cgo::draw::buffers_indexed*>(*pc);
6060   int mode = sp->mode, nindices = sp->nindices,
6061     nverts = sp->nverts, n_data = sp->n_data;
6062   size_t vboid = sp->vboid, iboid = sp->iboid;
6063   VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(vboid);
6064   IndexBuffer * ibo  = I->G->ShaderMgr->getGPUBuffer<IndexBuffer>(iboid);
6065   GLenum err ;
6066   CHECK_GL_ERROR_OK("beginning of CGO_gl_draw_buffers_indexed err=%d\n");
6067 
6068   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6069 
6070   if (!shaderPrg){
6071     return;
6072   }
6073 
6074   if (I->isPicking){
6075     int attr_a_Color = shaderPrg->GetAttribLocation("a_Color");
6076     vbo->maskAttributes({ attr_a_Color });
6077     shaderPrg->Set1i("fog_enabled", 0);
6078     shaderPrg->Set1i("lighting_enabled", 0);
6079     if (I->use_shader){
6080       if (sp->pickvboid){
6081         VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6082         pickvbo->bind(shaderPrg->id, I->pick_pass());
6083       } else {
6084         glEnableVertexAttribArray(attr_a_Color);
6085         glVertexAttribPointer(attr_a_Color, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_TRUE, 0, sp->floatdata);
6086       }
6087     }
6088   }
6089   if (n_data){
6090     // if transparency data, then sort it
6091     int n_tri = nindices/3;
6092     float *sum = sp->floatdata + nverts*3;
6093     float *z_value = sum + (nindices*3);
6094     int *ix = (int *)(z_value + n_tri);
6095     int *sort_mem = ix + n_tri;
6096     int t_mode;
6097     CSetting * set1 = NULL, * set2 = NULL;
6098     if (I->rep&&I->rep->cs) set1 = I->rep->cs->Setting;
6099     if (I->rep&&I->rep->obj) set2 = I->rep->obj->Setting;
6100     t_mode = SettingGet_i(I->G, set1, set2, cSetting_transparency_mode);
6101     if (t_mode!=3){
6102       GL_C_INT_TYPE *vertexIndicesOriginalTI = (GL_C_INT_TYPE *)(sort_mem + n_tri + 256);
6103       GL_C_INT_TYPE *vertexIndicesTI = vertexIndicesOriginalTI + nindices;
6104       TransparentInfoSortIX(I->G, sum, z_value, ix, n_tri, sort_mem, t_mode);
6105       CGOReorderIndicesWithTransparentInfo(I->G, nindices, iboid, n_tri, ix,
6106                                            vertexIndicesOriginalTI, vertexIndicesTI);
6107     }
6108   }
6109 
6110   if (I->debug){
6111     mode = CGOConvertDebugMode(I->debug, mode);
6112   }
6113   vbo->bind(shaderPrg->id);
6114   ibo->bind();
6115 
6116   CHECK_GL_ERROR_OK("CGO_gl_draw_buffers_indexed: before glDrawElements err=%d\n");
6117   glDrawElements(mode, nindices, GL_C_INT_ENUM, 0);
6118   CHECK_GL_ERROR_OK("CGO_gl_draw_buffers_indexed: after glDrawElements err=%d\n");
6119 
6120   vbo->unbind();
6121   ibo->unbind();
6122 
6123   if (I->isPicking) {
6124     VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6125     if (pickvbo)
6126       pickvbo->unbind();
6127   }
6128 
6129   CHECK_GL_ERROR_OK("CGO_gl_draw_buffers_indexed: end err=%d\n");
6130 }
6131 
CGO_gl_draw_buffers_not_indexed(CCGORenderer * I,CGO_op_data pc)6132 static void CGO_gl_draw_buffers_not_indexed(CCGORenderer * I, CGO_op_data pc){
6133   const cgo::draw::buffers_not_indexed * sp = reinterpret_cast<decltype(sp)>(*pc);
6134   int mode = sp->mode;
6135 
6136   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6137   if (!shaderPrg){
6138     return;
6139   }
6140   VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6141   if (!vbo)
6142     return;
6143   if (I->isPicking){
6144     int attr_a_Color = shaderPrg->GetAttribLocation("a_Color");
6145     vbo->maskAttributes({ attr_a_Color });
6146     shaderPrg->Set1i("fog_enabled", 0);
6147     shaderPrg->Set1i("lighting_enabled", 0);
6148     if (I->use_shader){
6149       if (sp->pickvboid){
6150         VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6151         pickvbo->bind(shaderPrg->id, I->pick_pass());
6152       } else {
6153         glEnableVertexAttribArray(attr_a_Color);
6154         glVertexAttribPointer(attr_a_Color, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_TRUE, 0, sp->floatdata);
6155       }
6156     }
6157   }
6158 
6159   if (I->debug){
6160     mode = CGOConvertDebugMode(I->debug, mode);
6161   }
6162 
6163   vbo->bind(shaderPrg->id);
6164   glDrawArrays(mode, 0, sp->nverts);
6165   vbo->unbind();
6166 
6167   if (I->isPicking) {
6168     VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6169     if (pickvbo)
6170       pickvbo->unbind();
6171   }
6172 }
6173 
CGO_gl_mask_attribute_if_picking(CCGORenderer * I,CGO_op_data pc)6174 static void CGO_gl_mask_attribute_if_picking(CCGORenderer * I, CGO_op_data pc){
6175   if (I->isPicking){
6176     const cgo::draw::mask_attribute_if_picking * sp = reinterpret_cast<decltype(sp)>(*pc);
6177     auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6178     if (!shaderPrg){
6179       return;
6180     }
6181     VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6182     if (!vbo)
6183       return;
6184     int loc = shaderPrg->GetAttribLocation(I->G->ShaderMgr->GetAttributeName(sp->attr_lookup_idx));
6185     vbo->maskAttribute(loc);
6186   }
6187 }
6188 
CGO_gl_bind_vbo_for_picking(CCGORenderer * I,CGO_op_data pc)6189 static void CGO_gl_bind_vbo_for_picking(CCGORenderer * I, CGO_op_data pc){
6190   if (I->isPicking){
6191     const cgo::draw::bind_vbo_for_picking * sp = reinterpret_cast<decltype(sp)>(*pc);
6192     auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6193     if (!shaderPrg){
6194       return;
6195     }
6196     VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6197     if (!vbo)
6198       return;
6199     vbo->bind(shaderPrg->id, sp->which_attr_idx + sp->npickattrs * I->pick_pass());
6200   }
6201 }
6202 
CGO_gl_draw_custom(CCGORenderer * I,CGO_op_data pc)6203 static void CGO_gl_draw_custom(CCGORenderer * I, CGO_op_data pc){
6204   const cgo::draw::custom * sp = reinterpret_cast<decltype(sp)>(*pc);
6205 
6206   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6207   if (!shaderPrg){
6208     return;
6209   }
6210   VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6211   if (!vbo)
6212     return;
6213   IndexBuffer * ibo = NULL;
6214   if (sp->iboid){
6215     ibo = I->G->ShaderMgr->getGPUBuffer<IndexBuffer>(sp->iboid);
6216   }
6217   vbo->bind(shaderPrg->id);
6218   if (ibo){
6219     ibo->bind();
6220     glDrawElements(sp->mode, sp->nindices, GL_C_INT_ENUM, 0);
6221   } else {
6222     glDrawArrays(sp->mode, 0, sp->nverts);
6223   }
6224   vbo->unbind();
6225   if (sp->pickvboid) {
6226     VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6227     if (pickvbo)
6228       pickvbo->unbind();
6229   }
6230   if (ibo)
6231     ibo->unbind();
6232 
6233 }
6234 
CGO_gl_draw_sphere_buffers(CCGORenderer * I,CGO_op_data pc)6235 static void CGO_gl_draw_sphere_buffers(CCGORenderer * I, CGO_op_data pc) {
6236   const cgo::draw::sphere_buffers * sp = reinterpret_cast<decltype(sp)>(*pc);
6237   int num_spheres = sp->num_spheres;
6238   int attr_color;
6239   VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6240   VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6241   CShaderPrg *shaderPrg;
6242   int pickable = 0;
6243 
6244   shaderPrg = I->G->ShaderMgr->Get_DefaultSphereShader(I->info ? I->info->pass : 0);
6245   if (!shaderPrg){
6246     return;
6247   }
6248 
6249   attr_color = shaderPrg->GetAttribLocation("a_Color");
6250 
6251   if (I->isPicking){
6252     vbo->maskAttributes({ attr_color });
6253     pickable = SettingGet_i(I->G, I->set1, I->set2, cSetting_pickable);
6254     shaderPrg->Set1i("lighting_enabled", 0);
6255     if (pickable){
6256       pickvbo->bind(shaderPrg->id, I->pick_pass());
6257     } else {
6258       assert(I->info->pick);
6259       unsigned char nopick[4] = {};
6260       I->info->pick->colorNoPick(nopick);
6261       glVertexAttrib4ubv(attr_color, nopick);
6262     }
6263   }
6264 
6265   vbo->bind(shaderPrg->id);
6266   glDrawArrays(GL_QUADS, 0, num_spheres * 4);
6267 
6268   vbo->unbind();
6269 }
6270 
CGO_gl_draw_cylinder_buffers(CCGORenderer * I,CGO_op_data pc)6271 static void CGO_gl_draw_cylinder_buffers(CCGORenderer * I, CGO_op_data pc) {
6272   const cgo::draw::cylinder_buffers * sp = reinterpret_cast<decltype(sp)>(*pc);
6273   int  num_cyl = sp->num_cyl;
6274   int min_alpha = sp->alpha;
6275   int attr_colors, attr_colors2;
6276   CShaderPrg *shaderPrg;
6277   int pickable = 0;
6278   VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6279   IndexBuffer * ibo = I->G->ShaderMgr->getGPUBuffer<IndexBuffer>(sp->iboid);
6280   VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6281 
6282   shaderPrg = I->G->ShaderMgr->Get_CylinderShader(I->info ? I->info->pass : 0);
6283 
6284   if (!shaderPrg){
6285     return;
6286   }
6287   attr_colors = shaderPrg->GetAttribLocation("a_Color");
6288   attr_colors2 = shaderPrg->GetAttribLocation("a_Color2");
6289 
6290   if (I->isPicking){
6291     pickable = SettingGet_i(I->G, I->set1, I->set2, cSetting_pickable);
6292     shaderPrg->Set1i("lighting_enabled", 0);
6293   }
6294   if (I->isPicking){
6295     vbo->maskAttributes({ attr_colors, attr_colors2 });
6296     if (pickable){
6297       // in first pass: 1st half of vbo, in second pass: 2nd half of vbo
6298       // first color (offset 0)
6299       pickvbo->bind(shaderPrg->id, I->pick_pass());
6300       // second color (offset 4)
6301       pickvbo->bind(shaderPrg->id, I->pick_pass() + SHADER_PICKING_PASSES_MAX);
6302     } else {
6303       assert(I->info->pick);
6304       unsigned char nopick[4] = {};
6305       I->info->pick->colorNoPick(nopick);
6306       glVertexAttrib4ubv(attr_colors, nopick);
6307       glVertexAttrib4ubv(attr_colors2, nopick);
6308     }
6309   }
6310 
6311   vbo->bind(shaderPrg->id);
6312   ibo->bind();
6313 
6314   if (min_alpha < 255) {
6315     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
6316     glDrawElements(GL_TRIANGLES, num_cyl * NUM_TOTAL_VERTICES_PER_CYLINDER, GL_C_INT_ENUM, 0);
6317     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
6318     glDepthFunc(GL_LEQUAL);
6319   }
6320   glDrawElements(GL_TRIANGLES, num_cyl * NUM_TOTAL_VERTICES_PER_CYLINDER, GL_C_INT_ENUM, 0);
6321 
6322   if (min_alpha < 255) {
6323     glDepthFunc(GL_LESS);
6324   }
6325 
6326   ibo->unbind();
6327   vbo->unbind();
6328   if (I->isPicking)
6329     pickvbo->unbind();
6330 }
6331 #include "Texture.h"
6332 
CGO_gl_draw_labels(CCGORenderer * I,CGO_op_data pc)6333 static void CGO_gl_draw_labels(CCGORenderer * I, CGO_op_data pc) {
6334   const cgo::draw::labels * sp = reinterpret_cast<decltype(sp)>(*pc);
6335 
6336   CShaderPrg * shaderPrg;
6337   int t_mode = SettingGetGlobal_i(I->G, cSetting_transparency_mode);
6338 
6339   if (t_mode==3 && I->info && !(I->info->pass<0)){
6340     // in transparency_mode=3, labels are drawn in the transparency pass=-1
6341     return;
6342   }
6343   shaderPrg = I->G->ShaderMgr->Get_LabelShader(I->info ? I->info->pass : 0);
6344   if (I->rep){
6345     float label_size;
6346     CSetting * set1 = NULL, * set2 = NULL;
6347     if (I->rep->cs) set1 = I->rep->cs->Setting;
6348     if (I->rep->obj) set2 = I->rep->obj->Setting;
6349     label_size = SettingGet_f(I->G, set1, set2, cSetting_label_size);
6350     shaderPrg->Set1f("scaleByVertexScale", label_size < 0.f ? 1.f : 0.f);
6351     if (label_size<0.f){
6352       shaderPrg->Set1f("labelTextureSize", (float)-2.f* I->info->texture_font_size/label_size);
6353     }
6354   }
6355 
6356   if (!shaderPrg){
6357     return;
6358   }
6359 
6360   VertexBuffer * vbo     = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6361   VertexBuffer * pickvbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->pickvboid);
6362 
6363   if (I->isPicking){
6364     pickvbo->bind(shaderPrg->id, I->pick_pass());
6365   }
6366 
6367   if (!vbo)
6368     return;
6369   vbo->bind(shaderPrg->id);
6370 
6371   glDrawArrays(GL_TRIANGLES, 0, sp->ntextures*6);
6372 
6373   vbo->unbind();
6374   pickvbo->unbind();
6375 }
6376 
CGO_gl_draw_connectors(CCGORenderer * I,CGO_op_data pc)6377 static void CGO_gl_draw_connectors(CCGORenderer * I, CGO_op_data pc) {
6378   int use_geometry_shaders = SettingGetGlobal_b(I->G, cSetting_use_geometry_shaders);
6379 
6380   const cgo::draw::connectors * sp = reinterpret_cast<decltype(sp)>(*pc);
6381 
6382   GLenum mode = GL_LINES;
6383   int factor = 2;
6384   float lineWidth;
6385   if (I->isPicking){
6386     return;
6387   }
6388   {
6389     GLenum err ;
6390     CHECK_GL_ERROR_OK("ERROR: CGO_gl_draw_connectors begin returns err=%d\n");
6391   }
6392 
6393   if (use_geometry_shaders){
6394     mode = GL_POINTS;
6395     factor = 1;
6396   } else {
6397     factor = 4;
6398   }
6399   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6400   if (!shaderPrg){
6401     return;
6402   }
6403   if (I->rep){
6404     float label_size;
6405     CSetting * set1 = NULL, * set2 = NULL;
6406     float v_scale = SceneGetScreenVertexScale(I->G, NULL);
6407     if (I->rep->cs) set1 = I->rep->cs->Setting;
6408     if (I->rep->obj) set2 = I->rep->obj->Setting;
6409     label_size = SettingGet_f(I->G, set1, set2, cSetting_label_size);
6410     shaderPrg->Set1f("scaleByVertexScale", label_size < 0.f ? 1.f : 0.f);
6411     lineWidth = SettingGet_f(I->G, set1, set2, cSetting_label_connector_width);
6412     if (label_size<0.f){
6413       shaderPrg->Set1f("textureToLabelSize", v_scale * (float)I->info->texture_font_size/label_size);
6414     } else {
6415       shaderPrg->Set1f("textureToLabelSize", 1.f);
6416     }
6417   } else {
6418     lineWidth = SettingGetGlobal_f(I->G, cSetting_label_connector_width);
6419   }
6420 #ifndef _WEBGL
6421   if (!use_geometry_shaders)
6422     glLineWidth(lineWidth);
6423 #endif
6424 
6425   VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6426   if (!vbo)
6427     return;
6428   vbo->bind(shaderPrg->id);
6429   glDrawArrays(mode, 0, sp->nconnectors*factor);
6430   vbo->unbind();
6431   {
6432     GLenum err ;
6433     CHECK_GL_ERROR_OK("ERROR: CGO_gl_draw_connectors end returns err=%d\n");
6434   }
6435 }
6436 
CGO_gl_draw_textures(CCGORenderer * I,CGO_op_data pc)6437 static void CGO_gl_draw_textures(CCGORenderer * I, CGO_op_data pc) {
6438   const cgo::draw::textures * sp = reinterpret_cast<decltype(sp)>(*pc);
6439   int ntextures = sp->ntextures;
6440   VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6441   CShaderPrg * shaderPrg;
6442   int attr_pickcolor = 0;
6443   shaderPrg = I->G->ShaderMgr->Get_LabelShader(I->info ? I->info->pass : 0);
6444   if (!shaderPrg){
6445     return;
6446   }
6447   if (I->isPicking){
6448     attr_pickcolor = shaderPrg->GetAttribLocation("attr_pickcolor");
6449   }
6450   if (attr_pickcolor){
6451     glBindBuffer(GL_ARRAY_BUFFER, 0);
6452     glEnableVertexAttribArray(attr_pickcolor);
6453     glVertexAttribPointer(attr_pickcolor, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_TRUE, 0, sp->floatdata);
6454   }
6455   vbo->bind(shaderPrg->id);
6456   glDrawArrays(GL_TRIANGLES, 0, ntextures*6);
6457   vbo->unbind();
6458   if (attr_pickcolor){
6459     glDisableVertexAttribArray(attr_pickcolor);
6460   }
6461 }
6462 
CGO_gl_draw_screen_textures_and_polygons(CCGORenderer * I,CGO_op_data pc)6463 static void CGO_gl_draw_screen_textures_and_polygons(CCGORenderer * I, CGO_op_data pc) {
6464   const cgo::draw::screen_textures * sp = reinterpret_cast<decltype(sp)>(*pc);
6465   int nverts = sp->nverts;
6466   CShaderPrg * shaderPrg;
6467 
6468   shaderPrg = I->G->ShaderMgr->Get_ScreenShader();
6469   if (!shaderPrg){
6470     return;
6471   }
6472 
6473   VertexBuffer * vb = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(sp->vboid);
6474   if (!vb)
6475     return;
6476   vb->bind(shaderPrg->id);
6477 
6478   glDrawArrays(GL_TRIANGLES, 0, nverts);
6479 
6480   vb->unbind();
6481 }
6482 
CGO_gl_draw_trilines(CCGORenderer * I,CGO_op_data pc)6483 static void CGO_gl_draw_trilines(CCGORenderer * I, CGO_op_data pc) {
6484   int nverts = CGO_get_int(*pc);
6485   int buffer = CGO_get_int(*pc+1);
6486   int a_vertex, a_othervertex, a_uv, a_color, a_color2;
6487   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6488   if (!shaderPrg){
6489     return;
6490   }
6491   a_vertex = 0; // a_Vertex is bound to 0 (see ShaderMgr) CShaderPrg_GetAttribLocation(shaderPrg, "a_Vertex");
6492   a_othervertex = shaderPrg->GetAttribLocation("a_OtherVertex");
6493   a_uv = shaderPrg->GetAttribLocation("a_UV");
6494   a_color = shaderPrg->GetAttribLocation("a_Color");
6495   a_color2 = shaderPrg->GetAttribLocation("a_Color2");
6496 
6497   glEnableVertexAttribArray(a_vertex);
6498   glEnableVertexAttribArray(a_othervertex);
6499   glEnableVertexAttribArray(a_uv);
6500   glEnableVertexAttribArray(a_color);
6501   glEnableVertexAttribArray(a_color2);
6502 
6503   glBindBuffer(GL_ARRAY_BUFFER, buffer);
6504 
6505   glVertexAttribPointer(a_vertex, 3, GL_FLOAT, GL_FALSE, 32, (const void *)0);
6506   glVertexAttribPointer(a_othervertex, 3, GL_FLOAT, GL_FALSE, 32, (const void *)12);
6507   glVertexAttribPointer(a_uv, 1, GL_FLOAT, GL_FALSE, 32, (const void *)24);
6508   glVertexAttribPointer(a_color, 4, GL_UNSIGNED_BYTE, GL_TRUE, 32, (const void *)28);
6509   glVertexAttribPointer(a_color2, 4, GL_UNSIGNED_BYTE, GL_TRUE, 32, (const void *)28);
6510   glDrawArrays(GL_TRIANGLES, 0, nverts);
6511 
6512   glDisableVertexAttribArray(a_vertex);
6513   glDisableVertexAttribArray(a_othervertex);
6514   glDisableVertexAttribArray(a_uv);
6515   glDisableVertexAttribArray(a_color);
6516   glDisableVertexAttribArray(a_color2);
6517 }
6518 
6519 /* CGO_gl_uniform3f - this is the implementation for the
6520  * CGOUniform3f/CGO_UNIFORM3F operation. From the uniform_id
6521  * it looks up the uniform location from the current shader,
6522  * and sets it to the values in this op.
6523  *
6524  */
CGO_gl_uniform3f(CCGORenderer * I,CGO_op_data pc)6525 static void CGO_gl_uniform3f(CCGORenderer * I, CGO_op_data pc) {
6526   int uniform_id = CGO_get_int(*pc);
6527   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6528   if (!shaderPrg){
6529     return;
6530   }
6531   int loc = shaderPrg->GetUniformLocation(
6532       shaderPrg->uniformLocations[uniform_id].c_str());
6533   const float *pcp = *pc + 1;
6534   glUniform3f(loc, pcp[0], pcp[1], pcp[2]);
6535 }
6536 
CGO_gl_linewidth(CCGORenderer * I,CGO_op_data pc)6537 static void CGO_gl_linewidth(CCGORenderer * I, CGO_op_data pc)
6538 {
6539 #ifndef _WEBGL
6540   glLineWidth(**pc);
6541 #endif
6542 }
6543 
6544 /*
6545  * call glLineWidth and set the "line_width" uniform
6546  */
glLineWidthAndUniform(float line_width,CShaderPrg * shaderPrg=NULL)6547 static void glLineWidthAndUniform(float line_width,
6548     CShaderPrg * shaderPrg=NULL) {
6549 #ifndef _WEBGL
6550   glLineWidth(line_width);
6551 #endif
6552 
6553   if (shaderPrg && shaderPrg->name == "trilines")
6554     shaderPrg->Set1f("line_width", line_width);
6555 }
6556 
6557 /* CGO_gl_special - this is the implementation function for
6558    CGOSpecial/CGO_SPECIAL.  Each op has its own implementation.
6559  */
CGO_gl_special(CCGORenderer * I,CGO_op_data pc)6560 static void CGO_gl_special(CCGORenderer * I, CGO_op_data pc)
6561 {
6562   int mode = CGO_get_int(*pc);
6563   bool openVR = SceneGetStereo(I->G) == cStereo_openvr;
6564   char varwidth = 0;
6565   float vScale = (I->info ? I->info->vertex_scale : SceneGetScreenVertexScale(I->G, NULL));
6566 
6567   CSetting *csSetting = NULL, *objSetting = NULL;
6568   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
6569   if (I->rep && I->rep->cs){
6570     csSetting = I->rep->cs->Setting;
6571   }
6572   if (I->rep && I->rep->obj){
6573     objSetting = I->rep->obj->Setting;
6574   }
6575   switch (mode){
6576   case LINEWIDTH_DYNAMIC_WITH_SCALE_RIBBON:
6577     {
6578       float line_width = SettingGet_f(I->G, NULL, NULL, cSetting_ribbon_width);
6579       if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width);
6580       if (I->info && I->info->width_scale_flag){
6581         line_width *= I->info->width_scale;
6582       }
6583       glLineWidthAndUniform(line_width, shaderPrg);
6584     }
6585     break;
6586   case LINEWIDTH_DYNAMIC_WITH_SCALE_DASH:
6587     {
6588       float line_width = SettingGet_f(I->G, NULL, NULL, cSetting_dash_width);
6589       if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width);
6590       if (I->info && I->info->width_scale_flag){
6591         line_width *= I->info->width_scale;
6592       }
6593       glLineWidthAndUniform(line_width, shaderPrg);
6594     }
6595     break;
6596   case LINEWIDTH_DYNAMIC_WITH_SCALE:
6597     {
6598       float line_width = SettingGet_f(I->G, NULL, NULL, cSetting_line_width);
6599       if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width);
6600       if (I->info && I->info->width_scale_flag){
6601         line_width *= I->info->width_scale;
6602       }
6603       glLineWidthAndUniform(line_width, shaderPrg);
6604     }
6605     break;
6606   case LINEWIDTH_WITH_SCALE:
6607     {
6608       float line_width = SettingGet_f(I->G, NULL, NULL, cSetting_line_width);
6609       if (I->info && I->info->width_scale_flag){
6610         line_width *= I->info->width_scale;
6611       }
6612       glLineWidthAndUniform(line_width, shaderPrg);
6613     }
6614     break;
6615   case LINEWIDTH_DYNAMIC_MESH:
6616     {
6617       float line_width;
6618       if (I->rep){
6619         line_width = SettingGet_f(I->G, I->rep->cs->Setting, I->rep->obj->Setting, cSetting_mesh_width);
6620       } else {
6621         line_width = SettingGet_f(I->G, NULL, NULL, cSetting_mesh_width);
6622       }
6623       if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width);
6624       glLineWidthAndUniform(line_width, shaderPrg);
6625     }
6626     break;
6627   case POINTSIZE_DYNAMIC_DOT_WIDTH:
6628     {
6629       float ps;
6630       if(I->info && I->info->width_scale_flag){
6631         ps = SettingGet_f
6632           (I->G, csSetting, objSetting,
6633            cSetting_dot_width) * I->info->width_scale;
6634       }
6635       else {
6636         ps = SettingGet_f
6637           (I->G, csSetting, objSetting, cSetting_dot_width);
6638       }
6639       glPointSize(ps);
6640       break;
6641     }
6642   case CYLINDERWIDTH_DYNAMIC_MESH:
6643     {
6644       CSetting *setting = NULL;
6645       float mesh_width;
6646       if (I && I->rep && I->rep->obj){
6647         setting = I->rep->obj->Setting;
6648       }
6649       mesh_width = SettingGet_f(I->G, setting, NULL, cSetting_mesh_width);
6650       if (shaderPrg) {
6651         const float * color = I->color ? I->color : g_ones4f;
6652 	shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylinders(I->G, I->info, mesh_width));
6653         shaderPrg->SetAttrib4fLocation("a_Color", color[0], color[1], color[2], I->alpha);
6654         shaderPrg->SetAttrib4fLocation("a_Color2", color[0], color[1], color[2], I->alpha);
6655       }
6656     }
6657     break;
6658   case DOTSIZE_WITH_SPHERESCALE:
6659     {
6660       float radius = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width);
6661       radius *= vScale;
6662       if (shaderPrg)
6663 	shaderPrg->Set1f("sphere_size_scale", fabs(radius));
6664     }
6665     break;
6666   case MESH_WIDTH_FOR_SURFACES:
6667     {
6668       float mesh_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_mesh_width);
6669       if (shaderPrg)
6670 	shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylinders(I->G, I->info, mesh_width));
6671     }
6672     break;
6673   case CYLINDER_WIDTH_FOR_DISTANCES:
6674     {
6675       float line_width, radius;
6676       int round_ends;
6677       round_ends =
6678         SettingGet_b(I->G, csSetting, objSetting, cSetting_dash_round_ends);
6679       line_width =
6680         SettingGet_f(I->G, csSetting, objSetting, cSetting_dash_width);
6681       radius =
6682         SettingGet_f(I->G, csSetting, objSetting, cSetting_dash_radius);
6683 
6684       line_width = SceneGetDynamicLineWidth(I->info, line_width);
6685 
6686       if (shaderPrg) {
6687 	if(radius == 0.0F) {
6688           float dash_size = SettingGet_f(I->G, csSetting, objSetting, cSetting_dash_width);
6689 	  shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylindersStatic(I->G, I->info, line_width, dash_size));
6690 	} else {
6691 	  shaderPrg->Set1f("uni_radius", radius);
6692 	}
6693 	if (!round_ends){
6694 	  shaderPrg->Set1i("no_flat_caps", 0);
6695 	}
6696       }
6697     }
6698     break;
6699   case CYLINDER_WIDTH_FOR_RIBBONS:
6700     {
6701       float pixel_scale_value = SettingGetGlobal_f(I->G, cSetting_ray_pixel_scale);
6702       float line_width, radius;
6703       line_width =
6704         SettingGet_f(I->G, csSetting, objSetting, cSetting_ribbon_width);
6705       radius =
6706         SettingGet_f(I->G, csSetting, objSetting, cSetting_ribbon_radius);
6707 
6708       line_width = SceneGetDynamicLineWidth(I->info, line_width);
6709       if(pixel_scale_value < 0)
6710         pixel_scale_value = 1.0F;
6711       if (shaderPrg) {
6712 	if(radius == 0.0F) {
6713 	  shaderPrg->Set1f("uni_radius", vScale * pixel_scale_value * line_width/ 2.f);
6714 	} else {
6715 	  shaderPrg->Set1f("uni_radius", radius);
6716 	}
6717       }
6718     }
6719     break;
6720   case DOT_WIDTH_FOR_DOTS:
6721     {
6722       float dot_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width);
6723       float radius;
6724       if(I->info && I->info->width_scale_flag)
6725         radius = (dot_width * I->info->width_scale);
6726       else
6727         radius= dot_width;
6728       if (shaderPrg)
6729 	shaderPrg->Set1f("g_PointSize", radius);
6730 #ifndef _PYMOL_IOS
6731       glPointSize(radius);
6732 #endif
6733     }
6734     break;
6735   case DOT_WIDTH_FOR_DOT_SPHERES:
6736     {
6737       float dotSize = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_radius);
6738       float dot_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width);
6739       float radius;
6740       if(I->info && dotSize <= 0.0F) {
6741         if(I->info->width_scale_flag)
6742           radius = dot_width * I->info->width_scale * I->info->vertex_scale / 1.4142F;
6743         else
6744           radius = dot_width * I->info->vertex_scale;
6745       } else {
6746         radius = dotSize;
6747       }
6748       if (shaderPrg)
6749 	shaderPrg->Set1f("sphere_size_scale", fabs(radius));
6750     }
6751     break;
6752   case CYLINDER_WIDTH_FOR_NONBONDED:
6753     {
6754       if (shaderPrg){
6755         float line_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_line_width);
6756         shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylindersStatic(I->G, I->info, line_width, line_width));
6757       }
6758     }
6759     break;
6760   case CYLINDER_WIDTH_FOR_REPWIRE_VARWIDTH:
6761     varwidth = 1;
6762   case CYLINDER_WIDTH_FOR_REPWIRE:
6763     {
6764       float radius = SettingGet_f(I->G, csSetting, objSetting, cSetting_line_radius);
6765       if (radius < R_SMALL8) {
6766         float line_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_line_width);
6767         float pixel_scale_value = SettingGetGlobal_f(I->G, cSetting_ray_pixel_scale);
6768         float vertex_scale = vScale;
6769         float scale_bound = SettingGetGlobal_f(I->G, cSetting_field_of_view)  * cPI / 180.0f * 0.018f;
6770         if (!varwidth){
6771           line_width = SceneGetDynamicLineWidth(I->info, line_width);
6772         }
6773         if (vertex_scale < scale_bound) {
6774           vertex_scale = scale_bound;
6775         }
6776         if(pixel_scale_value < 0)
6777           pixel_scale_value = 1.0F;
6778         radius = vertex_scale * pixel_scale_value * line_width / 2.f;
6779       }
6780       if (shaderPrg){
6781 	shaderPrg->Set1f("uni_radius", radius);
6782       }
6783     }
6784     break;
6785   case ENABLE_BACK_FACES_IF_NOT_TWO_SIDED:
6786     {
6787       int two_sided_lighting = SettingGet_i(I->G, csSetting, objSetting, cSetting_two_sided_lighting) > 0;
6788       if (!two_sided_lighting){
6789         glCullFace(GL_BACK);
6790         glEnable(GL_CULL_FACE);
6791       }
6792     }
6793     break;
6794   case DISABLE_BACK_FACES_IF_NOT_TWO_SIDED:
6795     {
6796       int two_sided_lighting = SettingGet_i(I->G, csSetting, objSetting, cSetting_two_sided_lighting) > 0;
6797       if (!two_sided_lighting){
6798         glDisable(GL_CULL_FACE);
6799       }
6800     }
6801     break;
6802   case SET_SURFACE_UNIFORMS:
6803     {
6804       float ambient_occlusion_scale = 0.f;
6805       int ambient_occlusion_mode = SettingGet_i(I->G, csSetting, objSetting, cSetting_ambient_occlusion_mode);
6806 
6807       if (ambient_occlusion_mode){
6808         ambient_occlusion_scale = SettingGet_f(I->G, csSetting, objSetting, cSetting_ambient_occlusion_scale);
6809       }
6810       if (shaderPrg)
6811 	shaderPrg->Set1f("ambient_occlusion_scale", ambient_occlusion_scale);
6812     }
6813     break;
6814   case SET_ALIGNMENT_UNIFORMS_ATTRIBS:
6815     {
6816       float linewidth = SettingGet_f(I->G, csSetting, objSetting, cSetting_cgo_line_width);
6817       float lineradius = SettingGet_f(I->G, csSetting, objSetting, cSetting_cgo_line_radius);
6818       float pixel_scale_value = SettingGetGlobal_f(I->G, cSetting_ray_pixel_scale);
6819       if (linewidth < 0.f){
6820         linewidth = 1.f;
6821       }
6822       if(pixel_scale_value < 0)
6823         pixel_scale_value = 1.0F;
6824       if (lineradius < 0.f){
6825         lineradius = linewidth * vScale * pixel_scale_value / 2.f;
6826       }
6827       shaderPrg->Set1f("uni_radius", lineradius);
6828       if (I->color){
6829         shaderPrg->SetAttrib4fLocation("a_Color", I->color[0], I->color[1], I->color[2], 1.f);
6830         shaderPrg->SetAttrib4fLocation("a_Color2", I->color[0], I->color[1], I->color[2], 1.f);
6831       }
6832       glLineWidthAndUniform(lineradius*2.f / vScale, shaderPrg);
6833     }
6834     break;
6835   case LINEWIDTH_FOR_LINES:
6836     {
6837       float line_width = SceneGetDynamicLineWidth(I->info,
6838           SettingGet_f(I->G, NULL, NULL, cSetting_line_width));
6839       if (I->info && I->info->width_scale_flag){
6840         line_width *= I->info->width_scale;
6841       }
6842       glLineWidthAndUniform(line_width, shaderPrg);
6843     }
6844     break;
6845   case SET_LABEL_SCALE_UNIFORMS:
6846   {
6847     if (I->rep){
6848       float label_size;
6849       CSetting * set1 = NULL, * set2 = NULL;
6850       if (I->rep->cs) set1 = I->rep->cs->Setting;
6851       if (I->rep->obj) set2 = I->rep->obj->Setting;
6852       label_size = SettingGet_f(I->G, set1, set2, cSetting_label_size);
6853       shaderPrg->Set1f("scaleByVertexScale", label_size < 0.f ? 1.f : 0.f);
6854       if (label_size<0.f){
6855         shaderPrg->Set1f("labelTextureSize", (float)-2.f* I->info->texture_font_size/label_size);
6856       }
6857     }
6858 
6859 
6860   }
6861   break;
6862   default:
6863     PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_special(): bad mode=%d\n", mode ENDFB(I->G);
6864   }
6865 }
6866 
6867 /* CGO_gl_special_with_arg - this is the implementation function for
6868    CGOSpecialWithArg/CGO_SPECIAL_WITH_ARG.  Each op has its own implementation.
6869  */
CGO_gl_special_with_arg(CCGORenderer * I,CGO_op_data pc)6870 static void CGO_gl_special_with_arg(CCGORenderer * I, CGO_op_data pc)
6871 {
6872 #ifndef PURE_OPENGL_ES_2
6873   int mode = CGO_get_int(*pc);
6874   float argval = *((*pc) + 1);
6875   bool use_shaders = SettingGetGlobal_b(I->G, cSetting_use_shaders);
6876   bool sphere_use_shaders = use_shaders && SettingGetGlobal_b(I->G, cSetting_use_shaders);
6877   switch(mode){
6878   case LINEWIDTH_FOR_LINES:
6879     {
6880       if (!use_shaders){
6881         glEnd();
6882         glLineWidth(argval);
6883         glBegin(GL_LINES);
6884       }
6885     }
6886     break;
6887   case LINE_LIGHTING:
6888     if (!I->isPicking && !SettingGetGlobal_b(I->G, cSetting_use_shaders)) {
6889       if (!I->info->line_lighting){
6890         bool enableLighting = (int)argval;
6891         if (enableLighting)
6892           glEnable(GL_LIGHTING);
6893         else
6894           glDisable(GL_LIGHTING);
6895       }
6896     }
6897     break;
6898   case SPHERE_MODE_OPS:
6899     {
6900       float pixel_scale = 1.0F / I->info->vertex_scale;
6901       int sphere_mode = (int)fabs(argval);
6902       bool enable = argval > 0.f;
6903       if (enable){
6904         float pointSize;
6905         if((sphere_mode == 1) || (sphere_mode == 6)) {
6906           pointSize = SettingGet_f(I->G, I->set1, I->set2, cSetting_sphere_point_size);
6907           glDisable(GL_POINT_SMOOTH);
6908           glDisable(GL_ALPHA_TEST);
6909           if (!I->isPicking && !sphere_use_shaders){
6910             glEnable(GL_LIGHTING);
6911             glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
6912           }
6913         } else {
6914           float sphere_scale = SettingGet_f(I->G, I->set1, I->set2, cSetting_sphere_scale);
6915           if((sphere_mode == 3) || (sphere_mode == 8)) {
6916             glEnable(GL_POINT_SMOOTH);
6917             glAlphaFunc(GL_GREATER, 0.5F);
6918             glEnable(GL_ALPHA_TEST);
6919             glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
6920             pointSize = sphere_scale * pixel_scale * 2.0F;
6921           } else {
6922             glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
6923             glDisable(GL_POINT_SMOOTH);
6924             glDisable(GL_ALPHA_TEST);
6925             pointSize = sphere_scale * pixel_scale * 1.4F;
6926           }
6927         }
6928         if(!I->isPicking && ((sphere_mode == 7) || (sphere_mode == 8)))
6929           glEnable(GL_LIGHTING);
6930         glPointSize(pointSize);
6931       } else {
6932         if(sphere_mode == 3) {
6933           glDisable(GL_POINT_SMOOTH);
6934           glAlphaFunc(GL_GREATER, 0.05F);
6935         } else {
6936           glEnable(GL_ALPHA_TEST);
6937         }
6938       }
6939     }
6940   }
6941 #endif
6942 }
6943 
CGO_gl_dotwidth(CCGORenderer * I,CGO_op_data pc)6944 static void CGO_gl_dotwidth(CCGORenderer * I, CGO_op_data pc)
6945 {
6946   glPointSize(**pc);
6947 }
6948 
CGO_gl_enable(CCGORenderer * I,CGO_op_data pc)6949 static void CGO_gl_enable(CCGORenderer * I, CGO_op_data pc)
6950 {
6951   GLenum mode = CGO_get_int(*pc);
6952   CShaderMgr *shaderMgr = I->G->ShaderMgr;
6953   CShaderPrg *shaderPrg = shaderMgr->Get_Current_Shader();
6954   if (I->use_shader){
6955     if (true){
6956       switch(mode){
6957       case CGO_GL_LIGHTING:
6958         {
6959           if (shaderPrg){
6960             shaderPrg->SetLightingEnabled(1);
6961           }
6962         }
6963         break;
6964       case GL_SHADER_LIGHTING:
6965         if (!I->isPicking){
6966           if (shaderPrg){
6967             shaderPrg->SetLightingEnabled(1);
6968           }
6969         }
6970         break;
6971       case GL_TWO_SIDED_LIGHTING:
6972         {
6973           if (shaderPrg){
6974             shaderPrg->Set1i("two_sided_lighting_enabled", 1);
6975           }
6976         }
6977         break;
6978       case GL_MESH_LIGHTING:
6979         {
6980           int lighting =
6981             SettingGet_i(I->G, I->set1, I->set2, cSetting_mesh_lighting);
6982           if (shaderPrg){
6983             shaderPrg->SetLightingEnabled(lighting);
6984           }
6985         }
6986         break;
6987       case GL_DOT_LIGHTING:
6988         {
6989           int lighting =
6990             SettingGet_i(I->G, I->set1, I->set2, cSetting_dot_lighting);
6991           if (shaderPrg && !I->isPicking){
6992             shaderPrg->SetLightingEnabled(lighting);
6993             shaderPrg->Set1i("two_sided_lighting_enabled", 0);
6994           }
6995         }
6996         break;
6997       case GL_LABEL_FLOAT_TEXT:
6998         {
6999           int float_text =
7000             SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels);
7001           if (float_text){
7002             glDisable(GL_DEPTH_TEST);
7003           }
7004         }
7005         break;
7006       case GL_DASH_TRANSPARENCY_DEPTH_TEST:
7007         {
7008           float dash_transparency =
7009             SettingGet_f(I->G, I->set1, I->set2, cSetting_dash_transparency);
7010           short dash_transparency_enabled;
7011           bool t_mode_3 =
7012             SettingGet_i(I->G, I->set1, I->set2, cSetting_transparency_mode) == 3;
7013           dash_transparency = (dash_transparency < 0.f ? 0.f : (dash_transparency > 1.f ? 1.f : dash_transparency));
7014           dash_transparency_enabled = (dash_transparency > 0.f);
7015           if (dash_transparency_enabled && !t_mode_3 && !I->isPicking){
7016             glDisable(GL_DEPTH_TEST);
7017           }
7018         }
7019         break;
7020       case GL_DEFAULT_SHADER:
7021         shaderMgr->Enable_DefaultShader(I->info ? I->info->pass : 0);
7022         break;
7023       case GL_LINE_SHADER:
7024         shaderMgr->Enable_LineShader(I->info ? I->info->pass : 0);
7025         break;
7026       case GL_SURFACE_SHADER:
7027         shaderMgr->Enable_SurfaceShader(I->info ? I->info->pass : 0);
7028         break;
7029       case GL_CYLINDER_SHADER:
7030         shaderMgr->Enable_CylinderShader(I->info ? I->info->pass : 0);
7031         break;
7032       case GL_SPHERE_SHADER:
7033         shaderMgr->Enable_DefaultSphereShader(I->info ? I->info->pass : 0);
7034         break;
7035       case GL_RAMP_SHADER:
7036         shaderMgr->Enable_RampShader();
7037         break;
7038       case GL_DEFAULT_SHADER_WITH_SETTINGS:
7039         shaderMgr->Enable_DefaultShaderWithSettings(I->set1, I->set2, I->info ? I->info->pass : 0);
7040         break;
7041       case GL_BACKGROUND_SHADER:
7042         shaderMgr->Enable_BackgroundShader();
7043         break;
7044       case GL_LABEL_SHADER:
7045         shaderMgr->Enable_LabelShader(I->info ? I->info->pass : 0);
7046         break;
7047       case GL_CONNECTOR_SHADER:
7048         shaderMgr->Enable_ConnectorShader(I->info ? I->info->pass : 0);
7049         break;
7050       case GL_SCREEN_SHADER:
7051         shaderMgr->Enable_ScreenShader();
7052         break;
7053       case GL_TRILINES_SHADER:
7054         shaderMgr->Enable_TriLinesShader();
7055         break;
7056 #ifndef _PYMOL_NO_AA_SHADERS
7057 #endif
7058       case GL_OIT_SHADER:
7059         shaderMgr->Enable_OITShader();
7060         break;
7061       case GL_OIT_COPY_SHADER:
7062         shaderMgr->Enable_OITCopyShader();
7063         break;
7064       case GL_BACK_FACE_CULLING:
7065         glCullFace(GL_BACK);
7066         glEnable(GL_CULL_FACE);
7067         break;
7068       case GL_DEPTH_TEST:
7069         glEnable(mode);
7070         break;
7071       case GL_DEPTH_TEST_IF_FLOATING:
7072         {
7073           int float_text = SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels);
7074           if(float_text)
7075             glEnable(GL_DEPTH_TEST);
7076         }
7077         break;
7078       }
7079     }
7080   } else {
7081     if (!I->isPicking){
7082       if (mode==CGO_GL_LIGHTING){
7083         glEnable(GL_LIGHTING);
7084       }
7085     }
7086   }
7087 }
7088 
CGO_gl_disable(CCGORenderer * I,CGO_op_data pc)7089 static void CGO_gl_disable(CCGORenderer * I, CGO_op_data pc)
7090 {
7091   GLenum mode = CGO_get_int(*pc);
7092   auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
7093   if (I->use_shader){
7094       switch(mode){
7095       case GL_SHADER_LIGHTING:
7096         {
7097           if (shaderPrg){
7098             shaderPrg->SetLightingEnabled(0);
7099           }
7100         }
7101         break;
7102       case GL_CYLINDER_SHADER:
7103         glDisable(GL_CULL_FACE);
7104       case GL_RAMP_SHADER:
7105       case GL_SCREEN_SHADER:
7106       case GL_LABEL_SHADER:
7107       case GL_CONNECTOR_SHADER:
7108       case GL_DEFAULT_SHADER:
7109       case GL_SURFACE_SHADER:
7110       case GL_SPHERE_SHADER:
7111       case GL_TRILINES_SHADER:
7112       case GL_OIT_COPY_SHADER:
7113       case GL_LINE_SHADER:
7114         I->G->ShaderMgr->Disable_Current_Shader();
7115         break;
7116       case GL_LABEL_FLOAT_TEXT:
7117         {
7118           int float_text =
7119             SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels);
7120           if (float_text){
7121             glEnable(GL_DEPTH_TEST);
7122           }
7123         }
7124         break;
7125       case GL_DASH_TRANSPARENCY_DEPTH_TEST:
7126         {
7127           float dash_transparency =
7128             SettingGet_f(I->G, I->set1, I->set2, cSetting_dash_transparency);
7129           short dash_transparency_enabled;
7130           bool t_mode_3 =
7131             SettingGet_i(I->G, I->set1, I->set2, cSetting_transparency_mode) == 3;
7132           dash_transparency = (dash_transparency < 0.f ? 0.f : (dash_transparency > 1.f ? 1.f : dash_transparency));
7133           dash_transparency_enabled = (dash_transparency > 0.f);
7134           if (dash_transparency_enabled && !t_mode_3 && !I->isPicking){
7135             glEnable(GL_DEPTH_TEST);
7136           }
7137         }
7138         break;
7139       case CGO_GL_LIGHTING:
7140         {
7141           if (shaderPrg){
7142             shaderPrg->SetLightingEnabled(0);
7143           }
7144         }
7145         break;
7146       case GL_TWO_SIDED_LIGHTING:
7147         {
7148           if (shaderPrg){
7149             shaderPrg->Set1i("two_sided_lighting_enabled", 0);
7150           }
7151         }
7152         break;
7153 #if !defined(PURE_OPENGL_ES_2) || defined(_WEBGL)
7154       case GL_OIT_SHADER:
7155       case GL_SMAA1_SHADER:
7156       case GL_SMAA2_SHADER:
7157         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, I->G->ShaderMgr->default_framebuffer_id);
7158         break;
7159 #endif
7160       case GL_BACK_FACE_CULLING:
7161         glDisable(GL_CULL_FACE);
7162         break;
7163       case GL_DEPTH_TEST:
7164         glDisable(mode);
7165         break;
7166       case GL_DEPTH_TEST_IF_FLOATING:
7167         {
7168           int float_text = SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels);
7169           if(float_text)
7170             glDisable(GL_DEPTH_TEST);
7171         }
7172         break;
7173       }
7174   } else {
7175     if (mode!=CGO_GL_LIGHTING || !I->isPicking){
7176         if (mode==CGO_GL_LIGHTING)
7177             mode = GL_LIGHTING;
7178       glDisable(mode);
7179     }
7180   }
7181 }
7182 
CGO_gl_alpha(CCGORenderer * I,CGO_op_data pc)7183 static void CGO_gl_alpha(CCGORenderer * I, CGO_op_data pc)
7184 {
7185   I->alpha = **pc;
7186 }
7187 
CGO_gl_reset_normal(CCGORenderer * I,CGO_op_data pc)7188 static void CGO_gl_reset_normal(CCGORenderer * I, CGO_op_data pc)
7189 {
7190   SceneResetNormalUseShader(I->G, CGO_get_int(*pc), I->use_shader);
7191 }
7192 
CGO_gl_null(CCGORenderer * I,CGO_op_data pc)7193 static void CGO_gl_null(CCGORenderer * I, CGO_op_data pc)
7194 {
7195 }
7196 
CGO_gl_error(CCGORenderer * I,CGO_op_data pc)7197 static void CGO_gl_error(CCGORenderer * I, CGO_op_data pc)
7198 {
7199   PRINTFB(I->G, FB_CGO, FB_Warnings)
7200   " CGO_gl_error() is not suppose to be called op=%d\n",
7201       CGO_get_int((*pc) - 1) ENDFB(I->G);
7202 }
7203 
CGO_gl_color(CCGORenderer * I,CGO_op_data varg)7204 static void CGO_gl_color(CCGORenderer* I, CGO_op_data varg)
7205 {
7206   auto* v = *varg;
7207   if (I->use_shader){
7208     auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
7209     if (shaderPrg){
7210       int attr_a_Color = shaderPrg->GetAttribLocation("a_Color");
7211       glVertexAttrib4f(attr_a_Color, v[0], v[1], v[2], I->alpha);
7212     }
7213   } else {
7214     glColor4f(v[0], v[1], v[2], I->alpha);
7215   }
7216 }
7217 
CGO_gl_sphere(CCGORenderer * I,CGO_op_data varg)7218 static void CGO_gl_sphere(CCGORenderer * I, CGO_op_data varg)
7219 {
7220   auto *v = *varg;
7221   if (I->isPicking){
7222     SphereRender(I->G, 0, v, I->color, I->alpha, v[3]);
7223   } else {
7224     SphereRender(I->G, I->sphere_quality, v, NULL, I->alpha, v[3]);
7225   }
7226 }
7227 
CGO_gl_vertex_attribute_3f(CCGORenderer * I,CGO_op_data varg)7228 static void CGO_gl_vertex_attribute_3f(CCGORenderer * I, CGO_op_data varg)
7229 {
7230     auto vertex_attr = reinterpret_cast<const cgo::draw::vertex_attribute_3f *>(*varg);
7231     auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
7232     int loc = shaderPrg->GetAttribLocation(I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx));
7233     if (loc >= 0)
7234       glVertexAttrib3fv(loc, vertex_attr->values);
7235 }
7236 
CGO_gl_vertex_attribute_4ub(CCGORenderer * I,CGO_op_data varg)7237 static void CGO_gl_vertex_attribute_4ub(CCGORenderer * I, CGO_op_data varg)
7238 {
7239     auto vertex_attr = reinterpret_cast<const cgo::draw::vertex_attribute_4ub *>(*varg);
7240     auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
7241     int loc = shaderPrg->GetAttribLocation(I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx));
7242     if (loc >= 0)
7243       glVertexAttrib4ubv(loc, vertex_attr->ubdata);
7244 }
7245 
CGO_gl_vertex_attribute_4ub_if_picking(CCGORenderer * I,CGO_op_data varg)7246 static void CGO_gl_vertex_attribute_4ub_if_picking(CCGORenderer * I, CGO_op_data varg)
7247 {
7248   if (I->isPicking){
7249     auto vertex_attr = reinterpret_cast<const cgo::draw::vertex_attribute_4ub_if_picking *>(*varg);
7250     auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
7251     int loc = shaderPrg->GetAttribLocation(I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx));
7252     if (loc >= 0)
7253       glVertexAttrib4ubv(loc, vertex_attr->ubdata);
7254   }
7255 }
7256 
CGO_gl_vertex_attribute_1f(CCGORenderer * I,CGO_op_data varg)7257 static void CGO_gl_vertex_attribute_1f(CCGORenderer * I, CGO_op_data varg)
7258 {
7259     auto vertex_attr = reinterpret_cast<const cgo::draw::vertex_attribute_1f *>(*varg);
7260     auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
7261     const char *name = I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx);
7262     int loc = shaderPrg->GetAttribLocation(name);
7263     if (loc >= 0)
7264       glVertexAttrib1f(loc, vertex_attr->value);
7265 }
7266 
7267 /* dispatch table for OpenGL */
7268 
7269 CGO_op_fn CGO_gl[] = {
7270   CGO_gl_null,                  /* 0x00 */
7271   CGO_gl_null,                  /* 0x01 */
7272   CGO_gl_begin,                 /* 0x02 */
7273   CGO_gl_end,                   /* 0x03 */
7274   CGO_gl_vertex,                /* 0x04 */
7275   CGO_gl_normal,                /* 0x05 */
7276   CGO_gl_color,                 /* 0x06 */
7277   CGO_gl_sphere,                /* 0x07 */
7278   CGO_gl_null,                  /* 0x08 */
7279   CGO_gl_null,                  /* 0x09 */
7280 
7281   CGO_gl_linewidth,             /* 0x0A */
7282   CGO_gl_null,                  /* 0x0B */
7283   CGO_gl_enable,                /* 0x0C */
7284   CGO_gl_disable,               /* 0x0D */
7285   CGO_gl_null,                  /* 0x0E */
7286   CGO_gl_null,                  /* 0x0F */
7287 
7288   CGO_gl_dotwidth,              /* 0X10 */
7289   CGO_gl_null,                  /* 0x11 */
7290   CGO_gl_null,                  /* 0x12 */
7291   CGO_gl_null,                  /* 0X13 */
7292 
7293   CGO_gl_null,                  /* 0X14 */
7294   CGO_gl_null,                  /* 0x15 */
7295   CGO_gl_null,                  /* 0x16 */
7296   CGO_gl_null,                  /* 0X17 */
7297 
7298   CGO_gl_null,                  /* 0X18 */
7299   CGO_gl_alpha,                 /* 0x19 */
7300   CGO_gl_null,                  /* 0x1A */
7301   CGO_gl_null,                  /* 0X1B */
7302   CGO_gl_draw_arrays,           /* 0x1C DrawArrays() */
7303   CGO_gl_null,                  /* 0x1D */
7304   CGO_gl_reset_normal,          /* 0x1E */
7305   CGO_gl_null,                  /* pick color  0X1F */
7306   CGO_gl_null,                  /* 0x20 draw buffers REMOVED */
7307   CGO_gl_draw_buffers_indexed,          /* 0x21 draw buffers indexed */
7308   CGO_gl_null,                  /* 0x22 bounding box */
7309   CGO_gl_draw_buffers_not_indexed,          /* 0x23 draw buffers not indexed */
7310   CGO_gl_special,                /* 0x24 special */
7311   CGO_gl_draw_cylinder_buffers,  /* 0x25 draw GLSL cylinders */
7312   CGO_gl_null,                  /* 0x26 shader cylinder */
7313   CGO_gl_null,                  /* 0x27 shader cylinder with 2nd color */
7314   CGO_gl_draw_sphere_buffers,   /* 0x28 draw sphere buffers */
7315   CGO_gl_null,                  /* 0x29 accessibility used for ambient occlusion */
7316   CGO_gl_error,          /* 0x2A draw texture */
7317   CGO_gl_draw_textures,          /* 0x2B draw textures */
7318   CGO_gl_draw_screen_textures_and_polygons,          /* 0x2C draw screen textures and polygons */
7319   CGO_gl_error,
7320   CGO_gl_error,  CGO_gl_draw_labels,
7321   CGO_gl_error,  CGO_gl_draw_connectors,  CGO_gl_draw_trilines,  CGO_gl_uniform3f,  CGO_gl_special_with_arg,
7322   CGO_gl_line,  CGO_gl_splitline,  CGO_gl_draw_custom,
7323   CGO_gl_vertex_attribute_3f, CGO_gl_vertex_attribute_4ub,
7324   CGO_gl_vertex_attribute_1f,
7325   CGO_gl_mask_attribute_if_picking, CGO_gl_bind_vbo_for_picking,
7326   CGO_gl_vertex,
7327   CGO_gl_null, // interpolated
7328   CGO_gl_vertex_cross, // CGO_VERTEX_CROSS
7329   CGO_gl_vertex_attribute_4ub_if_picking,
7330   CGO_gl_error
7331 };
7332 
7333 #if 0
7334 static
7335 void SetUCColorToPrev(uchar *color){
7336   color[0] = color[-4];
7337   color[1] = color[-3];
7338   color[2] = color[-2];
7339   color[3] = color[-1];
7340 }
7341 
7342 static
7343 void SetUCColorToPrev8(uchar *color){
7344   color[0] = color[-8];
7345   color[1] = color[-7];
7346   color[2] = color[-6];
7347   color[3] = color[-5];
7348 }
7349 #endif
7350 
7351 static
SetUCColorToPrevN(int n,uchar * color)7352 void SetUCColorToPrevN(int n, uchar *color){
7353   color[0] = color[-n*4];
7354   color[1] = color[-n*4+1];
7355   color[2] = color[-n*4+2];
7356   color[3] = color[-n*4+3];
7357 }
7358 
7359 static
get_pickcolorsset_ptr(int op,float * pc)7360 int * get_pickcolorsset_ptr(int op, float * pc) {
7361 #define RETURN_PICKCOLORSETPTR_CASE(cls) \
7362   case cgo::draw::cls::op_code: \
7363     return &(reinterpret_cast<cgo::draw::cls*>(pc)->pickcolorsset)
7364   switch (op) {
7365     RETURN_PICKCOLORSETPTR_CASE(buffers_indexed);
7366     RETURN_PICKCOLORSETPTR_CASE(buffers_not_indexed);
7367     RETURN_PICKCOLORSETPTR_CASE(labels);
7368     RETURN_PICKCOLORSETPTR_CASE(sphere_buffers);
7369     RETURN_PICKCOLORSETPTR_CASE(cylinder_buffers);
7370     RETURN_PICKCOLORSETPTR_CASE(custom);
7371   }
7372   return NULL;
7373 }
7374 
CGORenderGLPicking(CGO * I,RenderInfo * info,PickContext * context,CSetting * set1,CSetting * set2,Rep * rep)7375 void CGORenderGLPicking(CGO * I, RenderInfo *info, PickContext * context, CSetting * set1,
7376                         CSetting * set2, Rep *rep)
7377 {
7378   PyMOLGlobals *G = I->G;
7379 
7380   if (!G->ValidContext)
7381     return;
7382 
7383   if (!I->c)
7384     return;
7385 
7386   CCGORenderer *R = G->CGORenderer;
7387   bool pickable = (!I->no_pick) &&
7388     SettingGet_b(G, set1, set2, cSetting_pickable);
7389   auto pick = info->pick;
7390   bool reset_colors = !pick->pickColorsValid();
7391 
7392   R->use_shader = I->use_shader;
7393   R->isPicking = true;
7394   R->set1 = set1;
7395   R->set2 = set2;
7396   R->info = info;
7397   R->rep = rep;
7398 
7399 #ifndef _WEBGL
7400       glLineWidth(SettingGet_f(G, set1, set2, cSetting_cgo_line_width));
7401 #endif
7402 
7403   for (auto it = I->begin(); !it.is_stop(); ++it) {
7404     const auto op = it.op_code();
7405     CGO_OP_DATA_CONST float* pc = it.data();
7406 
7407     switch (op) {
7408       case CGO_COLOR:
7409         continue;
7410 
7411       case CGO_PICK_COLOR:
7412 
7413         if (reset_colors){ // only if picking info is invalid
7414           unsigned char col[4];
7415           AssignNewPickColor(I, pick, col, context, CGO_get_uint(pc),
7416               pickable ? CGO_get_int(pc + 1) : cPickableNoPick);
7417 #ifndef PURE_OPENGL_ES_2
7418           if (!I->use_shader){
7419             glColor4ubv(col);
7420           }
7421 #endif
7422         } else {
7423           PRINTFB(G, FB_CGO, FB_Warnings)
7424           " %s: unexpected CGO_PICK_COLOR with !reset_colors\n",
7425               __func__ ENDFB(G);
7426         }
7427         continue;
7428 
7429       case CGO_DRAW_ARRAYS:
7430         {
7431           const cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
7432           int arrays = sp->arraybits;
7433           if (reset_colors && arrays & CGO_PICK_COLOR_ARRAY){ // only if picking info is invalid
7434             int nverts = sp->nverts, v, idx = -1, bnd = -1;
7435             float *pca = sp->floatdata;
7436 
7437             if (arrays & CGO_VERTEX_ARRAY){ pca += nverts * 3; }
7438             if (arrays & CGO_NORMAL_ARRAY){ pca += nverts * 3; }
7439             if (arrays & CGO_COLOR_ARRAY){ pca += nverts * 4; }
7440 
7441             auto pickColorValsUC = (uchar*)pca;
7442             auto pickColorVals = (int*)(pca + nverts);
7443 
7444             for (v=0;v<nverts; v++) {
7445               bnd = pickable ? pickColorVals[v * 2 + 1] : cPickableNoPick;
7446               idx = pickColorVals[v * 2];
7447               AssignNewPickColor(
7448                   I, pick, pickColorValsUC + (v * 4), context, idx, bnd);
7449             }
7450           }
7451         }
7452         break;
7453 
7454       case CGO_DRAW_BUFFERS_INDEXED:
7455       case CGO_DRAW_BUFFERS_NOT_INDEXED:
7456       case CGO_DRAW_TEXTURES:
7457       case CGO_DRAW_LABELS:
7458       case CGO_DRAW_SPHERE_BUFFERS:
7459       case CGO_DRAW_CYLINDER_BUFFERS:
7460       case CGO_DRAW_CUSTOM:
7461         {
7462           int pickcolors_are_set = true;
7463           int* pickcolors_are_set_ptr = get_pickcolorsset_ptr(op, const_cast<float*>(pc));
7464           if (!pickcolors_are_set_ptr)
7465             pickcolors_are_set_ptr = &pickcolors_are_set;
7466 
7467           // TODO remove `pickcolorsset` fields from CGOs
7468           // This assert can fail during "Roving Detail" demo. However, I still
7469           // question the need of the `pickcolorsset` fields.
7470           // assert(reset_colors || *pickcolors_are_set_ptr);
7471 
7472           if (reset_colors || !*pickcolors_are_set_ptr){ // only if picking info is invalid
7473             int nverts = 0;
7474             int nvertsperfrag = 1;
7475             int v, pl;
7476             int bnd = cPickableNoPick;
7477             unsigned int idx = 0;
7478             int srcp;
7479             float *pca = nullptr;
7480             int *pickDataSrc ;
7481             uchar *pickColorDestUC = NULL;
7482             bool free_pick_color_dest = false;
7483             int destOffset = 0, bufsizemult = 1;
7484             size_t pickvbo = 0;
7485             switch (op){
7486               case CGO_DRAW_CUSTOM:
7487               {
7488                 const cgo::draw::custom * sp = reinterpret_cast<decltype(sp)>(pc);
7489                 nverts = sp->nverts;
7490                 pickvbo = sp->pickvboid;
7491                 if (!pickvbo)
7492                   continue;
7493                 pca = sp->floatdata;
7494                 nvertsperfrag = sp->vertsperpickinfo;
7495                 bufsizemult = sp->npickbufs;
7496 
7497                 pickColorDestUC = new uchar[bufsizemult * nverts * 4];
7498               }
7499                 break;
7500               case CGO_DRAW_BUFFERS_INDEXED:
7501               {
7502                 const cgo::draw::buffers_indexed * sp = reinterpret_cast<decltype(sp)>(pc);
7503                 nverts = sp->nverts;
7504                 pickvbo = sp->pickvboid;
7505                 pca = sp->floatdata;
7506               }
7507                 break;
7508               case CGO_DRAW_BUFFERS_NOT_INDEXED:
7509               {
7510                 const cgo::draw::buffers_not_indexed * sp = reinterpret_cast<decltype(sp)>(pc);
7511                 nverts = sp->nverts;
7512                 pickvbo = sp->pickvboid;
7513                 pca = sp->floatdata;
7514               }
7515                 break;
7516               case CGO_DRAW_SPHERE_BUFFERS:
7517               {
7518                 const cgo::draw::sphere_buffers * sp = reinterpret_cast<decltype(sp)>(pc);
7519                 nverts = sp->num_spheres * VERTICES_PER_SPHERE;
7520                 nvertsperfrag = VERTICES_PER_SPHERE;
7521                 pickvbo = sp->pickvboid;
7522                 pca = sp->floatdata;
7523 
7524                 pickColorDestUC = new uchar[nverts * 4];
7525               }
7526               break;
7527               case CGO_DRAW_CYLINDER_BUFFERS:
7528               {
7529                 const cgo::draw::cylinder_buffers * sp = reinterpret_cast<decltype(sp)>(pc);
7530                 nverts = sp->num_cyl * NUM_VERTICES_PER_CYLINDER;
7531                 nvertsperfrag = NUM_VERTICES_PER_CYLINDER;
7532                 pickvbo = sp->pickvboid;
7533                 pca = sp->floatdata;
7534                 bufsizemult = 2;
7535 
7536                 pickColorDestUC = new uchar[bufsizemult * nverts * 4];
7537               }
7538               break;
7539               case CGO_DRAW_TEXTURES:
7540               {
7541                 const cgo::draw::textures * sp = reinterpret_cast<decltype(sp)>(pc);
7542                 nverts = sp->ntextures * 6;
7543                 pca = sp->floatdata;
7544               }
7545               break;
7546               case CGO_DRAW_LABELS:
7547               {
7548                 const cgo::draw::labels * sp;
7549                 sp = reinterpret_cast<decltype(sp)>(pc);
7550                 nverts = sp->ntextures * 6;
7551                 pca = sp->floatdata;
7552                 pickvbo = sp->pickvboid;
7553               }
7554                 break;
7555               }
7556 
7557             if (pickColorDestUC) {
7558               free_pick_color_dest = true;
7559               pickDataSrc = (int*)(pca);
7560             } else {
7561               pickColorDestUC = (uchar*)pca;
7562               pickDataSrc = (int*)(pca + nverts);
7563             }
7564 
7565             destOffset = R->pick_pass() * sizeof(float) * nverts * bufsizemult;
7566 
7567             if (!pickable){
7568               for (int i = 0; i < nverts * bufsizemult; ++i) {
7569                 pick->colorNoPick(pickColorDestUC + 4 * i);
7570               }
7571             } else {
7572               int npickbufs = bufsizemult;
7573               int ploffsetforbuf = 0;
7574               if (op == CGO_DRAW_CYLINDER_BUFFERS){
7575                   // disabled 2016-07-19 TH: code looks almost identical to
7576                   // else branch and CGO_DRAW_CYLINDER_BUFFERS seem to be
7577                   // not used anymore.
7578                   PRINTFB(I->G, FB_CGO, FB_Errors)
7579                     " FIXME: SUPPOSEDLY UNUSED CODE EXECUTED in CGORenderGLPicking!\n"
7580                     ENDFB(I->G);
7581               } else {
7582                 if (op == CGO_DRAW_CUSTOM){
7583                   ploffsetforbuf = sizeof(float) * nverts; // for multiple picking attributes
7584                 }
7585                 for (v=0, pl = 0;v<nverts; v++, pl += 4){
7586                   if (v % nvertsperfrag){
7587                     // if same fragment, same color
7588                     for (int pi = 0; pi < npickbufs; ++pi){
7589                       int ploffset = ploffsetforbuf * pi;
7590                       SetUCColorToPrevN(1, &pickColorDestUC[pl+ploffset]);
7591                     }
7592                     continue;
7593                   }
7594 
7595                   int frag = (int)(v / nvertsperfrag);
7596                   for (int pi = 0; pi < npickbufs; ++pi){
7597                     int ploffset = ploffsetforbuf * pi;
7598                     srcp = 2* ((npickbufs * frag) + pi);
7599                     idx = pickDataSrc[srcp];
7600                     bnd = pickDataSrc[srcp + 1];
7601 
7602                     AssignNewPickColor(I, pick, &pickColorDestUC[pl + ploffset],
7603                         context, idx, bnd);
7604                   }
7605                 }
7606               }
7607             }
7608 
7609             if (pickvbo) {
7610               // reload entire vbo
7611               VertexBuffer * vbo = I->G->ShaderMgr->getGPUBuffer<VertexBuffer>(pickvbo);
7612               vbo->bufferReplaceData(destOffset, sizeof(float) * nverts * bufsizemult, pickColorDestUC);
7613               (*pickcolors_are_set_ptr) = true;
7614             }
7615 
7616             if (free_pick_color_dest){
7617               delete[] pickColorDestUC;
7618               pickColorDestUC = NULL;
7619               free_pick_color_dest = false;
7620             }
7621           }
7622         }
7623         break;
7624     }
7625 
7626     CGO_gl[op] (R, &pc);
7627   }
7628 
7629   R->isPicking = false;
7630 }
7631 
CGORenderGL(CGO * I,const float * color,CSetting * set1,CSetting * set2,RenderInfo * info,Rep * rep)7632 void CGORenderGL(CGO * I, const float *color, CSetting * set1, CSetting * set2,
7633                  RenderInfo * info, Rep *rep)
7634 /* this should be as fast as you can make it...
7635 
7636  * the ASM loop is about 2X long as raw looped GL calls,
7637 
7638  * but hopefully superscaler processors won't care */
7639 {
7640   PyMOLGlobals *G = I->G;
7641 
7642   const float zee[] = {0.f, 0.f, 1.f};
7643   const float color_tmp[] = {1.f, 1.f, 1.f};
7644 
7645   if (I->render_alpha){
7646     // for now, the render_alpha_only flag calls CGOSetZVector/CGORenderGLAlpha
7647     float *ModMatrix = SceneGetModMatrix(G);
7648     CGOSetZVector(I, ModMatrix[2], ModMatrix[6], ModMatrix[10]);
7649     CGORenderGLAlpha(I, info, 1);
7650     if (I->render_alpha == 1) // right now, render_alpha 1: renders alpha only, 2: renders both alpha and rest
7651       return;
7652   }
7653 
7654   if (!G->ValidContext) {
7655     return;
7656   }
7657 
7658   if (!I->c) {
7659     return;
7660   }
7661 
7662   {
7663     CCGORenderer *R = G->CGORenderer;
7664     R->info = info;
7665     R->use_shader = I->use_shader;
7666     R->debug = I->debug;
7667     R->sphere_quality = I->sphere_quality;
7668     R->rep = rep;
7669     R->color = color;
7670     R->alpha = 1.0F - SettingGet_f(G, set1, set2, cSetting_cgo_transparency);
7671     R->set1 = set1;
7672     R->set2 = set2;
7673     // normals should be initialized to the view vector
7674     // (changed BB 9/14 from SceneResetNormalUseShader(), to CScene->LinesNormal, which was arbitrary, I believe)
7675     SceneResetNormalToViewVector(I->G, I->use_shader);
7676 
7677     if (!color) {
7678       color = color_tmp;
7679     }
7680 
7681     {
7682       auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
7683       if (shaderPrg && I->use_shader) {
7684           shaderPrg->SetAttrib4fLocation("a_Color", color[0], color[1], color[2], R->alpha);
7685       }
7686       else {
7687           glColor4f(color[0], color[1], color[2], R->alpha);
7688       }
7689     }
7690 
7691 #ifndef PURE_OPENGL_ES_2
7692     const float width_scale = (info && info->width_scale_flag) ? info->width_scale : 1.f;
7693     glLineWidth(SettingGet_f(G, set1, set2, cSetting_cgo_line_width) * width_scale);
7694     glPointSize(SettingGet_f(G, set1, set2, cSetting_cgo_dot_width) * width_scale);
7695 #endif
7696 
7697     if (!(info && info->alpha_cgo)) {
7698       // Regular CGO dispatch table rendering
7699       for (auto it = I->begin(); !it.is_stop(); ++it) {
7700         const auto op = it.op_code();
7701         assert(op < CGO_sz_size());
7702         CGO_OP_DATA_CONST float* const pc = it.data();
7703 
7704         CGO_gl[op](R, &pc);
7705       }
7706       return;
7707     }
7708 
7709     /* we're sorting transparent triangles globally */
7710     {
7711       {
7712         int mode = -1;
7713         int vc = 0;
7714         // triangle normals
7715         const float *n0 = NULL, *n1 = NULL, *n2 = NULL;
7716         // triangle vertices
7717         const float *v0 = NULL, *v1 = NULL, *v2 = NULL;
7718         // triangle colors
7719         const float *c0 = color, *c1 = NULL, *c2 = NULL;
7720 
7721         for (auto it = I->begin(); !it.is_stop(); ++it) {
7722           const auto op = it.op_code();
7723           assert(op < CGO_sz_size());
7724           CGO_OP_DATA_CONST float* const pc = it.data();
7725 
7726           if((R->alpha != 1.f)) {
7727             switch (op) {       /* transparency */
7728             case CGO_BEGIN:
7729               mode = CGO_get_int(pc);
7730               CGO_gl_begin(R, &pc);
7731               vc = 0;
7732               n0 = zee;
7733               break;
7734             case CGO_END:
7735               CGO_gl_end(R, &pc);
7736               mode = -1;
7737               break;
7738             case CGO_NORMAL:
7739               switch (mode) {
7740               case GL_TRIANGLES:
7741               case GL_TRIANGLE_STRIP:
7742               case GL_TRIANGLE_FAN:
7743                 n0 = pc;
7744                 break;
7745               default:
7746                 CGO_gl_normal(R, &pc);
7747               }
7748               break;
7749             case CGO_COLOR:
7750               c0 = pc;
7751               CGO_gl_color(R, &pc);
7752               break;
7753             case CGO_TRIANGLE:
7754               CGOAlphaTriangle(info->alpha_cgo,
7755                                pc, pc + 3, pc + 6, pc + 9, pc + 12, pc + 15, pc + 18,
7756                                pc + 21, pc + 24, R->alpha, R->alpha, R->alpha, false);
7757               break;
7758 	    case CGO_DRAW_ARRAYS:
7759 	      {
7760                 const cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
7761 		int mode = sp->mode, arrays = sp->arraybits, nverts = sp->nverts;
7762 		float *vertexVals = 0, *nxtVals = 0, *colorVals = 0, *normalVals;
7763 		float *vertexVals_tmp = 0, *colorVals_tmp = 0, *normalVals_tmp = 0;
7764 		int step;
7765 		short nxtn = 3;
7766 		nxtVals = vertexVals = vertexVals_tmp = sp->floatdata;
7767 		if (arrays & CGO_NORMAL_ARRAY){
7768 		  nxtVals = normalVals = normalVals_tmp = vertexVals + (nxtn*nverts);
7769 		}
7770 		if (arrays & CGO_COLOR_ARRAY){
7771 		  nxtVals = colorVals = colorVals_tmp = nxtVals + (nxtn*nverts);
7772 		  nxtn = 4;
7773 		}
7774 		switch (mode){
7775 		case GL_TRIANGLES:
7776 		  {
7777 		    for (step = 0; step < nverts; step += 3){
7778 		      if (colorVals_tmp){
7779 			c0 = colorVals_tmp; c1 = colorVals_tmp+4; c2 = colorVals_tmp+8;
7780 		      } else {
7781 			c1 = c2 = c0;
7782 		      }
7783 		      if (normalVals_tmp){
7784 			n0 = normalVals_tmp; n1 = normalVals_tmp+3; n2 = normalVals_tmp+6;
7785 		      } else {
7786 			n1 = n2 = n0;
7787 		      }
7788 		      CGOAlphaTriangle(info->alpha_cgo,
7789 				       vertexVals_tmp, vertexVals_tmp+3, vertexVals_tmp+6,
7790 				       n0, n1, n2,
7791 				       c0, c1, c2,
7792 				       R->alpha, R->alpha, R->alpha, false);
7793 		      vertexVals_tmp += 9;
7794 		      if (normalVals_tmp){
7795 			normalVals_tmp += 9;
7796 		      }
7797 		      if (colorVals_tmp){
7798 			colorVals_tmp += 12;
7799 		      }
7800 		    }
7801 		  }
7802 		  break;
7803 		case GL_TRIANGLE_STRIP:
7804 		  {
7805 		    if (colorVals_tmp){
7806 		      c1 = colorVals_tmp; c2 = colorVals_tmp+4;
7807 		      colorVals_tmp += 8;
7808 		    } else {
7809 		      c1 = c2 = c0;
7810 		    }
7811 		    if (normalVals_tmp){
7812 		      n1 = normalVals_tmp; n2 = normalVals_tmp+3;
7813 		      normalVals_tmp+= 6;
7814 		    } else {
7815 		      n1 = n2 = n0;
7816 		    }
7817 		    vertexVals_tmp += 6;
7818 		    for (step = 2; step < nverts; step++){
7819 		      if (colorVals_tmp){
7820 			c0 = c1; c1 = c2; c2 = colorVals_tmp;
7821 		      }
7822 		      if (normalVals_tmp){
7823 			n0 = n1; n1 = n2; n2 = normalVals_tmp;
7824 		      }
7825 		      CGOAlphaTriangle(info->alpha_cgo,
7826 				       vertexVals_tmp-6, vertexVals_tmp-3, vertexVals_tmp,
7827 				       n0, n1, n2,
7828 				       c0, c1, c2,
7829 				       R->alpha, R->alpha, R->alpha, false);
7830 		      vertexVals_tmp += 3;
7831 		      if (normalVals_tmp){
7832 			normalVals_tmp += 3;
7833 		      }
7834 		      if (colorVals_tmp){
7835 			colorVals_tmp += 4;
7836 		      }
7837 		    }
7838 		  }
7839 		  break;
7840 		case GL_TRIANGLE_FAN:
7841 		  {
7842 		    float *firstVertex = vertexVals_tmp;
7843 		    if (colorVals_tmp){
7844 		      c0 = colorVals_tmp;
7845 		      c2 = colorVals_tmp + 4;
7846 		      colorVals_tmp += 8;
7847 		    } else {
7848 		      c1 = c2 = c0;
7849 		    }
7850 		    if (normalVals_tmp){
7851 		      n0 = normalVals_tmp;
7852 		      n2 = normalVals_tmp + 3;
7853 		      normalVals_tmp += 6;
7854 		    }
7855 		    vertexVals_tmp += 6;
7856 		    for (step = 2; step < nverts; step++){
7857 		      if (colorVals_tmp){
7858 			c1 = c2; c2 = colorVals_tmp;
7859 		      }
7860 		      if (normalVals_tmp){
7861 			n1 = n2; n2 = normalVals_tmp;
7862 		      }
7863 		      CGOAlphaTriangle(info->alpha_cgo,
7864 				       firstVertex, vertexVals_tmp-3, vertexVals_tmp,
7865 				       n0, n1, n2,
7866 				       c0, c1, c2,
7867 				       R->alpha, R->alpha, R->alpha, false);
7868 		      vertexVals_tmp += 3;
7869 		      if (normalVals_tmp){
7870 			normalVals_tmp += 3;
7871 		      }
7872 		      if (colorVals_tmp){
7873 			colorVals_tmp += 4;
7874 		      }
7875 		    }
7876 		  }
7877 		  break;
7878 		}
7879 	      }
7880 	      break;
7881             case CGO_VERTEX:
7882               v0 = pc;
7883               switch (mode) {
7884               case GL_TRIANGLES:
7885                 if(3 * ((vc + 1) / 3) == vc + 1) {
7886                   CGOAlphaTriangle(info->alpha_cgo,
7887                                    v0, v1, v2, n0, n1, n2, c0, c1, c2,
7888                                    R->alpha, R->alpha, R->alpha, true);
7889                 }
7890                 v2 = v1;
7891                 c2 = c1;
7892                 n2 = n1;
7893                 v1 = v0;
7894                 c1 = c0;
7895                 n1 = n0;
7896                 vc++;
7897                 break;
7898               case GL_TRIANGLE_STRIP:
7899                 if(vc > 1) {
7900                   CGOAlphaTriangle(info->alpha_cgo,
7901                                    v0, v1, v2, n0, n1, n2, c0, c1, c2,
7902                                    R->alpha, R->alpha, R->alpha, !(vc & 0x1));
7903                 }
7904                 v2 = v1;
7905                 c2 = c1;
7906                 n2 = n1;
7907                 v1 = v0;
7908                 c1 = c0;
7909                 n1 = n0;
7910                 vc++;
7911                 break;
7912               case GL_TRIANGLE_FAN:
7913                 if(vc > 1) {
7914                   CGOAlphaTriangle(info->alpha_cgo,
7915                                    v0, v1, v2, n0, n1, n2, c0, c1, c2,
7916                                    R->alpha, R->alpha, R->alpha, false);
7917                 } else if(!vc) {
7918                   n2 = n0;
7919                   v2 = v0;
7920                   c2 = c0;
7921                 }
7922                 v1 = v0;
7923                 c1 = c0;
7924                 n1 = n0;
7925                 vc++;
7926                 break;
7927               default:
7928                 CGO_gl_vertex(R, &pc);
7929                 break;
7930               }
7931               break;
7932             default:
7933               CGO_gl[op] (R, &pc);
7934               break;
7935             }
7936           } else {              /* opaque */
7937 	    switch(op){
7938 	    case CGO_COLOR:
7939 	      /* Since CGO operations are done in sequence, alpha could happen
7940 		 after color is set.  In this case, we still need to keep track of the color
7941 		 in case there is a transparent object */
7942 	      c0 = pc;
7943 	      break;
7944 	    default:
7945 	      break;
7946 	    }
7947             CGO_gl[op] (R, &pc);
7948           }
7949         }
7950       }
7951     }
7952   }
7953 }
7954 
CGORenderGLAlpha(CGO * I,RenderInfo * info,bool calcDepth)7955 void CGORenderGLAlpha(CGO * I, RenderInfo * info, bool calcDepth)
7956 {
7957   PyMOLGlobals *G = I->G;
7958   if(G->ValidContext && I->c) {
7959     int mode = GL_TRIANGLES;
7960     if (I->debug){
7961       mode = CGOConvertDebugMode(I->debug, GL_TRIANGLES);
7962     }
7963 #ifndef PURE_OPENGL_ES_2
7964     // not sure why shader is set, but disable it for now,
7965     // since we are doing immediate mode rendering for global transparency
7966     G->ShaderMgr->Disable_Current_Shader();
7967 #endif
7968     /* 1. transform and measure range (if not already known)
7969        2. bin into linked lists based on Z-centers
7970        3. render by layer */
7971 
7972     if(I->z_flag) {
7973       if(!I->i_start) {
7974         I->i_size = 256;
7975         I->i_start = pymol::calloc<int>(I->i_size);
7976       } else {
7977         UtilZeroMem(I->i_start, sizeof(int) * I->i_size);
7978       }
7979       {
7980         const int i_size = I->i_size;
7981         const float* base = I->op;
7982         int *start = I->i_start;
7983         int delta = 1, ntris = 0;
7984         /* bin the triangles */
7985 	if (calcDepth){
7986           for (auto it = I->begin(); !it.is_stop(); ++it) {
7987             if (it.op_code() == CGO_ALPHA_TRIANGLE) {
7988               float* const pc = it.data();
7989               const float z = dot_product3f(pc + 1, I->z_vector);
7990 	      if(z > I->z_max)
7991 		I->z_max = z;
7992 	      if(z < I->z_min)
7993 		I->z_min = z;
7994 	      pc[4] = z;
7995 	      ntris++;
7996 	    }
7997           }
7998         }
7999 
8000         const float range_factor = (0.9999F * i_size) / (I->z_max - I->z_min);
8001 
8002         for (auto it = I->begin(); !it.is_stop(); ++it) {
8003           if (it.op_code() == CGO_ALPHA_TRIANGLE) {
8004             float* const pc = it.data();
8005             assert(base < pc && pc < I->op + I->c);
8006             auto i = pymol::clamp<int>((pc[4] - I->z_min) * range_factor, 0, i_size);
8007             CGO_put_int(pc, start[i]);
8008             start[i] = (pc - base);     /* NOTE: will always be > 0 since we have CGO_read_int'd */
8009           }
8010         }
8011 
8012         // for single-layer transparency, render front-to-back
8013         if(SettingGetGlobal_i(G, cSetting_transparency_mode) == 2) {
8014           delta = -1;
8015           start += (i_size - 1);
8016         }
8017 
8018         /* now render by bin */
8019 #ifndef PURE_OPENGL_ES_2
8020         glBegin(mode);
8021         for (int i = 0; i < i_size; i++) {
8022           int ii = *start;
8023           start += delta;
8024           while(ii) {
8025             const float* pc = base + ii;
8026             glColor4fv(pc + 23);
8027             glNormal3fv(pc + 14);
8028             glVertex3fv(pc + 5);
8029             glColor4fv(pc + 27);
8030             glNormal3fv(pc + 17);
8031             glVertex3fv(pc + 8);
8032             glColor4fv(pc + 31);
8033             glNormal3fv(pc + 20);
8034             glVertex3fv(pc + 11);
8035 
8036             ii = CGO_get_int(pc);
8037           }
8038         }
8039         glEnd();
8040 #endif
8041       }
8042     } else {
8043 #ifndef PURE_OPENGL_ES_2
8044       glBegin(mode);
8045       for (auto it = I->begin(); !it.is_stop(); ++it) {
8046         if (it.op_code() == CGO_ALPHA_TRIANGLE) {
8047           float* const pc = it.data();
8048           glColor4fv(pc + 23);
8049           glNormal3fv(pc + 14);
8050           glVertex3fv(pc + 5);
8051           glColor4fv(pc + 27);
8052           glNormal3fv(pc + 17);
8053           glVertex3fv(pc + 8);
8054           glColor4fv(pc + 31);
8055           glNormal3fv(pc + 20);
8056           glVertex3fv(pc + 11);
8057         }
8058       }
8059       glEnd();
8060 #endif
8061     }
8062   }
8063 }
8064 
8065 
8066 /* translation function which turns cylinders and spheres into triangles */
8067 
CGOSimpleSphere(CGO * I,const float * v,float vdw,short sphere_quality)8068 static int CGOSimpleSphere(CGO * I, const float *v, float vdw, short sphere_quality)
8069 {
8070   SphereRec *sp;
8071   int *q, *s;
8072   int b, c;
8073   int ok = true;
8074   /* cgo_sphere_quality is between 0 and (NUMBER_OF_SPHERE_LEVELS-1) */
8075 
8076   sp = I->G->Sphere->Sphere[CLAMPVALUE<short>(sphere_quality, 0, (NUMBER_OF_SPHERE_LEVELS-1)) ];
8077 
8078   q = sp->Sequence;
8079   s = sp->StripLen;
8080 
8081   for(b = 0; b < sp->NStrip; b++) {
8082     if (ok)
8083       ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
8084     for(c = 0; ok && c < (*s); c++) {
8085       ok &= CGONormalv(I, sp->dot[*q]);
8086       if (ok)
8087 	ok &= CGOVertex(I, v[0] + vdw * sp->dot[*q][0],
8088 			v[1] + vdw * sp->dot[*q][1], v[2] + vdw * sp->dot[*q][2]);
8089       q++;
8090     }
8091     if (ok)
8092       ok &= CGOEnd(I);
8093     s++;
8094   }
8095   return ok;
8096 }
8097 
CGOSimpleQuadric(CGO * I,const float * v,float r,const float * q)8098 static int CGOSimpleQuadric(CGO * I, const float *v, float r, const float *q)
8099 {
8100   float r_el, n0[3], n1[3], n2[3];
8101   int ok = true;
8102   if(CGOQuadricToEllipsoid(v, r, q, &r_el, n0, n1, n2))
8103     ok &= CGOSimpleEllipsoid(I, v, r_el, n0, n1, n2);
8104   return ok;
8105 }
8106 
CGOSimpleEllipsoid(CGO * I,const float * v,float vdw,const float * n0,const float * n1,const float * n2)8107 static int CGOSimpleEllipsoid(CGO* I, const float* v, float vdw,
8108     const float* n0, const float* n1, const float* n2)
8109 {
8110   SphereRec *sp;
8111   int *q, *s;
8112   int b, c;
8113   int ds;
8114   float nn0[3], nn1[3], nn2[3];
8115   float scale[3], scale_sq[3];
8116   int ok = true;
8117 
8118   normalize23f(n0, nn0);
8119   normalize23f(n1, nn1);
8120   normalize23f(n2, nn2);
8121 
8122   scale[0] = (float) length3f(n0);
8123   scale[1] = (float) length3f(n1);
8124   scale[2] = (float) length3f(n2);
8125 
8126   scale_sq[0] = scale[0] * scale[0];
8127   scale_sq[1] = scale[1] * scale[1];
8128   scale_sq[2] = scale[2] * scale[2];
8129 
8130   ds = SettingGet_i(I->G, NULL, NULL, cSetting_cgo_ellipsoid_quality);
8131   if(ds < 0)
8132     ds = SettingGet_i(I->G, NULL, NULL, cSetting_ellipsoid_quality);
8133   if(ds < 0)
8134     ds = 0;
8135   if(ds > 3)
8136     ds = 3;
8137   sp = I->G->Sphere->Sphere[ds];
8138 
8139   q = sp->Sequence;
8140   s = sp->StripLen;
8141 
8142   for(b = 0; b < sp->NStrip; b++) {
8143     ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
8144     for(c = 0; ok && c < (*s); c++) {
8145       float *sp_dot_q = sp->dot[*q];
8146       float s0 = vdw * sp_dot_q[0];
8147       float s1 = vdw * sp_dot_q[1];
8148       float s2 = vdw * sp_dot_q[2];
8149       float d0[3], d1[3], d2[3], vv[3], direction[3];
8150       float dd0, dd1, dd2, ss0, ss1, ss2;
8151       float comp0[3], comp1[3], comp2[3];
8152       float surfnormal[3];
8153       int i;
8154 
8155       scale3f(n0, s0, d0);
8156       scale3f(n1, s1, d1);
8157       scale3f(n2, s2, d2);
8158 
8159       for(i = 0; i < 3; i++) {
8160 	vv[i] = d0[i] + d1[i] + d2[i];
8161       }
8162       normalize23f(vv, direction);
8163       add3f(v, vv, vv);
8164 
8165       dd0 = dot_product3f(direction, nn0);
8166       dd1 = dot_product3f(direction, nn1);
8167       dd2 = dot_product3f(direction, nn2);
8168 
8169       if(scale[0] > R_SMALL8) {
8170 	ss0 = dd0 / scale_sq[0];
8171       } else {
8172 	ss0 = 0.0F;
8173       }
8174       if(scale[1] > R_SMALL8) {
8175 	ss1 = dd1 / scale_sq[1];
8176       } else {
8177 	ss1 = 0.0F;
8178       }
8179 
8180       if(scale[2] > R_SMALL8) {
8181 	ss2 = dd2 / scale_sq[2];
8182       } else {
8183 	ss2 = 0.0F;
8184       }
8185 
8186       scale3f(nn0, ss0, comp0);
8187       scale3f(nn1, ss1, comp1);
8188       scale3f(nn2, ss2, comp2);
8189 
8190       for(i = 0; i < 3; i++) {
8191 	surfnormal[i] = comp0[i] + comp1[i] + comp2[i];
8192       }
8193       normalize3f(surfnormal);
8194 
8195       ok &= CGONormalv(I, surfnormal);
8196       if (ok)
8197 	ok &= CGOVertexv(I, vv);
8198       q++;
8199     }
8200     if (ok)
8201       ok &= CGOEnd(I);
8202     s++;
8203   }
8204   return ok;
8205 }
8206 
8207 /*
8208  * Triangulated round cap (half-globe)
8209  */
CGORoundNub(CGO * I,const float * v1,const float * p0,const float * p1,const float * p2,int direction,int nEdge,float size)8210 void CGORoundNub(CGO * I,
8211     const float *v1,    // cap center
8212     const float *p0,    // normal along axis
8213     const float *p1,    // x coord in cap space
8214     const float *p2,    // y coord in cap space
8215     int direction,      // 1 or -1
8216     int nEdge,          // "quality"
8217     float size)
8218 {
8219   const int cmax = (nEdge + 3) / 2;
8220   const float PI_over_cmax = PI / ((cmax - 1) * 2);
8221   const float PI_over_nEdge = (PI * 2) / nEdge;
8222   float z2 = 1.f;
8223 
8224   // z coord in cap space
8225   float p3[3];
8226   scale3f(p0, direction, p3);
8227 
8228   CGOBegin(I, GL_TRIANGLE_STRIP);
8229 
8230   // from equator to pole (latitudinal)
8231   for (int c = 1; c < cmax; c += 1){
8232     float z1 = z2;
8233     z2 = cos(c * PI_over_cmax);
8234 
8235     // around cylinder axis (longitudinal)
8236     for (int d = (nEdge + 1) * (-direction); d; d += direction){
8237       float z3 = z1;
8238 
8239       // 2 vertices
8240       for (int e = -1; e < 1; ++e) {
8241         float x = cos(d * PI_over_nEdge) * sin((c + e) * PI_over_cmax);
8242         float y = sin(d * PI_over_nEdge) * sin((c + e) * PI_over_cmax);
8243         float normal[3], vertex[3];
8244 
8245         normal[0] = p1[0] * x + p2[0] * y + p3[0] * z3;
8246         normal[1] = p1[1] * x + p2[1] * y + p3[1] * z3;
8247         normal[2] = p1[2] * x + p2[2] * y + p3[2] * z3;
8248 
8249         vertex[0] = v1[0] + normal[0] * size;
8250         vertex[1] = v1[1] + normal[1] * size;
8251         vertex[2] = v1[2] + normal[2] * size;
8252 
8253         normalize3f(normal);
8254         CGONormalv(I, normal);
8255         CGOVertexv(I, vertex);
8256 
8257         z3 = z2;
8258       }
8259     }
8260   }
8261 
8262   CGOEnd(I);
8263 }
8264 
CGOSimpleCylinder(CGO * I,const float * v1,const float * v2,const float tube_size,const float * c1,const float * c2,const float alpha1,const float alpha2,const bool interp,const int cap1,const int cap2,const Pickable * pickcolor2,const bool stick_round_nub)8265 static int CGOSimpleCylinder(CGO * I, const float *v1, const float *v2, const float tube_size,
8266                              const float *c1, const float *c2, const float alpha1,
8267                              const float alpha2, const bool interp, const int cap1, const int cap2,
8268                              const Pickable *pickcolor2, const bool stick_round_nub)
8269 {
8270 #define MAX_EDGE 50
8271 
8272   float d[3], t[3], p0[3], p1[3], p2[3], vv1[3], vv2[3], v_buf[9], *v;
8273   float x[MAX_EDGE + 1], y[MAX_EDGE + 1];
8274   float overlap;
8275   float nub;
8276   bool colorFlag, interpColorFlag;
8277   int nEdge;
8278   int c;
8279   int ok = true;
8280   float midcolor[3];
8281   float midalpha{alpha1};
8282   Pickable pickcolor[2];
8283     pickcolor[0].index = I->current_pick_color_index;
8284     pickcolor[0].bond = I->current_pick_color_bond;
8285   if (pickcolor2){
8286     pickcolor[1].index = pickcolor2->index;
8287     pickcolor[1].bond = pickcolor2->bond;
8288   } else {
8289     pickcolor[1].index = pickcolor[0].index;
8290     pickcolor[1].bond = pickcolor[0].bond;
8291   }
8292   v = v_buf;
8293   nEdge = SettingGetGlobal_i(I->G, cSetting_stick_quality);
8294   overlap = tube_size * SettingGetGlobal_f(I->G, cSetting_stick_overlap);
8295   nub = tube_size * SettingGetGlobal_f(I->G, cSetting_stick_nub);
8296 
8297   if(nEdge > MAX_EDGE)
8298     nEdge = MAX_EDGE;
8299   subdivide(nEdge, x, y);
8300 
8301   colorFlag = (c1 != c2) && c2;
8302   colorFlag |= alpha1 != alpha2;
8303 
8304   interpColorFlag = c2 && interp && pickcolor2;
8305   if (interpColorFlag){
8306     average3f(c1, c2, midcolor);
8307     midalpha = (alpha1 + alpha2) / 2.0f;
8308   }
8309   /* direction vector */
8310 
8311   p0[0] = (v2[0] - v1[0]);
8312   p0[1] = (v2[1] - v1[1]);
8313   p0[2] = (v2[2] - v1[2]);
8314 
8315   normalize3f(p0);
8316 
8317   if(cap1 == cCylCapRound && !stick_round_nub) {
8318     vv1[0] = v1[0] - p0[0] * overlap;
8319     vv1[1] = v1[1] - p0[1] * overlap;
8320     vv1[2] = v1[2] - p0[2] * overlap;
8321   } else {
8322     vv1[0] = v1[0];
8323     vv1[1] = v1[1];
8324     vv1[2] = v1[2];
8325   }
8326   if(cap2 == cCylCapRound && !stick_round_nub) {
8327     vv2[0] = v2[0] + p0[0] * overlap;
8328     vv2[1] = v2[1] + p0[1] * overlap;
8329     vv2[2] = v2[2] + p0[2] * overlap;
8330   } else {
8331     vv2[0] = v2[0];
8332     vv2[1] = v2[1];
8333     vv2[2] = v2[2];
8334   }
8335 
8336   d[0] = (vv2[0] - vv1[0]);
8337   d[1] = (vv2[1] - vv1[1]);
8338   d[2] = (vv2[2] - vv1[2]);
8339   if (pickcolor2){
8340     mult3f(d, .5f, d);
8341   }
8342   get_divergent3f(d, t);
8343   cross_product3f(d, t, p1);
8344   normalize3f(p1);
8345 
8346   cross_product3f(d, p1, p2);
8347 
8348   normalize3f(p2);
8349 
8350   /* now we have a coordinate system */
8351 
8352   if (ok)
8353     ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
8354   for(c = nEdge; ok && c >= 0; c--) {
8355     v[0] = p1[0] * x[c] + p2[0] * y[c];
8356     v[1] = p1[1] * x[c] + p2[1] * y[c];
8357     v[2] = p1[2] * x[c] + p2[2] * y[c];
8358 
8359     v[3] = vv1[0] + v[0] * tube_size;
8360     v[4] = vv1[1] + v[1] * tube_size;
8361     v[5] = vv1[2] + v[2] * tube_size;
8362 
8363     v[6] = v[3] + d[0];
8364     v[7] = v[4] + d[1];
8365     v[8] = v[5] + d[2];
8366 
8367     ok &= CGONormalv(I, v);
8368     if(ok && (colorFlag || interpColorFlag) ){
8369       ok &= CGOColorv(I, c1);
8370       ok &= CGOAlpha(I, alpha1);
8371     }
8372     if (ok)
8373       ok &= CGOVertexv(I, v + 3);
8374     if (ok && interpColorFlag){
8375       ok &= CGOColorv(I, midcolor);
8376       ok &= CGOAlpha(I, midalpha);
8377     } else if(ok && colorFlag && !pickcolor2){
8378       ok &= CGOColorv(I, c2);
8379       ok &= CGOAlpha(I, alpha2);
8380     }
8381     if (ok)
8382       ok &= CGOVertexv(I, v + 6);
8383   }
8384   if (ok)
8385     ok &= CGOEnd(I);
8386   if (pickcolor2){
8387     ok &= CGOColorv(I, c2);
8388     ok &= CGOAlpha(I, alpha2);
8389     CGOPickColor(I, pickcolor2->index, pickcolor2->bond);
8390     if (ok)
8391       ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
8392     for(c = nEdge; ok && c >= 0; c--) {
8393       v[0] = p1[0] * x[c] + p2[0] * y[c];
8394       v[1] = p1[1] * x[c] + p2[1] * y[c];
8395       v[2] = p1[2] * x[c] + p2[2] * y[c];
8396 
8397       v[3] = vv1[0] + v[0] * tube_size + d[0];
8398       v[4] = vv1[1] + v[1] * tube_size + d[1];
8399       v[5] = vv1[2] + v[2] * tube_size + d[2];
8400 
8401       v[6] = v[3] + d[0];
8402       v[7] = v[4] + d[1];
8403       v[8] = v[5] + d[2];
8404 
8405       ok &= CGONormalv(I, v);
8406       if (ok && interpColorFlag){
8407         ok &= CGOColorv(I, midcolor);
8408         ok &= CGOAlpha(I, midalpha);
8409       }
8410       if (ok)
8411         ok &= CGOVertexv(I, v + 3);
8412       if (ok && interpColorFlag){
8413         ok &= CGOColorv(I, c2);
8414         ok &= CGOAlpha(I, alpha2);
8415       }
8416       if (ok)
8417         ok &= CGOVertexv(I, v + 6);
8418     }
8419     if (ok)
8420       ok &= CGOEnd(I);
8421   }
8422 
8423   if(ok && cap1) {
8424     if(ok && colorFlag && c1){
8425       ok &= CGOColorv(I, c1);
8426       ok &= CGOAlpha(I, alpha1);
8427     }
8428     if (pickcolor2)
8429       CGOPickColor(I, pickcolor[0].index, pickcolor[0].bond);
8430 
8431     if(stick_round_nub && cap1 == cCylCapRound) {
8432       CGORoundNub(I, v1, p0, p1, p2, -1, nEdge, tube_size);
8433     } else {
8434       v[0] = -p0[0];
8435       v[1] = -p0[1];
8436       v[2] = -p0[2];
8437 
8438       if(cap1 == cCylCapRound) {
8439         v[3] = vv1[0] - p0[0] * nub;
8440         v[4] = vv1[1] - p0[1] * nub;
8441         v[5] = vv1[2] - p0[2] * nub;
8442       } else {
8443         v[3] = vv1[0];
8444         v[4] = vv1[1];
8445         v[5] = vv1[2];
8446       }
8447 
8448       if (ok)  ok &= CGOBegin(I, GL_TRIANGLE_FAN);
8449       if (ok)  ok &= CGONormalv(I, v);
8450       if (ok)  ok &= CGOVertexv(I, v + 3);
8451 
8452       for(c = nEdge; ok && c >= 0; c--) {
8453         v[0] = p1[0] * x[c] + p2[0] * y[c];
8454         v[1] = p1[1] * x[c] + p2[1] * y[c];
8455         v[2] = p1[2] * x[c] + p2[2] * y[c];
8456 
8457         v[3] = vv1[0] + v[0] * tube_size;
8458         v[4] = vv1[1] + v[1] * tube_size;
8459         v[5] = vv1[2] + v[2] * tube_size;
8460 
8461         if(cap1 == cCylCapRound)
8462           ok &= CGONormalv(I, v);
8463         if (ok)
8464           ok &= CGOVertexv(I, v + 3);
8465       }
8466       if (ok)
8467         ok &= CGOEnd(I);
8468     }
8469   }
8470 
8471   if(ok && cap2) {
8472     if(ok && colorFlag && c2){
8473       ok &= CGOColorv(I, c2);
8474       ok &= CGOAlpha(I, alpha2);
8475     }
8476     if (pickcolor2)
8477       CGOPickColor(I, pickcolor2->index, pickcolor2->bond);
8478 
8479     if(stick_round_nub && cap2 == cCylCapRound) {
8480       CGORoundNub(I, v2, p0, p1, p2, 1, nEdge, tube_size);
8481     } else {
8482       v[0] = p0[0];
8483       v[1] = p0[1];
8484       v[2] = p0[2];
8485 
8486       if(cap2 == cCylCapRound) {
8487         v[3] = vv2[0] + p0[0] * nub;
8488         v[4] = vv2[1] + p0[1] * nub;
8489         v[5] = vv2[2] + p0[2] * nub;
8490       } else {
8491         v[3] = vv2[0];
8492         v[4] = vv2[1];
8493         v[5] = vv2[2];
8494       }
8495 
8496       if (ok) ok &= CGOBegin(I, GL_TRIANGLE_FAN);
8497       if (ok) ok &= CGONormalv(I, v);
8498       if (ok) ok &= CGOVertexv(I, v + 3);
8499 
8500       for(c = 0; ok && c <= nEdge; c++) {
8501         v[0] = p1[0] * x[c] + p2[0] * y[c];
8502         v[1] = p1[1] * x[c] + p2[1] * y[c];
8503         v[2] = p1[2] * x[c] + p2[2] * y[c];
8504 
8505         v[3] = vv2[0] + v[0] * tube_size;
8506         v[4] = vv2[1] + v[1] * tube_size;
8507         v[5] = vv2[2] + v[2] * tube_size;
8508 
8509         if(cap2 == cCylCapRound)
8510           ok &= CGONormalv(I, v);
8511         if (ok)
8512           ok &= CGOVertexv(I, v + 3);
8513       }
8514       if (ok) ok &= CGOEnd(I);
8515     }
8516   }
8517   return ok;
8518 }
8519 
8520 template <typename CylinderT>
CGOSimpleCylinder(CGO * I,const CylinderT & cyl,const float a1,const float a2,const bool interp,const int cap1,const int cap2,const Pickable * pickcolor2,const bool stick_round_nub)8521 static int CGOSimpleCylinder(CGO* I, const CylinderT& cyl, const float a1,
8522     const float a2, const bool interp, const int cap1, const int cap2,
8523     const Pickable* pickcolor2, const bool stick_round_nub)
8524 {
8525   return CGOSimpleCylinder(I, cyl.vertex1, cyl.vertex2, cyl.radius, cyl.color1,
8526       cyl.color2, a1, a2, interp, cap1, cap2, pickcolor2, stick_round_nub);
8527 }
8528 
CGOSimpleCone(CGO * I,const float * v1,const float * v2,float r1,float r2,const float * c1,const float * c2,int cap1,int cap2)8529 static int CGOSimpleCone(CGO* I, const float* v1, const float* v2, float r1,
8530     float r2, const float* c1, const float* c2, int cap1, int cap2)
8531 {
8532 #define MAX_EDGE 50
8533 
8534   float d[3], t[3], p0[3], p1[3], p2[3], vv1[3], vv2[3], v_buf[9], *v;
8535   float x[MAX_EDGE + 1], y[MAX_EDGE + 1], edge_normal[3 * (MAX_EDGE + 1)];
8536   int colorFlag;
8537   int nEdge;
8538   int c;
8539   int ok = true;
8540 
8541   v = v_buf;
8542   nEdge = SettingGetGlobal_i(I->G, cSetting_cone_quality);
8543 
8544   if(nEdge > MAX_EDGE)
8545     nEdge = MAX_EDGE;
8546   subdivide(nEdge, x, y);
8547 
8548   colorFlag = (c1 != c2) && c2;
8549 
8550   ok &= CGOColorv(I, c1);
8551 
8552   /* direction vector */
8553 
8554   p0[0] = (v2[0] - v1[0]);
8555   p0[1] = (v2[1] - v1[1]);
8556   p0[2] = (v2[2] - v1[2]);
8557 
8558   normalize3f(p0);
8559 
8560   {
8561     vv1[0] = v1[0];
8562     vv1[1] = v1[1];
8563     vv1[2] = v1[2];
8564   }
8565 
8566   {
8567     vv2[0] = v2[0];
8568     vv2[1] = v2[1];
8569     vv2[2] = v2[2];
8570   }
8571 
8572   d[0] = (vv2[0] - vv1[0]);
8573   d[1] = (vv2[1] - vv1[1]);
8574   d[2] = (vv2[2] - vv1[2]);
8575 
8576   get_divergent3f(d, t);
8577 
8578   cross_product3f(d, t, p1);
8579 
8580   normalize3f(p1);
8581 
8582   cross_product3f(d, p1, p2);
8583 
8584   normalize3f(p2);
8585 
8586   /* now we have a coordinate system */
8587 
8588   {
8589     float len = diff3f(v1, v2);
8590     float vt[3], nt[3];
8591     float slope = 0.0F;
8592 
8593     if(len) {
8594       slope = (r1 - r2) / len;
8595     }
8596     for(c = nEdge; c >= 0; c--) {
8597       vt[0] = p1[0] * x[c] + p2[0] * y[c];
8598       vt[1] = p1[1] * x[c] + p2[1] * y[c];
8599       vt[2] = p1[2] * x[c] + p2[2] * y[c];
8600 
8601       scale3f(p0, slope, nt);
8602       add3f(nt, vt, vt);
8603       normalize3f(vt);
8604       copy3f(vt, edge_normal + 3 * c);
8605     }
8606   }
8607 
8608   /* now we have normals */
8609   if (ok)
8610     ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
8611   for(c = nEdge; ok && c >= 0; c--) {
8612     v[0] = p1[0] * x[c] + p2[0] * y[c];
8613     v[1] = p1[1] * x[c] + p2[1] * y[c];
8614     v[2] = p1[2] * x[c] + p2[2] * y[c];
8615 
8616     v[3] = vv1[0] + v[0] * r1;
8617     v[4] = vv1[1] + v[1] * r1;
8618     v[5] = vv1[2] + v[2] * r1;
8619 
8620     v[6] = vv1[0] + v[0] * r2 + d[0];
8621     v[7] = vv1[1] + v[1] * r2 + d[1];
8622     v[8] = vv1[2] + v[2] * r2 + d[2];
8623 
8624     ok &= CGONormalv(I, edge_normal + 3 * c);
8625     if(ok && colorFlag)
8626       CGOColorv(I, c1);
8627     if (ok)
8628       CGOVertexv(I, v + 3);
8629     if(ok && colorFlag)
8630       CGOColorv(I, c2);
8631     if (ok)
8632       CGOVertexv(I, v + 6);
8633   }
8634   if (ok)
8635     ok &= CGOEnd(I);
8636 
8637   if(ok && cap1) {
8638     v[0] = -p0[0];
8639     v[1] = -p0[1];
8640     v[2] = -p0[2];
8641 
8642     {
8643       v[3] = vv1[0];
8644       v[4] = vv1[1];
8645       v[5] = vv1[2];
8646     }
8647 
8648     if(colorFlag)
8649       ok &= CGOColorv(I, c1);
8650     if (ok)
8651       ok &= CGOBegin(I, GL_TRIANGLE_FAN);
8652     if (ok)
8653       ok &= CGONormalv(I, v);
8654     if (ok)
8655       ok &= CGOVertexv(I, v + 3);
8656 
8657     for(c = nEdge; ok && c >= 0; c--) {
8658       v[0] = p1[0] * x[c] + p2[0] * y[c];
8659       v[1] = p1[1] * x[c] + p2[1] * y[c];
8660       v[2] = p1[2] * x[c] + p2[2] * y[c];
8661 
8662       v[3] = vv1[0] + v[0] * r1;
8663       v[4] = vv1[1] + v[1] * r1;
8664       v[5] = vv1[2] + v[2] * r1;
8665 
8666       if(cap1 == cCylCapRound)
8667         ok &= CGONormalv(I, v);
8668       if (ok)
8669 	ok &= CGOVertexv(I, v + 3);
8670     }
8671     if (ok)
8672       ok &= CGOEnd(I);
8673   }
8674 
8675   if(ok && cap2) {
8676 
8677     v[0] = p0[0];
8678     v[1] = p0[1];
8679     v[2] = p0[2];
8680 
8681     {
8682       v[3] = vv2[0];
8683       v[4] = vv2[1];
8684       v[5] = vv2[2];
8685     }
8686 
8687     if(colorFlag)
8688       ok &= CGOColorv(I, c2);
8689     if (ok)
8690       ok &= CGOBegin(I, GL_TRIANGLE_FAN);
8691     if (ok)
8692       ok &= CGONormalv(I, v);
8693     if (ok)
8694       ok &= CGOVertexv(I, v + 3);
8695 
8696     for(c = 0; ok && c <= nEdge; c++) {
8697       v[0] = p1[0] * x[c] + p2[0] * y[c];
8698       v[1] = p1[1] * x[c] + p2[1] * y[c];
8699       v[2] = p1[2] * x[c] + p2[2] * y[c];
8700 
8701       v[3] = vv2[0] + v[0] * r2;
8702       v[4] = vv2[1] + v[1] * r2;
8703       v[5] = vv2[2] + v[2] * r2;
8704 
8705       if(cap2 == cCylCapRound)
8706         ok &= CGONormalv(I, v);
8707       if (ok)
8708 	ok &= CGOVertexv(I, v + 3);
8709     }
8710     if (ok)
8711       ok &= CGOEnd(I);
8712   }
8713   return ok;
8714 }
8715 
8716 /* CGOGetNextDrawBufferedIndex: This is used by RepSurface to */
8717 /* get the data from the CGO_DRAW_BUFFERS_INDEXED operation so */
8718 /* that it can update the indices for semi-transparent surfaces. */
CGOGetNextDrawBufferedNotIndex(const CGO * cgo)8719 const cgo::draw::buffers_not_indexed* CGOGetNextDrawBufferedNotIndex(
8720     const CGO* cgo)
8721 {
8722   for (auto it = cgo->begin(); !it.is_stop(); ++it) {
8723     if (it.op_code() == CGO_DRAW_BUFFERS_NOT_INDEXED) {
8724       return it.cast<cgo::draw::buffers_not_indexed>();
8725     }
8726   }
8727   return nullptr;
8728 }
8729 
append(const CGO * source,bool stopAtEnd)8730 int CGO::append(const CGO * source, bool stopAtEnd) {
8731   int ok = 1;
8732 
8733   for (auto it = source->begin(); !it.is_stop(); ++it) {
8734     add_to_cgo(it.op_code(), it.data());
8735   }
8736 
8737   if (stopAtEnd)
8738     ok &= CGOStop(this);
8739   has_draw_buffers |= source->has_draw_buffers;
8740   has_draw_cylinder_buffers |= source->has_draw_cylinder_buffers;
8741   return ok;
8742 }
8743 
8744 /*
8745  * Appends `src` to the end of this CGO. Takes ownership of data
8746  * (incl. VBOs) and leaves `src` as a valid but empty CGO.
8747  */
move_append(CGO * src)8748 void CGO::move_append(CGO * src) {
8749   if (!src->c)
8750     return;
8751 
8752   // copy buffer
8753   VLACheck(op, float, c + src->c);
8754   memcpy(op + c, src->op, src->c * sizeof(float));
8755 
8756   // update sizes
8757   c += src->c;
8758   src->c = 0;
8759 
8760   // null terminators (CGO_STOP)
8761   *(op + c) = 0;
8762   *(src->op) = 0;
8763 
8764   // move heap data
8765   for (auto& ref : src->_data_heap) {
8766     _data_heap.emplace_back(std::move(ref));
8767   }
8768   src->_data_heap.clear();
8769 
8770   // copy boolean flags
8771   has_draw_buffers            |= src->has_draw_buffers;
8772   has_draw_cylinder_buffers   |= src->has_draw_cylinder_buffers;
8773   has_draw_sphere_buffers     |= src->has_draw_sphere_buffers;
8774   has_begin_end               |= src->has_begin_end;
8775   use_shader                  |= src->use_shader;
8776   render_alpha                |= src->render_alpha;
8777 }
8778 
8779 /*
8780  * Appends `src` to the end of this CGO and then free's `src`
8781  * and sets the pointer to NULL.
8782  */
free_append(CGO * & src)8783 void CGO::free_append(CGO * &src) {
8784   move_append(src);
8785   CGOFreeWithoutVBOs(src);
8786 }
8787 
CGOAppend(CGO * dest,const CGO * source,bool stopAtEnd)8788 int CGOAppend(CGO *dest, const CGO *source, bool stopAtEnd){
8789   int ok = dest->append(source, stopAtEnd);
8790   return ok;
8791 }
8792 
CGOCountNumberOfOperationsOfType(const CGO * I,int optype)8793 int CGOCountNumberOfOperationsOfType(const CGO *I, int optype){
8794   std::set<int> ops = { optype };
8795   return CGOCountNumberOfOperationsOfTypeN(I, ops);
8796 }
8797 
CGOCountNumberOfOperationsOfTypeN(const CGO * I,const std::set<int> & optype)8798 int CGOCountNumberOfOperationsOfTypeN(const CGO* I, const std::set<int>& optype)
8799 {
8800   int numops = 0;
8801   for (auto cgoit = I->begin(); !cgoit.is_stop(); ++cgoit) {
8802     if (optype.count(cgoit.op_code()))
8803       numops++;
8804   }
8805     return (numops);
8806 }
8807 
CGOCountNumberOfOperationsOfTypeN(const CGO * I,const std::map<int,int> & optype)8808 int CGOCountNumberOfOperationsOfTypeN(const CGO *I, const std::map<int, int> &optype){
8809   int numops = 0;
8810   for (auto cgoit = I->begin(); !cgoit.is_stop(); ++cgoit) {
8811     auto it = optype.find(cgoit.op_code());
8812     if (it != optype.end())
8813       numops += it->second;
8814   }
8815   return (numops);
8816 }
8817 
CGOHasOperationsOfType(const CGO * I,int optype)8818 bool CGOHasOperationsOfType(const CGO *I, int optype){
8819   std::set<int> ops = { optype };
8820   return CGOHasOperationsOfTypeN(I, ops);
8821 }
8822 
CGOHasOperations(const CGO * I)8823 bool CGOHasOperations(const CGO *I) {
8824   return !I->begin().is_stop();
8825 }
8826 
CGOHasOperationsOfTypeN(const CGO * I,const std::set<int> & optype)8827 bool CGOHasOperationsOfTypeN(const CGO *I, const std::set<int> &optype){
8828   if (!I->op)
8829     return false;
8830 
8831   for (auto it = I->begin(); !it.is_stop(); ++it) {
8832     if (optype.count(it.op_code()))
8833       return 1;
8834   }
8835   return (0);
8836 }
8837 
8838 static
CGOFilterOutOperationsOfTypeN(const CGO * I,CGO * cgo,const std::set<int> & optype)8839 bool CGOFilterOutOperationsOfTypeN(const CGO *I, CGO *cgo, const std::set<int> &optype){
8840   if (!I->op)
8841     return false;
8842 
8843   bool ret = false;
8844   for (auto it = I->begin(); !it.is_stop(); ++it) {
8845     auto op = it.op_code();
8846     if (optype.find(op) == optype.end()){
8847       auto pc = it.data();
8848       cgo->add_to_cgo(op, pc);
8849     } else {
8850       ret = true; // returns if filtered anything
8851     }
8852   }
8853   return ret;
8854 }
8855 
CGOFilterOutCylinderOperationsInto(const CGO * I,CGO * cgo)8856 bool CGOFilterOutCylinderOperationsInto(const CGO *I, CGO *cgo){
8857   static std::set<int> optypes = { CGO_SHADER_CYLINDER,
8858                                    CGO_SHADER_CYLINDER_WITH_2ND_COLOR,
8859                                    CGO_SAUSAGE,
8860                                    CGO_CYLINDER,
8861                                    CGO_CUSTOM_CYLINDER,
8862                                    CGO_CUSTOM_CYLINDER_ALPHA };
8863   return CGOFilterOutOperationsOfTypeN(I, cgo, optypes);
8864 }
8865 
CGOHasCylinderOperations(const CGO * I)8866 bool CGOHasCylinderOperations(const CGO *I){
8867   static std::set<int> optypes = { CGO_SHADER_CYLINDER,
8868                                    CGO_SHADER_CYLINDER_WITH_2ND_COLOR,
8869                                    CGO_SAUSAGE,
8870                                    CGO_CYLINDER,
8871                                    CGO_CUSTOM_CYLINDER,
8872                                    CGO_CUSTOM_CYLINDER_ALPHA };
8873   return CGOHasOperationsOfTypeN(I, optypes);
8874 }
8875 
CGOHasSphereOperations(const CGO * I)8876 bool CGOHasSphereOperations(const CGO *I){
8877   static std::set<int> optypes = { CGO_SPHERE };
8878   return CGOHasOperationsOfTypeN(I, optypes);
8879 }
8880 
CGOCheckWhetherToFree(PyMOLGlobals * G,CGO * I)8881 bool CGOCheckWhetherToFree(PyMOLGlobals * G, CGO *I){
8882   if (I->use_shader){
8883     if (I->cgo_shader_ub_color != SettingGetGlobal_i(G, cSetting_cgo_shader_ub_color) ||
8884 	I->cgo_shader_ub_normal != SettingGetGlobal_i(G, cSetting_cgo_shader_ub_normal)){
8885       return true;
8886     }
8887   }
8888   return false;
8889 }
8890 
CGOConvertLinesToShaderCylinders(const CGO * I,int est)8891 CGO *CGOConvertLinesToShaderCylinders(const CGO * I, int est){
8892 
8893   int tot_nverts = 0, tot_ncyls = 0;
8894 
8895   CGO *cgo = CGONewSized(I->G, I->c + est);
8896 
8897   for (auto it = I->begin(); !it.is_stop(); ++it) {
8898     auto pc = it.data();
8899     int op = it.op_code();
8900 
8901     switch (op) {
8902     case CGO_DRAW_ARRAYS:
8903       {
8904         const cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
8905 	float *vals = cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
8906 	int nvals = sp->narrays*sp->nverts;
8907         memcpy(vals, sp->floatdata, nvals);
8908       }
8909       break;
8910     case CGO_END:
8911       PRINTFB(I->G, FB_CGO, FB_Warnings) " CGOConvertLinesToShaderCylinders: CGO_END encountered without CGO_BEGIN but skipped for OpenGLES\n" ENDFB(I->G);
8912       break;
8913     case CGO_VERTEX:
8914       PRINTFB(I->G, FB_CGO, FB_Warnings) " CGOConvertLinesToShaderCylinders: CGO_VERTEX encountered without CGO_BEGIN but skipped for OpenGLES\n" ENDFB(I->G);
8915       break;
8916     case CGO_BEGIN:
8917       {
8918 	const float *last_vertex = NULL, *last_color = NULL, *current_color = NULL, *color = NULL ;
8919         unsigned int last_pick_color_idx = 0;
8920 	int last_pick_color_bnd = cPickableNoPick ;
8921 	int nverts = 0, err = 0;
8922 	int mode = CGO_get_int(pc);
8923 
8924         for (++it; !err && it != CGO_END; ++it) {
8925           auto pc = it.data();
8926           int op = it.op_code();
8927 
8928 	  switch (op) {
8929 	  case CGO_VERTEX:
8930 	    if (last_vertex){
8931 	      switch (mode){
8932 	      case GL_LINES:
8933 	      case GL_LINE_STRIP:
8934 		{
8935 		  float axis[3];
8936                   bool pick_color_diff = false;
8937 		  axis[0] = pc[0] - last_vertex[0];
8938 		  axis[1] = pc[1] - last_vertex[1];
8939 		  axis[2] = pc[2] - last_vertex[2];
8940                   pick_color_diff = (cgo->current_pick_color_index != last_pick_color_idx ||
8941                                      cgo->current_pick_color_bond != last_pick_color_bnd);
8942 		  if (last_color && current_color &&
8943                       (!equal3f(last_color, current_color) || pick_color_diff)){
8944 		    CGOColorv(cgo, last_color);
8945                     if (pick_color_diff){
8946                       Pickable pickcolor2 = { cgo->current_pick_color_index, cgo->current_pick_color_bond };
8947                       CGOPickColor(cgo, last_pick_color_idx, last_pick_color_bnd);
8948                       cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, last_vertex, axis, 1.f, cCylShaderBothCapsRound, current_color, &pickcolor2);
8949                       CGOPickColor(cgo, pickcolor2.index, pickcolor2.bond);
8950                     } else {
8951                       cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, last_vertex, axis, 1.f, cCylShaderBothCapsRound, current_color);
8952                     }
8953 		    CGOColorv(cgo, current_color);
8954 		  } else {
8955 		    cgo->add<cgo::draw::shadercylinder>(last_vertex, axis, 1.f, cCylShaderBothCapsRound);
8956 		  }
8957 		  last_vertex = pc;
8958                   last_pick_color_idx = cgo->current_pick_color_index;
8959                   last_pick_color_bnd = cgo->current_pick_color_bond;
8960 		  tot_ncyls++;
8961 		}
8962 		if (mode==GL_LINES){
8963 		  last_vertex = NULL;
8964 		  last_color = NULL;
8965 		}
8966 	      }
8967 	    } else {
8968 	      last_vertex = pc;
8969 	      current_color = color;
8970               last_pick_color_idx = cgo->current_pick_color_index;
8971               last_pick_color_bnd = cgo->current_pick_color_bond;
8972 	    }
8973 	    nverts++;
8974             break;
8975           case CGO_LINE:
8976 	    {
8977               float axis[3];
8978               auto line = reinterpret_cast<const cgo::draw::line *>(pc);
8979               subtract3f(line->vertex2, line->vertex1, axis);
8980               cgo->add<cgo::draw::shadercylinder>(line->vertex1, axis, 1.f, cCylShaderBothCapsRound);
8981               tot_ncyls++;
8982             }
8983             break;
8984           case CGO_SPLITLINE:
8985 	    {
8986               float axis[3];
8987               auto splitline = reinterpret_cast<const cgo::draw::splitline *>(pc);
8988               Pickable pickcolor2 = { splitline->index, splitline->bond };
8989               float color2[] = { CONVERT_COLOR_VALUE(splitline->color2[0]),
8990                                  CONVERT_COLOR_VALUE(splitline->color2[1]),
8991                                  CONVERT_COLOR_VALUE(splitline->color2[2]) };
8992               unsigned char flags = splitline->flags;
8993               subtract3f(splitline->vertex2, splitline->vertex1, axis);
8994               if ((flags & cgo::draw::splitline::equal_colors) &&
8995                   (flags & cgo::draw::splitline::no_split_for_pick)){
8996                 cgo->add<cgo::draw::shadercylinder>(splitline->vertex1, axis, 1., cCylShaderBothCapsRound);
8997               } else {
8998                 int cap = cCylShaderBothCapsRound;
8999                 if (flags & splitline->flags & cgo::draw::splitline::interpolation){
9000                   cap |= cCylShaderInterpColor;
9001                 }
9002                 cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, splitline->vertex1, axis, 1., cap, color2, &pickcolor2);
9003                 last_pick_color_idx = splitline->index;
9004                 last_pick_color_bnd = splitline->bond;
9005               }
9006               tot_ncyls++;
9007             }
9008             break;
9009 	  case CGO_COLOR:
9010 	    if (op == CGO_COLOR){
9011 	      last_color = current_color;
9012 	      current_color = pc;
9013 	      color = pc;
9014 	    }
9015           case CGO_PICK_COLOR:
9016             if (op == CGO_PICK_COLOR){
9017               cgo->current_pick_color_index = CGO_get_uint(pc);
9018               cgo->current_pick_color_bond = CGO_get_int(pc + 1);
9019             }
9020 	  default:
9021             cgo->add_to_cgo(op, pc);
9022 	  }
9023 	}
9024 
9025 	tot_nverts += nverts;
9026       }
9027       break;
9028     default:
9029       cgo->add_to_cgo(op, pc);
9030     }
9031   }
9032   CGOStop(cgo);
9033   cgo->use_shader = I->use_shader;
9034   if (cgo->use_shader){
9035     cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
9036     cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
9037   }
9038   if (tot_ncyls){
9039     return (cgo);
9040   } else {
9041     CGOFree(cgo);
9042     return NULL;
9043   }
9044 }
9045 /* CGOSplitUpLinesForPicking: This operation goes through */
9046 /* a CGO and returns a new CGO that has the same lines but */
9047 /* a line that has two different pick colors will get split */
9048 /* at its midpoint into two separate lines so that it can */
9049 /* be used for picking */
CGOSplitUpLinesForPicking(const CGO * I)9050 CGO *CGOSplitUpLinesForPicking(const CGO * I){
9051   auto G = I->G;
9052 
9053   std::unique_ptr<CGO> cgo_managed(new CGO(G));
9054   CGO* cgo = cgo_managed.get();
9055   int tot_nverts = 0;
9056 
9057   CGOBegin(cgo, GL_LINES);
9058 
9059   for (auto it = I->begin(); !it.is_stop(); ++it) {
9060     const auto op = it.op_code();
9061     const auto pc = it.data();
9062 
9063     switch (op) {
9064     case CGO_PICK_COLOR:
9065       {
9066         cgo->current_pick_color_index = CGO_get_uint(pc);
9067         cgo->current_pick_color_bond = CGO_get_int(pc + 1);
9068       }
9069       break;
9070     case CGO_END:
9071     case CGO_VERTEX:
9072       WARN_UNEXPECTED_OPERATION(G, op);
9073       return nullptr;
9074     case CGO_BEGIN:
9075       {
9076 	const float *last_vertex = nullptr, *last_color = nullptr,
9077               *current_color = nullptr, *color = nullptr;
9078         unsigned int last_pick_color_idx = 0;
9079 	int last_pick_color_bnd = cPickableNoPick ;
9080 	int nverts = 0;
9081 	const int mode = it.cast<cgo::draw::begin>()->mode;
9082 
9083         for (++it;; ++it) {
9084           if (it.is_stop()) {
9085             WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
9086             return nullptr;
9087           }
9088 
9089           const auto op = it.op_code();
9090           if (op == CGO_END) {
9091             break;
9092           }
9093 
9094           const auto pc = it.data();
9095 
9096 	  switch (op) {
9097 	  case CGO_VERTEX:
9098 	    if (last_vertex){
9099 	      switch (mode){
9100 	      case GL_LINES:
9101 	      case GL_LINE_STRIP:
9102 		{
9103                   bool pick_color_diff = false;
9104                   pick_color_diff = (cgo->current_pick_color_index != last_pick_color_idx ||
9105                                      cgo->current_pick_color_bond != last_pick_color_bnd);
9106 		  if (pick_color_diff ||
9107                       (last_color && current_color &&
9108                        (!equal3f(last_color, current_color)))){
9109                     if (pick_color_diff){
9110                       float haxis[3];
9111                       float mid[3];
9112                       uint curp_idx = cgo->current_pick_color_index;
9113                       int curp_bnd = cgo->current_pick_color_bond;
9114                       haxis[0] = .5f * (pc[0] - last_vertex[0]);
9115                       haxis[1] = .5f * (pc[1] - last_vertex[1]);
9116                       haxis[2] = .5f * (pc[2] - last_vertex[2]);
9117                       add3f(last_vertex, haxis, mid);
9118                       CGOPickColor(cgo, last_pick_color_idx, last_pick_color_bnd);
9119                       CGOVertexv(cgo, last_vertex);
9120                       CGOVertexv(cgo, mid);
9121                       CGOPickColor(cgo, curp_idx, curp_bnd);
9122                       CGOVertexv(cgo, mid);
9123                       CGOVertexv(cgo, pc);
9124                     } else {
9125                       CGOVertexv(cgo, last_vertex);
9126                       CGOVertexv(cgo, pc);
9127                     }
9128 		  } else {
9129                     CGOVertexv(cgo, last_vertex);
9130                     CGOVertexv(cgo, pc);
9131 		  }
9132 		  last_vertex = pc;
9133                   last_pick_color_idx = cgo->current_pick_color_index;
9134                   last_pick_color_bnd = cgo->current_pick_color_bond;
9135 		}
9136 		if (mode==GL_LINES){
9137 		  last_vertex = NULL;
9138 		  last_color = NULL;
9139 		}
9140 	      }
9141 	    } else {
9142 	      last_vertex = pc;
9143 	      current_color = color;
9144               last_pick_color_idx = cgo->current_pick_color_index;
9145               last_pick_color_bnd = cgo->current_pick_color_bond;
9146 	    }
9147 	    nverts++;
9148             break;
9149 	  case CGO_COLOR:
9150 	    {
9151 	      last_color = current_color;
9152 	      current_color = pc;
9153 	      color = pc;
9154 	    }
9155             break;
9156           case CGO_PICK_COLOR:
9157             {
9158               cgo->current_pick_color_index = CGO_get_uint(pc);
9159               cgo->current_pick_color_bond = CGO_get_int(pc + 1);
9160             }
9161 	    break;
9162 	  }
9163 	}
9164 	tot_nverts += nverts;
9165       }
9166       break;
9167     }
9168   }
9169 
9170   if (!tot_nverts) {
9171     return nullptr;
9172   }
9173 
9174   CGOEnd(cgo);
9175   CGOStop(cgo);
9176   cgo->use_shader = I->use_shader;
9177   if (cgo->use_shader){
9178     cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
9179     cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
9180   }
9181 
9182   return cgo_managed.release();
9183 }
9184 
trilinesBufferAddVertex(float * & buffer,const float * v1,const float * v2,const float * color,float alpha,signed char uv)9185 static void trilinesBufferAddVertex(float * &buffer,
9186     const float * v1,           // vertex
9187     const float * v2,           // vertex other end of line
9188     const float * color,        // RGB color
9189     float alpha,                // alpha
9190     signed char uv)              // uv
9191 {
9192   // vertex
9193   (*buffer++) = v1[0];
9194   (*buffer++) = v1[1];
9195   (*buffer++) = v1[2];
9196 
9197   // othervertex
9198   (*buffer++) = v2[0];
9199   (*buffer++) = v2[1];
9200   (*buffer++) = v2[2];
9201 
9202   (*buffer++) = (float) uv;
9203 
9204   // RGBA
9205   unsigned char *byte_view = (unsigned char *)(buffer++);
9206   (*byte_view++) = CLIP_COLOR_VALUE(color[0]);
9207   (*byte_view++) = CLIP_COLOR_VALUE(color[1]);
9208   (*byte_view++) = CLIP_COLOR_VALUE(color[2]);
9209   (*byte_view++) = CLIP_COLOR_VALUE(alpha);
9210 }
9211 
trilinesBufferAddVertices(float * & buffer,const float * v1,const float * v2,const float * color,float alpha)9212 static void trilinesBufferAddVertices(float * &buffer,
9213     const float * v1,           // vertex
9214     const float * v2,           // vertex other end of line
9215     const float * color,        // RGB color
9216     float alpha)                // alpha
9217 {
9218   // Vertex 1
9219   trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 1); //-1, 1);
9220   // Vertex 3
9221   trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 3); //1, 1);
9222   // Vertex 2
9223   trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 0); //-1, -1);
9224   // Vertex 4
9225   trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 3); //1, 1);
9226   // Vertex 3
9227   trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 2); //1, -1);
9228   // Vertex 1
9229   trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 1); //-1, 1);
9230 }
9231 
9232 static
CGOTrilines_GetCurrentColor(const float * & current_color,const float * colorv,const float * last_color,const float * cc)9233 void CGOTrilines_GetCurrentColor(const float*& current_color,
9234     const float* colorv, const float* last_color, const float* cc)
9235 {
9236   if (!current_color) {
9237     if (colorv) {
9238       current_color = colorv;
9239     } else if (last_color) {
9240       current_color = last_color;
9241     } else {
9242       current_color = cc;
9243     }
9244   }
9245 }
9246 
9247 /**
9248  * Changes
9249  *   CGO_ENABLE <frommode>
9250  * to
9251  *   CGO_ENABLE <tomode>
9252  * in place.
9253  */
CGOChangeShadersTo(CGO * I,int frommode,int tomode)9254 void CGOChangeShadersTo(CGO *I, int frommode, int tomode){
9255   for (auto it = I->begin(); !it.is_stop(); ++it) {
9256     if (it.op_code() == CGO_ENABLE) {
9257       auto eo = it.cast<cgo::draw::enable>();
9258       if (eo->mode == frommode) {
9259         eo->mode = tomode;
9260       }
9261     }
9262   }
9263 }
9264 
CGOOptimizeScreenTexturesAndPolygons(CGO * I,int est)9265 CGO *CGOOptimizeScreenTexturesAndPolygons(CGO * I, int est)
9266 {
9267   auto G = I->G;
9268   int ok = true;
9269 
9270   int num_total_indices = CGOCountNumVerticesForScreen(I);
9271   if (num_total_indices <= 0)
9272     return nullptr;
9273 
9274   std::unique_ptr<CGO> cgo_managed(new CGO(G));
9275   auto* const cgo = cgo_managed.get();
9276 
9277   {
9278     float *colorVals = 0, *texcoordVals;
9279     int tot, nxtn;
9280     uchar *colorValsUC = 0;
9281     CGOAlpha(cgo, 1.f);
9282     cgo->alpha = 1.f;
9283     cgo->color[0] = 1.f; cgo->color[1] = 1.f; cgo->color[2] = 1.f;
9284 
9285     {
9286       int mul = 6; // 3 - screenoffset/vertex, 2 - texture coordinates, 1 - color
9287       /*
9288       if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
9289 	mul++;
9290       } else {
9291 	mul += 4;
9292 	}*/
9293       tot = num_total_indices * mul ;
9294     }
9295 
9296     auto vertexVals_managed = std::vector<float>(tot);
9297     float* vertexVals = vertexVals_managed.data();
9298     texcoordVals = vertexVals + 3 * num_total_indices;
9299     nxtn = 2;
9300     colorVals = texcoordVals + nxtn * num_total_indices;
9301     colorValsUC = (uchar*) colorVals;
9302     nxtn = 1;
9303     ok = CGOProcessScreenCGOtoArrays(G, I, vertexVals, texcoordVals, colorVals, colorValsUC);
9304     RETURN_VAL_IF_FAIL(ok && !G->Interrupt, nullptr);
9305     if (ok){
9306       VertexBuffer * vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>();
9307       ok = vbo->bufferData({
9308           BufferDesc( "attr_screenoffset", GL_FLOAT,         3, sizeof(float) * num_total_indices * 3, vertexVals,   GL_FALSE ),
9309           BufferDesc( "attr_texcoords", GL_FLOAT,         2, sizeof(float) * num_total_indices * 2, texcoordVals, GL_FALSE ),
9310           BufferDesc( "attr_backgroundcolor", GL_UNSIGNED_BYTE, 4, sizeof(uchar) * num_total_indices * 4, colorValsUC,  GL_TRUE )
9311         });
9312       size_t vboid = vbo->get_hash_id();
9313       if (ok){
9314 	CGOEnable(cgo, GL_SCREEN_SHADER);
9315 	cgo->add<cgo::draw::screen_textures>(num_total_indices, vboid);
9316 	if (ok)
9317 	  ok &= CGODisable(cgo, GL_SCREEN_SHADER);
9318         RETURN_VAL_IF_FAIL(ok, nullptr);
9319       } else {
9320         I->G->ShaderMgr->freeGPUBuffer(vboid);
9321       }
9322     }
9323     cgo->use_shader = true;
9324   }
9325 
9326   return cgo_managed.release();
9327 }
9328 
CGOColorByRamp(PyMOLGlobals * G,const CGO * I,ObjectGadgetRamp * ramp,int state,CSetting * set1)9329 CGO* CGOColorByRamp(PyMOLGlobals* G, const CGO* I, ObjectGadgetRamp* ramp,
9330     int state, CSetting* set1)
9331 {
9332   if (!I) {
9333     return nullptr;
9334   }
9335 
9336   auto cgo = CGONewSized(G, 0);
9337   if (!cgo) {
9338     return nullptr;
9339   }
9340 
9341   int ok = true;
9342   float white[3] = { 1.f, 1.f, 1.f};
9343   float probe_radius = SettingGet_f(G, set1, NULL, cSetting_solvent_radius);
9344   float v_above[3], n0[3] = { 0.f, 0.f, 0.f };
9345   int ramp_above = SettingGet_i(G, set1, NULL, cSetting_surface_ramp_above_mode) == 1;
9346 
9347   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
9348     const auto op = it.op_code();
9349     const auto pc = it.data();
9350 
9351     bool skipCopy = false;
9352     switch (op) {
9353     case CGO_DRAW_ARRAYS:
9354       {
9355         const cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
9356 	float *vals = cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
9357 	int nvals = sp->narrays*sp->nverts;
9358 	ok &= vals ? true : false;
9359 	if (ok)
9360           memcpy(vals, sp->floatdata, nvals);
9361 	skipCopy = true;
9362       }
9363       break;
9364     case CGO_NORMAL:
9365       copy3f(pc, n0);
9366       break;
9367     case CGO_VERTEX:
9368       {
9369 	float color[3];
9370 	copy3f(white, color);
9371 	if (ramp_above){
9372 	  copy3f(n0, v_above);
9373 	  scale3f(v_above, probe_radius, v_above);
9374 	  add3f(pc, v_above, v_above);
9375 	} else {
9376 	  copy3f(pc, v_above);
9377 	}
9378 	if (ObjectGadgetRampInterVertex(ramp, v_above, color, state)){
9379 	  CGOColorv(cgo, color);
9380 	} else {
9381 	  CGOColorv(cgo, white);
9382 	}
9383       }
9384       break;
9385     }
9386     if (!skipCopy){
9387       cgo->add_to_cgo(op, pc);
9388     }
9389   }
9390   if (ok){
9391     ok &= CGOStop(cgo);
9392     if (ok){
9393       cgo->use_shader = I->use_shader;
9394       if (cgo->use_shader){
9395 	cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
9396 	cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
9397       }
9398     }
9399   }
9400   if (!ok){
9401     CGOFree(cgo);
9402   }
9403   return (cgo);
9404 }
9405 
9406 /**
9407  * FIXME: This function always returns true for `checkOpaque=ture`
9408  */
CGOHasTransparency(const CGO * I,bool checkTransp,bool checkOpaque)9409 int CGOHasTransparency(const CGO *I, bool checkTransp, bool checkOpaque){
9410   for (auto it = I->begin(); !it.is_stop(); ++it) {
9411     if (it.op_code() == CGO_ALPHA) {
9412       const auto pc = it.data();
9413       if (checkTransp && *pc < 1.f)
9414 	return 1;
9415       if (checkOpaque && *pc == 1.f)
9416         return 1;
9417     }
9418   }
9419 
9420   return checkOpaque;
9421 }
9422 
9423 /* TransparentInfoSortIX - This function sorts all n_tri triangle
9424  * centroids in the array sum by:
9425  * 1) computing z-value in array z_value
9426  * 2) bin sorting z_values and placing indices in ix array (using Util.cpp)
9427  *
9428  * - uses sort_mem as pre-allocated memory to sort
9429  * - t_mode - either forward (1) or backwards (0) sort
9430  */
TransparentInfoSortIX(PyMOLGlobals * G,float * sum,float * z_value,int * ix,int n_tri,int * sort_mem,int t_mode)9431 void TransparentInfoSortIX(PyMOLGlobals * G,
9432 			   float *sum, float *z_value, int *ix,
9433 			   int n_tri, int *sort_mem, int t_mode){
9434   float *zv;
9435   float *sv;
9436   float matrix[16];
9437   int idx;
9438 
9439 #ifdef PURE_OPENGL_ES_2
9440   copy44f(SceneGetModelViewMatrix(G), matrix);
9441 #else
9442   glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
9443 #endif
9444   zv = z_value;
9445   sv = sum;
9446 
9447   /* for each triangle, computes the z */
9448   for (idx = 0; idx<n_tri; ++idx){
9449     *(zv++) = matrix[2] * sv[0] + matrix[6] * sv[1] + matrix[10] * sv[2];
9450     sv += 3;
9451   }
9452 
9453   UtilZeroMem(sort_mem, sizeof(int) * (n_tri + 256));
9454 
9455   switch (t_mode) {
9456   case 1:
9457     UtilSemiSortFloatIndexWithNBinsImpl(sort_mem, n_tri, 256, z_value, ix, true); // front to back
9458     /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZOrderFn); */
9459     break;
9460   default:
9461     UtilSemiSortFloatIndexWithNBinsImpl(sort_mem, n_tri, 256, z_value, ix, false); // back to front
9462     /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZRevOrderFn); */
9463     break;
9464   }
9465 }
9466 
CGOConvertTrianglesToAlpha(const CGO * I)9467 CGO *CGOConvertTrianglesToAlpha(const CGO * I){
9468   int tot_nverts = 0;
9469 
9470   CGO *cgo = CGONewSized(I->G, I->c);
9471 
9472   for (auto it = I->begin(); !it.is_stop(); ++it) {
9473     auto pc = it.data();
9474     int op = it.op_code();
9475 
9476     switch (op) {
9477     case CGO_DRAW_ARRAYS:
9478       {
9479         const cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
9480         int mode = sp->mode, arrays = sp->arraybits, nverts = sp->nverts;
9481         int nxtn = 3;
9482         float *vertexValsDA = 0, *nxtVals = 0, *colorValsDA = 0, *normalValsDA = 0;
9483         float *vertexVals0, *normalVals0, *colorVals0;
9484 
9485         nxtVals = vertexValsDA = sp->floatdata;
9486         if (arrays & CGO_NORMAL_ARRAY){
9487           nxtVals = normalValsDA = vertexValsDA + (nxtn*nverts);
9488         }
9489         if (arrays & CGO_COLOR_ARRAY){
9490           nxtVals = colorValsDA = nxtVals + (nxtn*nverts);
9491           nxtn = 4;
9492         }
9493         if (arrays & CGO_PICK_COLOR_ARRAY){
9494           nxtVals = nxtVals + (nxtn*nverts);
9495           nxtn = 3;
9496         }
9497         vertexVals0 = vertexValsDA;
9498         normalVals0 = normalValsDA;
9499         colorVals0 = colorValsDA;
9500         switch (mode){
9501         case GL_TRIANGLES:
9502           {
9503             for (int cnt = 0; cnt < nverts; cnt+=3){
9504               if (colorVals0){
9505                 CGOAlphaTriangle(cgo, vertexValsDA, vertexValsDA+3, vertexValsDA+6,
9506                                  normalValsDA, normalValsDA+3, normalValsDA+6,
9507                                  colorValsDA, colorValsDA+4, colorValsDA+8,
9508                                  *(colorValsDA + 3), *(colorValsDA + 7), *(colorValsDA + 11), 0);
9509               } else {
9510                 CGOAlphaTriangle(cgo, vertexValsDA, vertexValsDA+3, vertexValsDA+6,
9511                                  normalValsDA, normalValsDA+3, normalValsDA+6,
9512                                  cgo->color, cgo->color, cgo->color,
9513                                  cgo->alpha, cgo->alpha, cgo->alpha, 0);
9514               }
9515               vertexValsDA += 9;
9516               normalValsDA += 9;
9517               if (colorVals0)
9518                 colorValsDA += 12;
9519             }
9520           }
9521           tot_nverts += nverts;
9522           break;
9523         case GL_TRIANGLE_STRIP:
9524           {
9525             short flip = 0;
9526             vertexValsDA += 6;
9527             normalValsDA += 6;
9528             if (colorVals0)
9529               colorValsDA += 8;
9530 
9531             for (int cnt = 2; cnt < nverts; cnt++){
9532               if (colorVals0){
9533                 CGOAlphaTriangle(cgo, vertexValsDA-6, vertexValsDA-3, vertexValsDA,
9534                                  normalValsDA-6, normalValsDA-3, normalValsDA,
9535                                  colorValsDA-8, colorValsDA-4, colorValsDA,
9536                                  *(colorValsDA - 5), *(colorValsDA - 1), *(colorValsDA + 3), flip);
9537               } else {
9538                 CGOAlphaTriangle(cgo, vertexValsDA-6, vertexValsDA-3, vertexValsDA,
9539                                  normalValsDA-6, normalValsDA-3, normalValsDA,
9540                                  cgo->color, cgo->color, cgo->color,
9541                                  cgo->alpha, cgo->alpha, cgo->alpha, flip);
9542               }
9543               vertexValsDA += 3;
9544               normalValsDA += 3;
9545               if (colorVals0)
9546                 colorValsDA += 4;
9547               flip = !flip;
9548             }
9549           }
9550           tot_nverts += nverts;
9551           break;
9552         case GL_TRIANGLE_FAN:
9553           {
9554             vertexValsDA += 6;
9555             normalValsDA += 6;
9556             if (colorVals0)
9557               colorValsDA += 8;
9558             for (int cnt = 2; cnt < nverts; cnt++){
9559               if (colorVals0){
9560                 CGOAlphaTriangle(cgo, vertexVals0, vertexValsDA-3, vertexValsDA,
9561                                  normalVals0, normalValsDA-3, normalValsDA,
9562                                  colorVals0, colorValsDA-4, colorValsDA,
9563                                  *(colorVals0 + 3), *(colorValsDA - 1), *(colorValsDA + 3), 0);
9564               } else {
9565                 CGOAlphaTriangle(cgo, vertexVals0, vertexValsDA-3, vertexValsDA,
9566                                  normalVals0, normalValsDA-3, normalValsDA,
9567                                  cgo->color, cgo->color, cgo->color,
9568                                  cgo->alpha, cgo->alpha, cgo->alpha, 0);
9569               }
9570               vertexValsDA += 3;
9571               normalValsDA += 3;
9572               if (colorVals0)
9573                 colorValsDA += 4;
9574             }
9575             }
9576           tot_nverts += nverts;
9577           break;
9578         }
9579       }
9580       break;
9581     case CGO_END:
9582       PRINTFB(I->G, FB_CGO, FB_Warnings) " CGOConvertTrianglesToAlpha: CGO_END encountered without CGO_BEGIN but skipped for OpenGLES\n" ENDFB(I->G);
9583       break;
9584     case CGO_VERTEX:
9585       PRINTFB(I->G, FB_CGO, FB_Warnings) " CGOConvertTrianglesToAlpha: CGO_VERTEX encountered without CGO_BEGIN but skipped for OpenGLES\n" ENDFB(I->G);
9586       break;
9587     case CGO_BEGIN:
9588       {
9589         float vertices[3][3], colors[4][3], normals[4][3], alpha[4] ;
9590         short verticespl = 2, colorspl = 2, normalspl = 2, alphapl = 2;
9591         short hasShifted = 0;
9592         int nverts = 0, err = 0;
9593         int mode = CGO_get_int(pc);
9594         short mode_is_triangles = 0, flip = 0, mode_is_fan = 0;
9595         copy3f(cgo->color, colors[3]);
9596         copy3f(cgo->normal, normals[3]);
9597         alpha[3] = cgo->alpha;
9598         switch (mode){
9599         case GL_TRIANGLE_FAN:
9600           mode_is_fan = 1;
9601         case GL_TRIANGLES:
9602         case GL_TRIANGLE_STRIP:
9603           mode_is_triangles = 1;
9604         }
9605         if (!mode_is_triangles){
9606           CGOBegin(cgo, mode);
9607         }
9608 
9609         for (++it; !err && it != CGO_END; ++it) {
9610           auto pc = it.data();
9611           int op = it.op_code();
9612           short add_to_cgo = 1;
9613           switch (op) {
9614           case CGO_VERTEX:
9615             if (mode_is_triangles){
9616               if (!(hasShifted & 1)){ // colors
9617                 if (colorspl>=0){
9618                   copy3f(colors[colorspl+1], colors[colorspl]);
9619                   colorspl--;
9620                 } else {
9621                   if (!mode_is_fan)
9622                     copy3f(colors[1], colors[2]);
9623                   copy3f(colors[0], colors[1]);
9624                 }
9625               }
9626               if (!(hasShifted & 2)){ // normals
9627                 if (normalspl>=0){
9628                   copy3f(normals[normalspl+1], normals[normalspl]);
9629                   normalspl--;
9630                 } else {
9631                   if (!mode_is_fan)
9632                     copy3f(normals[1], normals[2]);
9633                   copy3f(normals[0], normals[1]);
9634                 }
9635               }
9636               if (!(hasShifted & 4)){ // alphas
9637                 if (alphapl>=0){
9638                   alpha[alphapl] = alpha[alphapl+1];
9639                   alphapl--;
9640                 } else {
9641                   if (!mode_is_fan)
9642                     alpha[2] = alpha[1];
9643                   alpha[1] = alpha[0];
9644                 }
9645               }
9646               if (verticespl>=0){
9647                 copy3f(pc, vertices[verticespl]) ;
9648                 verticespl--;
9649               } else {
9650                 if (!mode_is_fan)
9651                   copy3f(vertices[1], vertices[2]);
9652                 copy3f(vertices[0], vertices[1]);
9653                 copy3f(pc, vertices[0]);
9654               }
9655 
9656               nverts++;
9657               switch (mode){
9658               case GL_TRIANGLES:
9659                 if (!(nverts % 3)){
9660                   CGOAlphaTriangle(cgo, vertices[2], vertices[1], vertices[0],
9661                                    normals[2], normals[1], normals[0],
9662                                    colors[2], colors[1], colors[0],
9663                                    alpha[2], alpha[1], alpha[0], 0);
9664                 }
9665                 break;
9666               case GL_TRIANGLE_STRIP:
9667                 if (verticespl<0){
9668                   int off0, off2;
9669                   if (flip){ off0 = 0; off2 = 2; } else { off0 = 2; off2 = 0; }
9670                   flip = !flip;
9671                   CGOAlphaTriangle(cgo, vertices[off0], vertices[1], vertices[off2],
9672                                    normals[off0], normals[1], normals[off2],
9673                                    colors[off0], colors[1], colors[off2],
9674                                    alpha[off0], alpha[1], alpha[off2], 0);
9675                 }
9676                 break;
9677               case GL_TRIANGLE_FAN:
9678                 if (verticespl<0){
9679                   CGOAlphaTriangle(cgo, vertices[2], vertices[1], vertices[0],
9680                                    normals[2], normals[1], normals[0],
9681                                    colors[2], colors[1], colors[0],
9682                                    alpha[2], alpha[1], alpha[0], 0);
9683                 }
9684               }
9685               add_to_cgo = !mode_is_triangles;
9686               hasShifted = 0;
9687             } else {
9688               add_to_cgo = 1;
9689             }
9690           case CGO_COLOR:
9691             if (op == CGO_COLOR){
9692               add_to_cgo = !mode_is_triangles;
9693               if (mode_is_triangles){
9694                 if (colorspl>=0){
9695                   copy3f(pc, colors[colorspl]);
9696                   colorspl--;
9697                 } else {
9698                   if (!mode_is_fan)
9699                     copy3f(colors[1], colors[2]);
9700                   copy3f(colors[0], colors[1]);
9701                   copy3f(pc, colors[0]);
9702 
9703                 }
9704                 hasShifted |= 1;
9705               }
9706             }
9707           case CGO_NORMAL:
9708             if (op == CGO_NORMAL){
9709               add_to_cgo = !mode_is_triangles;
9710               if (mode_is_triangles){
9711                 if (normalspl>=0){
9712                   copy3f(pc, normals[normalspl]);
9713                   normalspl--;
9714                 } else {
9715                   if (!mode_is_fan)
9716                     copy3f(normals[1], normals[2]);
9717                   copy3f(normals[0], normals[1]);
9718                   copy3f(pc, normals[0]);
9719                 }
9720                 hasShifted |= 2;
9721               }
9722             }
9723           case CGO_ALPHA:
9724             if (op == CGO_ALPHA){
9725               add_to_cgo = !mode_is_triangles;
9726               if (mode_is_triangles){
9727                 if (alphapl>=0)
9728                   alpha[alphapl--] = *pc;
9729                 else {
9730                   if (!mode_is_fan)
9731                     alpha[2] = alpha[1];
9732                   alpha[1] = alpha[0];
9733                   alpha[0] = *pc;
9734                 }
9735                 hasShifted |= 4;
9736               }
9737             }
9738           default:
9739             if (add_to_cgo){
9740               cgo->add_to_cgo(op, pc);
9741             }
9742           }
9743         }
9744 
9745         if (!mode_is_triangles) {
9746           CGOEnd(cgo);
9747         }
9748 
9749         tot_nverts += nverts;
9750       }
9751       break;
9752     case CGO_COLOR:
9753       if (op==CGO_COLOR){
9754         copy3f(pc, cgo->color);
9755       }
9756     case CGO_NORMAL:
9757       if (op==CGO_NORMAL){
9758         copy3f(pc, cgo->normal);
9759       }
9760     case CGO_ALPHA:
9761       if (op==CGO_ALPHA){
9762         cgo->alpha = *pc;
9763       }
9764     default:
9765       cgo->add_to_cgo(op, pc);
9766     }
9767   }
9768   CGOStop(cgo);
9769   cgo->use_shader = I->use_shader;
9770   if (cgo->use_shader){
9771     cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
9772     cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
9773   }
9774   if (tot_nverts){
9775     return (cgo);
9776   } else {
9777     CGOFree(cgo);
9778     return NULL;
9779   }
9780 }
9781 
9782 /*
9783  * Converts TRIANGLE(_STRIP|_FAN) to TRIANGLES and generates
9784  * normals for all triangles. Discards any existing normals for
9785  * triangles.
9786  *
9787  * I: primitive CGO
9788  * return: new primitive CGO with normals on triangles
9789  */
CGOGenerateNormalsForTriangles(const CGO * I)9790 CGO *CGOGenerateNormalsForTriangles(const CGO * I){
9791   auto G = I->G;
9792   auto cgo = CGONewSized(G, I->c);
9793 
9794   float vertices[3][3];
9795   float current_color[3] = {0.f, 0.f, 0.f}, colors[3][3];
9796   float current_normal[3];
9797   float current_alpha = 0, alphas[3];
9798 
9799   bool has_alpha = false;
9800   bool has_color = false;
9801 
9802   int mode = 0;
9803   bool inside_begin_triangles = false;
9804   int current_i = 0;
9805   int vertex_count = 0;
9806   bool flip = false;
9807   bool emit;
9808 
9809   const int indices_regular[] = {0, 1, 2};
9810   const int indices_flipped[] = {0, 2, 1};
9811 
9812   for (auto it = I->begin(); !it.is_stop(); ++it) {
9813     auto pc = it.data();
9814     auto op = it.op_code();
9815 
9816     if (op == CGO_BEGIN) {
9817       mode = *reinterpret_cast<const int*>(pc);
9818 
9819       switch (mode) {
9820         case GL_TRIANGLE_STRIP:
9821         case GL_TRIANGLE_FAN:
9822         case GL_TRIANGLES:
9823           current_i = 0;
9824           vertex_count = 0;
9825           flip = false;
9826           inside_begin_triangles = true;
9827 
9828           CGOBegin(cgo, GL_TRIANGLES);
9829           continue; // for-loop, no add_to_cgo
9830       }
9831 
9832       inside_begin_triangles = false;
9833     } else if (op == CGO_END) {
9834       inside_begin_triangles = false;
9835     }
9836 
9837     if (!inside_begin_triangles) {
9838       cgo->add_to_cgo(op, pc);
9839       continue;
9840     }
9841 
9842     // handle operations inside BEGIN/END TRIANGLE(S|_STRIP|_FAN)
9843     switch (op) {
9844       case CGO_VERTEX:
9845         copy3(reinterpret_cast<const float*>(pc), vertices[current_i]);
9846         copy3(current_color, colors[current_i]);
9847         alphas[current_i] = current_alpha;
9848 
9849         ++vertex_count;
9850 
9851         switch (mode) {
9852           case GL_TRIANGLE_STRIP:
9853             current_i = vertex_count % 3;
9854             emit = (vertex_count > 2);
9855             break;
9856           case GL_TRIANGLE_FAN:
9857             current_i = ((vertex_count + 1) % 2) + 1;
9858             emit = (vertex_count > 2);
9859             break;
9860           default:
9861             current_i = vertex_count % 3;
9862             emit = (current_i == 0);
9863         }
9864 
9865         if (emit) {
9866           auto * indices = flip ? indices_flipped : indices_regular;
9867 
9868           if (mode != GL_TRIANGLES) {
9869             flip = !flip;
9870           }
9871 
9872           CalculateTriangleNormal(vertices[0],
9873               vertices[indices[1]],
9874               vertices[indices[2]], current_normal);
9875 
9876           CGONormalv(cgo, current_normal);
9877 
9878           for (int j = 0; j < 3; ++j) {
9879             int k = indices[j];
9880             if (has_color) CGOColorv(cgo, colors[k]);
9881             if (has_alpha) CGOAlpha(cgo, alphas[k]);
9882             CGOVertexv(cgo, vertices[k]);
9883           }
9884         }
9885 
9886         break;
9887       case CGO_COLOR:
9888         copy3(reinterpret_cast<const float*>(pc), current_color);
9889         has_color = true;
9890         break;
9891       case CGO_ALPHA:
9892         current_alpha = *reinterpret_cast<const float*>(pc);
9893         has_alpha = true;
9894         break;
9895       case CGO_NORMAL:
9896         // discard, we will generate new normals
9897         break;
9898       default:
9899         PRINTFB(G, FB_CGO, FB_Warnings)
9900           " CGO-Warning: CGOGenerateNormalsForTriangles: unhandled op=0x%02x inside BEGIN/END\n",
9901           op ENDFB(G);
9902         cgo->add_to_cgo(op, pc);
9903     }
9904   }
9905 
9906   CGOStop(cgo);
9907   cgo->use_shader = I->use_shader;
9908   if (cgo->use_shader){
9909     cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
9910     cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
9911   }
9912   return (cgo);
9913 }
9914 
CGOTurnLightingOnLinesOff(const CGO * I,bool use_shader)9915 CGO* CGOTurnLightingOnLinesOff(const CGO* I, bool use_shader)
9916 {
9917   bool cur_mode_is_lines = false;
9918   auto cgo = CGONewSized(I->G, I->c);
9919 
9920   for (auto it = I->begin(); !it.is_stop(); ++it) {
9921     const auto op = it.op_code();
9922     const auto pc = it.data();
9923 
9924     switch (op) {
9925     case CGO_DRAW_ARRAYS:
9926       {
9927         const cgo::draw::arrays * sp = reinterpret_cast<decltype(sp)>(pc);
9928         float *vals;
9929         int nvals = sp->narrays*sp->nverts;
9930         switch (sp->mode){
9931         case GL_LINES:
9932         case GL_LINE_STRIP:
9933           CGODisable(cgo, CGO_GL_LIGHTING);
9934           cur_mode_is_lines = true;
9935         }
9936         vals = cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
9937         memcpy(vals, sp->floatdata, nvals);
9938         if (cur_mode_is_lines){
9939           CGOEnable(cgo, CGO_GL_LIGHTING);
9940           cur_mode_is_lines = false;
9941         }
9942       }
9943       break;
9944     case CGO_DRAW_BUFFERS_INDEXED:
9945       {
9946         const cgo::draw::buffers_indexed * sp = reinterpret_cast<decltype(sp)>(pc);
9947         int mode = sp->mode, mode_is_lines = 0;
9948         switch (mode){
9949         case GL_LINES:
9950         case GL_LINE_STRIP:
9951           mode_is_lines = true;
9952         }
9953         if (mode_is_lines){
9954           CGODisable(cgo, CGO_GL_LIGHTING);
9955         }
9956         cgo->copy_op_from<cgo::draw::buffers_indexed>(pc);
9957         if (mode_is_lines){
9958           CGOEnable(cgo, CGO_GL_LIGHTING);
9959         }
9960       }
9961       break;
9962     case CGO_DRAW_BUFFERS_NOT_INDEXED:
9963       {
9964         const cgo::draw::buffers_not_indexed * sp = reinterpret_cast<decltype(sp)>(pc);
9965         int mode = sp->mode, mode_is_lines = 0;
9966         switch (mode){
9967         case GL_LINES:
9968         case GL_LINE_STRIP:
9969           mode_is_lines = true;
9970         }
9971         if (mode_is_lines){
9972           CGODisable(cgo, CGO_GL_LIGHTING);
9973         }
9974         cgo->copy_op_from<cgo::draw::buffers_not_indexed>(pc);
9975         if (mode_is_lines){
9976           CGOEnable(cgo, CGO_GL_LIGHTING);
9977         }
9978       }
9979     case CGO_END:
9980       {
9981         CGOEnd(cgo);
9982         if (cur_mode_is_lines){
9983           CGOEnable(cgo, CGO_GL_LIGHTING);
9984           cur_mode_is_lines = 0;
9985         }
9986       }
9987       break;
9988     case CGO_BEGIN:
9989       {
9990         int mode = CGO_get_int(pc);
9991         switch (mode){
9992         case GL_LINES:
9993         case GL_LINE_STRIP:
9994           CGODisable(cgo, CGO_GL_LIGHTING);
9995           cur_mode_is_lines = true;
9996           break;
9997         default:
9998           if (!use_shader) { // no shaders, not lines, turn lighting on
9999             CGOEnable(cgo, CGO_GL_LIGHTING);
10000           }
10001         }
10002         CGOBegin(cgo, mode);
10003       }
10004       break;
10005     default:
10006       cgo->add_to_cgo(op, pc);
10007     }
10008   }
10009   cgo->use_shader = use_shader;
10010   if (cgo->use_shader){
10011     cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
10012     cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
10013   }
10014   return (cgo);
10015 }
10016 
CGOHasAnyTriangleVerticesWithoutNormals(const CGO * I,bool checkTriangles)10017 bool CGOHasAnyTriangleVerticesWithoutNormals(const CGO *I, bool checkTriangles){
10018   bool inside = false;
10019   bool hasNormal = false;
10020 
10021   for (auto it = I->begin(); !it.is_stop(); ++it) {
10022     const auto op = it.op_code();
10023     const auto pc = it.data();
10024 
10025     switch (op) {
10026     case CGO_BEGIN:
10027       switch (CGO_get_int(pc)){
10028       case GL_TRIANGLE_FAN:
10029       case GL_TRIANGLES:
10030       case GL_TRIANGLE_STRIP:
10031         if (checkTriangles)
10032           inside = 1;
10033         break;
10034       case GL_LINE_STRIP:
10035       case GL_LINES:
10036         if (!checkTriangles)
10037           inside = 1;
10038         break;
10039       }
10040       break;
10041     case CGO_END:
10042       inside = 0;
10043       break;
10044     case CGO_NORMAL:
10045       hasNormal = 1;
10046       break;
10047     case CGO_VERTEX:
10048       if (inside && !hasNormal)
10049         return 1;
10050       break;
10051     case CGO_DRAW_ARRAYS:
10052       {
10053         const auto sp = it.cast<cgo::draw::arrays>();
10054         switch (sp->mode){
10055         case GL_TRIANGLE_FAN:
10056         case GL_TRIANGLES:
10057         case GL_TRIANGLE_STRIP:
10058           if (checkTriangles){
10059             if (!(sp->arraybits & CGO_NORMAL_ARRAY)){
10060               return 1;
10061             }
10062           }
10063           break;
10064         case GL_LINE_STRIP:
10065         case GL_LINES:
10066           if (!checkTriangles){
10067             if (!(sp->arraybits & CGO_NORMAL_ARRAY)){
10068               return 1;
10069             }
10070           }
10071           break;
10072         }
10073       }
10074       break;
10075     }
10076   }
10077   return 0;
10078 }
10079 
10080 /*
10081  * CGOReorderIndicesWithTransparentInfo : This function
10082  * takes the triangle index array ix (result from TransparentInfoSortIX)
10083  * and sets the vertices (vertexIndices) for each triangle from the original
10084  * indices (vertexIndicesOriginal), then uses glBufferData to set the
10085  * GL_ELEMENT_ARRAY_BUFFER to these indices.
10086  *
10087  */
CGOReorderIndicesWithTransparentInfo(PyMOLGlobals * G,int nindices,size_t vbuf,int n_tri,int * ix,GL_C_INT_TYPE * vertexIndicesOriginal,GL_C_INT_TYPE * vertexIndices)10088 void CGOReorderIndicesWithTransparentInfo(PyMOLGlobals * G,
10089 					  int nindices, size_t vbuf,
10090 					  int n_tri, int *ix,
10091 					  GL_C_INT_TYPE *vertexIndicesOriginal,
10092 					  GL_C_INT_TYPE *vertexIndices){
10093   int c, pl, idx;
10094   IndexBuffer * ibo = G->ShaderMgr->getGPUBuffer<IndexBuffer>( vbuf );
10095   if (!vertexIndices){
10096     PRINTFB(G, FB_RepSurface, FB_Errors) "ERROR: RepSurfaceRender() vertexIndices is not set, nindices=%d\n", nindices ENDFB(G);
10097   }
10098   /* updates the vertexIndices from the ix array */
10099   for(c = 0, pl=0; c < n_tri; c++) {
10100     idx = ix[c] * 3;
10101     vertexIndices[pl++] = vertexIndicesOriginal[idx];
10102     vertexIndices[pl++] = vertexIndicesOriginal[idx + 1];
10103     vertexIndices[pl++] = vertexIndicesOriginal[idx + 2];
10104   }
10105   ibo->bufferSubData(0, sizeof(GL_C_INT_TYPE) * nindices, vertexIndices);
10106 }
10107 
add_to_cgo(int op,const float * pc)10108 void CGO::add_to_cgo(int op, const float * pc) {
10109   switch (op) {
10110   case CGO_STOP:
10111     // only append to buffer, don't increment size
10112     CGOStop(this);
10113     break;
10114   case CGO_DRAW_ARRAYS:
10115     copy_op_from<cgo::draw::arrays>(pc);
10116     break;
10117   case CGO_DRAW_BUFFERS_INDEXED:
10118     copy_op_from<cgo::draw::buffers_indexed>(pc);
10119     break;
10120   case CGO_DRAW_TEXTURES:
10121     copy_op_from<cgo::draw::textures>(pc);
10122     break;
10123   case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
10124     copy_op_from<cgo::draw::screen_textures>(pc);
10125     break;
10126   case CGO_DRAW_LABELS:
10127     copy_op_from<cgo::draw::labels>(pc);
10128     break;
10129   case CGO_DRAW_CONNECTORS:
10130     copy_op_from<cgo::draw::connectors>(pc);
10131     break;
10132   case CGO_DRAW_BUFFERS_NOT_INDEXED:
10133     copy_op_from<cgo::draw::buffers_not_indexed>(pc);
10134     break;
10135   case CGO_DRAW_SPHERE_BUFFERS:
10136     copy_op_from<cgo::draw::sphere_buffers>(pc);
10137     break;
10138   case CGO_DRAW_CYLINDER_BUFFERS:
10139     copy_op_from<cgo::draw::cylinder_buffers>(pc);
10140     break;
10141   case CGO_DRAW_CUSTOM:
10142     copy_op_from<cgo::draw::custom>(pc);
10143     break;
10144   default:
10145     int sz = CGO_sz[op];
10146     std::copy_n(pc - 1, sz + 1, add_to_buffer(sz + 1));
10147   };
10148 }
10149 
print_table() const10150 void CGO::print_table() const {
10151 }
10152 
CGOConvertSpheresToPoints(const CGO * I)10153 CGO* CGOConvertSpheresToPoints(const CGO* I)
10154 {
10155   CGO *cgo;
10156 
10157   int ok = true;
10158   cgo = CGONew(I->G);
10159   CHECKOK(ok, cgo);
10160   CGOBegin(cgo, GL_POINTS);
10161 
10162   for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
10163     const auto pc = it.data();
10164     const auto op = it.op_code();
10165 
10166     switch (op) {
10167     case CGO_PICK_COLOR:
10168       cgo->current_pick_color_index = CGO_get_uint(pc);
10169       cgo->current_pick_color_bond = CGO_get_int(pc + 1);
10170       CGOPickColor(cgo, cgo->current_pick_color_index, cgo->current_pick_color_bond);
10171       break;
10172     case CGO_SHADER_CYLINDER:
10173     case CGO_SHADER_CYLINDER_WITH_2ND_COLOR:
10174     case CGO_CYLINDER:
10175     case CGO_CONE:
10176     case CGO_SAUSAGE:
10177     case CGO_CUSTOM_CYLINDER:
10178     case CGO_CUSTOM_CYLINDER_ALPHA:
10179     case CGO_END:
10180     case CGO_VERTEX:
10181     case CGO_BEGIN:
10182     case CGO_ELLIPSOID:
10183     case CGO_QUADRIC:
10184     case CGO_DRAW_BUFFERS_INDEXED:
10185     case CGO_DRAW_BUFFERS_NOT_INDEXED:
10186     case CGO_DRAW_SPHERE_BUFFERS:
10187     case CGO_DRAW_CYLINDER_BUFFERS:
10188     case CGO_DRAW_LABELS:
10189       break;
10190     case CGO_SPHERE:
10191       CGOVertexv(cgo, pc);
10192       break;
10193     case CGO_ALPHA:
10194       cgo->alpha = *pc;
10195     default:
10196       cgo->add_to_cgo(op, pc);
10197     }
10198     ok &= !I->G->Interrupt;
10199   }
10200   CGOEnd(cgo);
10201   if (ok){
10202     ok &= CGOStop(cgo);
10203   }
10204   if (!ok){
10205     CGOFree(cgo);
10206   }
10207   return (cgo);
10208 }
10209 
10210 #ifdef _PYMOL_ARB_SHADERS
CGORenderSpheresARB(RenderInfo * info,const CGO * I,const float * fog_info)10211 void CGORenderSpheresARB(RenderInfo* info, const CGO* I, const float* fog_info)
10212 {
10213   static const float _00[2] = { 0.0F, 0.0F };
10214   static const float _01[2] = { 0.0F, 1.0F };
10215   static const float _11[2] = { 1.0F, 1.0F };
10216   static const float _10[2] = { 1.0F, 0.0F };
10217   if(I->c) {
10218     float last_radius;
10219     last_radius = -1.f;
10220     glNormal3fv(info->view_normal);
10221     glBegin(GL_QUADS);
10222 
10223     for (auto it = I->begin(); !it.is_stop(); ++it) {
10224       const auto pc = it.data();
10225       const auto op = it.op_code();
10226 
10227       switch (op) {
10228       case CGO_SPHERE:
10229         {
10230           float sphereCenter[] = { *(pc), *(pc+1), *(pc+2) };
10231           float sphereRadius = *(pc+3);
10232           if(last_radius != sphereRadius) {
10233             glEnd();
10234             glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB,
10235                                        0, 0.0F, 0.0F, sphereRadius, 0.0F);
10236             glProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB,
10237                                        0, fog_info[0], fog_info[1], 0.0F, 0.0F);
10238             glBegin(GL_QUADS);
10239             last_radius = sphereRadius;
10240           }
10241           glTexCoord2fv(_00);
10242           glVertex3fv(sphereCenter);
10243           glTexCoord2fv(_10);
10244           glVertex3fv(sphereCenter);
10245           glTexCoord2fv(_11);
10246           glVertex3fv(sphereCenter);
10247           glTexCoord2fv(_01);
10248           glVertex3fv(sphereCenter);
10249         }
10250         break;
10251       case CGO_COLOR:
10252         glColor3f(*pc, *(pc + 1), *(pc + 2));
10253         break;
10254       }
10255     }
10256     glEnd();
10257   }
10258 }
10259 #endif
10260 
10261 // Will create an interleaved VBO with { vertex, otherVertex, uv, and texcoord info }
10262 // Currently, this function does not support/parse lines inside CGODrawArrays, i.e.,
10263 // a CGO that CGOCombineBeginEnd was used on
CGOConvertLinesToTrilines(const CGO * I,bool addshaders)10264 CGO *CGOConvertLinesToTrilines(const CGO * I, bool addshaders){
10265   static std::set<int> lineops = { CGO_VERTEX, CGO_LINE, CGO_SPLITLINE };
10266   auto G = I->G;
10267   const int nLines = CGOCountNumberOfOperationsOfTypeN(I, lineops ) + 1;
10268 
10269   if (nLines == 0) {
10270     return nullptr;
10271   }
10272 
10273   int line_counter = 0;
10274   GLuint glbuff = 0;
10275   const float *colorv = NULL;
10276   unsigned int buff_size = nLines * 6 * (8 * sizeof(float));
10277 
10278   // VBO memory -- number of lines x 4 vertices per line x (vertex + otherVertex + normal + texCoord + color)
10279   std::vector<float> buffer_start(buff_size);
10280   float *buffer = buffer_start.data();
10281 
10282   std::unique_ptr<CGO> cgo(new CGO(G));
10283 
10284   for (auto it = I->begin(); !it.is_stop(); ++it) {
10285     const auto op = it.op_code();
10286     const auto pc = it.data();
10287 
10288     switch (op) {
10289     case CGO_DRAW_ARRAYS:
10290     {
10291         auto sp = it.cast<cgo::draw::arrays>();
10292 	float *vals = cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
10293 	int nvals = sp->narrays*sp->nverts;
10294         memcpy(vals, sp->floatdata, nvals);
10295       }
10296       break;
10297     case CGO_END:
10298       WARN_UNEXPECTED_OPERATION(G, op);
10299       return nullptr;
10300     case CGO_BEGIN:
10301       {
10302         const float *last_vertex = nullptr, *last_color = nullptr,
10303                     *current_color = nullptr, *color = nullptr;
10304         const int mode = it.cast<cgo::draw::begin>()->mode;
10305 
10306         for (++it;; ++it) {
10307           if (it.is_stop()) {
10308             WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
10309             return nullptr;
10310           }
10311 
10312           const auto op = it.op_code();
10313           if (op == CGO_END) {
10314             break;
10315           }
10316 
10317           const auto pc = it.data();
10318 
10319 	  switch (op) {
10320 	  case CGO_VERTEX:
10321 	    if (last_vertex){
10322 	      switch (mode){
10323 	      case GL_LINES:
10324 	      case GL_LINE_STRIP:
10325 		{
10326 		  float cc[3] = { 1, 1, 1 };
10327 		  float alpha = cgo->alpha;
10328                   CGOTrilines_GetCurrentColor(current_color, colorv, last_color, cc);
10329 		  trilinesBufferAddVertices(buffer, pc, last_vertex, current_color, alpha);
10330 		  line_counter++;
10331 		  last_vertex = pc;
10332 		}
10333 		if (mode==GL_LINES){
10334 		  last_vertex = NULL;
10335 		  last_color = NULL;
10336 		}
10337 	      }
10338 	    } else {
10339 	      last_vertex = pc;
10340 	      current_color = color;
10341 	    }
10342             break;
10343 	  case CGO_LINE:
10344             {
10345               auto line = it.cast<cgo::draw::line>();
10346               float cc[3] = { 1, 1, 1 };
10347               float alpha = cgo->alpha;
10348               CGOTrilines_GetCurrentColor(current_color, colorv, last_color, cc);
10349               trilinesBufferAddVertices(buffer, line->vertex1, line->vertex2, current_color, alpha);
10350               line_counter++;
10351             }
10352             break;
10353 	  case CGO_SPLITLINE:
10354             {
10355               auto splitline = it.cast<cgo::draw::splitline>();
10356               float cc[3] = { 1, 1, 1 };
10357               float alpha = cgo->alpha;
10358               float mid[3];
10359               float color2[] = { CONVERT_COLOR_VALUE(splitline->color2[0]),
10360                                  CONVERT_COLOR_VALUE(splitline->color2[1]),
10361                                  CONVERT_COLOR_VALUE(splitline->color2[2]) };
10362               add3f(splitline->vertex1, splitline->vertex2, mid);
10363               mult3f(mid, .5f, mid);
10364               CGOTrilines_GetCurrentColor(current_color, colorv, last_color, cc);
10365               trilinesBufferAddVertices(buffer, splitline->vertex1, mid, current_color, alpha);
10366               trilinesBufferAddVertices(buffer, mid, splitline->vertex2, color2, alpha);
10367               line_counter+=2;
10368             }
10369             break;
10370 	  case CGO_COLOR:
10371 	      last_color = current_color;
10372 	      current_color = pc;
10373 	      color = pc;
10374             break;
10375 	  }
10376 	}
10377       }
10378       break;
10379     case CGO_ALPHA:
10380       cgo->alpha = *pc;
10381       break;
10382     case CGO_COLOR:
10383       colorv = pc;
10384       break;
10385     }
10386   }
10387 
10388   cgo->use_shader = I->use_shader;
10389   if (cgo->use_shader){
10390     cgo->cgo_shader_ub_color = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
10391     cgo->cgo_shader_ub_normal = SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
10392   }
10393 
10394   {
10395     int err = 0;
10396     glGenBuffers(1, &glbuff);
10397     glBindBuffer(GL_ARRAY_BUFFER, glbuff);
10398     glBufferData(GL_ARRAY_BUFFER, line_counter * 6 * 8 * sizeof(float), buffer_start.data(), GL_STATIC_DRAW);
10399     CHECK_GL_ERROR_OK("ERROR: CGOConvertLinesToTriangleStrips() glBindBuffer returns err=%d\n");
10400     if (addshaders)
10401       cgo->add<cgo::draw::enable>(GL_TRILINES_SHADER);
10402     cgo->add<cgo::draw::trilines>(line_counter * 6, glbuff);
10403     cgo->has_draw_buffers = true;
10404     if (addshaders)
10405       cgo->add<cgo::draw::disable>(GL_TRILINES_SHADER);
10406     CGOStop(cgo.get());
10407   }
10408 
10409   return cgo.release();
10410 }
10411 
10412 /*
10413  * copies data for a particular attribute operation into the array used to load the VBO.
10414  * this takes into account whether it is interleaved or not.
10415  *
10416  * isInterleaved    : whether the VBO is interleaved
10417  * nvert            : which vertex in the VBO
10418  * attribOp         : the attribute op
10419  * vertexDataSize   : total vertex data size in VBO (for interleaved)
10420  * dataPtrs         : all data pointers for attributes (for interleaved, they are all the pointer to the one array)
10421  * attrOffset       : offsets of the attributes (for interleaved)
10422  * pcarg            : pc pointer to CGO operation data
10423  * pick_data        : pointer to pick data for current vertex (writes to if pick data)
10424  * has_pick_colorBS : keeps track of which pick attributes have been set
10425  *
10426  */
10427 static
copyAttributeForOp(bool isInterleaved,int & nvert,AttribOp * attribOp,int vertexDataSize,vector<void * > & dataPtrs,vector<int> & attrOffset,const float * pcarg,float * pick_data,int & has_pick_colorBS,int pstride)10428 void copyAttributeForOp(bool isInterleaved, int &nvert, AttribOp *attribOp, int vertexDataSize, vector<void*> &dataPtrs,
10429                         vector<int> &attrOffset, const float *pcarg, float *pick_data, int &has_pick_colorBS, int pstride){
10430   auto attrDesc = attribOp->desc;
10431   int ord = attrDesc->order;
10432   int copyord = -1;
10433   void *dataPtr = dataPtrs[ord];
10434   unsigned char *pc = ((unsigned char *)pcarg) + attribOp->offset;
10435   if (isInterleaved){
10436     dataPtr = (unsigned char*) dataPtr + nvert * vertexDataSize + attrOffset[ord];
10437     if (attribOp->copyAttribDesc){
10438       copyord = attribOp->copyAttribDesc->order;
10439       pc = ((unsigned char*) dataPtrs[ord]) + nvert * vertexDataSize + attrOffset[copyord];
10440     }
10441   } else {
10442     int sz = gl_sizeof(attrDesc->type_size) * attrDesc->type_dim;
10443     dataPtr = (unsigned char*) dataPtr + nvert * sz;
10444     if (attribOp->copyAttribDesc){
10445       copyord = attribOp->copyAttribDesc->order;
10446       int copysz = gl_sizeof(attribOp->copyAttribDesc->type_size) * attribOp->copyAttribDesc->type_dim;
10447       pc = (unsigned char*) dataPtr + nvert * copysz;
10448     }
10449   }
10450   switch (attribOp->conv_type){
10451   case NO_COPY:
10452     break;
10453   case FLOAT_TO_FLOAT:
10454     *((float *) dataPtr) = *((float *)pc);
10455     break;
10456   case FLOAT2_TO_FLOAT2:
10457     *((float *) dataPtr) = *((float *)pc);
10458     *((float *) dataPtr + 1) = *((float *)pc + 1);
10459     break;
10460   case FLOAT3_TO_FLOAT3:
10461     copy3f((float*)pc, (float*)dataPtr);
10462     break;
10463   case FLOAT4_TO_FLOAT4:
10464     *((float *) dataPtr) = *((float *)pc);
10465     *((float *) dataPtr + 1) = *((float *)pc + 1);
10466     *((float *) dataPtr + 2) = *((float *)pc + 2);
10467     *((float *) dataPtr + 3) = *((float *)pc + 3);
10468     break;
10469   case FLOAT3_TO_UB3:
10470     {
10471       auto dataPtrUB = (unsigned char *)dataPtr;
10472       float *pcf = (float*)pc;
10473       dataPtrUB[0] = CLIP_COLOR_VALUE(pcf[0]);
10474       dataPtrUB[1] = CLIP_COLOR_VALUE(pcf[1]);
10475       dataPtrUB[2] = CLIP_COLOR_VALUE(pcf[2]);
10476     }
10477     break;
10478   case FLOAT1_TO_UB_4TH:
10479     {
10480       auto dataPtrUB = (unsigned char *)dataPtr;
10481       float *pcf = (float*)pc;
10482       dataPtrUB[3] = CLIP_COLOR_VALUE(pcf[0]);
10483     }
10484     break;
10485   case UB3_TO_UB3:
10486     {
10487       auto dataPtrUB = (unsigned char *)dataPtr;
10488       auto pcUB = (unsigned char *)pc;
10489       dataPtrUB[0] = pcUB[0];
10490       dataPtrUB[1] = pcUB[1];
10491       dataPtrUB[2] = pcUB[2];
10492       break;
10493     }
10494   case UINT_INT_TO_PICK_DATA:
10495     {
10496       float *pcf = (float*)pc;
10497       unsigned int index = CGO_get_uint(pcf);
10498       int bond = CGO_get_int(pcf+1);
10499       CGO_put_uint(ord * 2 + pick_data, index);
10500       CGO_put_int(ord * 2 + pick_data + 1, bond);
10501       has_pick_colorBS |= (1 << ord) ;
10502     }
10503     break;
10504   case FLOAT4_TO_UB4:
10505     {
10506       auto dataPtrUB = (unsigned char *)dataPtr;
10507       float *pcf = (float*)pc;
10508       dataPtrUB[0] = CLIP_COLOR_VALUE(pcf[0]);
10509       dataPtrUB[1] = CLIP_COLOR_VALUE(pcf[1]);
10510       dataPtrUB[2] = CLIP_COLOR_VALUE(pcf[2]);
10511       dataPtrUB[3] = CLIP_COLOR_VALUE(pcf[3]);
10512     }
10513     break;
10514   case CYL_CAP_TO_CAP:
10515     {
10516       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10517       dataPtrUB[0] = *pc;
10518     }
10519     break;
10520   case CYL_CAPS_ARE_ROUND:
10521     {
10522       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10523       dataPtrUB[0] = cCylShaderBothCapsRound | cCylShaderInterpColor;
10524     }
10525     break;
10526   case CYL_CAPS_ARE_FLAT:
10527     {
10528       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10529       dataPtrUB[0] = cCylShaderBothCapsFlat | cCylShaderInterpColor;
10530     }
10531     break;
10532   case CYL_CAPS_ARE_CUSTOM:
10533     {
10534       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10535       float *pcf = (float*)pc;
10536       int pci[] = { (int)pcf[0], (int)pcf[1] };
10537       dataPtrUB[0] = (((pci[0]) == 1) ? cCylShaderCap1Flat : (((pci[0]) == 2) ? cCylShaderCap1Round : cCylCapNone)) |
10538                      (((pci[1]) == 1) ? cCylShaderCap2Flat : (((pci[1]) == 2) ? cCylShaderCap2Round : cCylCapNone)) |
10539                      cCylShaderInterpColor;
10540     }
10541     break;
10542   case UB1_TO_INTERP:
10543     {
10544       bool interp = (pc[0] & cgo::draw::splitline::interpolation);
10545       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10546       dataPtrUB[0] = interp ? 1 : 0;
10547     }
10548     break;
10549   case UB1_INTERP_TO_CAP:
10550     {
10551       bool interp = (pc[0] & cgo::draw::splitline::interpolation);
10552       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10553       dataPtrUB[0] = (cCylShaderBothCapsRound | (interp ? cCylShaderInterpColor : 0));
10554     }
10555     break;
10556   case FLOAT1_TO_INTERP:
10557     {
10558       float interp = *((float*)pc);
10559       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10560       dataPtrUB[0] = (interp > .5f) ? 1 : 0;
10561     }
10562     break;
10563   case FLOAT1_INTERP_TO_CAP:
10564     {
10565       float interp = *((float*)pc);
10566       unsigned char *dataPtrUB = (unsigned char *)dataPtr;
10567       dataPtrUB[0] = (cCylShaderBothCapsRound | ((interp > .5f) ? cCylShaderInterpColor : 0));
10568     }
10569     break;
10570   case UB4_TO_UB4:
10571     {
10572       auto dataPtrUB = (unsigned char *)dataPtr;
10573       auto pcUB = (unsigned char *)pc;
10574       dataPtrUB[0] = pcUB[0];
10575       dataPtrUB[1] = pcUB[1];
10576       dataPtrUB[2] = pcUB[2];
10577       dataPtrUB[3] = pcUB[3];
10578       break;
10579     }
10580   case PICK_DATA_TO_PICK_DATA:
10581     {
10582       float *pcf;
10583       if (copyord < 0){
10584         pcf = (float*)pc;
10585       } else {
10586         pcf = (copyord * 2 + pick_data);
10587         if (nvert){
10588           pcf -= pstride;
10589         }
10590       }
10591       unsigned int index = CGO_get_uint(pcf);
10592       int bond = CGO_get_int(pcf+1);
10593       CGO_put_uint(ord * 2 + pick_data, index);
10594       CGO_put_int(ord * 2 + pick_data + 1, bond);
10595       has_pick_colorBS |= (1 << ord) ;
10596       break;
10597     }
10598   }
10599 }
10600 
10601 /*
10602  * copies data for a particular attribute into the array used to load the VBO.
10603  * this takes into account whether it is interleaved or not, and if an attribute
10604  * has repeat values
10605  *
10606  * isInterleaved  : whether the VBO is interleaved
10607  * nvert          : which vertex in the VBO
10608  * attribDesc     : the attribute description
10609  * vertexDataSize : total vertex data size in VBO (for interleaved)
10610  * dataPtrs       : all data pointers for attributes (for interleaved, they are all the pointer to the one array)
10611  * attrOffset     : offsets of the attributes (for interleaved)
10612  *
10613  */
10614 static
copyAttributeForVertex(bool isInterleaved,int & nvert,AttribDesc & attribDesc,const int vertexDataSize,vector<void * > & dataPtrs,vector<int> & attrOffset)10615 void copyAttributeForVertex(bool isInterleaved, int &nvert, AttribDesc &attribDesc,
10616                             const int vertexDataSize, vector<void*> &dataPtrs, vector<int> &attrOffset){
10617   int ord = attribDesc.order;
10618   void *dataPtr = dataPtrs[ord];
10619   unsigned char *pc = NULL;
10620   int attrSize = gl_sizeof(attribDesc.type_size) * attribDesc.type_dim;
10621   if (isInterleaved){
10622     dataPtr = (unsigned char*) dataPtr + nvert * vertexDataSize + attrOffset[ord];
10623     pc = (unsigned char*) dataPtr - vertexDataSize;
10624   } else {
10625     dataPtr = (unsigned char*) dataPtr + nvert * attrSize;
10626     pc = (unsigned char*) dataPtr - attrSize;
10627   }
10628   if (attribDesc.repeat_value && attribDesc.repeat_value_length){
10629     int pos = (nvert % attribDesc.repeat_value_length);
10630     pc = attribDesc.repeat_value + pos * attrSize;
10631     memcpy(dataPtr, pc, attrSize);
10632   } else {
10633     memcpy(dataPtr, pc, attrSize);
10634   }
10635 }
10636 /*
10637  * check all attributes (pick and non-pick) to see if they are specified in the CGO (I)
10638  * also checks to see if any picking is specified and sets has_picking argument
10639  * if any of the attributes are not specified and if a default_value is set (in the
10640  * AttribDesc) then the associated vertex_attribute CGO operation is inserted into the
10641  * cgo that is passed in.
10642  *
10643  * I:           primitive CGO that is processed
10644  * attrData:    definition of attributes
10645  * pickData:    definition of pick attributes
10646  * cgo:         new cgo that could have vertex_attribute CGO operations added
10647  * has_picking: if there are any operations in the CGO (I) that specifies different values for picking.
10648  *              if has_picking is set, then a VBO for picking is generated in CGOConvertToShader()
10649  *
10650  */
10651 static
CheckAttributesForUsage(const CGO * I,AttribDataDesc & attrData,AttribDataDesc & pickData,CGO * cgo,bool & has_picking)10652 void CheckAttributesForUsage(const CGO *I, AttribDataDesc &attrData, AttribDataDesc &pickData, CGO *cgo, bool &has_picking)
10653 {
10654   size_t attrIdx = 0;
10655 
10656   // need to check attributes:
10657   //  - remove any that are not needed
10658   //  - add glVertexAttrib for those that are removed
10659   std::map<int, int> opToAttrUsed; // bitmask for each op to which attributes are set
10660   for (auto &attrDesc : attrData){
10661     auto attrOps = &attrDesc.attrOps;
10662     attrDesc.order = attrIdx++;
10663     for (auto attrOpIt = attrOps->begin(); attrOpIt!=attrOps->end(); ++attrOpIt){
10664       auto attrOp = &(*attrOpIt);
10665       if (opToAttrUsed.find(attrOp->op) == opToAttrUsed.end())
10666         opToAttrUsed[attrOp->op] = 1 << attrDesc.order;
10667       else
10668         opToAttrUsed[attrOp->op] |= 1 << attrDesc.order;
10669     }
10670   }
10671   // add picking ops (1 << attrIdx) i.e., any pick op is the last bit
10672   int pidx = 0;
10673   for (auto pickDataIt = pickData.begin(); pickDataIt!=pickData.end(); ++pickDataIt){
10674     auto pickDesc = &(*pickDataIt);
10675     auto pickOps = &pickDesc->attrOps;
10676     pickDesc->order = pidx++;
10677     for (auto pickOpIt = pickOps->begin(); pickOpIt!=pickOps->end(); ++pickOpIt){
10678       auto pickOp = &(*pickOpIt);
10679       pickOp->desc = pickDesc;
10680       if (opToAttrUsed.find(pickOp->op) == opToAttrUsed.end())
10681         opToAttrUsed[pickOp->op] = 1 << attrIdx;
10682       else
10683         opToAttrUsed[pickOp->op] |= 1 << attrIdx;
10684     }
10685   }
10686   size_t totAttrIdx = (1 << (attrIdx+1)) - 1;
10687   size_t allAttrIdxUsed = 0;
10688   for (auto it = I->begin(); !it.is_stop(); ++it) {
10689     auto pc = it.data();
10690     int op = it.op_code();
10691     if (opToAttrUsed.find(op) != opToAttrUsed.end()){
10692       int attrUsed = opToAttrUsed[op];
10693       if (attrUsed & (1 << attrIdx)){ // if picking, need to check values, and take them out if cPickableNoPick
10694         switch (op){
10695         case cgo::draw::shadercylinder2ndcolor::op_code:
10696           if (reinterpret_cast<const cgo::draw::shadercylinder2ndcolor *>(pc)->pick_color_bond == cPickableNoPick)
10697             attrUsed ^= (1 << attrIdx);
10698           break;
10699         case cgo::draw::splitline::op_code:
10700           if (reinterpret_cast<const cgo::draw::splitline *>(pc)->bond == cPickableNoPick)
10701             attrUsed ^= (1 << attrIdx);
10702         }
10703       }
10704       allAttrIdxUsed |= attrUsed;
10705       if (allAttrIdxUsed == totAttrIdx)
10706         break;
10707     }
10708   }
10709   has_picking = allAttrIdxUsed & (1 << attrIdx);  // has_picking if the last bit is set
10710 
10711   if (allAttrIdxUsed != totAttrIdx){
10712     // go through any attributes that aren't used:
10713     //   - add associated vertex_attribute type (if default_value is set)
10714     //   - remove attribute from attrData description so that it isn't included in VBO
10715     AttribDataDesc attrDataNew;
10716     for (auto idx = 0; idx < attrIdx; ++idx){
10717       if (!attrData[idx].repeat_value && !(allAttrIdxUsed & (1 << idx))) {
10718         // attribute not used, need to create glVertexAttrib
10719         if (attrData[idx].default_value){
10720           // need to add glVertexAttrib CGO OP
10721           int attr_lookup_idx = I->G->ShaderMgr->GetAttributeUID(attrData[idx].attr_name);
10722           switch (attrData[idx].type_size){
10723           case GL_FLOAT:
10724             switch (attrData[idx].type_dim){
10725             case 1:
10726               cgo->add<cgo::draw::vertex_attribute_1f>(attr_lookup_idx, *(float*)attrData[idx].default_value);
10727               break;
10728             case 3:
10729               cgo->add<cgo::draw::vertex_attribute_3f>(attr_lookup_idx, attrData[idx].default_value);
10730               break;
10731             default:
10732               std::cerr << "\tNOT IMPLEMENTED: attrData[idx].type_size=" << attrData[idx].type_size << " attrData[idx].type_dim=" << attrData[idx].type_dim << endl;
10733             }
10734             break;
10735           case GL_UNSIGNED_BYTE:
10736             switch (attrData[idx].type_dim){
10737             case 1:
10738               {
10739                 float val;
10740                 unsigned char valuc = *attrData[idx].default_value;
10741                 if (attrData[idx].data_norm){
10742                   val = CLAMPVALUE(valuc / 255.f, 0.f, 1.f);
10743                 } else {
10744                   val = (float)valuc;
10745                 }
10746                 cgo->add<cgo::draw::vertex_attribute_1f>(attr_lookup_idx, val);
10747               }
10748               break;
10749             case 4:
10750               cgo->add<cgo::draw::vertex_attribute_4ub>(attr_lookup_idx, attrData[idx].default_value);
10751               break;
10752             default:
10753               std::cerr << "\tNOT IMPLEMENTED: attrData[idx].type_size=" << attrData[idx].type_size << " attrData[idx].type_dim=" << attrData[idx].type_dim << endl;
10754             }
10755           }
10756         }
10757       } else {
10758         attrDataNew.push_back(attrData[idx]);
10759       }
10760     }
10761     attrData.swap(attrDataNew);  // only keep attributes that are used
10762   }
10763 }
10764 
10765 /*
10766  * Populates two structures and sets vertsperpickinfo
10767  * opToCntPer           : CGO op to how many vertices are generated for each op.
10768  * opToOrderedAttribOps : CGO op to an ordered map of AttribOps that define how we operate on
10769  *                        the attribute arrays for each CGO op.
10770  * attrData             : definition of attributes
10771  * pickData             : definition of pick attributes
10772  * vertsperpickinfo     : number of vertices per pick (only used when
10773  *
10774  */
10775 static
PopulateOpsIntoStructuresForConversion(std::map<int,int> & opToCntPer,std::map<int,std::map<int,AttribOp * >> & opToOrderedAttribOps,AttribDataDesc & attrData,AttribDataDesc & pickData,int & vertsperpickinfo,const bool has_picking)10776 void PopulateOpsIntoStructuresForConversion(std::map<int,int> &opToCntPer,
10777                                             std::map< int, std::map<int, AttribOp*> > &opToOrderedAttribOps,
10778                                             AttribDataDesc &attrData, AttribDataDesc &pickData,
10779                                             int &vertsperpickinfo, const bool has_picking){
10780   size_t attrIdx = 0;
10781   for (auto &attrDesc : attrData){
10782     auto attrOps = &attrDesc.attrOps;
10783     attrDesc.order = attrIdx++;
10784     for (auto attrOpIt = attrOps->begin(); attrOpIt!=attrOps->end(); ++attrOpIt){
10785       auto attrOp = &(*attrOpIt);
10786       attrOp->desc = &attrDesc;
10787       if (attrOp->copyFromAttr >= 0){
10788         attrOp->copyAttribDesc = &attrData[attrOp->copyFromAttr];
10789       }
10790       if (attrOp->incr_vertices > 0){
10791         if (!vertsperpickinfo){
10792           vertsperpickinfo = attrOp->incr_vertices;
10793         } else {
10794           if (attrOp->incr_vertices != vertsperpickinfo){
10795             std::cerr << "WARNING: attrOp->incr_vertices set to multiple values, vertsperpickinfo=" << vertsperpickinfo << " attrOp->incr_vertices=" << attrOp->incr_vertices << " : picking might get confused" << std::endl;
10796           }
10797         }
10798         if (opToCntPer.find(attrOp->op) == opToCntPer.end())
10799           opToCntPer[attrOp->op] = attrOp->incr_vertices;
10800         else
10801           opToCntPer[attrOp->op] += attrOp->incr_vertices;
10802       }
10803       if (opToOrderedAttribOps.find(attrOp->op) == opToOrderedAttribOps.end())
10804         opToOrderedAttribOps[attrOp->op] = std::map<int, AttribOp*>({});
10805       opToOrderedAttribOps[attrOp->op][attrOp->order] = attrOp;
10806     }
10807   }
10808   if (has_picking){
10809     for (auto pickDataIt = pickData.begin(); pickDataIt!=pickData.end(); ++pickDataIt){
10810       auto pickDesc = &(*pickDataIt);
10811       auto pickOps = &pickDesc->attrOps;
10812       for (auto pickOpIt = pickOps->begin(); pickOpIt!=pickOps->end(); ++pickOpIt){
10813         auto pickOp = &(*pickOpIt);
10814         if (pickOp->copyFromAttr >= 0){
10815           pickOp->copyAttribDesc = &pickData[pickOp->copyFromAttr];
10816         }
10817         if (opToOrderedAttribOps.find(pickOp->op) == opToOrderedAttribOps.end())
10818           opToOrderedAttribOps[pickOp->op] = std::map<int, AttribOp*>({});
10819         opToOrderedAttribOps[pickOp->op][pickOp->order] = pickOp;
10820       }
10821     }
10822   }
10823 }
10824 
10825 /*
10826  * converts a "primitive" CGO into a CGO that renders a custom operation
10827  *
10828  * I:                   primitive CGO that is processed
10829  * attrData:            definition of attributes that are accumulated and put into the VBO
10830  * pickData:            definition of pick attributes that accumulate pick data and put into the interleaved picking VBO
10831  * mode:                which openGL mode to use for rendering (e.g., GL_POINTS, GL_TRIANGLES, GL_LINE_STRIPS, etc.)
10832  * layout:              SEPARATE, SEQUENTIAL, or INTERLEAVED : how the VBO is layed out in memory (default: INTERLEAVED)
10833  * check_attr_for_data: if true, this function checks whether all attributes are used, and if any are not specified,
10834  *                      and default values are defined in AttribDesc, then glVertexAttrib CGO ops are created on the
10835  *                      returned CGO (default: true)
10836  * idx_array:           if specified, this array is used to specify indices for each fragment in an Indexed Buffer
10837  *                      and glDrawElements is used to render (instead of glDrawArrays)
10838  * nvertsperfrag:       in conjunction with idx_array, how many vertices for each set of indices (default: 0)
10839  *                      both idx_array and nvertsperfrag are used to specify vertices and geometry for each fragment,
10840  *                      such as the box for cylinders
10841  * nfragspergroup:      Currently, this represents the number of fragments that are inside of a group.  For example,
10842  *                      crosses as cylinders (CGOConvertCrossesToCylinderShader) have 36 indices per fragment (nvertsperfrag=36)
10843  *                      and 3 fragments per group (nfragspergroup=3) (i.e., one for each line).
10844  *                      note: idx_array, nvertsperfrag and nfragspergroup should probably be moved to AttrOp in
10845  *                            the future to support different types of fragments.
10846  *
10847  * returns a CGO that consists of vertex_attrib_* (e.g., glVertexAttrib) and a custom CGO operation that calls
10848  * either glDrawArrays or glDrawElements. It also supports picking if specified in the pickData.
10849  *
10850  */
CGOConvertToShader(const CGO * I,AttribDataDesc & attrData,AttribDataDesc & pickData,int mode,const VertexBuffer::buffer_layout layout,bool check_attr_for_data,int * idx_array,int nvertsperfrag,int nfragspergroup)10851 CGO *CGOConvertToShader(const CGO *I, AttribDataDesc &attrData, AttribDataDesc &pickData, int mode,
10852                         const VertexBuffer::buffer_layout layout, bool check_attr_for_data,
10853                         int *idx_array, int nvertsperfrag, int nfragspergroup){
10854   CGO *cgo;
10855   int ok = true;
10856   bool isInterleaved = (layout == VertexBuffer::INTERLEAVED);
10857   bool has_picking = true;
10858   std::map<string, AttribDesc*> attrToDesc;
10859 
10860   cgo = CGONew(I->G);
10861   cgo->use_shader = true;
10862 
10863   if (check_attr_for_data){
10864     CheckAttributesForUsage(I, attrData, pickData, cgo, has_picking);
10865   } else {
10866     // if attributes aren't checked, still need to set pick order and desc pointer
10867     int pidx = 0;
10868     for (auto &pickDesc : pickData){
10869       pickDesc.order = pidx++;
10870       for (auto &pickOp : pickDesc.attrOps){
10871         pickOp.desc = &pickDesc;
10872       }
10873     }
10874   }
10875   std::map<int,int> opToCntPer;
10876   std::map< int, std::map<int, AttribOp*> > opToOrderedAttribOps ;
10877 
10878   int vertsperpickinfo = 0;
10879   PopulateOpsIntoStructuresForConversion(opToCntPer, opToOrderedAttribOps, attrData,
10880                                          pickData, vertsperpickinfo, has_picking);
10881 
10882 
10883   // Populate these variables used for accumulating and setting VBO data arrays
10884   int vertexDataSize = 0;
10885   vector<int> attrSizes;
10886   vector<int> attrOffset; // for interleaved
10887   int curoffset = 0;      // for interleaved
10888   size_t attrIdx = 0;
10889   for (auto &attrDesc : attrData){
10890     attrDesc.order = attrIdx++;
10891     int attrSize = gl_sizeof(attrDesc.type_size) * attrDesc.type_dim;
10892     attrSizes.push_back(attrSize);
10893     vertexDataSize += attrSize;
10894 
10895     if (isInterleaved){
10896       attrOffset.push_back(curoffset);
10897       curoffset += attrSize;
10898     } else {
10899       attrOffset.push_back(0);  // no offset when not interleaved
10900     }
10901     attrToDesc[attrDesc.attr_name] = &attrDesc;
10902   }
10903 
10904   // populate funcData.attrib
10905   for (auto &attrDesc : attrData)
10906     for (auto &attrop : attrDesc.attrOps)
10907       for (auto &funcData : attrop.funcDataConversions){
10908         if (attrToDesc.find(funcData.attribName) != attrToDesc.end()){
10909           funcData.attrib = attrToDesc[funcData.attribName];
10910         }
10911       }
10912 
10913   // Since some cards require word-aligned strides (e.g., ATI)
10914   // we need to make sure it is word-aligned.  If it isn't, and you
10915   // want to save memory, maybe SEQUENTIAL is a better option
10916   if (vertexDataSize % 4){
10917     vertexDataSize += 4 - (vertexDataSize % 4);
10918   }
10919 
10920   int ntotalverts = CGOCountNumberOfOperationsOfTypeN(I, opToCntPer);
10921 
10922   // PYMOL-2668
10923   if (ntotalverts == 0) {
10924     CGOStop(cgo);
10925     return cgo;
10926   }
10927 
10928   size_t pickvbohash = 0;
10929   int pickDataSize = pickData.size();
10930   int pstride = 2 * pickDataSize;
10931   // Generate VBOs: both for vertex data (vbo) and picking color data (pickvbo) if necessary
10932   // - pickvbo is interleaved so that for multiple channels, pick data for each vertex
10933   //   is contiguous
10934   VertexBuffer *vbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(layout);
10935   VertexBuffer *pickvbo = NULL;
10936   if (pickDataSize){
10937     pickvbo = I->G->ShaderMgr->newGPUBuffer<VertexBuffer>(VertexBuffer::SEQUENTIAL, GL_DYNAMIC_DRAW);
10938     pickvbohash = pickvbo->get_hash_id();
10939   }
10940 
10941   // adding SPECIAL OPERATIONS (for now) before adding custom OP
10942   // - for now, this is the only operation that needs to be passed to the new CGO
10943   for (auto it = I->begin(); !it.is_stop(); ++it) {
10944     auto pc = it.data();
10945     int op = it.op_code();
10946     switch (op){
10947     case CGO_SPECIAL:
10948       cgo->add_to_cgo(op, pc);
10949       break;
10950     }
10951   }
10952 
10953   // defines how many passes we have
10954   // We *could* set npickcolattr=1 if we had access to PickColorConverter here and getTotalBits() == 32
10955   const int npickcolattr = SHADER_PICKING_PASSES_MAX;
10956   /* for picking, we need to mask the attributes and bind the buffer in the the picking VBO */
10957   int pl = 0;
10958   int npickattr = pickData.size();
10959   for (auto &pickDesc : pickData){
10960     cgo->add<cgo::draw::mask_attribute_if_picking>(I->G->ShaderMgr->GetAttributeUID(pickDesc.attr_name), vbo->get_hash_id());
10961     if (has_picking){
10962       cgo->add<cgo::draw::bind_vbo_for_picking>(pickvbohash, pl++, npickattr);
10963     } else {
10964       /* if no picking, should render black */
10965       static unsigned char zerocolor[] { 0,0,0,0 };
10966       cgo->add<cgo::draw::vertex_attribute_4ub_if_picking>(I->G->ShaderMgr->GetAttributeUID(pickDesc.attr_name), zerocolor);
10967     }
10968   }
10969   size_t iboid = 0;
10970   int num_total_indexes = 0;
10971   if (nvertsperfrag){
10972     GL_C_INT_TYPE *vertexIndices;
10973     int nfrags = nfragspergroup * ntotalverts/vertsperpickinfo;
10974     int nvertsperindivfrag = vertsperpickinfo/nfragspergroup;
10975     num_total_indexes = nfrags * nvertsperfrag;
10976     vertexIndices = pymol::calloc<GL_C_INT_TYPE>(num_total_indexes);
10977     int idxpl=0;
10978     // using vertsperpickinfo as verts per frag
10979     for (int cnt = 0, vpl = 0; cnt < nfrags; ++cnt){
10980       for (int idx_array_pl = 0; idx_array_pl < nvertsperfrag; ++idx_array_pl){
10981         vertexIndices[idxpl] = idx_array[idx_array_pl] + vpl;
10982         idxpl++;
10983       }
10984       vpl+=nvertsperindivfrag;
10985     }
10986     IndexBuffer * ibo = I->G->ShaderMgr->newGPUBuffer<IndexBuffer>();
10987     ok &= ibo->bufferData({
10988         BufferDesc( GL_C_INT_ENUM, sizeof(GL_C_INT_TYPE) * num_total_indexes, vertexIndices )
10989           });
10990     FreeP(vertexIndices);
10991     iboid = ibo->get_hash_id();
10992   }
10993 
10994   // pick_data is interleaved if more than one attribute
10995   float * pick_data = cgo->add<cgo::draw::custom>(mode, ntotalverts, vbo->get_hash_id(), pickvbohash, vertsperpickinfo, pickDataSize, iboid, num_total_indexes);
10996   void *allData = malloc(ntotalverts * vertexDataSize);
10997   vector<void*> dataPtrs;
10998   vector<int> repeat_attr_idx;
10999   int allAttrBS = 0;
11000 
11001   // Initialize first entry in array(s) with default values and populate dataPtrs
11002   if (isInterleaved){
11003     int pl = 0;
11004     auto attrDataIt = attrData.begin();
11005     auto attrOffsetIt = attrOffset.begin();
11006     for (; attrDataIt!=attrData.end() && attrOffsetIt!=attrOffset.end(); ++attrDataIt, ++attrOffsetIt){
11007       auto attrDesc = &(*attrDataIt);
11008       if (attrDesc->repeat_value){
11009         repeat_attr_idx.push_back(pl);
11010       } else {
11011         allAttrBS |= (1 << attrDesc->order);
11012       }
11013       auto attrOffset = *attrOffsetIt;
11014       unsigned char *first_value = NULL;
11015       first_value = (attrDesc->default_value ? attrDesc->default_value :
11016                      (attrDesc->repeat_value ? attrDesc->repeat_value : NULL));
11017       if (first_value){
11018         int attrSize = gl_sizeof(attrDesc->type_size) * attrDesc->type_dim;
11019         memcpy(((unsigned char*)allData)+attrOffset, first_value, attrSize);
11020       }
11021       dataPtrs.push_back((void*)allData);
11022       ++pl;
11023     }
11024   } else {
11025     void *curAllDataPtr = (void*)allData;
11026     int pl = 0;
11027     for (auto &attrDesc : attrData){
11028       if (attrDesc.repeat_value){
11029         repeat_attr_idx.push_back(pl);
11030       } else {
11031         allAttrBS |= (1 << attrDesc.order);
11032       }
11033       dataPtrs.push_back(curAllDataPtr);
11034       unsigned char *first_value = NULL;
11035       first_value = (attrDesc.default_value ? attrDesc.default_value :
11036                      (attrDesc.repeat_value ? attrDesc.repeat_value : NULL));
11037       if (first_value){
11038         memcpy((unsigned char*)curAllDataPtr, first_value, attrSizes[pl]);
11039       }
11040       curAllDataPtr = ((unsigned char*)curAllDataPtr) + ntotalverts * attrSizes[pl];
11041       ++pl;
11042     }
11043   }
11044 
11045   int nvert = 0;
11046   int attrBS = 0;
11047   int allPickAttrBS = (1 << pickData.size()) - 1;
11048   int has_pick_colorBS = allPickAttrBS;
11049   for (int pi = 0; pi<pickDataSize; ++pi){
11050     CGO_put_uint(2*pi + pick_data, 0);
11051     CGO_put_int(2*pi + pick_data+1, cPickableNoPick);
11052   }
11053   bool cont = true; // need to break while statement as well if past all vertices
11054 
11055   // This is the loop that goes through the CGO and accumulates all of the data
11056   // for both the rendering and picking VBOs.
11057   // - For each OP, go through the list of Attribute OPs
11058   // - Each Attribute OP:
11059   //    - copies attribute data from the OP
11060   //    - can generate vertices (incr_vertices)
11061   // - Attributes are kept track of (attrBS) and when vertices are generated,
11062   //   the attributes that have not been newly written for the current vertex
11063   //   are copied from the previous one.
11064   for (auto it = I->begin(); cont && !it.is_stop(); ++it) {
11065     auto pc = it.data();
11066     int op = it.op_code();
11067     if (opToOrderedAttribOps.find(op) != opToOrderedAttribOps.end()){
11068       std::map<int, AttribOp*> *attribOpsInOrder = &opToOrderedAttribOps[op];
11069       for (auto attribOpIt : *attribOpsInOrder){
11070         AttribOp *attribOp = attribOpIt.second;
11071         int ord = attribOp->desc->order;
11072 	cont = nvert < ntotalverts;
11073 	if (!cont)
11074 	  break;
11075         copyAttributeForOp(isInterleaved, nvert, attribOp, vertexDataSize, dataPtrs, attrOffset, pc, pick_data, has_pick_colorBS, pstride);
11076         if (ord >= 0)  // picking is negative, has_pick_colorBS is used instead
11077           attrBS |= (1 << ord);
11078         else
11079           cout << "   ord=%d\n" << ord << endl;
11080         if (attribOp->incr_vertices){
11081           if (has_pick_colorBS!=allPickAttrBS){
11082             // copy pick colors that haven't been set from previous vertex
11083             for (int pi = 0; pi<pickDataSize; ++pi){
11084               if (has_pick_colorBS ^ (1 << pi)){
11085                 CGO_put_uint(pick_data, CGO_get_uint(pick_data-pstride));
11086                 CGO_put_int(pick_data + 1, CGO_get_int(pick_data-pstride+1));
11087               }
11088               pick_data += 2;
11089             }
11090           } else {
11091             pick_data += pstride;
11092           }
11093           has_pick_colorBS = 0;
11094           if (!nvert && attrBS!=allAttrBS){
11095             // for the first vertex, all attributes should be set
11096             for (auto idx = 0; idx < attrData.size(); ++idx){
11097               if (!(attrBS & (1 << idx))) {
11098                 if (!attrData[idx].default_value){
11099                   std::cerr << "WARNING: attribute #" << idx <<
11100                     " (" << attrData[idx].attr_name << ") not set for first"
11101                     " vertex and does not have default value" << std::endl;
11102                 }
11103               }
11104             }
11105           }
11106           if (nvert && attrBS!=allAttrBS){
11107             // for each vertex that hasn't been written for the current vertex, copy it from the previous vertex
11108             for (auto idx = 0; idx < attrData.size(); ++idx){
11109               if (!(attrBS & (1 << idx))) {
11110                 copyAttributeForVertex(isInterleaved, nvert, attrData[idx], vertexDataSize, dataPtrs, attrOffset);
11111               }
11112             }
11113           }
11114           attrBS = 0;
11115 
11116           // creating new vertices, all attribute data should be copied into new vertex.
11117           for (int nxt = 0; nxt < attribOp->incr_vertices; ++nxt){
11118             /* for now, always copy values from previous */
11119             ++nvert;
11120             if (nvert < ntotalverts){
11121               {
11122                 // last should not need to copy into next, since we call copyAttributeForVertex (above)
11123                 // for all attributes that haven't been set
11124                 // - note: it might be faster (especially for interleaved) to copy into the next
11125                 //         vertex, then the above copyAttributeForVertex() would not be needed
11126                 if (isInterleaved){
11127                   void *dest = ((unsigned char*)allData)+vertexDataSize*nvert;
11128                   memcpy(dest, ((unsigned char*)dest) - vertexDataSize, vertexDataSize);
11129                 } else {
11130                   auto dataPtrIt = dataPtrs.begin();
11131                   auto attrDataIt = attrData.begin();
11132                   for (; attrDataIt!=attrData.end() && dataPtrIt!=dataPtrs.end(); ++attrDataIt, ++dataPtrIt){
11133                     auto attrDesc = &(*attrDataIt);
11134                     auto dataPtr = *dataPtrIt;
11135                     int attrSize = gl_sizeof(attrDesc->type_size) * attrDesc->type_dim;
11136                     void *dest = ((unsigned char*)dataPtr)+attrSize*nvert;
11137                     memcpy((unsigned char*)dest, ((unsigned char*)dest) - attrSize, attrSize);
11138                   }
11139                 }
11140               }
11141               // always copy repeat attributes
11142               if (!repeat_attr_idx.empty()){
11143                 for (auto ridx = repeat_attr_idx.begin(); ridx != repeat_attr_idx.end(); ++ridx){
11144                   copyAttributeForVertex(isInterleaved, nvert, attrData[*ridx], vertexDataSize, dataPtrs, attrOffset);
11145                 }
11146               }
11147             }
11148             if (!attribOp->funcDataConversions.empty()){
11149               // for all attributes, call the funcDataConversion() if defined
11150               int nvert_m_1 = nvert - 1;
11151               for (auto funcData : attribOp->funcDataConversions){
11152                 auto funcAttrib = funcData.attrib;
11153                 auto order = funcAttrib->order;
11154                 if (isInterleaved){
11155                   unsigned char *dest = ((unsigned char*)allData)+vertexDataSize*nvert_m_1 + attrOffset[order];
11156                   funcData.funcDataConversion(dest, pc, funcData.funcDataGlobalArg, nxt);
11157                 } else {
11158                   auto dataPtr = dataPtrs[order];
11159                   void *dest = ((unsigned char*)dataPtr)+attrSizes[order]*nvert_m_1;
11160                   funcData.funcDataConversion(dest, pc, funcData.funcDataGlobalArg, nxt);
11161                 }
11162               }
11163             }
11164 
11165           }
11166         }
11167       }
11168     }
11169   }
11170 
11171   /* Generate Pick Buffers with all pick attributes (if necessary) */
11172   if (pickvbo){
11173     BufferDataDesc pickBufferData;
11174     for (int i=0; i < npickcolattr; i++){
11175       for (auto &pickDesc : pickData){
11176 	int pickSize = gl_sizeof(pickDesc.type_size) * pickDesc.type_dim;
11177         pickBufferData.push_back(BufferDesc(pickDesc.attr_name, pickDesc.type_size,
11178                                             pickDesc.type_dim, pickSize * nvert, NULL, pickDesc.data_norm));
11179       }
11180     }
11181     pickvbo->bufferData(std::move(pickBufferData));
11182   }
11183 
11184   /* Generate VBO Buffers with all pick attributes based on the VertexBuffer type SEPARATE/SEQUENTIAL/INTERLEAVED*/
11185   BufferDataDesc bufferData;
11186   switch (layout){
11187   case VertexBuffer::SEPARATE:
11188   case VertexBuffer::SEQUENTIAL:
11189     {
11190       auto attrDataIt = attrData.begin();
11191       auto dataPtrIt = dataPtrs.begin();
11192       auto attrSizeIt = attrSizes.begin();
11193         for (; attrDataIt!=attrData.end() &&
11194                dataPtrIt!=dataPtrs.end() &&
11195                attrSizeIt!=attrSizes.end(); ++attrDataIt, ++dataPtrIt, ++attrSizeIt){
11196         auto attrDesc = &(*attrDataIt);
11197         auto dataPtr = *dataPtrIt;
11198         auto attrSize = *attrSizeIt;
11199         bufferData.push_back(BufferDesc(attrDesc->attr_name, attrDesc->type_size,
11200                                         attrDesc->type_dim, nvert * attrSize, dataPtr, attrDesc->data_norm));
11201       }
11202         vbo->bufferData(std::move(bufferData));
11203     break;
11204     }
11205     break;
11206   case VertexBuffer::INTERLEAVED:
11207     {
11208       auto attrDataIt = attrData.begin();
11209       auto attrOffsetIt = attrOffset.begin();
11210       for (; attrDataIt!=attrData.end() && attrOffsetIt!=attrOffset.end(); ++attrDataIt, ++attrOffsetIt){
11211         auto attrDesc = &(*attrDataIt);
11212         auto offset = *attrOffsetIt;
11213         bufferData.push_back(BufferDesc(attrDesc->attr_name, attrDesc->type_size,
11214                                         attrDesc->type_dim, offset, attrDesc->data_norm));
11215       }
11216       vbo->bufferData(std::move(bufferData), (const void *)allData,
11217                       (size_t)(nvert*vertexDataSize), (size_t)vertexDataSize);
11218     break;
11219     }
11220   }
11221   free(allData);
11222 
11223   CGOStop(cgo);
11224   return cgo;
11225 }
11226 
11227 // CGOCheckSplitLineInterpolationIsSame:
11228 //   - returns true if always the same
11229 //   - returns false if not always the same
CGOCheckSplitLineInterpolationIsSame(const CGO * I,bool & interp_value)11230 bool CGOCheckSplitLineInterpolationIsSame(const CGO *I, bool &interp_value){
11231   bool interp_value_first = false;
11232   bool interp_value_is_set = false;
11233 
11234   for (auto it = I->begin(); !it.is_stop(); ++it) {
11235     switch (it.op_code()) {
11236     case cgo::draw::splitline::op_code:
11237       interp_value = (it.cast<cgo::draw::splitline>()->flags & cgo::draw::splitline::interpolation);
11238       break;
11239     case CGO_INTERPOLATED:
11240       interp_value = it.cast<float>()[0] > 0.5f;
11241       break;
11242     default:
11243       continue;
11244     }
11245     if (!interp_value_is_set){
11246       interp_value_first = interp_value;
11247       interp_value_is_set = true;
11248     } else if (interp_value != interp_value_first){
11249       return false;
11250     }
11251   }
11252   return true;
11253 }
11254 
11255 // CGOCheckShaderCylinderCapInfoIsSame:
11256 //   - returns true if always the same
11257 //   - returns false if not always the same
11258 static
CGOCheckShaderCylinderCapInfoIsSame(const CGO * I,unsigned char & cap_value)11259 bool CGOCheckShaderCylinderCapInfoIsSame(const CGO *I, unsigned char &cap_value){
11260   unsigned char cap_value_first = 0;
11261   bool cap_value_first_is_set = false;
11262 
11263   for (auto it = I->begin(); !it.is_stop(); ++it) {
11264     switch (it.op_code()) {
11265     case cgo::draw::shadercylinder::op_code:
11266       cap_value = it.cast<cgo::draw::shadercylinder>()->cap;
11267       break;
11268     case cgo::draw::shadercylinder2ndcolor::op_code:
11269       cap_value = it.cast<cgo::draw::shadercylinder2ndcolor>()->cap;
11270       break;
11271     case cgo::draw::sausage::op_code:
11272       cap_value = cCylShaderBothCapsRound | cCylShaderInterpColor;
11273       break;
11274     case cgo::draw::cylinder::op_code:
11275       cap_value = cCylShaderBothCapsFlat | cCylShaderInterpColor;
11276       break;
11277     case cgo::draw::custom_cylinder::op_code:
11278       {
11279         auto cc = it.cast<cgo::draw::custom_cylinder>();
11280         int cap1 = (int) cc->cap1;
11281         int cap2 = (int) cc->cap2;
11282         cap_value = ((cap1 == 1) ? cCylShaderCap1Flat : (cap1 == 2) ? cCylShaderCap1Round : cCylCapNone) |
11283                     ((cap2 == 1) ? cCylShaderCap2Flat : (cap2 == 2) ? cCylShaderCap2Round : cCylCapNone) |
11284                     cCylShaderInterpColor;
11285       }
11286       break;
11287     case cgo::draw::custom_cylinder_alpha::op_code:
11288       {
11289         auto cc = it.cast<cgo::draw::custom_cylinder_alpha>();
11290         int cap1 = (int) cc->cap1;
11291         int cap2 = (int) cc->cap2;
11292         cap_value = ((cap1 == 1) ? cCylShaderCap1Flat : (cap1 == 2) ? cCylShaderCap1Round : cCylCapNone) |
11293                     ((cap2 == 1) ? cCylShaderCap2Flat : (cap2 == 2) ? cCylShaderCap2Round : cCylCapNone) |
11294                     cCylShaderInterpColor;
11295       }
11296       break;
11297     default:
11298       continue;
11299     }
11300 
11301     if (!cap_value_first_is_set) {
11302       cap_value_first = cap_value;
11303       cap_value_first_is_set = true;
11304     } else if (cap_value != cap_value_first) {
11305       return false;
11306     }
11307   }
11308 
11309   return true;
11310 }
11311 
CGOConvertToTrilinesShader(const CGO * I,CGO * addTo,bool add_color)11312 CGO *CGOConvertToTrilinesShader(const CGO *I, CGO *addTo, bool add_color){
11313   PyMOLGlobals *G = I->G;
11314 
11315   AttribDataOp vertexOps =
11316     { { CGO_LINE,       1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::line, vertex1), 0 },
11317       { CGO_SPLITLINE,  2, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::splitline, vertex1), 0 } };
11318   AttribDataOp vertexOtherOps =
11319     { { CGO_LINE,       2, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::line, vertex2), 6 },
11320       { CGO_SPLITLINE,  5, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::splitline, vertex2), 6 } };
11321   AttribDataOp colorOps =
11322     { { CGO_COLOR,      0, FLOAT3_TO_UB3,         0 },
11323       { CGO_ALPHA,      0, FLOAT1_TO_UB_4TH,      0 },
11324       { CGO_SPLITLINE,  6, UB3_TO_UB3,            offsetof(cgo::draw::splitline, color2) } };
11325   AttribDataOp color2Ops =
11326     { { CGO_COLOR,      1, FLOAT3_TO_UB3,         0 },
11327       { CGO_ALPHA,      1, FLOAT1_TO_UB_4TH,      0 },
11328       { CGO_SPLITLINE,  3, UB3_TO_UB3,            offsetof(cgo::draw::splitline, color2) } };
11329   AttribDataOp extraPickColorOps =
11330     { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 },
11331       { CGO_SPLITLINE,  7, UINT_INT_TO_PICK_DATA, offsetof(cgo::draw::splitline, index), 0 } };
11332   AttribDataOp extraPickColor2Ops =
11333     { { CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0 },
11334       { CGO_SPLITLINE,  4, UINT_INT_TO_PICK_DATA, offsetof(cgo::draw::splitline, index), 0 } };
11335   AttribDataDesc pickDesc =
11336     { { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColorOps },
11337       { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColor2Ops }};
11338   AttribDataDesc attrDesc =
11339     { { "a_Vertex", GL_FLOAT, 3, GL_FALSE, vertexOps },
11340       { "a_OtherVertex", GL_FLOAT, 3, GL_FALSE, vertexOtherOps },
11341       { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, colorOps },
11342       { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, color2Ops },
11343       { "a_UV", GL_UNSIGNED_BYTE, 1, GL_FALSE } };
11344 
11345   if (add_color){
11346     static unsigned char default_color[] = { 255, 255, 255, 255 }; // to write in alpha if CGO doesn't have it
11347     attrDesc[2].default_value = default_color;
11348     attrDesc[3].default_value = default_color;
11349   }
11350   AttribDesc *uvdesc = &attrDesc[attrDesc.size()-1];
11351   uvdesc->repeat_value_length = 6;
11352   static unsigned char uv_bits[] = { 1, 3, 0, 3, 2, 1 };
11353   uvdesc->repeat_value = uv_bits;
11354 
11355   bool interp_same, interp_value;
11356   if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))){
11357     addTo->add<cgo::draw::vertex_attribute_1f>(G->ShaderMgr->GetAttributeUID("a_interpolate"), interp_value ? 1.f : 0.f);
11358   } else {
11359     AttribDataOp interpOps =
11360       { { CGO_SPLITLINE, 1, UB1_TO_INTERP, offsetof(cgo::draw::splitline, flags), 0 } };
11361     // need to add a_interpolate attribute
11362     attrDesc.push_back({ "a_interpolate", GL_UNSIGNED_BYTE, 1, GL_FALSE, interpOps } );
11363   }
11364   if (!add_color){
11365     attrDesc.erase(attrDesc.begin()+2); // a_Color
11366     attrDesc.erase(attrDesc.begin()+2); // a_Color2
11367   }
11368 
11369   return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, VertexBuffer::INTERLEAVED);
11370 }
11371 
CGOConvertToLinesShader(const CGO * I,CGO * addTo,bool add_color)11372 CGO *CGOConvertToLinesShader(const CGO *I, CGO *addTo, bool add_color){
11373   /* Lines that pass in two vertices per line */
11374   PyMOLGlobals *G = I->G;
11375   AttribDataOp vertexOps =
11376     { { CGO_LINE,       1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::line, vertex1), 1 },
11377       { CGO_SPLITLINE,  2, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::splitline, vertex1), 1 },
11378       { CGO_LINE,       2, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::line, vertex2), 1 },
11379       { CGO_SPLITLINE,  5, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::splitline, vertex2), 1 } };
11380   AttribDataOp colorOps =
11381     { { CGO_COLOR,      0, FLOAT3_TO_UB3,         0 },
11382       { CGO_ALPHA,      0, FLOAT1_TO_UB_4TH,      0 },
11383       { CGO_SPLITLINE,  3, UB3_TO_UB3,            offsetof(cgo::draw::splitline, color2) } };
11384   AttribDataOp extraPickColorOps =
11385     { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 },
11386       { CGO_SPLITLINE,  4, UINT_INT_TO_PICK_DATA, offsetof(cgo::draw::splitline, index), 0 } };
11387 
11388   AttribDataDesc pickDesc =
11389     { { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColorOps } };
11390   AttribDataDesc attrDesc =
11391     { { "a_Vertex", GL_FLOAT, 3, GL_FALSE, vertexOps },
11392       { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, colorOps } };
11393   if (add_color){
11394     static unsigned char default_color[] = { 255, 255, 255, 255 }; // to write in alpha if CGO doesn't have it
11395     attrDesc[1].default_value = default_color;
11396   }
11397   bool interp_same, interp_value;
11398   if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))){
11399     addTo->add<cgo::draw::vertex_attribute_1f>(G->ShaderMgr->GetAttributeUID("a_interpolate"), interp_value ? 1.f : 0.f);
11400   } else {
11401     AttribDataOp interpOps =
11402       { { CGO_SPLITLINE, 1, UB1_TO_INTERP, offsetof(cgo::draw::splitline, flags), 0 } };
11403     // need to add a_interpolate attribute
11404     attrDesc.push_back({ "a_interpolate", GL_UNSIGNED_BYTE, 1, GL_FALSE, interpOps } );
11405   }
11406 #ifndef PURE_OPENGL_ES_2
11407   {
11408     attrDesc.push_back({ "a_line_position", GL_UNSIGNED_BYTE, 1, GL_FALSE } );
11409     AttribDesc *lpdesc = &attrDesc[attrDesc.size()-1];
11410     lpdesc->repeat_value_length = 2;
11411     static unsigned char flip_bits[] = { 0, 1 };
11412     lpdesc->repeat_value = flip_bits;
11413   }
11414 #endif
11415   if (!add_color){
11416     attrDesc.erase(attrDesc.begin()+1); // a_Color
11417   }
11418 
11419   return CGOConvertToShader(I, attrDesc, pickDesc, GL_LINES, VertexBuffer::INTERLEAVED);
11420 }
11421 
CGOConvertLinesToCylinderShader(const CGO * I,CGO * addTo,bool add_color)11422 CGO *CGOConvertLinesToCylinderShader(const CGO *I, CGO *addTo, bool add_color){
11423   /* Lines that pass in two vertices per line */
11424   PyMOLGlobals *G = I->G;
11425 
11426   AttribDataOp vertex1Ops =
11427     { { CGO_LINE,       1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::line, vertex1), 0 },
11428       { CGO_SPLITLINE,  2, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::splitline, vertex1), 0 } };
11429   AttribDataOp vertex2Ops =
11430     { { CGO_LINE,       2, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::line, vertex2), 8 },
11431       { CGO_SPLITLINE,  5, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::splitline, vertex2), 8 } };
11432   static AttribDataOp colorOps =
11433     { { CGO_COLOR,      0, FLOAT3_TO_UB3,         0 },
11434       { CGO_ALPHA,      0, FLOAT1_TO_UB_4TH,      0 },
11435       { CGO_SPLITLINE,  6, UB3_TO_UB3,            offsetof(cgo::draw::splitline, color2) } };
11436   static AttribDataOp color2Ops =
11437     { { CGO_COLOR,      1, FLOAT3_TO_UB3,         0 },
11438       { CGO_ALPHA,      1, FLOAT1_TO_UB_4TH,      0 },
11439       { CGO_SPLITLINE,  3, UB3_TO_UB3,            offsetof(cgo::draw::splitline, color2) } };
11440 
11441   AttribDataDesc attrDesc = { { "attr_vertex1", GL_FLOAT, 3, GL_FALSE, vertex1Ops },
11442                               { "attr_vertex2", GL_FLOAT, 3, GL_FALSE, vertex2Ops },
11443                               { "a_Color",  GL_UNSIGNED_BYTE, 4, GL_TRUE, colorOps },
11444                               { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, color2Ops },
11445                               { "attr_radius", GL_FLOAT, 1, GL_FALSE } };
11446   AttribDesc *fdesc;
11447   static unsigned char cyl_flags[] = { 0, 4, 6, 2, 1, 5, 7, 3 }; // right(4)/up(2)/out(1)
11448 
11449   attrDesc.push_back( { "attr_flags", GL_UNSIGNED_BYTE, 1, GL_FALSE } ) ;
11450   fdesc = &attrDesc[attrDesc.size()-1];
11451   fdesc->repeat_value = cyl_flags;
11452   fdesc->repeat_value_length = 8;
11453 
11454   if (add_color){
11455     static unsigned char default_color[] = { 255, 255, 255, 255 }; // to write in alpha if CGO doesn't have it
11456     fdesc = &attrDesc[2];
11457     fdesc->default_value = default_color;
11458     fdesc = &attrDesc[3];
11459     fdesc->default_value = default_color;
11460   }
11461 
11462   float default_radius = 1.f;
11463   attrDesc[4].default_value = (unsigned char*)&default_radius;
11464 
11465   int box_indices[36] = { // box indices
11466     0, 2, 1, 2, 0, 3, 1, 6, 5, 6, 1, 2, 0, 1, 5, 5, 4, 0,
11467     0, 7, 3, 7, 0, 4, 3, 6, 2, 6, 3, 7, 4, 5, 6, 6, 7, 4 };
11468   int *box_indices_ptr = NULL;
11469   box_indices_ptr = box_indices;
11470 
11471   bool interp_same, interp_value = false;
11472   if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))){
11473     addTo->add<cgo::draw::vertex_attribute_1f>(G->ShaderMgr->GetAttributeUID("a_cap"), (cCylShaderBothCapsRound | (interp_value ? cCylShaderInterpColor : 0)));
11474   } else {
11475     AttribDataOp interpOps =
11476       { { CGO_SPLITLINE, 1, UB1_INTERP_TO_CAP, offsetof(cgo::draw::splitline, flags), 0 } };
11477     // need to add a_cap attribute
11478     attrDesc.push_back({ "a_cap", GL_UNSIGNED_BYTE, 1, GL_FALSE, interpOps } );
11479   }
11480 
11481   if (!add_color){
11482     attrDesc.erase(attrDesc.begin()+2); // attr_colors
11483     attrDesc.erase(attrDesc.begin()+2); // attr_colors2
11484   }
11485 
11486   AttribDataOp extraPickColorOps = { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 },
11487                                      { CGO_SPLITLINE,  7, UINT_INT_TO_PICK_DATA, offsetof(cgo::draw::splitline, index), 0 } };
11488   AttribDataOp extraPickColor2Ops = { { CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0 },
11489                                       { CGO_SPLITLINE,  4, UINT_INT_TO_PICK_DATA, offsetof(cgo::draw::splitline, index), 0 } };
11490   AttribDataDesc pickDesc = { { "a_Color",  GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColorOps },
11491                               { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColor2Ops }};
11492   return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, VertexBuffer::INTERLEAVED, true, box_indices_ptr, 36);
11493 }
11494 
11495 struct CrossSizeData {
11496   float cross_size;
11497   bool forward;
CrossSizeDataCrossSizeData11498   CrossSizeData(float _cross_size, bool _forward) : cross_size(_cross_size), forward(_forward){ }
11499 };
11500 
CrossVertexConversion(void * varData,const float * pc,void * crossData,int idx)11501 static void CrossVertexConversion(void *varData, const float * pc, void *crossData, int idx){
11502   CrossSizeData *csd = (CrossSizeData*)crossData;
11503   int idxpl = idx / 8; // X Y or Z
11504   float *varDataF = ((float*)varData);
11505   varDataF[idxpl] += (csd->forward ? csd->cross_size : -csd->cross_size);
11506 }
11507 
CGOConvertCrossesToCylinderShader(const CGO * I,CGO * addTo,float cross_size_arg)11508 CGO *CGOConvertCrossesToCylinderShader(const CGO *I, CGO *addTo, float cross_size_arg){
11509   /* Lines that pass in two vertices per line */
11510   PyMOLGlobals *G = I->G;
11511   AttribDataOp vertex1Ops =
11512     { { CGO_VERTEX_CROSS,       1, FLOAT3_TO_FLOAT3,      0, 0 } };
11513   AttribDataOp vertex2Ops =
11514     { { CGO_VERTEX_CROSS,       2, FLOAT3_TO_FLOAT3,      0, 3 * 8, 0 } };
11515 
11516   static AttribDataOp colorOps =
11517     { { CGO_COLOR,      0, FLOAT3_TO_UB3,         0 },
11518       { CGO_ALPHA,      0, FLOAT1_TO_UB_4TH,      0 } };
11519   static AttribDataOp color2Ops =
11520     { { CGO_COLOR,      1, FLOAT3_TO_UB3,         0 },
11521       { CGO_ALPHA,      1, FLOAT1_TO_UB_4TH,      0 } };
11522 
11523   CrossSizeData crossData[] = { { cross_size_arg, false }, { cross_size_arg, true } };
11524   AttribDataDesc attrDesc = { { "attr_vertex1", GL_FLOAT, 3, GL_FALSE, vertex1Ops },
11525                               { "attr_vertex2", GL_FLOAT, 3, GL_FALSE, vertex2Ops },
11526                               { "a_Color",  GL_UNSIGNED_BYTE, 4, GL_TRUE, colorOps },
11527                               { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, color2Ops },
11528                               { "attr_radius", GL_FLOAT, 1, GL_FALSE } };
11529   attrDesc.reserve(10);
11530   attrDesc[1].attrOps[0].funcDataConversions.push_back( { CrossVertexConversion, &crossData[0], "attr_vertex1" } );
11531   attrDesc[1].attrOps[0].funcDataConversions.push_back( { CrossVertexConversion, &crossData[1], "attr_vertex2" } );
11532 
11533   AttribDesc *fdesc;
11534   static unsigned char cyl_flags[] = { 0, 4, 6, 2, 1, 5, 7, 3 }; // right(4)/up(2)/out(1)
11535 
11536   attrDesc.push_back( { "attr_flags", GL_UNSIGNED_BYTE, 1, GL_FALSE } ) ;
11537   fdesc = &attrDesc[attrDesc.size()-1];
11538   fdesc->repeat_value = cyl_flags;
11539   fdesc->repeat_value_length = 8;
11540 
11541   unsigned char default_color[] = { 255, 255, 255, 255 }; // to write in alpha if CGO doesn't have it
11542   fdesc = &attrDesc[2];
11543   fdesc->default_value = default_color;
11544   fdesc = &attrDesc[3];
11545   fdesc->default_value = default_color;
11546   float default_radius = 1.f;
11547   attrDesc[4].default_value = (unsigned char*)&default_radius;
11548 
11549   int box_indices[36] = { // box indices
11550     0, 2, 1, 2, 0, 3, 1, 6, 5, 6, 1, 2, 0, 1, 5, 5, 4, 0,
11551     0, 7, 3, 7, 0, 4, 3, 6, 2, 6, 3, 7, 4, 5, 6, 6, 7, 4 };
11552   int *box_indices_ptr = NULL;
11553   box_indices_ptr = box_indices;
11554 
11555   addTo->add<cgo::draw::vertex_attribute_1f>(G->ShaderMgr->GetAttributeUID("a_cap"), cCylShaderBothCapsRound);
11556 
11557   AttribDataOp extraPickColorOps = { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 } };
11558   AttribDataOp extraPickColor2Ops = { { CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0 } };
11559   AttribDataDesc pickDesc = { { "a_Color",  GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColorOps },
11560                               { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColor2Ops }};
11561   return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, VertexBuffer::INTERLEAVED, true, box_indices_ptr, 36, 3);
11562 }
11563 
11564 struct CrossSizeDataLines {
11565   float cross_size;
CrossSizeDataLinesCrossSizeDataLines11566   CrossSizeDataLines(float _cross_size) : cross_size(_cross_size){ }
11567 };
11568 
CrossVertexConversionLines(void * varData,const float * pc,void * crossData,int idx)11569 static void CrossVertexConversionLines(void *varData, const float * pc, void *crossData, int idx){
11570   CrossSizeDataLines *csd = (CrossSizeDataLines*)crossData;
11571   int idxpl = idx / 2; // X Y or Z
11572   bool forward = idx % 2;
11573   float *varDataF = ((float*)varData);
11574   varDataF[idxpl] += (forward ? csd->cross_size : -csd->cross_size);
11575 }
11576 
CGOConvertCrossesToLinesShader(const CGO * I,CGO * addTo,float cross_size_arg)11577 CGO *CGOConvertCrossesToLinesShader(const CGO *I, CGO *addTo, float cross_size_arg){
11578   /* Lines that pass in two vertices per line */
11579   PyMOLGlobals *G = I->G;
11580   AttribDataOp vertexOps =
11581     { { CGO_VERTEX_CROSS,       1, FLOAT3_TO_FLOAT3,      0, 6 } };  // 6 vertices for a cross
11582   AttribDataOp colorOps =
11583     { { CGO_COLOR,      0, FLOAT3_TO_UB3,         0 },
11584       { CGO_ALPHA,      0, FLOAT1_TO_UB_4TH,      0 } };
11585   AttribDataOp extraPickColorOps =
11586     { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 } };
11587 
11588   AttribDataDesc pickDesc =
11589     { { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColorOps } };
11590   AttribDataDesc attrDesc =
11591     { { "a_Vertex", GL_FLOAT, 3, GL_FALSE, vertexOps },
11592       { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, colorOps } };
11593   unsigned char default_color[] = { 255, 255, 255, 255 }; // to write in alpha if CGO doesn't have it
11594   attrDesc[1].default_value = default_color;
11595 
11596   CrossSizeDataLines crossData = { cross_size_arg };
11597   attrDesc[0].attrOps[0].funcDataConversions.push_back( { CrossVertexConversionLines, &crossData, "a_Vertex" } );
11598 
11599   bool interp_same, interp_value = false;
11600   if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))){
11601     addTo->add<cgo::draw::vertex_attribute_1f>(G->ShaderMgr->GetAttributeUID("a_interpolate"), interp_value ? 1.f : 0.f);
11602   } else {
11603     AttribDataOp interpOps =
11604       { { CGO_SPLITLINE, 1, UB1_TO_INTERP, offsetof(cgo::draw::splitline, flags), 0 } };
11605     // need to add a_interpolate attribute
11606     attrDesc.push_back({ "a_interpolate", GL_UNSIGNED_BYTE, 1, GL_FALSE, interpOps } );
11607   }
11608 #ifndef PURE_OPENGL_ES_2
11609   {
11610     attrDesc.push_back({ "a_line_position", GL_UNSIGNED_BYTE, 1, GL_FALSE } );
11611     AttribDesc *lpdesc = &attrDesc[attrDesc.size()-1];
11612     lpdesc->repeat_value_length = 2;
11613     static unsigned char flip_bits[] = { 0, 1 };
11614     lpdesc->repeat_value = flip_bits;
11615   }
11616 #endif
11617   return CGOConvertToShader(I, attrDesc, pickDesc, GL_LINES, VertexBuffer::INTERLEAVED);
11618 }
11619 
CrossVertexConversionTrilines(void * varData,const float * pc,void * crossData,int idx)11620 static void CrossVertexConversionTrilines(void *varData, const float * pc, void *crossData, int idx){
11621   CrossSizeData *csd = (CrossSizeData*)crossData;
11622   int idxpl = idx / 6; // X Y or Z
11623   float *varDataF = ((float*)varData);
11624   varDataF[idxpl] += (csd->forward ? csd->cross_size : -csd->cross_size);
11625 }
11626 
CGOConvertCrossesToTrilinesShader(const CGO * I,CGO * addTo,float cross_size_arg)11627 CGO *CGOConvertCrossesToTrilinesShader(const CGO *I, CGO *addTo, float cross_size_arg){
11628   PyMOLGlobals *G = I->G;
11629 
11630   AttribDataOp vertexOps =
11631     { { CGO_VERTEX_CROSS,       1, FLOAT3_TO_FLOAT3,      0, 0 } };
11632   AttribDataOp vertexOtherOps =
11633     { { CGO_VERTEX_CROSS,       2, FLOAT3_TO_FLOAT3,      0, 6 * 3 } };
11634   AttribDataOp colorOps =
11635     { { CGO_COLOR,      0, FLOAT3_TO_UB3,         0 },
11636       { CGO_ALPHA,      0, FLOAT1_TO_UB_4TH,      0 } };
11637   AttribDataOp color2Ops =
11638     { { CGO_COLOR,      1, FLOAT3_TO_UB3,         0 },
11639       { CGO_ALPHA,      1, FLOAT1_TO_UB_4TH,      0 } };
11640   AttribDataOp extraPickColorOps =
11641     { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 } };
11642   AttribDataOp extraPickColor2Ops =
11643     { { CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0 } };
11644   AttribDataDesc pickDesc =
11645     { { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColorOps },
11646       { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColor2Ops } };
11647   AttribDataDesc attrDesc =
11648     { { "a_Vertex", GL_FLOAT, 3, GL_FALSE, vertexOps },
11649       { "a_OtherVertex", GL_FLOAT, 3, GL_FALSE, vertexOtherOps },
11650       { "a_Color", GL_UNSIGNED_BYTE, 4, GL_TRUE, colorOps },
11651       { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, color2Ops },
11652       { "a_UV", GL_UNSIGNED_BYTE, 1, GL_FALSE } };
11653 
11654   CrossSizeData crossData[] = { { cross_size_arg, false }, { cross_size_arg, true } };
11655 
11656   attrDesc[1].attrOps[0].funcDataConversions.push_back( { CrossVertexConversionTrilines, &crossData[0], "a_Vertex" } );
11657   attrDesc[1].attrOps[0].funcDataConversions.push_back( { CrossVertexConversionTrilines, &crossData[1], "a_OtherVertex" } );
11658 
11659   unsigned char default_color[] = { 255, 255, 255, 255 }; // to write in alpha if CGO doesn't have it
11660   attrDesc[2].default_value = default_color;
11661   attrDesc[3].default_value = default_color;
11662 
11663   AttribDesc *uvdesc = &attrDesc[attrDesc.size()-1];
11664   uvdesc->repeat_value_length = 6;
11665   static unsigned char uv_bits[] = { 1, 3, 0, 3, 2, 1 };
11666   uvdesc->repeat_value = uv_bits;
11667 
11668   addTo->add<cgo::draw::vertex_attribute_1f>(G->ShaderMgr->GetAttributeUID("a_interpolate"), 0.f);
11669 
11670   return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, VertexBuffer::INTERLEAVED);
11671 }
11672 
shadercylinder2ndcolor(CGO * I,const float * _origin,const float * _axis,const float _tube_size,int _cap,const float * _color2,Pickable * pickcolor2,const float _alpha)11673 cgo::draw::shadercylinder2ndcolor::shadercylinder2ndcolor(CGO *I, const float *_origin,
11674                                                           const float *_axis, const float _tube_size,
11675                                                           int _cap, const float *_color2, Pickable *pickcolor2,
11676                                                           const float _alpha) :
11677   tube_size(_tube_size), alpha(_alpha) {
11678   copy3f(_origin, origin);
11679   copy3f(_axis, axis);
11680   cap = _cap;
11681   copy3f(_color2, color2);
11682   if (pickcolor2){
11683     I->current_pick_color_index = pick_color_index = pickcolor2->index;
11684     I->current_pick_color_bond = pick_color_bond = pickcolor2->bond;
11685   } else {
11686     pick_color_index = I->current_pick_color_index;
11687     pick_color_bond = I->current_pick_color_bond;
11688   }
11689 };
11690 
SetVertexFromOriginAxisForCylinder(void * varData,const float * pc,void * np,int idx)11691 static void SetVertexFromOriginAxisForCylinder(void *varData, const float * pc, void *np, int idx){
11692   float *varDataF = ((float*)varData);
11693   add3f(pc, pc + 3, varDataF); // adding origin and axis for both shadercylinder and shadercylinder2ndcolor
11694 }
11695 
11696 /*
11697  * converts all cylinders in the input CGO to a CGO custom operation, which includes picking information (if it exists)
11698  *
11699  * I     - input CGO (includes cylinders)
11700  * addTo - CGO that vertex_attribute operations are added to (if needed), in this case for caps if values for all
11701  *         cylinders are the same
11702  *
11703  */
CGOConvertShaderCylindersToCylinderShader(const CGO * I,CGO * addTo)11704 CGO *CGOConvertShaderCylindersToCylinderShader(const CGO *I, CGO *addTo){
11705   /* Lines that pass in two vertices per line */
11706   PyMOLGlobals *G = I->G;
11707 
11708   // TODO: NEED TO ADD: CGO_CUSTOM_CYLINDER and CGO_CYLINDER
11709   AttribDataOp vertex1Ops =
11710     { { CGO_SHADER_CYLINDER,                 1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::shadercylinder, origin), 0 },
11711       { CGO_SHADER_CYLINDER_WITH_2ND_COLOR,  1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::shadercylinder2ndcolor, origin), 0 },
11712       { CGO_SAUSAGE,                         1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::sausage, vertex1), 0 },
11713       { CGO_CYLINDER,                        1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::cylinder, vertex1), 0 },
11714       { CGO_CUSTOM_CYLINDER,                 1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::custom_cylinder, vertex1), 0 },
11715       { CGO_CUSTOM_CYLINDER_ALPHA,           1, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::custom_cylinder_alpha, vertex1), 0 } };
11716   AttribDataOp vertex2Ops =
11717     { { CGO_SHADER_CYLINDER,                 5, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::shadercylinder, axis), 8 },
11718       { CGO_SHADER_CYLINDER_WITH_2ND_COLOR,  6, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::shadercylinder2ndcolor, axis), 8 },
11719       { CGO_SAUSAGE,                         6, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::sausage, vertex2), 8 },
11720       { CGO_CYLINDER,                        6, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::cylinder, vertex2), 8 },
11721       { CGO_CUSTOM_CYLINDER,                 6, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::custom_cylinder, vertex2), 8 },
11722       { CGO_CUSTOM_CYLINDER_ALPHA,           6, FLOAT3_TO_FLOAT3,      offsetof(cgo::draw::custom_cylinder_alpha, vertex2), 8 } };
11723   static AttribDataOp colorOps =
11724     { { CGO_COLOR,                           0, FLOAT3_TO_UB3,         0 },
11725       { CGO_ALPHA,                           0, FLOAT1_TO_UB_4TH,      0 },
11726       { CGO_SAUSAGE,                         4, FLOAT3_TO_UB3,         offsetof(cgo::draw::sausage, color1) },
11727       { CGO_CYLINDER,                        4, FLOAT3_TO_UB3,         offsetof(cgo::draw::cylinder, color1) },
11728       { CGO_CUSTOM_CYLINDER,                 4, FLOAT3_TO_UB3,         offsetof(cgo::draw::custom_cylinder, color1) },
11729       { CGO_CUSTOM_CYLINDER_ALPHA,           4, FLOAT4_TO_UB4,         offsetof(cgo::draw::custom_cylinder_alpha, color1) } };
11730   static AttribDataOp color2Ops =
11731     { { CGO_COLOR,                           1, FLOAT3_TO_UB3,         0 },
11732       { CGO_ALPHA,                           1, FLOAT1_TO_UB_4TH,      0 },
11733       { CGO_SHADER_CYLINDER_WITH_2ND_COLOR,  2, FLOAT3_TO_UB3,         offsetof(cgo::draw::shadercylinder2ndcolor, color2) },
11734       { CGO_SAUSAGE,                         5, FLOAT3_TO_UB3,         offsetof(cgo::draw::sausage, color2) },
11735       { CGO_CYLINDER,                        5, FLOAT3_TO_UB3,         offsetof(cgo::draw::cylinder, color2) },
11736       { CGO_CUSTOM_CYLINDER,                 5, FLOAT3_TO_UB3,         offsetof(cgo::draw::custom_cylinder, color2) },
11737       { CGO_CUSTOM_CYLINDER_ALPHA,           5, FLOAT4_TO_UB4,         offsetof(cgo::draw::custom_cylinder_alpha, color2) } };
11738   AttribDataOp radiusOps =
11739     { { CGO_SHADER_CYLINDER,                 2, FLOAT_TO_FLOAT,        offsetof(cgo::draw::shadercylinder, tube_size), 0 },
11740       { CGO_SHADER_CYLINDER_WITH_2ND_COLOR,  3, FLOAT_TO_FLOAT,        offsetof(cgo::draw::shadercylinder2ndcolor, tube_size), 0 },
11741       { CGO_SAUSAGE,                         3, FLOAT_TO_FLOAT,        offsetof(cgo::draw::sausage, radius), 0 },
11742       { CGO_CYLINDER,                        3, FLOAT_TO_FLOAT,        offsetof(cgo::draw::cylinder, radius), 0 },
11743       { CGO_CUSTOM_CYLINDER,                 3, FLOAT_TO_FLOAT,        offsetof(cgo::draw::custom_cylinder, radius), 0 },
11744       { CGO_CUSTOM_CYLINDER_ALPHA,           3, FLOAT_TO_FLOAT,        offsetof(cgo::draw::custom_cylinder_alpha, radius), 0 } };
11745 
11746   AttribDataDesc attrDesc = { { "attr_vertex1", GL_FLOAT,         3, GL_FALSE, vertex1Ops },
11747                               { "attr_vertex2", GL_FLOAT,         3, GL_FALSE, vertex2Ops },
11748                               { "a_Color",  GL_UNSIGNED_BYTE, 4, GL_TRUE,  colorOps },
11749                               { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE,  color2Ops },
11750                               { "attr_radius",  GL_FLOAT,         1, GL_FALSE, radiusOps } };
11751   AttribDesc *fdesc;
11752   static unsigned char cyl_flags[] = { 0, 4, 6, 2, 1, 5, 7, 3 }; // right(4)/up(2)/out(1)
11753 
11754   attrDesc[1].attrOps[0].funcDataConversions.push_back( { SetVertexFromOriginAxisForCylinder, NULL, "attr_vertex2" } );
11755   attrDesc[1].attrOps[1].funcDataConversions.push_back( { SetVertexFromOriginAxisForCylinder, NULL, "attr_vertex2" } );
11756 
11757   attrDesc.push_back( { "attr_flags", GL_UNSIGNED_BYTE, 1, GL_FALSE } ) ;
11758   fdesc = &attrDesc[attrDesc.size()-1];
11759   fdesc->repeat_value = cyl_flags;
11760   fdesc->repeat_value_length = 8;
11761 
11762   unsigned char default_color[] = { 255, 255, 255, 255 }; // to write in alpha if CGO doesn't have it
11763   fdesc = &attrDesc[2];
11764   fdesc->default_value = default_color;
11765   fdesc = &attrDesc[3];
11766   fdesc->default_value = default_color;
11767   float default_radius = 1.f;
11768   attrDesc[4].default_value = (unsigned char*)&default_radius;
11769 
11770   int box_indices[36] = { // box indices
11771     0, 2, 1, 2, 0, 3, 1, 6, 5, 6, 1, 2, 0, 1, 5, 5, 4, 0,
11772     0, 7, 3, 7, 0, 4, 3, 6, 2, 6, 3, 7, 4, 5, 6, 6, 7, 4 };
11773   int *box_indices_ptr = NULL;
11774   box_indices_ptr = box_indices;
11775 
11776   bool interp_same;
11777   unsigned char cap_value = 0;
11778   if ((interp_same = CGOCheckShaderCylinderCapInfoIsSame(I, cap_value))){
11779     addTo->add<cgo::draw::vertex_attribute_1f>(G->ShaderMgr->GetAttributeUID("a_cap"), cap_value );
11780   } else {
11781     AttribDataOp interpOps =
11782       { { CGO_SHADER_CYLINDER,                3, CYL_CAP_TO_CAP,      offsetof(cgo::draw::shadercylinder, cap), 0 },
11783         { CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 4, CYL_CAP_TO_CAP,      offsetof(cgo::draw::shadercylinder2ndcolor, cap), 0 },
11784         { CGO_SAUSAGE,                        2, CYL_CAPS_ARE_ROUND,  0, 0 },
11785         { CGO_CYLINDER,                       2, CYL_CAPS_ARE_FLAT,   0, 0 },
11786         { CGO_CUSTOM_CYLINDER,                2, CYL_CAPS_ARE_CUSTOM, offsetof(cgo::draw::custom_cylinder, cap1), 0 },
11787         { CGO_CUSTOM_CYLINDER_ALPHA,          2, CYL_CAPS_ARE_CUSTOM, offsetof(cgo::draw::custom_cylinder_alpha, cap1), 0 },
11788       };
11789     attrDesc.push_back({ "a_cap", GL_UNSIGNED_BYTE, 1, GL_FALSE, interpOps } );
11790   }
11791 
11792   AttribDataOp extraPickColorOps = { { CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0 },
11793                                      { CGO_SHADER_CYLINDER_WITH_2ND_COLOR,  8, UINT_INT_TO_PICK_DATA, offsetof(cgo::draw::shadercylinder2ndcolor, pick_color_index), 0 } };
11794   AttribDataOp extraPickColor2Ops = { { CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0 },
11795                                       { CGO_SHADER_CYLINDER_WITH_2ND_COLOR,  5, UINT_INT_TO_PICK_DATA, offsetof(cgo::draw::shadercylinder2ndcolor, pick_color_index), 0 } };
11796   AttribDataDesc pickDesc = { { "a_Color",  GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColorOps },
11797                               { "a_Color2", GL_UNSIGNED_BYTE, 4, GL_TRUE, extraPickColor2Ops }};
11798   return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, VertexBuffer::INTERLEAVED, true, box_indices_ptr, 36);
11799 }
11800 
11801 /**
11802  * CGO iterator increment
11803  */
operator ++()11804 CGO::const_iterator& CGO::const_iterator::operator++()
11805 {
11806   const unsigned op = op_code();
11807 
11808   // Corrupted OP codes should never make it into a CGO, so we don't want to
11809   // handle this gracefully. Only import of CGOs (from PSEs, from Python, etc.)
11810   // should handle invalid codes gracefully.
11811   assert(op < CGO_sz_size());
11812 
11813   m_pc += CGO_sz[op] + 1;
11814   return *this;
11815 }
11816