layer2/RepSurface.cpp (4,502 lines of code) (raw):

/* A* ------------------------------------------------------------------- B* This file contains source code for the PyMOL computer program C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. D* ------------------------------------------------------------------- E* It is unlawful to modify or remove this copyright notice. F* ------------------------------------------------------------------- G* Please see the accompanying LICENSE file for further information. H* ------------------------------------------------------------------- I* Additional authors of this source file include: -* -* -* Z* ------------------------------------------------------------------- */ #include"os_python.h" #include"os_predef.h" #include"os_std.h" #include"os_gl.h" #include"Base.h" #include"MemoryDebug.h" #include"OOMac.h" #include"RepSurface.h" #include"Map.h" #include"Scene.h" #include"Sphere.h" #include"Setting.h" #include"Color.h" #include"ObjectMolecule.h" #include"Triangle.h" #include"Vector.h" #include"Feedback.h" #include"main.h" #include"Util.h" #include"CGO.h" #include"P.h" #include"PConv.h" #include"Selector.h" #include"ShaderMgr.h" #ifdef NT #undef NT #endif typedef struct RepSurface { Rep R; int N; int NT; int proximity; float *V, *VN, *VC, *VA, *VAO; /* VAO - Ambient Occlusion per vertex */ int *RC; int *Vis; int *T, *S, *AT; /* T=vertices, S=strips, AT=closest atom for vertices */ int solidFlag; int oneColorFlag, oneColor; int allVisibleFlag; char *LastVisib; int *LastColor; int ColorInvalidated; int Type; float max_vdw; /* These variables are for using the shader. All of them */ /* are allocated/set when generate_shader_cgo to minimize */ /* allocation during the rendering loop. */ CGO *shaderCGO, *pickingCGO; short dot_as_spheres; #ifdef _PYMOL_IOS #endif } RepSurface; void RepSurfaceFree(RepSurface * I); int RepSurfaceSameVis(RepSurface * I, CoordSet * cs); void RepSurfaceColor(RepSurface * I, CoordSet * cs); void RepSurfaceSmoothEdges(RepSurface * I); static void setShaderCGO(RepSurface * I, CGO * cgo) { if (I->shaderCGO != I->pickingCGO) { CGOFree(I->shaderCGO); } I->shaderCGO = cgo; } static void setPickingCGO(RepSurface * I, CGO * cgo) { if (I->shaderCGO != I->pickingCGO) { CGOFree(I->pickingCGO); } I->pickingCGO = cgo; } void RepSurfaceFree(RepSurface * I) { VLAFreeP(I->V); VLAFreeP(I->VN); setPickingCGO(I, NULL); setShaderCGO(I, NULL); #ifdef _PYMOL_IOS #endif FreeP(I->VC); FreeP(I->VA); if (I->VAO){ VLAFreeP(I->VAO); I->VAO = 0; } FreeP(I->RC); FreeP(I->Vis); FreeP(I->LastColor); FreeP(I->LastVisib); VLAFreeP(I->T); VLAFreeP(I->S); VLAFreeP(I->AT); RepPurge(&I->R); /* unnecessary, but a good idea */ OOFreeP(I); } typedef struct { int nDot; float *dot; float *dotNormal; int *dotCode; } SolventDot; typedef struct { float vdw; int flags; } SurfaceJobAtomInfo; static SolventDot *SolventDotNew(PyMOLGlobals * G, float *coord, SurfaceJobAtomInfo * atom_info, float probe_radius, SphereRec * sp, int *present, int circumscribe, int surface_mode, int surface_solvent, int cavity_cull, int all_visible_flag, float max_vdw, int cavity_mode, float cavity_radius, float cavity_cutoff); static void SolventDotFree(SolventDot * I) { if(I) { VLAFreeP(I->dot); VLAFreeP(I->dotNormal); VLAFreeP(I->dotCode); } OOFreeP(I); } #ifndef PURE_OPENGL_ES_2 static void immediate_draw_masked_vertices( const float * vc, // colors const float * vn, // normals const float * v, // vertices const int * mask, // mask int count) { for (int i = 0; i < count; ++i) { if (!mask[i]) continue; int i3 = i * 3; if (vc) glColor3fv(vc + i3); if (vn) glNormal3fv(vn + i3); glVertex3fv(v + i3); } } static void immediate_draw_indexed_vertices( const float * vc, // colors const float * vn, // normals const float * v, // vertices const int * indices, // indices int count) { for (int i = 0; i < count; ++i) { int i3 = indices[i] * 3; if (vc) glColor3fv(vc + i3); if (vn) glNormal3fv(vn + i3); glVertex3fv(v + i3); } } static void immediate_draw_indexed_vertices_alpha( const float * vc, // colors const float * va, // alpha array float alpha, // alpha value if va is NULL const float * vn, // normals const float * v, // vertices const int * indices, // indices int count) { for (int i = 0; i < count; ++i) { int i3 = indices[i] * 3; if (vc) glColor4f(vc[i3], vc[i3 + 1], vc[i3 + 2], va ? va[indices[i]] : alpha); if (vn) glNormal3fv(vn + i3); glVertex3fv(v + i3); } } #endif static bool visibility_test( bool proximityFlag, const int * vi, // visibility array const int * t) // indices { if (proximityFlag) return (vi[t[0]] || vi[t[1]] || vi[t[2]]); return (vi[t[0]] && vi[t[1]] && vi[t[2]]); } static int check_and_add(int *cache, int spacing, int t0, int t1) { int *rec; int cnt; t0++; t1++; rec = cache + spacing * t0; cnt = spacing; while(cnt > 0) { if(*rec == t1) return 1; if(!*rec) { *rec = t1; break; } rec++; cnt--; } rec = cache + spacing * t1; cnt = spacing; while(cnt > 0) { if(*rec == t0) return 1; if(!*rec) { *rec = t0; break; } rec++; cnt--; } return 0; } #define CLAMP_VALUE(v) ((v>1.f) ? 1.f : (v < 0.f) ? 0.f : v) static int AtomInfoIsMasked(ObjectMolecule *obj, int atm){ AtomInfoType *ait; if (atm < 0) return cPickableNoPick; ait = &obj->AtomInfo[atm]; return (ait->masked ? cPickableNoPick : cPickableAtom); } static int RepSurfaceCGOGenerate(RepSurface * I, RenderInfo * info) { PyMOLGlobals *G = I->R.G; float *v = I->V; float *vn = I->VN; float *vc = I->VC; float *va = I->VA; int *t = I->T; int *s = I->S; int c = I->N; int *vi = I->Vis; int *at = I->AT; int ok = true; float alpha; int t_mode; CGO *convertcgo = NULL; bool pick_surface = SettingGet_b(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_pick_surface); short dot_as_spheres = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_as_spheres); alpha = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_transparency); alpha = 1.0F - alpha; if(fabs(alpha - 1.0) < R_SMALL4) alpha = 1.0F; setShaderCGO(I, CGONew(G)); if (!I->shaderCGO) return false; I->shaderCGO->use_shader = true; I->dot_as_spheres = dot_as_spheres; if (I->Type == 1) { /* no triangle information, so we're rendering dots only */ int normals = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_normals); if(!normals){ CGOResetNormal(I->shaderCGO, true); } if((alpha != 1.0)) { CGOAlpha(I->shaderCGO, alpha); } if (dot_as_spheres){ if(c) { ok &= CGOColor(I->shaderCGO, 1.0, 0.0, 0.0); if(ok && I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); } while(ok && c--) { if(*vi) { if(!I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, vc); } if(ok && normals) ok &= CGONormalv(I->shaderCGO, vn); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, *at, AtomInfoIsMasked((ObjectMolecule*)I->R.obj, *at)); if (ok) ok &= CGOSphere(I->shaderCGO, v, 1.f); } vi++; vc += 3; vn += 3; v += 3; at++; } } } else { ok &= CGODotwidth(I->shaderCGO, SettingGet_f (G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_width)); if(ok && c) { ok &= CGOColor(I->shaderCGO, 1.0, 0.0, 0.0); ok &= CGOBegin(I->shaderCGO, GL_POINTS); if(ok && I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); } while(ok && c--) { if(*vi) { if(!I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, vc); } if(normals){ CGONormalv(I->shaderCGO, vn); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, *at, AtomInfoIsMasked((ObjectMolecule*)I->R.obj, *at)); if (ok) ok &= CGOVertexv(I->shaderCGO, v); } vi++; vc += 3; vn += 3; v += 3; at++; } if (ok) ok &= CGOEnd(I->shaderCGO); } } } else if(I->Type == 2) { /* rendering triangle mesh */ int normals = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_mesh_normals); if(ok && !normals){ ok &= CGOResetNormal(I->shaderCGO, true); } if (ok) { float line_width = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_mesh_width); line_width = SceneGetDynamicLineWidth(info, line_width); ok &= CGOSpecial(I->shaderCGO, LINEWIDTH_DYNAMIC_MESH); c = I->NT; if(ok && c) { if(I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { if(normals) { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = (*t); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } else { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = *t; if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = *t; if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = *t; if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } } else t += 3; } } else { /* not oneColorFlag */ while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { if(normals) { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } else { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } } else t += 3; } } } } } else { /* we're rendering triangles */ if((alpha != 1.0) || va) { t_mode = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_transparency_mode); if(info && info->alpha_cgo) { t_mode = 0; } if(t_mode) { float **t_buf = NULL, **tb; float *z_value = NULL, *zv; int *ix = NULL; int n_tri = 0; float sum[3]; float matrix[16]; glGetFloatv(GL_MODELVIEW_MATRIX, matrix); if(I->oneColorFlag) { t_buf = Alloc(float *, I->NT * 6); } else { t_buf = Alloc(float *, I->NT * 12); } CHECKOK(ok, t_buf); if (ok){ z_value = Alloc(float, I->NT); CHECKOK(ok, z_value); } if (ok){ ix = Alloc(int, I->NT); CHECKOK(ok, ix); } zv = z_value; tb = t_buf; c = I->NT; if (ok){ if(I->oneColorFlag) { while(c--) { if (visibility_test(I->proximity, vi, t)) { *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-3], sum); add3f(sum, tb[-5], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } else { while(c--) { if (visibility_test(I->proximity, vi, t)) { if(va) *(tb++) = va + (*t); else *(tb++) = &alpha; *(tb++) = vc + (*t) * 3; *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; if(va) *(tb++) = va + (*(t + 1)); else *(tb++) = &alpha; *(tb++) = vc + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; if(va) *(tb++) = va + (*(t + 2)); else *(tb++) = &alpha; *(tb++) = vc + (*(t + 2)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-5], sum); add3f(sum, tb[-9], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } } switch (t_mode) { case 1: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, true); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZOrderFn); */ break; default: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, false); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZRevOrderFn); */ break; } c = n_tri; if(I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); if (ok){ CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); for(c = 0; ok && c < n_tri; c++) { int idx; tb = t_buf + 6 * c; // tb = t_buf + 6 * ix[c]; if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v)/3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + idx)); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v)/3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + idx)); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v)/3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + idx)); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); } if (ok) ok &= CGOEnd(I->shaderCGO); } } else { /* else I->oneColorFlag */ if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); for(c = 0; ok && c < n_tri; c++) { float *vv, *v_alpha; int idx; tb = t_buf + 12 * c; /* need to update index every frame */ // tb = t_buf + 12 * ix[c]; v_alpha = *(tb++); vv = *(tb++); ok &= CGOAlpha(I->shaderCGO, *v_alpha); if (ok) ok &= CGOColor(I->shaderCGO, vv[0], vv[1], vv[2]); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v)/3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + idx)); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); v_alpha = *(tb++); vv = *(tb++); if (ok) ok &= CGOAlpha(I->shaderCGO, *v_alpha); if (ok) ok &= CGOColor(I->shaderCGO, vv[0], vv[1], vv[2]); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v)/3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + idx)); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); v_alpha = *(tb++); vv = *(tb++); if (ok) ok &= CGOAlpha(I->shaderCGO, *v_alpha); if (ok) ok &= CGOColor(I->shaderCGO, vv[0], vv[1], vv[2]); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v)/3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[idx])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + idx)); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); } if (ok) ok &= CGOEnd(I->shaderCGO); } FreeP(ix); FreeP(z_value); FreeP(t_buf); } else if (ok) { if(info->alpha_cgo) { /* global transparency sort */ if(I->allVisibleFlag) { if(I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); c = *(s++); while(c) { int parity = 0; s += 2; while(ok && c--) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col, col, col, alpha, alpha, alpha, parity); s++; parity = !parity; } c = *(s++); } } else { c = *(s++); while(ok && c) { float *col0, *col1, *col2; int parity = 0; float alpha0, alpha1, alpha2; col0 = vc + (*s) * 3; if(va) { alpha0 = va[(*s)]; } else { alpha0 = alpha; } s++; col1 = vc + (*s) * 3; if(va) { alpha1 = va[(*s)]; } else { alpha1 = alpha; } s++; while(ok && c--) { col2 = vc + (*s) * 3; if(va) { alpha2 = va[(*s)]; } else { alpha2 = alpha; } ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col0, col1, col2, alpha0, alpha1, alpha2, parity); alpha0 = alpha1; alpha1 = alpha2; col0 = col1; col1 = col2; s++; parity = !parity; } c = *(s++); } } } else if (ok){ /* subset s */ c = I->NT; if(c) { if(I->oneColorFlag) { float color[3]; ColorGetEncoded(G, I->oneColor, color); while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, color, color, color, alpha, alpha, alpha, 0); } t += 3; } } else { while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { if(va) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, va[t[0]], va[t[1]], va[t[2]], 0); } else { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, alpha, alpha, alpha, 0); } } t += 3; } } if (ok) CGOEnd(info->alpha_cgo); } } } else if (ok){ /* fast and ugly */ /* glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glDepthMask(GL_FALSE); */ if(I->allVisibleFlag) { if(I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); c = *(s++); while(ok && c) { if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while(ok && c--) { ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO); c = *(s++); } } else { /* I->oneColorFlag */ c = *(s++); while(ok && c) { float *col; if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); col = vc + (*s) * 3; if(va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*s)]); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; col = vc + (*s) * 3; if(va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*s)]); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while(ok && c--) { col = vc + (*s) * 3; if(va) { ok &= CGOAlpha(I->shaderCGO, va[(*s)]); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } else { ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO); c = *(s++); } } } else { /* subset s */ c = I->NT; if(ok && c) { if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); if(I->oneColorFlag) { float color[3]; ColorGetEncoded(G, I->oneColor, color); if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, color[0], color[1], color[2]); while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } else { float *col; while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { col = vc + (*t) * 3; if(va) { ok &= CGOAlpha(I->shaderCGO, va[(*t)]); } else { ok &= CGOAlpha(I->shaderCGO, alpha); } if (ok) ok &= CGOColorv(I->shaderCGO, col); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; col = vc + (*t) * 3; if(va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*t)]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); } if (ok) ok &= CGOColorv(I->shaderCGO, col); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; col = vc + (*t) * 3; if(va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*t)]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); } if (ok) ok &= CGOColorv(I->shaderCGO, col); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } if (ok) ok &= CGOEnd(I->shaderCGO); } } } /* glDisable(GL_CULL_FACE); glDepthMask(GL_TRUE); */ } } else if (ok) { /* opaque */ if(I->allVisibleFlag) { if(I->oneColorFlag) { if (ok) { CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); c = *(s++); while(ok && c) { if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while(ok && c--) { ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO); c = *(s++); } } } else { /* not one color */ { c = *(s++); while(ok && c) { ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*s) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*s) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while(ok && c--) { ok &= CGOColorv(I->shaderCGO, vc + (*s) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *s)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO ); c = *(s++); } } } /* one color */ } else if (ok) { /* subsets */ c = I->NT; if(ok && c) { ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); if(I->oneColorFlag) { if (ok) ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } else { while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGOColorv(I->shaderCGO, vc + (*t) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*t) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*t) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } if (ok) ok &= CGOEnd(I->shaderCGO); } } /* if (use_shader) { CShaderPrg_Disable(shaderPrg); }*/ } } if(ok && SettingGetGlobal_i(G, cSetting_surface_debug)) { t = I->T; c = I->NT; if(c) { ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); while(ok && c--) { if(I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && I->VAO){ ok &= CGOAccessibility(I->shaderCGO, *(I->VAO + *t)); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else { t += 3; } } if (ok) ok &= CGOEnd(I->shaderCGO); } t = I->T; c = I->NT; if(ok && c) { ok &= CGOColor(I->shaderCGO, 0.0, 1.0, 0.0); if (ok) ok &= CGODotwidth(I->shaderCGO, 1.0F); while(ok && c--) { ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); if(I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked((ObjectMolecule*)I->R.obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else { t += 3; } if (ok) ok &= CGOEnd(I->shaderCGO); } } c = I->N; if(ok && c) { ok &= CGOColor(I->shaderCGO, 1.0, 0.0, 0.0); if (ok) ok &= CGOResetNormal(I->shaderCGO, true); if (ok) ok &= CGOBegin(I->shaderCGO, GL_LINES); while(ok && c--) { ok &= CGOVertexv(I->shaderCGO, v); if (ok) ok &= CGOVertex(I->shaderCGO, v[0] + vn[0] / 2, v[1] + vn[1] / 2, v[2] + vn[2] / 2); v += 3; vn += 3; } if (ok) ok &= CGOEnd(I->shaderCGO); } } if (ok) ok &= CGOStop(I->shaderCGO); if (I->Type != 2){ CGOCombineBeginEnd(&I->shaderCGO); if (I->Type == 1){ /* Only needed to simplify spheres in surface_type=1 */ CGO *simple = CGOSimplify(I->shaderCGO, 0); CGOCombineBeginEnd(&simple, 0); CHECKOK(ok, simple); setPickingCGO(I, simple); } else { setPickingCGO(I, I->shaderCGO); } } if(I->Type == 1){ if (dot_as_spheres) { CGO *tmpCGO = CGONew(G); CHECKOK(ok, tmpCGO); ok &= CGOEnable(tmpCGO, GL_SPHERE_SHADER); ok &= CGOEnable(tmpCGO, GL_DOT_LIGHTING); // TODO: this needs normals in sphere shader PYMOL-1870 ok &= CGOSpecial(tmpCGO, DOTSIZE_WITH_SPHERESCALE); convertcgo = CGOOptimizeSpheresToVBONonIndexedNoShader(I->shaderCGO, CGO_BOUNDING_BOX_SZ + fsizeof<cgo::draw::sphere_buffers>() + 2); ok &= CGOAppendNoStop(tmpCGO, convertcgo); CGOFreeWithoutVBOs(convertcgo); ok &= CGODisable(tmpCGO, GL_SPHERE_SHADER); CGOStop(tmpCGO); convertcgo = tmpCGO; } else { convertcgo = CGOOptimizeToVBONotIndexedNoShader(I->shaderCGO, 0); } CHECKOK(ok, convertcgo); if (ok) convertcgo->use_shader = true; } else if (I->Type == 2) { CGO *convertcgo2, *simple = NULL; CGO *tmpCGO = CGONew(G); CHECKOK(ok, tmpCGO); convertcgo2 = CGOConvertLinesToShaderCylinders(I->shaderCGO, 0); CHECKOK(ok, convertcgo2); CGOEnable(tmpCGO, GL_CYLINDER_SHADER); CGOSpecial(tmpCGO, MESH_WIDTH_FOR_SURFACES); convertcgo = CGOConvertShaderCylindersToCylinderShader(convertcgo2, tmpCGO); CGOAppendNoStop(tmpCGO, convertcgo); CGOFreeWithoutVBOs(convertcgo); CGODisable(tmpCGO, GL_CYLINDER_SHADER); CGOStop(tmpCGO); convertcgo = tmpCGO; convertcgo->use_shader = true; if (ok) simple = CGOSimplify(convertcgo2, 0); CHECKOK(ok, simple); if (ok) setPickingCGO(I, CGOCombineBeginEnd(simple, 0)); CHECKOK(ok, I->pickingCGO); CGOFree(simple); CGOFree(convertcgo2); } else { if((alpha != 1.0) || va) { // semi-transparent if (ok) convertcgo = CGOOptimizeToVBOIndexedWithColorEmbedTransparentInfo(I->shaderCGO, 0, 0, 0); CHECKOK(ok, convertcgo); #ifdef _PYMOL_IOS #endif } else { if (ok) convertcgo = CGOOptimizeToVBONotIndexedWithReturnedData(I->shaderCGO, 0, false, NULL); CHECKOK(ok, convertcgo); } if (ok) convertcgo->use_shader = true; { CGO *tmpCGO = NULL; tmpCGO = CGONew(G); CGOEnable(tmpCGO, GL_SURFACE_SHADER); //CGOEnable(tmpCGO, CGO_GL_LIGHTING); // do we need this? CGOSpecial(tmpCGO, SET_SURFACE_UNIFORMS); CGOAppendNoStop(tmpCGO, convertcgo); CGODisable(tmpCGO, GL_SURFACE_SHADER); CGOStop(tmpCGO); CGOFreeWithoutVBOs(convertcgo); convertcgo = tmpCGO; convertcgo->use_shader = true; } } if(ok && I->Type == 1 && !dot_as_spheres) { setShaderCGO(I, CGONew(G)); CHECKOK(ok, I->shaderCGO); if (ok){ I->shaderCGO->use_shader = true; if (ok) ok &= CGOResetNormal(I->shaderCGO, true); if (ok) ok &= CGOEnable(I->shaderCGO, GL_SURFACE_SHADER); if (ok) ok &= CGOSpecial(I->shaderCGO, POINTSIZE_DYNAMIC_DOT_WIDTH); if (ok) CGOAppendNoStop(I->shaderCGO, convertcgo); if (ok) ok &= CGODisable(I->shaderCGO, GL_SURFACE_SHADER); if (ok) ok &= CGOStop(I->shaderCGO); CGOFreeWithoutVBOs(convertcgo); convertcgo = NULL; } } else { setShaderCGO(I, convertcgo); convertcgo = NULL; } { CGO *simple = NULL; if (ok) simple = CGOOptimizeToVBONotIndexed(I->pickingCGO, 0); CHECKOK(ok, simple); if (ok){ CGOChangeShadersTo(simple, GL_DEFAULT_SHADER, GL_SURFACE_SHADER); } if (ok){ setPickingCGO(I, simple); I->pickingCGO->use_shader = true; I->pickingCGO->no_pick = !pick_surface; } } return ok; } static void RepSurfaceRender(RepSurface * I, RenderInfo * info) { CRay *ray = info->ray; auto pick = info->pick; PyMOLGlobals *G = I->R.G; float *v = I->V; float *vn = I->VN; float *vc = I->VC; float *va = I->VA; int *rc = I->RC; int *t = I->T; int *s = I->S; int c = I->N; int *vi = I->Vis; int ok = true; float alpha; int t_mode; float ambient_occlusion_scale = 0.f; int ambient_occlusion_mode = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_ambient_occlusion_mode); int ambient_occlusion_mode_div_4 = 0; if (ambient_occlusion_mode){ ambient_occlusion_scale = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_ambient_occlusion_scale); ambient_occlusion_mode_div_4 = ambient_occlusion_mode / 4; } if((I->Type != 1) && (!s)) { return; } alpha = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_transparency); alpha = 1.0F - alpha; if(fabs(alpha - 1.0) < R_SMALL4) alpha = 1.0F; if(ray) { #ifndef _PYMOL_NO_RAY ray->transparentf(1.0F - alpha); if(I->Type == 1) { /* dot surface */ float radius; radius = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_radius); if(radius == 0.0F) { radius = ray->PixelRadius * SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_width) / 1.4142F; } if(I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); ray->color3fv(col); } if(c) while(ok && c--) { if(*vi) { if(!I->oneColorFlag) { ray->color3fv(vc); } ok &= ray->sphere3fv(v, radius); } vi++; vc += 3; v += 3; } } else if((I->Type == 0) || (I->Type == 3) || (I->Type == 4) || (I->Type == 5)) { /* solid surface */ c = I->NT; if(I->oneColorFlag) { float col[3], col1[3], col2[3], col3[3]; ColorGetEncoded(G, I->oneColor, col); while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { copy3f(col, col1); copy3f(col, col2); copy3f(col, col3); if (I->VAO){ float ao1, ao2, ao3; switch (ambient_occlusion_mode_div_4){ case 1: ao1 = 1.f-ambient_occlusion_scale*(*(I->VAO + *t)); ao2 = 1.f-ambient_occlusion_scale*(*(I->VAO + *(t+1))); ao3 = 1.f-ambient_occlusion_scale*(*(I->VAO + *(t+2))); break; case 2: ao1 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale*(*(I->VAO + *t)))); ao2 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale*(*(I->VAO + *(t+1))))); ao3 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale*(*(I->VAO + *(t+2))))); break; default: ao1 = CLAMP_VALUE(1.f / (1.f + exp(.5f*((ambient_occlusion_scale*(*(I->VAO + *t))) - 10.f)))); ao2 = CLAMP_VALUE(1.f / (1.f + exp(.5f*((ambient_occlusion_scale*(*(I->VAO + *(t+1)))) - 10.f)))); ao3 = CLAMP_VALUE(1.f / (1.f + exp(.5f*((ambient_occlusion_scale*(*(I->VAO + *(t+2)))) - 10.f)))); } mult3f(col1, ao1, col1); mult3f(col2, ao2, col2); mult3f(col3, ao3, col3); } ok &= ray->triangle3fv(v + (*t) * 3, v + (*(t + 1)) * 3, v + (*(t + 2)) * 3, vn + (*t) * 3, vn + (*(t + 1)) * 3, vn + (*(t + 2)) * 3, col1, col2, col3); } t += 3; } } else { while(ok && c--) { int ttA = *t, ttB = *(t + 1), ttC = *(t + 2); if (visibility_test(I->proximity, vi, t)) { int ttA3 = ttA * 3, ttB3 = ttB * 3, ttC3 = ttC * 3; float cA[3], cB[3], cC[3]; copy3f(vc + ttA3, cA); copy3f(vc + ttB3, cB); copy3f(vc + ttC3, cC); // register float *cA = vc + ttA3, *cB = vc + ttB3, *cC = vc + ttC3; if(rc) { if(rc[ttA] < -1) ColorGetEncoded(G, rc[ttA], cA); // ColorGetEncoded(G, rc[ttA], (cA = colA)); if(rc[ttB] < -1) ColorGetEncoded(G, rc[ttB], cB); // ColorGetEncoded(G, rc[ttB], (cB = colB)); if(rc[ttC] < -1) ColorGetEncoded(G, rc[ttC], cC); // ColorGetEncoded(G, rc[ttC], (cC = colC)); } if((*(vi + ttA)) || (*(vi + ttB)) || (*(vi + ttC))) { if (I->VAO){ float ao1, ao2, ao3; switch (ambient_occlusion_mode_div_4){ case 1: ao1 = 1.f-ambient_occlusion_scale*(*(I->VAO + *t)); ao2 = 1.f-ambient_occlusion_scale*(*(I->VAO + *(t+1))); ao3 = 1.f-ambient_occlusion_scale*(*(I->VAO + *(t+2))); break; case 2: ao1 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale*(*(I->VAO + *t)))); ao2 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale*(*(I->VAO + *(t+1))))); ao3 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale*(*(I->VAO + *(t+2))))); break; default: ao1 = CLAMP_VALUE(1.f / (1.f + exp(.5f*((ambient_occlusion_scale*(*(I->VAO + *t))) - 10.f)))); ao2 = CLAMP_VALUE(1.f / (1.f + exp(.5f*((ambient_occlusion_scale*(*(I->VAO + *(t+1)))) - 10.f)))); ao3 = CLAMP_VALUE(1.f / (1.f + exp(.5f*((ambient_occlusion_scale*(*(I->VAO + *(t+2)))) - 10.f)))); } mult3f(cA, ao1, cA); mult3f(cB, ao2, cB); mult3f(cC, ao3, cC); } if(va) { ok &= ray->triangleTrans3fv(v + ttA3, v + ttB3, v + ttC3, vn + ttA3, vn + ttB3, vn + ttC3, cA, cB, cC, 1.0F - va[ttA], 1.0F - va[ttB], 1.0F - va[ttC]); } else { ok &= ray->triangle3fv(v + ttA3, v + ttB3, v + ttC3, vn + ttA3, vn + ttB3, vn + ttC3, cA, cB, cC); } } } t += 3; } } } else if(I->Type == 2) { /* triangle mesh surface */ float radius; int t0, t1, t2; int spacing = 10; int *cache = Calloc(int, spacing * (I->N + 1)); CHECKOK(ok, cache); radius = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_mesh_radius); if(ok && radius == 0.0F) { float line_width = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_mesh_width); line_width = SceneGetDynamicLineWidth(info, line_width); radius = ray->PixelRadius * line_width / 2.0F; } if (ok){ float col[3], *col0, *col1, *col2; c = I->NT; if(I->oneColorFlag) { ColorGetEncoded(G, I->oneColor, col); col0 = col1 = col2 = col; } while(ok && c--) { t0 = (*t); t1 = (*(t + 1)); t2 = (*(t + 2)); if (visibility_test(I->proximity, vi, t)) { if (!I->oneColorFlag) { col0 = vc + t0 * 3; col1 = vc + t1 * 3; col2 = vc + t2 * 3; } if(!check_and_add(cache, spacing, t0, t1)) ok &= ray->sausage3fv(v + t0 * 3, v + t1 * 3, radius, col0, col1); if(!check_and_add(cache, spacing, t1, t2)) ok &= ray->sausage3fv(v + t1 * 3, v + t2 * 3, radius, col1, col2); if(!check_and_add(cache, spacing, t2, t0)) ok &= ray->sausage3fv(v + t2 * 3, v + t0 * 3, radius, col2, col0); } t += 3; } FreeP(cache); } } if (ok){ ray->transparentf(0.0); } else { /* If not ok, then Clear Entire RepSurface, not just the ray object */ } #endif } else if(G->HaveGUI && G->ValidContext) { /* Not ray tracing, but rendering */ if(pick) { // Don't render transparent unpickable surfaces. Do render solid but // unpickable surfaces to write the depth buffer and prevent // through-picking. if (I->pickingCGO && !(I->pickingCGO->no_pick && alpha < 1.F)) { CGORenderGLPicking(I->pickingCGO, info, &I->R.context, I->R.cs->Setting, I->R.obj->Setting); } } else { #ifndef PURE_OPENGL_ES_2 bool use_shader = SettingGetGlobal_b(G, cSetting_surface_use_shader) && SettingGetGlobal_b(G, cSetting_use_shaders); if (use_shader && !info->alpha_cgo) #endif { bool dot_as_spheres = SettingGet_b(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_as_spheres); if (!I->shaderCGO || CGOCheckWhetherToFree(G, I->shaderCGO) || (I->Type == 1 && I->dot_as_spheres != dot_as_spheres)) { ok &= RepSurfaceCGOGenerate(I, info); } if (ok && I->shaderCGO) { const float *color = ColorGet(G, I->R.obj->Color); CGORenderGL(I->shaderCGO, color, NULL, NULL, info, &I->R); return; } PRINTFB(G, FB_RepSurface, FB_Errors) " RepSurfaceCGOGenerate failed\n" ENDFB(G); } setShaderCGO(I, NULL); #ifndef PURE_OPENGL_ES_2 bool two_sided_lighting = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_two_sided_lighting) > 0; if (two_sided_lighting){ GLLIGHTMODELI(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); } if(I->Type == 1) { /* no triangle information, so we're rendering dots only */ { int normals = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_normals); int lighting = info->line_lighting || SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_lighting); if(!normals){ SceneResetNormal(G, true); vn = NULL; } if(!lighting) glDisable(GL_LIGHTING); glPointSize(SettingGet_f (G, I->R.cs->Setting, I->R.obj->Setting, cSetting_dot_width)); if(c) { glBegin(GL_POINTS); if(I->oneColorFlag) { glColor3fv(ColorGet(G, I->oneColor)); vc = NULL; } else { glColor3f(1.0, 0.0, 0.0); } immediate_draw_masked_vertices(vc, vn, v, vi, c); glEnd(); } if(!lighting) glEnable(GL_LIGHTING); } /* else use shader */ } else if(I->Type == 2) { /* rendering triangle mesh */ if (ok) { c = I->NT; if(ok && c) { int normals = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_mesh_normals); int lighting = info->line_lighting || SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_mesh_lighting); if(!normals){ SceneResetNormal(G, true); vn = NULL; } if(!lighting) glDisable(GL_LIGHTING); float line_width = SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_mesh_width); glLineWidth(SceneGetDynamicLineWidth(info, line_width)); if(I->oneColorFlag) { glColor3fv(ColorGet(G, I->oneColor)); vc = NULL; } glBegin(GL_LINES); for (; c--; t += 3) { if (visibility_test(I->proximity, vi, t)) { int indices[] = {t[0], t[1], t[1], t[2], t[2], t[0]}; immediate_draw_indexed_vertices(vc, vn, v, indices, 6); } } glEnd(); if(!lighting) glEnable(GL_LIGHTING); } } } else { /* we're rendering triangles */ if((alpha != 1.0) || va) { t_mode = SettingGet_i(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_transparency_mode); if(info && info->alpha_cgo) { t_mode = 0; } if(t_mode) { float **t_buf = NULL, **tb; float *z_value = NULL, *zv; int *ix = NULL; int n_tri = 0; float sum[3]; float matrix[16]; glGetFloatv(GL_MODELVIEW_MATRIX, matrix); if(I->oneColorFlag) { t_buf = Alloc(float *, I->NT * 6); } else { t_buf = Alloc(float *, I->NT * 12); } CHECKOK(ok, t_buf); if (ok){ z_value = Alloc(float, I->NT); CHECKOK(ok, z_value); } if (ok){ ix = Alloc(int, I->NT); CHECKOK(ok, ix); } zv = z_value; tb = t_buf; c = I->NT; if (ok){ if(I->oneColorFlag) { while(c--) { if (visibility_test(I->proximity, vi, t)) { *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-3], sum); add3f(sum, tb[-5], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } else { while(c--) { if (visibility_test(I->proximity, vi, t)) { if(va) *(tb++) = va + (*t); else *(tb++) = &alpha; *(tb++) = vc + (*t) * 3; *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; if(va) *(tb++) = va + (*(t + 1)); else *(tb++) = &alpha; *(tb++) = vc + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; if(va) *(tb++) = va + (*(t + 2)); else *(tb++) = &alpha; *(tb++) = vc + (*(t + 2)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-5], sum); add3f(sum, tb[-9], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } } switch (t_mode) { case 1: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, true); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZOrderFn); */ break; default: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, false); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZRevOrderFn); */ break; } c = n_tri; if(I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); if (ok){ glColor4f(col[0], col[1], col[2], alpha); glBegin(GL_TRIANGLES); for(c = 0; c < n_tri; c++) { tb = t_buf + 6 * ix[c]; glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); } glEnd(); } } else { /* else I->oneColorFlag */ glBegin(GL_TRIANGLES); for(c = 0; c < n_tri; c++) { float *vv, *v_alpha; tb = t_buf + 12 * ix[c]; v_alpha = *(tb++); vv = *(tb++); glColor4f(vv[0], vv[1], vv[2], *v_alpha); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); v_alpha = *(tb++); vv = *(tb++); glColor4f(vv[0], vv[1], vv[2], *v_alpha); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); v_alpha = *(tb++); vv = *(tb++); glColor4f(vv[0], vv[1], vv[2], *v_alpha); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); } glEnd(); } { FreeP(ix); FreeP(z_value); FreeP(t_buf); } } else if (ok) { if(info->alpha_cgo) { /* global transparency sort */ if(I->allVisibleFlag) { if(I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); glColor4f(col[0], col[1], col[2], alpha); c = *(s++); while(c) { int parity = 0; s += 2; while(ok && c--) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col, col, col, alpha, alpha, alpha, parity); s++; parity = !parity; } c = *(s++); } } else { c = *(s++); while(ok && c) { float *col0, *col1, *col2; int parity = 0; float alpha0, alpha1, alpha2; col0 = vc + (*s) * 3; if(va) { alpha0 = va[(*s)]; } else { alpha0 = alpha; } s++; col1 = vc + (*s) * 3; if(va) { alpha1 = va[(*s)]; } else { alpha1 = alpha; } s++; while(ok && c--) { col2 = vc + (*s) * 3; if(va) { alpha2 = va[(*s)]; } else { alpha2 = alpha; } ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col0, col1, col2, alpha0, alpha1, alpha2, parity); alpha0 = alpha1; alpha1 = alpha2; col0 = col1; col1 = col2; s++; parity = !parity; } c = *(s++); } } } else if (ok){ /* subset s */ c = I->NT; if(c) { if(I->oneColorFlag) { float color[3]; ColorGetEncoded(G, I->oneColor, color); while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, color, color, color, alpha, alpha, alpha, 0); } t += 3; } } else { while(ok && c--) { if (visibility_test(I->proximity, vi, t)) { if(va) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, va[t[0]], va[t[1]], va[t[2]], 0); } else { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, alpha, alpha, alpha, 0); } } t += 3; } } if (ok) CGOEnd(info->alpha_cgo); } } } else if (ok){ /* fast and ugly */ if(I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); glColor4f(col[0], col[1], col[2], alpha); vc = NULL; } if(I->allVisibleFlag) { for (; (c = *(s++)); s += c + 2) { glBegin(GL_TRIANGLE_STRIP); immediate_draw_indexed_vertices_alpha(vc, va, alpha, vn, v, s, c + 2); glEnd(); } } else { c = I->NT; if(c) { glBegin(GL_TRIANGLES); for (; c--; t += 3) { if (visibility_test(I->proximity, vi, t)) { immediate_draw_indexed_vertices_alpha(vc, va, alpha, vn, v, t, 3); } } glEnd(); } } } } } else if (ok) { /* opaque */ if(I->oneColorFlag) { glColor3fv(ColorGet(G, I->oneColor)); vc = NULL; } if(I->allVisibleFlag) { for (; (c = *(s++)); s += c + 2) { glBegin(GL_TRIANGLE_STRIP); immediate_draw_indexed_vertices(vc, vn, v, s, c + 2); glEnd(); } } else { c = I->NT; if(c) { glBegin(GL_TRIANGLES); for (; c--; t += 3) { if (visibility_test(I->proximity, vi, t)) { immediate_draw_indexed_vertices(vc, vn, v, t, 3); } } glEnd(); } } } } if(ok && SettingGetGlobal_i(G, cSetting_surface_debug)) { t = I->T; c = I->NT; glLineWidth(1.0F); // draw green triangles, either filled or as line edges, // depending on surface_type if(c) { glColor3f(0.0, 1.0, 0.0); if (I->Type == 2) { // filled green triangles for surface as mesh glBegin(GL_TRIANGLES); for (; c--; t += 3) { if(I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { immediate_draw_indexed_vertices(NULL, vn, v, t, 3); } } glEnd(); } else { // line edges as green lines glBegin(GL_LINES); for (; c--; t += 3) { if(I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { int indices[] = {t[0], t[1], t[1], t[2], t[2], t[0]}; immediate_draw_indexed_vertices(NULL, vn, v, indices, 6); } } glEnd(); } } // draw normals as red lines c = I->N; if(c) { SceneResetNormal(G, true); glColor3f(1.0, 0.0, 0.0); glBegin(GL_LINES); while(c--) { glVertex3fv(v); glVertex3f(v[0] + vn[0] / 2, v[1] + vn[1] / 2, v[2] + vn[2] / 2); v += 3; vn += 3; } glEnd(); } } if (two_sided_lighting){ GLLIGHTMODELI(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); } #endif } } if (!ok){ I->R.fInvalidate(&I->R, I->R.cs, cRepInvPurge); I->R.cs->Active[cRepSurface] = false; } } int RepSurfaceSameVis(RepSurface * I, CoordSet * cs) { int same = true; char *lv; int a; AtomInfoType *ai; ai = cs->Obj->AtomInfo; lv = I->LastVisib; for(a = 0; a < cs->NIndex; a++) { if(*(lv++) != GET_BIT((ai + cs->IdxToAtm[a])->visRep,cRepSurface)) { same = false; break; } } return (same); } static void RepSurfaceInvalidate(struct Rep *I, struct CoordSet *cs, int level){ RepInvalidate(I, cs, level); if (level >= cRepInvColor) ((RepSurface*)I)->ColorInvalidated = true; } static int RepSurfaceSameColor(RepSurface * I, CoordSet * cs) { int *lc; int a; AtomInfoType *ai; if(I->ColorInvalidated) return false; { lc = I->LastColor; for(a = 0; a < cs->NIndex; a++) { ai = cs->getAtomInfo(a); if(ai->visRep & cRepSurfaceBit) { if(*(lc++) != ai->color) { return false; } } } } return true; } void RepSurfaceColor(RepSurface * I, CoordSet * cs) { PyMOLGlobals *G = cs->State.G; MapType *map = NULL, *ambient_occlusion_map = NULL; int a, i0, i, j, c1; float *v0, *vc, *va; const float *c0; float *n0; int *vi, *lc; char *lv; int first_color; float *v_pos, v_above[3]; int ramp_above; ObjectMolecule *obj; float probe_radius; float dist; float cutoff; int inclH; int inclInvis; int cullByFlag = false; int surface_mode, ambient_occlusion_mode; int surface_color; int *present = NULL; int *rc; int ramped_flag = false; int carve_state = 0; int carve_flag = false; float carve_cutoff; float carve_normal_cutoff; int carve_normal_flag; const char *carve_selection = NULL; float *carve_vla = NULL; MapType *carve_map = NULL; int clear_state = 0; int clear_flag = false; float clear_cutoff; const char *clear_selection = NULL; float *clear_vla = NULL; int state = I->R.context.state; float transp; int variable_alpha = false; MapType *clear_map = NULL; AtomInfoType *ai2 = NULL, *ai1; obj = cs->Obj; ambient_occlusion_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_ambient_occlusion_mode); surface_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_mode); ramp_above = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_ramp_above_mode); surface_color = SettingGet_color(G, cs->Setting, obj->Obj.Setting, cSetting_surface_color); cullByFlag = (surface_mode == cRepSurface_by_flags); inclH = !((surface_mode == cRepSurface_heavy_atoms) || (surface_mode == cRepSurface_vis_heavy_only)); inclInvis = !((surface_mode == cRepSurface_vis_only) || (surface_mode == cRepSurface_vis_heavy_only)); probe_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_solvent_radius); I->proximity = SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_surface_proximity); carve_cutoff = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_carve_cutoff); clear_cutoff = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_clear_cutoff); transp = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_transparency); carve_normal_cutoff = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_carve_normal_cutoff); carve_normal_flag = carve_normal_cutoff > (-1.0F); cutoff = I->max_vdw + 2 * probe_radius; if(!I->LastVisib) I->LastVisib = Alloc(char, cs->NIndex); if(!I->LastColor) I->LastColor = Alloc(int, cs->NIndex); lv = I->LastVisib; lc = I->LastColor; for(a = 0; a < cs->NIndex; a++) { ai2 = cs->getAtomInfo(a); *(lv++) = GET_BIT(ai2->visRep, cRepSurface); *(lc++) = ai2->color; } if(I->N) { if(carve_cutoff > 0.0F) { carve_state = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_carve_state) - 1; carve_selection = SettingGet_s(G, cs->Setting, obj->Obj.Setting, cSetting_surface_carve_selection); if(carve_selection) carve_map = SelectorGetSpacialMapFromSeleCoord(G, SelectorIndexByName(G, carve_selection), carve_state, carve_cutoff, &carve_vla); if(carve_map) MapSetupExpress(carve_map); carve_flag = true; } if(clear_cutoff > 0.0F) { clear_state = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_clear_state) - 1; clear_selection = SettingGet_s(G, cs->Setting, obj->Obj.Setting, cSetting_surface_clear_selection); if(clear_selection) clear_map = SelectorGetSpacialMapFromSeleCoord(G, SelectorIndexByName(G, clear_selection), clear_state, clear_cutoff, &clear_vla); if(clear_map) MapSetupExpress(clear_map); clear_flag = true; } if(!I->VC) I->VC = Alloc(float, 3 * I->N); vc = I->VC; if(!I->VA) I->VA = Alloc(float, I->N); va = I->VA; if(!I->RC) I->RC = Alloc(int, I->N); rc = I->RC; if(!I->Vis) I->Vis = Alloc(int, I->N); vi = I->Vis; if(ColorCheckRamped(G, surface_color)) { I->oneColorFlag = false; } else { I->oneColorFlag = true; } first_color = -1; present = Alloc(int, cs->NIndex); { int *ap = present; for(a = 0; a < cs->NIndex; a++) { ai1 = obj->AtomInfo + cs->IdxToAtm[a]; if((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate))))) *ap = 2; else *ap = 0; ap++; } } if(inclInvis) { float probe_radiusX2 = probe_radius * 2; map = MapNewFlagged(G, 2 * I->max_vdw + probe_radius, cs->Coord, cs->NIndex, NULL, present); MapSetupExpress(map); /* add in nearby invisibles */ for(a = 0; a < cs->NIndex; a++){ if(!present[a]) { ai1 = obj->AtomInfo + cs->IdxToAtm[a]; if((!cullByFlag) || !(ai1->flags & cAtomFlag_ignore)) { v0 = cs->Coord + 3 * a; i = *(MapLocusEStart(map, v0)); if(i && map->EList) { j = map->EList[i++]; while(j >= 0) { if(present[j] > 1) { ai2 = obj->AtomInfo + cs->IdxToAtm[j]; if(within3f (cs->Coord + 3 * j, v0, ai1->vdw + ai2->vdw + probe_radiusX2)) { present[a] = 1; break; } } j = map->EList[i++]; } } } } } MapFree(map); map = NULL; } if (ambient_occlusion_mode){ float maxDist = 0.f, maxDistA =0.f; int level_min = 64, level_max = 0; double start_time, cur_time; short vertex_map = 0; /* vertex or atom map */ float map_cutoff = cutoff; switch (ambient_occlusion_mode % 4){ case 1: case 3: vertex_map = 0; /* ambient_occlusion_mode - 1 atoms in map (default), 2 - vertices in map */ break; case 2: vertex_map = 1; } if (!I->VAO){ I->VAO = VLAlloc(float, I->N); } else { VLASize(I->VAO, float, I->N); } start_time = UtilGetSeconds(G); if (vertex_map){ ambient_occlusion_map = MapNewFlagged(G, map_cutoff, I->V, I->N, NULL, NULL); } else { ambient_occlusion_map = MapNewFlagged(G, map_cutoff, cs->Coord, cs->NIndex, NULL, present); } MapSetupExpress(ambient_occlusion_map); if (ambient_occlusion_mode==3){ /* per atom */ float *VAO = Alloc(float, cs->NIndex); short *nVAO = Alloc(short, cs->NIndex); memset(VAO, 0, sizeof(float)*cs->NIndex); memset(nVAO, 0, sizeof(short)*cs->NIndex); for(a = 0; a < I->N; a++) { int nbits = 0; short level1, level2, has; unsigned long bits = 0L, bit; float d[3], *vn0, v0mod[3]; int closeA = -1; float closeDist = MAXFLOAT; has = 0; v0 = I->V + 3 * a; vn0 = I->VN + 3 * a; mult3f(vn0, .01f, v0mod); add3f(v0, v0mod, v0mod); i = *(MapLocusEStart(ambient_occlusion_map, v0)); if(i && map->EList) { j = ambient_occlusion_map->EList[i++]; while(j >= 0) { subtract3f(cs->Coord + j * 3, v0, d); dist = (float) length3f(d); if (dist < closeDist){ closeA = j; closeDist = dist; } j = ambient_occlusion_map->EList[i++]; } } if (closeA >= 0){ if (nVAO[closeA]){ I->VAO[a] = VAO[closeA]; } else { v0 = cs->Coord + 3 * closeA; i = *(MapLocusEStart(ambient_occlusion_map, v0)); if (i){ j = ambient_occlusion_map->EList[i++]; while(j >= 0) { if (closeA==j){ j = ambient_occlusion_map->EList[i++]; continue; } subtract3f(cs->Coord + j * 3, v0, d); dist = (float) length3f(d); if (dist > 12.f){ j = ambient_occlusion_map->EList[i++]; continue; } has = 1; level1 = (d[2] < 0.f) ? 4 : 0; level1 |= (d[1] < 0.f) ? 2 : 0; level1 |= (d[0] < 0.f) ? 1 : 0; d[0] = fabs(d[0]); d[1] = fabs(d[1]); d[2] = fabs(d[2]); level2 = (d[0] <= d[1]) ? 4 : 0; level2 |= (d[1] <= d[2]) ? 2 : 0; level2 |= (d[0] <= d[2]) ? 1 : 0; bit = level1* 8 + level2; bits |= (1L << bit); j = ambient_occlusion_map->EList[i++]; } } if (has){ nbits = countBits(bits); if (nbits > level_max) level_max = nbits; if (nbits < level_min) level_min = nbits; I->VAO[a] = nbits; } else { level_min = 0; I->VAO[a] = 0.f; } VAO[closeA] = I->VAO[a]; nVAO[closeA] = 1; } } } FreeP(VAO); FreeP(nVAO); } else { float natomsL = 0; for(a = 0; a < I->N; a++) { int natoms = 0, nbits = 0; short level1, level2, has; unsigned long bits = 0L, bit; float d[3], *vn0, pt[3], v0mod[3]; if (a%1000==0){ PRINTFB(I->R.G, FB_RepSurface, FB_Debugging) "RepSurfaceColor(): Ambient Occlusion computing mode=%d #vertices=%d done=%d\n", ambient_occlusion_mode, I->N, a ENDFB(I->R.G); } v0 = I->V + 3 * a; vn0 = I->VN + 3 * a; mult3f(vn0, .01f, v0mod); add3f(v0, v0mod, v0mod); i = *(MapLocusEStart(ambient_occlusion_map, v0)); if(i && ambient_occlusion_map->EList) { j = ambient_occlusion_map->EList[i++]; maxDistA = 0.f; has = 0; while(j >= 0) { natomsL++; if (vertex_map && a==j){ j = ambient_occlusion_map->EList[i++]; continue; } if (vertex_map){ copy3f(I->V + j * 3, pt); subtract3f(I->V + j * 3, v0mod, d); } else { copy3f(cs->Coord + j * 3, pt); subtract3f(cs->Coord + j * 3, v0mod, d); } dist = (float) length3f(d); normalize3f(d); if (get_angle3f(vn0, d) >= (PI/2.f)){ j = ambient_occlusion_map->EList[i++]; continue; } if (dist <= .0001f || dist > 12.f){ j = ambient_occlusion_map->EList[i++]; continue; } has = 1; (dist > maxDistA) ? maxDistA = dist : 0; level1 = (d[2] < 0.f) ? 4 : 0; level1 |= (d[1] < 0.f) ? 2 : 0; level1 |= (d[0] < 0.f) ? 1 : 0; d[0] = fabs(d[0]); d[1] = fabs(d[1]); d[2] = fabs(d[2]); level2 = (d[0] <= d[1]) ? 4 : 0; level2 |= (d[1] <= d[2]) ? 2 : 0; level2 |= (d[0] <= d[2]) ? 1 : 0; bit = level1* 8 + level2; bits |= (1L << bit); j = ambient_occlusion_map->EList[i++]; natoms++; } if(has){ nbits = countBits(bits); if (nbits > level_max) level_max = nbits; if (nbits < level_min) level_min = nbits; (maxDistA > maxDist) ? maxDist = maxDistA : 0; I->VAO[a] = nbits; } else { level_min = 0; I->VAO[a] = 0.f; } maxDistA=0.f; } } PRINTFB(I->R.G, FB_RepSurface, FB_Debugging) "RepSurfaceColor(): #vertices=%d Ambient Occlusion average #atoms looked at per vertex = %f\n", I->N, (natomsL/I->N) ENDFB(I->R.G); } { int ambient_occlusion_smooth = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_ambient_occlusion_smooth); int surface_quality = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_quality); float min_max_diff = level_max - level_min; if (surface_quality>0) ambient_occlusion_smooth *= surface_quality; /* Now we should set VAO from min/max */ if (min_max_diff){ for(a = 0; a < I->N; a++) { I->VAO[a] = ((I->VAO[a] - level_min)/min_max_diff); } } else { for(a = 0; a < I->N; a++) { I->VAO[a] = 0.f; } } /* SMOOTH Accessibility VALUES */ if (ambient_occlusion_smooth && I->T){ int i, j, pt1, pt2, pt3; float ave; float *tmpVAO = Alloc(float, I->N); int *nVAO = Alloc(int, I->N), c, *t; for (j=0; j<ambient_occlusion_smooth; j++){ memset(nVAO, 0, sizeof(int)*I->N); memset(tmpVAO, 0, sizeof(float)*I->N); t = I->T; c = I->NT; while (c--){ if (visibility_test(I->proximity, vi, t)) { pt1 = *t; pt2 = *(t+1); pt3 = *(t+2); nVAO[pt1] += 1; nVAO[pt2] += 1; nVAO[pt3] += 1; ave = ave3(I->VAO[pt1], I->VAO[pt2], I->VAO[pt3]); tmpVAO[pt1] += ave; tmpVAO[pt2] += ave; tmpVAO[pt3] += ave; } t +=3; } for (i=0; i<I->N;i++){ if (nVAO[i]){ /* only update if added and greater than original */ // if ((tmpVAO[i]/(float)nVAO[i]) > I->VAO[i]) I->VAO[i] = tmpVAO[i]/(float)nVAO[i]; } } } FreeP(tmpVAO); FreeP(nVAO); } } MapFree(ambient_occlusion_map); cur_time = UtilGetSeconds(G); PRINTFB(I->R.G, FB_RepSurface, FB_Debugging) "RepSurfaceColor(): Ambient Occlusion computed #atoms=%d #vertices=%d time=%lf seconds\n", cs->NIndex, I->N, (cur_time-start_time) ENDFB(I->R.G); ambient_occlusion_map = NULL; } else { if (I->VAO){ VLAFreeP(I->VAO); I->VAO = 0; } } /* now, assign colors to each point */ map = MapNewFlagged(G, cutoff, cs->Coord, cs->NIndex, NULL, present); if(map) { short color_smoothing = SettingGetGlobal_i(G, cSetting_surface_color_smoothing); float color_smoothing_threshold = SettingGetGlobal_f(G, cSetting_surface_color_smoothing_threshold); int atm, ok = true; MapSetupExpress(map); ok &= !G->Interrupt; if (ok && !I->AT) I->AT = VLACalloc(int, I->N); for(a = 0; ok && a < I->N; a++) { float at_transp = transp; AtomInfoType *ai0 = NULL; float minDist = MAXFLOAT, minDist2 = MAXFLOAT, distDiff = MAXFLOAT; int pi = -1, catm = -1; /* variables for color smoothing */ AtomInfoType *pai = NULL, *pai2 = NULL; /* variables for color smoothing */ c1 = 1; i0 = -1; v0 = I->V + 3 * a; n0 = I->VN + 3 * a; vi = I->Vis + a; /* colors */ i = *(MapLocusEStart(map, v0)); if(i && map->EList) { j = map->EList[i++]; while(j >= 0) { atm = cs->IdxToAtm[j]; ai2 = obj->AtomInfo + atm; if((inclH || (!ai2->isHydrogen())) && ((!cullByFlag) || (!(ai2->flags & cAtomFlag_ignore)))) { dist = (float) diff3f(v0, cs->Coord + j * 3) - ai2->vdw; if(color_smoothing){ if (dist < minDist){ /* switching closest to 2nd closest */ pai2 = pai; minDist2 = minDist; pi = j; catm = atm; pai = ai2; minDist = dist; } else if (dist < minDist2){ /* just setting second closest */ pai2 = ai2; minDist2 = dist; } } else if (dist < minDist) { i0 = j; catm = atm; ai0 = ai2; minDist = dist; } } j = map->EList[i++]; } } I->AT[a] = catm; if (color_smoothing){ i0 = pi; ai0 = pai; /* TODO: should find point closest to v0 between atoms points (cs->Coord + pi * 3) and (cs->Coord + pi2 * 3) (including vdw), then set this distance to the distance between the vertex v0 and that point. We might want to use the normal to compute this distance. */ distDiff = fabs(minDist2-minDist); } if(i0 >= 0) { int at_surface_color = AtomSettingGetWD(G, ai0, cSetting_surface_color, surface_color); at_transp = AtomSettingGetWD(G, ai0, cSetting_transparency, transp); if(at_surface_color != -1) { c1 = at_surface_color; distDiff = MAXFLOAT; } else { c1 = ai0->color; } if(I->oneColorFlag) { if(first_color >= 0) { if(first_color != c1) I->oneColorFlag = false; } else first_color = c1; } if(I->allVisibleFlag) *vi = 1; else { ai2 = obj->AtomInfo + cs->IdxToAtm[i0]; if((ai2->visRep & cRepSurfaceBit) && (inclH || (!ai2->isHydrogen())) && ((!cullByFlag) || (!(ai2->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate))))) *vi = 1; else *vi = 0; } } else { *vi = 0; } if(carve_flag && (*vi)) { /* is point visible, and are we carving? */ *vi = 0; if(carve_map) { i = *(MapLocusEStart(carve_map, v0)); if(i && carve_map->EList) { j = carve_map->EList[i++]; while(j >= 0) { float *v_targ = carve_vla + 3 * j; if(within3f(v_targ, v0, carve_cutoff)) { if(!carve_normal_flag) { *vi = 1; break; } else { float v_to[3]; subtract3f(v_targ, v0, v_to); if(dot_product3f(v_to, n0) >= carve_normal_cutoff) { *vi = 1; break; } } } j = carve_map->EList[i++]; } } } } if(clear_flag && (*vi)) { /* is point visible, and are we clearing? */ if(clear_map) { i = *(MapLocusEStart(clear_map, v0)); if(i && clear_map->EList) { j = clear_map->EList[i++]; while(j >= 0) { if(within3f(clear_vla + 3 * j, v0, clear_cutoff)) { *vi = 0; break; } j = clear_map->EList[i++]; } } } } /* if(ColorCheckRamped(G,surface_color)) { c1 = surface_color; } */ if(ColorCheckRamped(G, c1)) { I->oneColorFlag = false; switch (ramp_above) { case 1: copy3f(n0, v_above); scale3f(v_above, probe_radius, v_above); add3f(v0, v_above, v_above); v_pos = v_above; rc[0] = -1; break; default: v_pos = v0; rc[0] = c1; ramped_flag = true; break; } ColorGetRamped(G, c1, v_pos, vc, state); vc += 3; rc++; } else { if (color_smoothing && distDiff < color_smoothing_threshold){ const float *c2; float weight, weight2; if (color_smoothing==1){ weight = 1.f + sin(.5f * PI * (distDiff / color_smoothing_threshold)); } else { weight = 1.f + (distDiff / color_smoothing_threshold); } weight2 = 2.f - weight; c0 = ColorGet(G, c1); c2 = ColorGet(G, pai2->color); *(rc++) = c1; *(vc++) = ((weight*(*(c0++))) + (weight2*(*(c2++)))) / 2.f; *(vc++) = ((weight*(*(c0++))) + (weight2*(*(c2++)))) / 2.f; *(vc++) = ((weight*(*(c0++))) + (weight2*(*(c2++)))) / 2.f; } else { c0 = ColorGet(G, c1); *(rc++) = c1; *(vc++) = *(c0++); *(vc++) = *(c0++); *(vc++) = *(c0++); } } if(at_transp != transp) variable_alpha = true; *(va++) = 1.0F - at_transp; if(!*vi) I->allVisibleFlag = false; vi++; } MapFree(map); } if(variable_alpha) I->oneColorFlag = false; if(I->oneColorFlag) { I->oneColor = first_color; } } /* if(surface_color>=0) { I->oneColorFlag=true; I->oneColor=surface_color; } */ if(G->HaveGUI) { setShaderCGO(I, NULL); #ifdef _PYMOL_IOS #endif } if(I->VA && (!variable_alpha)) { FreeP(I->VA); I->VA = NULL; } if(carve_map) MapFree(carve_map); VLAFreeP(carve_vla); if(clear_map) MapFree(clear_map); VLAFreeP(clear_vla); if((!ramped_flag) || (!SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_ray_color_ramps))) FreeP(I->RC); I->ColorInvalidated = false; FreeP(present); } typedef struct { /* input */ float *coord; SurfaceJobAtomInfo *atomInfo; float maxVdw; int allVisibleFlag; int nPresent; int *presentVla; int solventSphereIndex, sphereIndex; int surfaceType; int circumscribe; float probeRadius; float carveCutoff; float *carveVla; int surfaceMode; int surfaceSolvent; int cavityCull; float pointSep; float trimCutoff; float trimFactor; int cavityMode; float cavityRadius; float cavityCutoff; /* results */ float *V, *VN; int N, *T, *S, NT; } SurfaceJob; static void SurfaceJobPurgeResult(PyMOLGlobals * G, SurfaceJob * I) { I->N = 0; I->NT = 0; VLAFreeP(I->V); I->V = NULL; VLAFreeP(I->VN); I->VN = NULL; VLAFreeP(I->T); I->T = NULL; VLAFreeP(I->S); I->S = NULL; } #ifndef _PYMOL_NOPY OV_INLINE PyObject *SurfaceJobAtomInfoAsPyTuple(SurfaceJobAtomInfo * atom_info) { PyObject *result = NULL; if(atom_info) { ov_size size = 2 * VLAGetSize(atom_info) + 1; result = PyTuple_New(size); if(result) { ov_size i; PyTuple_SetItem(result, 0, PyInt_FromLong(2)); /* width of array */ for(i = 1; i < size; i += 2) { PyTuple_SetItem(result, i, PyFloat_FromDouble(atom_info->vdw)); PyTuple_SetItem(result, i + 1, PyInt_FromLong(atom_info->flags)); atom_info++; } } } return (PConvAutoNone(result)); } #if 0 OV_INLINE SurfaceJobAtomInfo *SurfaceJobAtomInfoVLAFromPyTuple(PyObject * tuple) { SurfaceJobAtomInfo *result = NULL; if(tuple && PyTuple_Check(tuple)) { ov_size size = PyTuple_Size(tuple); if(size) { ov_size width = PyInt_AsLong(PyTuple_GetItem(tuple, 0)); if(width == 2) { ov_size vla_size = (size - 1) / 2; result = VLAlloc(SurfaceJobAtomInfo, vla_size); if(result) { SurfaceJobAtomInfo *atom_info = result; ov_size i; for(i = 1; i < size; i += 2) { atom_info->vdw = (float) PyFloat_AsDouble(PyTuple_GetItem(tuple, i)); atom_info->flags = PyInt_AsLong(PyTuple_GetItem(tuple, i + 1)); atom_info++; } } } } } return (result); } #endif OV_INLINE PyObject *SurfaceJobInputAsTuple(PyMOLGlobals * G, SurfaceJob * I) { PyObject *result = PyTuple_New(24); if(result) { PyTuple_SetItem(result, 0, PyString_FromString("SurfaceJob")); PyTuple_SetItem(result, 1, PyInt_FromLong(1)); /* version */ PyTuple_SetItem(result, 2, PConvFloatVLAToPyTuple(I->coord)); PyTuple_SetItem(result, 3, SurfaceJobAtomInfoAsPyTuple(I->atomInfo)); PyTuple_SetItem(result, 4, PyFloat_FromDouble(I->maxVdw)); PyTuple_SetItem(result, 5, PyInt_FromLong(I->allVisibleFlag)); PyTuple_SetItem(result, 6, PyInt_FromLong(I->nPresent)); PyTuple_SetItem(result, 7, PConvIntVLAToPyTuple(I->presentVla)); PyTuple_SetItem(result, 8, PyInt_FromLong(I->solventSphereIndex)); PyTuple_SetItem(result, 9, PyInt_FromLong(I->sphereIndex)); PyTuple_SetItem(result, 10, PyInt_FromLong(I->surfaceType)); PyTuple_SetItem(result, 11, PyInt_FromLong(I->circumscribe)); PyTuple_SetItem(result, 12, PyFloat_FromDouble(I->probeRadius)); PyTuple_SetItem(result, 13, PyFloat_FromDouble(I->carveCutoff)); PyTuple_SetItem(result, 14, PConvFloatVLAToPyTuple(I->carveVla)); PyTuple_SetItem(result, 15, PyInt_FromLong(I->surfaceMode)); PyTuple_SetItem(result, 16, PyInt_FromLong(I->surfaceSolvent)); PyTuple_SetItem(result, 17, PyInt_FromLong(I->cavityCull)); PyTuple_SetItem(result, 18, PyFloat_FromDouble(I->pointSep)); PyTuple_SetItem(result, 19, PyFloat_FromDouble(I->trimCutoff)); PyTuple_SetItem(result, 20, PyFloat_FromDouble(I->trimFactor)); PyTuple_SetItem(result, 21, PyInt_FromLong(I->cavityMode)); PyTuple_SetItem(result, 22, PyFloat_FromDouble(I->cavityRadius)); PyTuple_SetItem(result, 23, PyFloat_FromDouble(I->cavityCutoff)); } return result; } OV_INLINE PyObject *SurfaceJobResultAsTuple(PyMOLGlobals * G, SurfaceJob * I) { PyObject *result = PyTuple_New(6); if(result) { PyTuple_SetItem(result, 0, PyInt_FromLong(I->N)); PyTuple_SetItem(result, 1, PConvFloatVLAToPyTuple(I->V)); PyTuple_SetItem(result, 2, PConvFloatVLAToPyTuple(I->VN)); PyTuple_SetItem(result, 3, PyInt_FromLong(I->NT)); PyTuple_SetItem(result, 4, PConvIntVLAToPyTuple(I->T)); PyTuple_SetItem(result, 5, PConvIntVLAToPyTuple(I->S)); } return result; } OV_INLINE ov_status SurfaceJobResultFromTuple(PyMOLGlobals * G, SurfaceJob * I, PyObject * tuple) { ov_status status = OV_STATUS_FAILURE; SurfaceJobPurgeResult(G, I); if(tuple && PyTuple_Check(tuple)) { ov_size size = PyTuple_Size(tuple); if(size >= 6) { status = OV_STATUS_SUCCESS; I->N = PyInt_AsLong(PyTuple_GetItem(tuple, 0)); if(OV_OK(status)) status = PConvPyTupleToFloatVLA(&I->V, PyTuple_GetItem(tuple, 1)); if(OV_OK(status)) status = PConvPyTupleToFloatVLA(&I->VN, PyTuple_GetItem(tuple, 2)); I->NT = PyInt_AsLong(PyTuple_GetItem(tuple, 3)); if(OV_OK(status)) status = PConvPyTupleToIntVLA(&I->T, PyTuple_GetItem(tuple, 4)); if(OV_OK(status)) status = PConvPyTupleToIntVLA(&I->S, PyTuple_GetItem(tuple, 5)); } if(OV_ERR(status)) SurfaceJobPurgeResult(G, I); } return status; } #endif static SurfaceJob *SurfaceJobNew(PyMOLGlobals * G) { OOCalloc(G, SurfaceJob); return I; } static void SurfaceJobFree(PyMOLGlobals * G, SurfaceJob * I) { SurfaceJobPurgeResult(G, I); VLAFreeP(I->coord); VLAFreeP(I->presentVla); VLAFreeP(I->atomInfo); VLAFreeP(I->carveVla); OOFreeP(I); } static int SurfaceJobEliminateCloseDotsType3orMore(PyMOLGlobals * G, SurfaceJob * I, int *repeat_flag, int *dot_flag) { int ok = true; int jj; float dist; float nearest; float point_sep = I->pointSep; float min_sep2 = point_sep * point_sep; float diff[3]; { int a; for(a = 0; a < I->N; a++) dot_flag[a] = 1; } { MapType *map = MapNew(G, point_sep + 0.05F, I->V, I->N, NULL); int a; float *v = I->V; float *vn = I->VN; float min_dot = 0.1F; CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); for(a = 0; ok && a < I->N; a++) { if(dot_flag[a]) { int i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; jj = I->N; nearest = point_sep + 1.0F; while(j >= 0) { if(j > a) { if(dot_flag[j]) { if(dot_product3f(I->VN + (3 * j), vn) > min_dot) { if(within3fret (I->V + (3 * j), v, point_sep, min_sep2, diff, &dist)) { *repeat_flag = true; if(dist < nearest) { /* try to be as determinstic as possible in terms of how we collapse points */ jj = j; nearest = dist; } else if((j < jj) && (fabs(dist - nearest) < R_SMALL4)) { jj = j; nearest = dist; } } } } } j = map->EList[i++]; } if(jj < I->N) { dot_flag[jj] = 0; add3f(vn, I->VN + (3 * jj), vn); average3f(I->V + (3 * jj), v, v); *repeat_flag = true; } } } v += 3; vn += 3; ok &= !G->Interrupt; } MapFree(map); } return ok; } static int SurfaceJobEliminateCloseDotsTypeLessThan3(PyMOLGlobals * G, SurfaceJob * I, int *repeat_flag, int *dot_flag) { int ok = true; int a; float point_sep = I->pointSep; MapType *map = MapNew(G, -point_sep, I->V, I->N, NULL); float *v = I->V; float *vn = I->VN; CHECKOK(ok, map); if (ok){ for(a = 0; a < I->N; a++) dot_flag[a] = 1; ok &= MapSetupExpress(map); } for(a = 0; ok && a < I->N; a++) { if(dot_flag[a]) { int i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; while(j >= 0) { if(j != a) { if(dot_flag[j]) { if(within3f(I->V + (3 * j), v, point_sep)) { dot_flag[j] = 0; add3f(vn, I->VN + (3 * j), vn); average3f(I->V + (3 * j), v, v); *repeat_flag = true; } } } j = map->EList[i++]; } } } v += 3; vn += 3; ok &= !G->Interrupt; } MapFree(map); return ok; } static void SurfaceJobEliminateFromVArrays(PyMOLGlobals * G, SurfaceJob * I, int *dot_flag, short normalize) { float *v0 = I->V; float *vn0 = I->VN; int *p = dot_flag; int c = I->N; int a; float *v = I->V; float *vn = I->VN; I->N = 0; for(a = 0; a < c; a++) { if(*(p++)) { *(v0++) = *(v++); *(v0++) = *(v++); *(v0++) = *(v++); if (normalize) normalize3f(vn); *(vn0++) = *(vn++); *(vn0++) = *(vn++); *(vn0++) = *(vn++); I->N++; } else { v += 3; vn += 3; } } } static int SurfaceJobEliminateCloseDots(PyMOLGlobals * G, SurfaceJob * I){ int ok = true; if(I->N) { int repeat_flag = true; int *dot_flag = Alloc(int, I->N); CHECKOK(ok, dot_flag); while(ok && repeat_flag) { repeat_flag = false; if(I->surfaceType >= 3) { ok = SurfaceJobEliminateCloseDotsType3orMore(G, I, &repeat_flag, dot_flag); } else { /* surface types < 3 */ ok = SurfaceJobEliminateCloseDotsTypeLessThan3(G, I, &repeat_flag, dot_flag); } if(ok) { SurfaceJobEliminateFromVArrays(G, I, dot_flag, true); } ok &= !G->Interrupt; } FreeP(dot_flag); } return ok; } /* For each vertex, lookup all vertices within the neighborhood, and sum the dot_product of the normals. If the average of the dot_products of the normals is less than the trim_cutoff, then the middle vertex is eliminated. */ static int SurfaceJobEliminateTroublesomeVerticesMark(PyMOLGlobals * G, SurfaceJob * I, int *repeat_flag, MapType *map, int *dot_flag, float neighborhood, float trim_cutoff) { int ok = true; int a; float *v = I->V; float *vn = I->VN; for(a = 0; ok && a < I->N; a++) { if(dot_flag[a]) { int i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; int n_nbr = 0; float dot_sum = 0.0F; while(j >= 0) { if(j != a) { if(dot_flag[j]) { float *v0 = I->V + 3 * j; if(within3f(v0, v, neighborhood)) { float *n0 = I->VN + 3 * j; dot_sum += dot_product3f(n0, vn); n_nbr++; } } } j = map->EList[i++]; } if(n_nbr) { dot_sum /= n_nbr; if(dot_sum < trim_cutoff) { dot_flag[a] = false; *repeat_flag = true; } } } } v += 3; vn += 3; ok &= !G->Interrupt; } return ok; } static int SurfaceJobEliminateTroublesomeVertices(PyMOLGlobals * G, SurfaceJob * I){ int ok = true; if((I->surfaceType != 3) && I->N && (I->trimCutoff > 0.0F) && (I->trimFactor > 0.0F)) { float trim_cutoff = I->trimCutoff; float trim_factor = I->trimFactor; int repeat_flag = true; float point_sep = I->pointSep; float neighborhood = trim_factor * point_sep; int *dot_flag = Alloc(int, I->N); CHECKOK(ok, dot_flag); if(ok && I->surfaceType == 6) { /* emprical tweaks */ trim_factor *= 2.5; trim_cutoff *= 1.5; } while(ok && repeat_flag) { MapType *map = MapNew(G, neighborhood, I->V, I->N, NULL); CHECKOK(ok, map); if (ok){ int a; for(a = 0; a < I->N; a++) dot_flag[a] = 1; ok &= MapSetupExpress(map); } repeat_flag = false; ok &= SurfaceJobEliminateTroublesomeVerticesMark(G, I, &repeat_flag, map, dot_flag, neighborhood, trim_cutoff); if(ok) { SurfaceJobEliminateFromVArrays(G, I, dot_flag, true); } MapFree(map); ok &= !G->Interrupt; } FreeP(dot_flag); } return ok; } static int SurfaceJobAtomProximityCleanupPass(PyMOLGlobals * G, SurfaceJob * I, int *dot_flag, int *present_vla, float probe_radius) { int ok = true; float cutoff = 0.5 * probe_radius; float *I_coord = I->coord; SurfaceJobAtomInfo *I_atom_info = I->atomInfo; int n_index = VLAGetSize(I->atomInfo); MapType *map = MapNewFlagged(G, I->maxVdw + probe_radius, I_coord, n_index, NULL, present_vla); int a; float *v; CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); v = I->V; for(a = 0; ok && a < I->N; a++) { int i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; while(j >= 0) { SurfaceJobAtomInfo *atom_info = I_atom_info + j; if((!present_vla) || present_vla[j]) { if(within3f(I_coord + 3 * j, v, atom_info->vdw + cutoff)) { dot_flag[a] = true; } } j = map->EList[i++]; } } v += 3; ok &= !G->Interrupt; } MapFree(map); return ok; } static int SurfaceJobRefineCopyNewPoints(SurfaceJob * I, float *new_dot, int n_new){ int ok = true; float *n1 = new_dot + 3; float *v1 = new_dot; float *v, *vn; VLASize(I->V, float, 3 * (I->N + n_new)); CHECKOK(ok, I->V); if (ok) VLASize(I->VN, float, 3 * (I->N + n_new)); CHECKOK(ok, I->VN); if (ok){ v = I->V + 3 * I->N; vn = I->VN + 3 * I->N; I->N += n_new; } while(ok && n_new--) { copy3f(v1, v); copy3f(n1, vn); v += 3; vn += 3; v1 += 6; n1 += 6; } return ok; } static int SurfaceJobRefineAddNewVerticesCheckPoint(SurfaceJob * I, MapType *map, int *n_new, float **new_dot, int j, float *v, float *vn, float map_cutoff, float neighborhood, float insert_cutoff) { int ok = true; float *v0 = I->V + 3 * j; if(within3f(v0, v, map_cutoff)) { int add_new = false; float *n0 = I->VN + 3 * j; VLACheck(*new_dot, float, (*n_new) * 6 + 5); CHECKOK(ok, *new_dot); if (ok){ float *v1 = (*new_dot) + (*n_new) * 6; average3f(v, v0, v1); if((dot_product3f(n0, vn) < 0.666 /* dot_cutoff, was hardcoded as a variable */ ) && (within3f(v0, v, neighborhood))){ // if the normals are further than dot_cutoff apart // and the related points are close to each other, than add new add_new = true; } else { /* if points are too far apart, insert a new one, i.e., search for any point within insert_cutoff, if not, add */ int ii = *(MapLocusEStart(map, v1)); if(ii) { int found = false; int jj = map->EList[ii++]; while(jj >= 0) { if(jj != j) { float *vv0 = I->V + 3 * jj; if(within3f(vv0, v1, insert_cutoff)) { found = true; break; } } jj = map->EList[ii++]; } if(!found) add_new = true; } } if(add_new) { /* highly divergent, add dot in-between v and v0 (averaged, v1 set above, compute the normal (averaged below)) and n_new incremented */ float *n1 = v1 + 3; (*n_new)++; average3f(vn, n0, n1); normalize3f(n1); } } } return ok; } static int SurfaceJobRefineAddNewVertices(PyMOLGlobals * G, SurfaceJob * I){ int ok = true; float point_sep = I->pointSep; int n_new = 0; float neighborhood = 2.6 * point_sep; /* these constants need more tuning... */ float insert_cutoff = 1.1 * point_sep; float map_cutoff = neighborhood; float *new_dot = VLAlloc(float, 1000); float *v, *vn; if(map_cutoff < (2.9 * point_sep)) { /* these constants need more tuning... */ map_cutoff = 2.9 * point_sep; } { MapType *map = NULL; int a; map = MapNew(G, map_cutoff, I->V, I->N, NULL); CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); v = I->V; vn = I->VN; for(a = 0; ok && a < I->N; a++) { int i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; while(ok && j >= 0) { if(j > a) { SurfaceJobRefineAddNewVerticesCheckPoint(I, map, &n_new, &new_dot, j, v, vn, map_cutoff, neighborhood, insert_cutoff); } j = map->EList[i++]; ok &= !G->Interrupt; } } v += 3; vn += 3; ok &= !G->Interrupt; } MapFree(map); } if(ok && n_new) { ok = SurfaceJobRefineCopyNewPoints(I, new_dot, n_new); } VLAFreeP(new_dot); return ok; } static void SurfaceJobCheckInteriorSolventSurface(MapType *solv_map, float *v, SolventDot *sol_dot, float probe_rad_less, float dist2, int a, int *flag){ int ii; ii = *(MapLocusEStart(solv_map, v)); if(ii && solv_map->EList) { float *i_dot = sol_dot->dot; float dist = probe_rad_less; int *elist_ii = solv_map->EList + ii; float v_0 = v[0]; int jj_next, jj = *(elist_ii++); float v_1 = v[1]; float *v1 = i_dot + 3 * jj; float v_2 = v[2]; while(jj >= 0) { /* huge bottleneck -- optimized for superscaler processors */ float dx = v1[0], dy, dz; jj_next = *(elist_ii++); dx -= v_0; if(jj != a) { dx = (dx < 0.0F) ? -dx : dx; dy = v1[1] - v_1; if(!(dx > dist)) { dy = (dy < 0.0F) ? -dy : dy; dz = v1[2] - v_2; if(!(dy > dist)) { dx = dx * dx; dz = (dz < 0.0F) ? -dz : dz; dy = dy * dy; if(!(dz > dist)) { dx = dx + dy; dz = dz * dz; if(!(dx > dist2)) if((dx + dz) <= dist2) { *flag = false; break; } } } } } v1 = i_dot + 3 * jj_next; jj = jj_next; } } } static void SurfaceJobCheckPresentAndWithin(MapType *map, SurfaceJob * I, int *present_vla, float *v, float probe_rad_more, int *flag){ SurfaceJobAtomInfo *I_atom_info = I->atomInfo; float *I_coord = I->coord; int i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; while(j >= 0) { SurfaceJobAtomInfo *atom_info = I_atom_info + j; if((!present_vla) || present_vla[j]) { if(within3f (I_coord + 3 * j, v, atom_info->vdw + probe_rad_more)) { *flag = false; break; } } j = map->EList[i++]; } } } static void SurfaceJobSetProbeRadius(int surface_type, float point_sep, float *probe_radius, float *probe_rad_more, float *probe_rad_less, float *probe_rad_less2) { float solv_tole = point_sep * 0.04F; if(*probe_radius < (2.5F * point_sep)) { /* minimum probe radius allowed */ *probe_radius = 2.5F * point_sep; } *probe_rad_more = *probe_radius * (1.0F + solv_tole); switch (surface_type) { case 0: /* solid */ case 3: case 4: case 5: case 6: *probe_rad_less = *probe_radius; break; default: *probe_rad_less = *probe_radius * (1.0F - solv_tole); break; } *probe_rad_less2 = (*probe_rad_less) * (*probe_rad_less); } static int SurfaceJobRun(PyMOLGlobals * G, SurfaceJob * I) { int ok = true; int MaxN; int n_present = I->nPresent; SphereRec *sp = G->Sphere->Sphere[I->sphereIndex]; SphereRec *ssp = G->Sphere->Sphere[I->solventSphereIndex]; SurfaceJobPurgeResult(G, I); { /* compute limiting storage requirements */ int tmp = n_present; if(tmp < 1) tmp = 1; if(sp->nDot < ssp->nDot) MaxN = tmp * ssp->nDot; else MaxN = tmp * sp->nDot; } MaxN = n_present; I->V = VLAlloc(float, (MaxN + 1) * 3); CHECKOK(ok, I->V); if (ok) I->VN = VLAlloc(float, (MaxN + 1) * 3); CHECKOK(ok, I->VN); ok &= !G->Interrupt; if(!(I->V && I->VN) || (!ok)) { /* bail out point -- try to reduce crashes */ PRINTFB(G, FB_RepSurface, FB_Errors) "Error-RepSurface: insufficient memory to calculate surface at this quality.\n" ENDFB(G); VLAFreeP(I->V); I->V = NULL; VLAFreeP(I->VN); I->VN = NULL; ok = false; } else { SolventDot *sol_dot = NULL; float *v = I->V; float *vn = I->VN; MapType *carve_map = NULL; float probe_radius = I->probeRadius; int circumscribe = I->circumscribe; int surface_type = I->surfaceType; float point_sep = I->pointSep; int *present_vla = I->presentVla; I->N = 0; sol_dot = SolventDotNew(G, I->coord, I->atomInfo, probe_radius, ssp, present_vla, circumscribe, I->surfaceMode, I->surfaceSolvent, I->cavityCull, I->allVisibleFlag, I->maxVdw, I->cavityMode, I->cavityRadius, I->cavityCutoff); CHECKOK(ok, sol_dot); ok &= !G->Interrupt; if(ok) { if(!I->surfaceSolvent) { float probe_rad_more, probe_rad_less, probe_rad_less2; SurfaceJobSetProbeRadius(surface_type, point_sep, &probe_radius, &probe_rad_more, &probe_rad_less, &probe_rad_less2); if(surface_type >= 5) { /* effectively double-weights atom points */ if(sol_dot->nDot) { int a; float *v0 = sol_dot->dot; float *n0 = sol_dot->dotNormal; for(a = 0; a < sol_dot->nDot; a++) { scale3f(n0, -probe_radius, v); add3f(v0, v, v); copy3f(n0, vn); v += 3; vn += 3; n0 += 3; v0 += 3; I->N++; } } } ok &= !G->Interrupt; if(ok) { MapType *map, *solv_map = NULL; map = MapNewFlagged(G, I->maxVdw + probe_rad_more, I->coord, VLAGetSize(I->coord) / 3, NULL, NULL); CHECKOK(ok, map); if (ok) solv_map = MapNew(G, probe_rad_less, sol_dot->dot, sol_dot->nDot, NULL); CHECKOK(ok, solv_map); if(ok) { ok &= MapSetupExpress(solv_map); if (ok) ok &= MapSetupExpress(map); ok &= !G->Interrupt; ok &= map->EList && solv_map->EList; if(sol_dot->nDot && ok) { Vector3f *dot = Alloc(Vector3f, sp->nDot); float *v0, *n0; CHECKOK(ok, dot); if (ok){ int b; for(b = 0; b < sp->nDot; b++) { scale3f(sp->dot[b], probe_radius, dot[b]); } } v0 = sol_dot->dot; n0 = sol_dot->dotNormal; if (ok) { int a, b; int sp_nDot = sp->nDot; for(a = 0; ok && a < sol_dot->nDot; a++) { if(sol_dot->dotCode[a] || (surface_type < 6)) { /* surface type 6 is completely scribed */ OrthoBusyFast(G, a + sol_dot->nDot * 2, sol_dot->nDot * 5); /* 2/5 to 3/5 */ for(b = 0; ok && b < sp_nDot; b++) { float *dot_b = dot[b]; v[0] = v0[0] + dot_b[0]; v[1] = v0[1] + dot_b[1]; v[2] = v0[2] + dot_b[2]; { int flag = true; SurfaceJobCheckInteriorSolventSurface(solv_map, v, sol_dot, probe_rad_less, probe_rad_less2, a, &flag); /* at this point, we have points on the interior of the solvent surface, so now we need to further trim that surface to cover atoms that are present */ if(flag) { SurfaceJobCheckPresentAndWithin(map, I, present_vla, v, probe_rad_more, &flag); if(!flag) { /* compute the normals */ vn[0] = -sp->dot[b][0]; vn[1] = -sp->dot[b][1]; vn[2] = -sp->dot[b][2]; I->N++; VLACheck(I->V, float, 3 * (I->N + 1)); VLACheck(I->VN, float, 3 * (I->N + 1)); CHECKOK(ok, I->V); CHECKOK(ok, I->VN); v = I->V + I->N * 3; vn = I->VN + I->N * 3; } } } ok &= !G->Interrupt; } } v0 += 3; n0 += 3; ok &= !G->Interrupt; } } FreeP(dot); } } MapFree(solv_map); MapFree(map); } } else { float *v0 = sol_dot->dot; float *n0 = sol_dot->dotNormal; int a; circumscribe = 0; if(sol_dot->nDot) { VLACheck(I->V, float, 3 * (I->N + sol_dot->nDot)); VLACheck(I->VN, float, 3 * (I->N + sol_dot->nDot)); v = I->V; vn = I->VN; for(a = 0; a < sol_dot->nDot; a++) { *(v++) = *(v0++); *(vn++) = *(n0++); *(v++) = *(v0++); *(vn++) = *(n0++); *(v++) = *(v0++); *(vn++) = *(n0++); I->N++; } } } } SolventDotFree(sol_dot); sol_dot = NULL; ok &= !G->Interrupt; if(ok) { int refine, ref_count = 1; if((surface_type == 0) && (circumscribe)) { ref_count = 2; /* these constants need more tuning... */ } for(refine = 0; ok && refine < ref_count; refine++) { /* add new vertices in regions where curvature is very high or where there are gaps with no points */ if(I->N && (surface_type == 0) && (circumscribe)) { ok = SurfaceJobRefineAddNewVertices(G, I); } if(ok && I->N && (surface_type == 0) && (circumscribe)) { /* combine scribing with an atom proximity cleanup pass */ int *dot_flag = Calloc(int, I->N); CHECKOK(ok, dot_flag); ok &= SurfaceJobAtomProximityCleanupPass(G, I, dot_flag, present_vla, probe_radius); /* purge unused dots */ if (ok) SurfaceJobEliminateFromVArrays(G, I, dot_flag, false); // not normalize? FreeP(dot_flag); } /* now, eliminate dots that are too close to each other */ ok &= SurfaceJobEliminateCloseDots(G, I); /* now eliminate troublesome vertices in regions of extremely high curvature */ ok &= SurfaceJobEliminateTroublesomeVertices(G, I); ok &= !G->Interrupt; } } if(ok && I->N && I->V && I->VN) { VLASizeForSure(I->V, float, 3 * I->N); CHECKOK(ok, I->V); if (ok) VLASizeForSure(I->VN, float, 3 * I->N); CHECKOK(ok, I->VN); } PRINTFB(G, FB_RepSurface, FB_Blather) " RepSurface: %i surface points.\n", I->N ENDFB(G); ok &= !G->Interrupt; OrthoBusyFast(G, 3, 5); if(I->N) { if(ok && surface_type != 1) { /* not a dot surface... */ float cutoff = point_sep * 5.0F; if((cutoff > probe_radius) && (!I->surfaceSolvent)) cutoff = probe_radius; I->T = TrianglePointsToSurface(G, I->V, I->VN, I->N, cutoff, &I->NT, &I->S, NULL, I->cavityMode); CHECKOK(ok, I->T); PRINTFB(G, FB_RepSurface, FB_Blather) " RepSurface: %i triangles.\n", I->NT ENDFB(G); } } else { if (ok) VLASizeForSure(I->V, float, 1); CHECKOK(ok, I->V); if (ok) VLASizeForSure(I->VN, float, 1); CHECKOK(ok, I->VN); } if(carve_map) MapFree(carve_map); } return ok; } static void RepSurfaceSetSettings(PyMOLGlobals * G, CoordSet * cs, ObjectMolecule *obj, int surface_quality, int surface_type, float *point_sep, int *sphere_idx, int *solv_sph_idx, int *circumscribe) { if(surface_quality >= 4) { /* totally impractical */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_best) / 4.f; *sphere_idx = 4; *solv_sph_idx = 4; if(*circumscribe < 0) *circumscribe = 91; } else { switch (surface_quality) { case 3: /* nearly impractical */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_best) / 3.f; *sphere_idx = 4; *solv_sph_idx = 3; if(*circumscribe < 0) *circumscribe = 71; break; case 2: /* nearly perfect */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_best) / 2.f; *sphere_idx = 3; *solv_sph_idx = 3; if(*circumscribe < 0) *circumscribe = 41; break; case 1: /* good */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_best); *sphere_idx = 2; *solv_sph_idx = 3; if((*circumscribe < 0) && (surface_type == 6)) *circumscribe = 40; break; case 0: /* 0 - normal */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_normal); *sphere_idx = 1; *solv_sph_idx = 2; if((*circumscribe < 0) && (surface_type == 6)) *circumscribe = 30; break; case -1: /* -1 */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_poor); *sphere_idx = 1; *solv_sph_idx = 2; if((*circumscribe < 0) && (surface_type == 6)) *circumscribe = 10; break; case -2: /* -2 god awful */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_poor) * 1.5F; *sphere_idx = 1; *solv_sph_idx = 1; break; case -3: /* -3 miserable */ *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_miserable); *sphere_idx = 1; *solv_sph_idx = 1; break; default: *point_sep = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_miserable) * 1.18F; *sphere_idx = 0; *solv_sph_idx = 1; } } /* Fixed problem with surface holes when surface_quality>2, it seems like circumscribe can only be used with surface_solvent */ if((*circumscribe < 0) || (!SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_surface_solvent))) *circumscribe = 0; } static int RepSurfacePrepareSurfaceJob(PyMOLGlobals * G, SurfaceJob *surf_job, RepSurface *I, CoordSet *cs, ObjectMolecule *obj, SurfaceJobAtomInfo **atom_info, float *carve_vla, int n_present, int *present_vla, int optimize, int sphere_idx, int solv_sph_idx, int surface_type, int circumscribe, float probe_radius, float point_sep, float carve_cutoff) { int ok = true; surf_job->maxVdw = I->max_vdw; surf_job->allVisibleFlag = I->allVisibleFlag; surf_job->atomInfo = *atom_info; (*atom_info) = NULL; surf_job->nPresent = n_present; if(present_vla && optimize) { /* implies that n_present < cs->NIndex, so eliminate irrelevant atoms & coordinates if we are optimizing subsets */ surf_job->coord = VLAlloc(float, n_present * 3); CHECKOK(ok, surf_job->coord); if (ok) { int *p = present_vla; SurfaceJobAtomInfo *ai_src = surf_job->atomInfo; SurfaceJobAtomInfo *ai_dst = surf_job->atomInfo; float *v_src = cs->Coord; float *v_dst = surf_job->coord; int a; for(a = 0; a < cs->NIndex; a++) { if(*(p++)) { copy3f(v_src, v_dst); v_dst += 3; if(ai_dst != ai_src) *ai_dst = *ai_src; ai_dst++; } v_src += 3; ai_src++; } } VLASize(surf_job->atomInfo, SurfaceJobAtomInfo, n_present); CHECKOK(ok, surf_job->atomInfo); } else { surf_job->presentVla = present_vla; present_vla = NULL; surf_job->coord = VLAlloc(float, cs->NIndex * 3); CHECKOK(ok, surf_job->coord); if(ok) UtilCopyMem(surf_job->coord, cs->Coord, sizeof(float) * 3 * cs->NIndex); } if (ok){ surf_job->sphereIndex = sphere_idx; surf_job->solventSphereIndex = solv_sph_idx; surf_job->surfaceType = surface_type; surf_job->circumscribe = circumscribe; surf_job->probeRadius = probe_radius; surf_job->pointSep = point_sep; surf_job->trimCutoff = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_trim_cutoff); surf_job->trimFactor = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_trim_factor); surf_job->cavityMode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_cavity_mode); surf_job->cavityRadius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_cavity_radius); surf_job->cavityCutoff = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_cavity_cutoff); if(carve_vla) surf_job->carveVla = VLACopy(carve_vla, float); surf_job->carveCutoff = carve_cutoff; surf_job->surfaceMode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_mode); surf_job->surfaceSolvent = SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_surface_solvent); surf_job->cavityCull = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cavity_cull); } return ok; } #ifndef _PYMOL_NOPY void RepSurfaceConvertSurfaceJobToPyObject(PyMOLGlobals *G, SurfaceJob *surf_job, CoordSet *cs, ObjectMolecule *obj, PyObject **entry, PyObject **input, PyObject **output, int *found){ int cache_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cache_mode); if(cache_mode > 0) { int blocked = PAutoBlock(G); *input = SurfaceJobInputAsTuple(G, surf_job); if(PCacheGet(G, output, entry, *input) == OV_STATUS_YES) { if(OV_OK(SurfaceJobResultFromTuple(G, surf_job, *output))) { *found = true; PXDecRef(*input); *input = NULL; PXDecRef(*entry); *entry = NULL; } PXDecRef(*output); *output = NULL; } if(PyErr_Occurred()) PyErr_Print(); PAutoUnblock(G, blocked); } } #endif static void RepSurfaceFindAllPresentAtoms(ObjectMolecule *obj, CoordSet *cs, int *present_vla, int inclH, int cullByFlag){ int *ap = present_vla; int *idx_to_atm = cs->IdxToAtm; AtomInfoType *obj_AtomInfo = obj->AtomInfo; int a, cs_NIndex = cs->NIndex; for(a = 0; a < cs_NIndex; a++) { AtomInfoType *ai1 = obj_AtomInfo + *(idx_to_atm++); if((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate))))) *ap = 2; else *ap = 0; ap++; } } static int RepSurfaceAddNearByAtomsIfNotSurfaced(PyMOLGlobals *G, MapType *map, ObjectMolecule *obj, CoordSet *cs, int *present_vla, int inclH, int cullByFlag, float probe_radius, int optimize) { int ok = true; float probe_radiusX2 = probe_radius * 2; int a; for(a = 0; ok && a < cs->NIndex; a++){ if(!present_vla[a]) { AtomInfoType *ai1 = obj->AtomInfo + cs->IdxToAtm[a]; if((inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || !(ai1->flags & cAtomFlag_ignore))) { float *v0 = cs->Coord + 3 * a; int i = *(MapLocusEStart(map, v0)); if(optimize) { if(i && map->EList) { int j = map->EList[i++]; while(j >= 0) { if(present_vla[j] > 1) { AtomInfoType *ai2 = obj->AtomInfo + cs->IdxToAtm[j]; if(within3f (cs->Coord + 3 * j, v0, ai1->vdw + ai2->vdw + probe_radiusX2)) { present_vla[a] = 1; break; } } j = map->EList[i++]; } } } else present_vla[a] = 1; } } ok &= !G->Interrupt; } return ok; } static int RepSurfaceRemoveAtomsNotWithinCutoff(PyMOLGlobals *G, MapType *carve_map, float *carve_vla, CoordSet *cs, int *present_vla, float carve_cutoff) { int ok = true; int a; for(a = 0; ok && a < cs->NIndex; a++) { int include_flag = false; if(carve_map) { float *v0 = cs->Coord + 3 * a; int i = *(MapLocusEStart(carve_map, v0)); if(i && carve_map->EList) { int j = carve_map->EList[i++]; while(j >= 0) { if(within3f(carve_vla + 3 * j, v0, carve_cutoff)) { include_flag = true; break; } j = carve_map->EList[i++]; } } } if(!include_flag) present_vla[a] = 0; ok &= !G->Interrupt; } return ok; } Rep *RepSurfaceNew(CoordSet * cs, int state) { int ok = true; PyMOLGlobals *G = cs->State.G; ObjectMolecule *obj = cs->Obj; OOCalloc(G, RepSurface); CHECKOK(ok, I); if (!ok) return NULL; I->pickingCGO = I->shaderCGO = 0; #ifdef _PYMOL_IOS #endif I->AT = 0; I->ColorInvalidated = false; { int surface_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_mode); int cullByFlag = (surface_mode == cRepSurface_by_flags); int inclH = !((surface_mode == cRepSurface_heavy_atoms) || (surface_mode == cRepSurface_vis_heavy_only)); int inclInvis = !((surface_mode == cRepSurface_vis_only) || (surface_mode == cRepSurface_vis_heavy_only)); int visFlag = false; if(GET_BIT(obj->RepVisCache,cRepSurface)) { int *idx_to_atm = cs->IdxToAtm; AtomInfoType *obj_AtomInfo = obj->AtomInfo; int a, cs_NIndex = cs->NIndex; for(a = 0; a < cs_NIndex; a++) { AtomInfoType *ai1 = obj_AtomInfo + *(idx_to_atm++); if((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_exfoliate | cAtomFlag_ignore))))) { visFlag = true; break; } } } if(!visFlag) { OOFreeP(I); return (NULL); /* skip if no thing visible */ } { int surface_flag = false; int surface_type = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_type); int surface_quality = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_quality); float probe_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_solvent_radius); int optimize = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_optimize_subsets); int circumscribe = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_circumscribe); int sphere_idx = 0, solv_sph_idx = 0; MapType *map = NULL; float point_sep; int *present_vla = NULL; int n_present = 0; int carve_state = 0; int carve_flag = false; float carve_cutoff; const char *carve_selection = NULL; float *carve_vla = NULL; MapType *carve_map = NULL; bool smooth_edges = SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_surface_smooth_edges); I->Type = surface_type; I->max_vdw = ObjectMoleculeGetMaxVDW(obj); RepSurfaceSetSettings(G, cs, obj, surface_quality, surface_type, &point_sep, &sphere_idx, &solv_sph_idx, &circumscribe); RepInit(G, &I->R); I->R.context.object = (void *) obj; I->R.context.state = state; I->R.fRender = (void (*)(struct Rep *, RenderInfo * info)) RepSurfaceRender; I->R.fFree = (void (*)(struct Rep *)) RepSurfaceFree; I->R.fRecolor = (void (*)(struct Rep *, struct CoordSet *)) RepSurfaceColor; I->R.fSameVis = (int (*)(struct Rep *, struct CoordSet *)) RepSurfaceSameVis; I->R.fSameColor = (int (*)(struct Rep *, struct CoordSet *)) RepSurfaceSameColor; I->R.fInvalidate = (void (*)(struct Rep *, struct CoordSet *, int)) RepSurfaceInvalidate; I->R.obj = (CObject *) (cs->Obj); I->R.cs = cs; I->allVisibleFlag = true; obj = cs->Obj; /* don't waist time computing a Surface unless we need it!! */ { int *idx_to_atm = cs->IdxToAtm; AtomInfoType *obj_AtomInfo = obj->AtomInfo; int a, cs_NIndex = cs->NIndex; for(a = 0; a < cs_NIndex; a++) { AtomInfoType *ai1 = obj_AtomInfo + *(idx_to_atm++); if((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_exfoliate | cAtomFlag_ignore))))) surface_flag = true; else { I->allVisibleFlag = false; if(surface_flag) break; } } } if(surface_flag) { SurfaceJobAtomInfo *atom_info = VLACalloc(SurfaceJobAtomInfo, cs->NIndex); CHECKOK(ok, atom_info); if(ok && atom_info) { AtomInfoType *i_ai, *obj_atom_info = obj->AtomInfo; int *idx_to_atm = cs->IdxToAtm; int n_index = cs->NIndex; SurfaceJobAtomInfo *i_atom_info = atom_info; int i; /* fill in surfacing flags into SurfaceJobAtomInfo array */ for(i = 0; i < n_index; i++) { i_ai = obj_atom_info + idx_to_atm[i]; i_atom_info->flags = i_ai->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate); i_atom_info->vdw = i_ai->vdw; i_atom_info++; } } OrthoBusyFast(G, 0, 1); if (ok){ n_present = cs->NIndex; carve_selection = SettingGet_s(G, cs->Setting, obj->Obj.Setting, cSetting_surface_carve_selection); carve_cutoff = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_surface_carve_cutoff); if((!carve_selection) || (!carve_selection[0])) carve_cutoff = 0.0F; if(carve_cutoff > 0.0F) { carve_state = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_surface_carve_state) - 1; carve_cutoff += 2 * I->max_vdw + probe_radius; if(carve_selection) carve_map = SelectorGetSpacialMapFromSeleCoord(G, SelectorIndexByName(G, carve_selection), carve_state, carve_cutoff, &carve_vla); if(carve_map) ok &= MapSetupExpress(carve_map); carve_flag = true; I->allVisibleFlag = false; } } if(ok && !I->allVisibleFlag) { /* optimize the space over which we calculate a surface */ /* first find out which atoms are actually to be surfaced */ present_vla = VLAlloc(int, cs->NIndex); CHECKOK(ok, present_vla); if (ok){ RepSurfaceFindAllPresentAtoms(obj, cs, present_vla, inclH, cullByFlag); } if (ok) map = MapNewFlagged(G, 2 * I->max_vdw + probe_radius, cs->Coord, cs->NIndex, NULL, present_vla); CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); if(ok && inclInvis) { /* then add in the nearby atoms which are not surfaced and not ignored */ ok &= RepSurfaceAddNearByAtomsIfNotSurfaced(G, map, obj, cs, present_vla, inclH, cullByFlag, probe_radius, optimize); } if(ok && carve_flag && (!optimize)) { /* and optimize for carved region */ ok &= RepSurfaceRemoveAtomsNotWithinCutoff(G, carve_map, carve_vla, cs, present_vla, carve_cutoff); } MapFree(map); map = NULL; /* now count how many atoms we actually need to think about */ n_present = 0; if (ok) { int a; for(a = 0; a < cs->NIndex; a++) { if(present_vla[a]) { n_present++; } } } } if (ok) { SurfaceJob *surf_job = SurfaceJobNew(G); CHECKOK(ok, surf_job); ok &= RepSurfacePrepareSurfaceJob(G, surf_job, I, cs, obj, &atom_info, carve_vla, n_present, present_vla, optimize, sphere_idx, solv_sph_idx, surface_type, circumscribe, probe_radius, point_sep, carve_cutoff); ok &= !G->Interrupt; if(ok) { int found = false; #ifndef _PYMOL_NOPY PyObject *entry = NULL; PyObject *output = NULL; PyObject *input = NULL; int cache_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cache_mode); RepSurfaceConvertSurfaceJobToPyObject(G, surf_job, cs, obj, &entry, &input, &output, &found); #endif if(ok && !found) { ok &= SurfaceJobRun(G, surf_job); #ifndef _PYMOL_NOPY if(cache_mode > 1) { int blocked = PAutoBlock(G); output = SurfaceJobResultAsTuple(G, surf_job); PCacheSet(G, entry, output); PXDecRef(entry); entry = NULL; PXDecRef(output); output = NULL; PXDecRef(input); input = NULL; PAutoUnblock(G, blocked); } #endif } #ifndef _PYMOL_NOPY if(entry || input || output) { int blocked = PAutoBlock(G); PXDecRef(entry); PXDecRef(input); PXDecRef(output); PAutoUnblock(G, blocked); } #endif } /* surf_job must be valid at this point */ if (ok){ I->N = surf_job->N; surf_job->N = 0; I->V = surf_job->V; surf_job->V = NULL; I->VN = surf_job->VN; surf_job->VN = NULL; I->NT = surf_job->NT; surf_job->NT = 0; I->T = surf_job->T; surf_job->T = NULL; I->S = surf_job->S; surf_job->S = NULL; } SurfaceJobPurgeResult(G, surf_job); SurfaceJobFree(G, surf_job); } VLAFreeP(atom_info); ok &= !G->Interrupt; if(ok) RepSurfaceColor(I, cs); if (ok && smooth_edges) RepSurfaceSmoothEdges(I); } if(carve_map) MapFree(carve_map); VLAFreeP(carve_vla); VLAFreeP(present_vla); OrthoBusyFast(G, 4, 4); } } if(!ok) { RepSurfaceFree(I); I = NULL; } return (Rep *) I; } void RepSurfaceSmoothEdges(RepSurface * I) { std::vector<std::vector<int>> edges(I->N); // Find all edges. // Uses the following edge structure: // edge[v0] -> { v1, t0, t1 } // where (v0, v1) defines an edge and (t0, t1) // are triangles the edge belongs to. // The edge is unique when t0 == t1. int *t = I->T; int *vi = I->Vis; for (auto i = 0; i < I->NT; i++) { if (visibility_test(I->proximity, vi, t)) { int v0 = t[0]; int v1 = t[1]; int v2 = t[2]; if (v0 < v1) { edges[v0].push_back(v1); edges[v0].push_back(i); edges[v0].push_back(i); } else { edges[v1].push_back(v0); edges[v1].push_back(i); edges[v1].push_back(i); } if (v1 < v2) { edges[v1].push_back(v2); edges[v1].push_back(i); edges[v1].push_back(i); } else { edges[v2].push_back(v1); edges[v2].push_back(i); edges[v2].push_back(i); } if (v2 < v0) { edges[v2].push_back(v0); edges[v2].push_back(i); edges[v2].push_back(i); } else { edges[v0].push_back(v2); edges[v0].push_back(i); edges[v0].push_back(i); } } t += 3; } // Assign triangles to every edge for (auto i = 0; i < I->N; i++) { for (auto j = 0; j < edges[i].size(); j += 3) { for (auto k = 0; k < j; k += 3) { if ((j != k) && (edges[i][j] == edges[i][k])) { edges[i][j + 2] = edges[i][k + 1]; edges[i][k + 2] = edges[i][j + 1]; } } } } std::vector<int> unique_edges; // Build a list of unique edges for (auto i = 0; i < I->N; i++) { for (auto j = 0; j < edges[i].size(); j += 3) { if (edges[i][j + 1] == edges[i][j + 2]) { unique_edges.push_back(i); unique_edges.push_back(edges[i][j]); } } } if (unique_edges.size() > 0) { std::vector<float> new_vertices(3 * I->N); UtilCopyMem(&new_vertices[0], I->V, sizeof(float) * 3 * I->N); // Average positions of vertices shared by consecutive unique edges for (auto i = 0; i < unique_edges.size(); i += 2) { for (auto j = 0; j < i; j += 2) { auto v0 = -1, v1 = -1, v2 = -1; if (unique_edges[i] == unique_edges[j]) { v0 = unique_edges[i]; v1 = unique_edges[i + 1]; v2 = unique_edges[j + 1]; } else if (unique_edges[i + 1] == unique_edges[j]) { v0 = unique_edges[i + 1]; v1 = unique_edges[i]; v2 = unique_edges[j + 1]; } else if (unique_edges[i] == unique_edges[j + 1]) { v0 = unique_edges[i]; v1 = unique_edges[i + 1]; v2 = unique_edges[j]; } else if (unique_edges[i + 1] == unique_edges[j + 1]) { v0 = unique_edges[i + 1]; v1 = unique_edges[i]; v2 = unique_edges[j]; } if (v0 >= 0) { for (auto k = 0; k < 3; k++) { new_vertices[3 * v0 + k] = (1. / 3.) * (I->V[3 * v0 + k] + I->V[3 * v1 + k] + I->V[3 * v2 + k]); } } } } UtilCopyMem(I->V, &new_vertices[0], sizeof(float) * 3 * I->N); } } static int SolventDotFilterOutSameXYZ(PyMOLGlobals * G, MapType *map, SurfaceJobAtomInfo * atom_info, SurfaceJobAtomInfo *a_atom_info, float *coord, int a, int *present, int *skip_flag) { int ok = true; float *v0 = coord + 3 * a; int i = *(MapLocusEStart(map, v0)); if(i && map->EList) { int j = map->EList[i++]; while(ok && j >= 0) { SurfaceJobAtomInfo *j_atom_info = atom_info + j; if(j > a) /* only check if this is atom trails */ if((!present) || present[j]) { if(j_atom_info->vdw == a_atom_info->vdw) { /* handle singularities */ float *v1 = coord + 3 * j; if((v0[0] == v1[0]) && (v0[1] == v1[1]) && (v0[2] == v1[2])){ // is this necessary? if some different atoms have exact same x/y/z *skip_flag = true; } } } j = map->EList[i++]; ok &= !G->Interrupt; } } return ok; } static int SolventDotGetDotsAroundVertexInSphere(PyMOLGlobals * G, SolventDot *I, MapType *map, SurfaceJobAtomInfo * atom_info, SurfaceJobAtomInfo *a_atom_info, float *coord, int a, int *present, SphereRec * sp, float radius, int *dotCnt, int stopDot, float *dotPtr, float *dotNormal, int *nDot) { float vdw = a_atom_info->vdw + radius; float *v0 = coord + 3 * a; int b, ok = true; float *v = dotPtr + (*nDot) * 3; float *n = NULL; Vector3f *sp_dot = sp->dot; if (dotNormal){ n = dotNormal + (*nDot) * 3; } for(b = 0; ok && b < sp->nDot; b++) { float *sp_dot_b = (float*)(sp_dot + b); int i; int flag = true; if (n){ n[0] = sp_dot_b[0]; n[1] = sp_dot_b[1]; n[2] = sp_dot_b[2]; } v[0] = v0[0] + vdw * sp_dot_b[0]; v[1] = v0[1] + vdw * sp_dot_b[1]; v[2] = v0[2] + vdw * sp_dot_b[2]; i = *(MapLocusEStart(map, v)); if(i) { int j = map->EList[i++]; while(ok && j >= 0) { SurfaceJobAtomInfo *j_atom_info = atom_info + j; if((!present) || present[j]) { if(j != a) { int skip_flag = false; if(j_atom_info->vdw == a_atom_info->vdw) { /* handle singularities */ float *v1 = coord + 3 * j; if((v0[0] == v1[0]) && (v0[1] == v1[1]) && (v0[2] == v1[2])){ skip_flag = true; } } if(!skip_flag) if(within3f(coord + 3 * j, v, j_atom_info->vdw + radius)) { flag = false; break; } } } j = map->EList[i++]; ok &= !G->Interrupt; } } if(ok && flag && (*dotCnt < stopDot)) { (*dotCnt)++; v += 3; if (n) n += 3; (*nDot)++; } } return ok; } static int SolventDotCircumscribeAroundVertex(PyMOLGlobals * G, SolventDot *I, MapType *map, float *vdw, float dist, float *v0, float *v2, int circumscribe, SurfaceJobAtomInfo * atom_info, SurfaceJobAtomInfo *a_atom_info, SurfaceJobAtomInfo *jj_atom_info, int *present, int a, int jj, float *coord, float probe_radius, int *dotCnt, int stopDot) { int ok = true; float vz[3], vx[3], vy[3], vp[3]; float tri_a = vdw[0], tri_b = vdw[2], tri_c = dist; float tri_s = (tri_a + tri_b + tri_c) * 0.5F; float area = (float) sqrt1f(tri_s * (tri_s - tri_a) * (tri_s - tri_b) * (tri_s - tri_c)); float radius = (2 * area) / dist; float adj = (float) sqrt1f(vdw[1] - radius * radius); int b; float *v = I->dot + I->nDot * 3; float *n = I->dotNormal + I->nDot * 3; subtract3f(v2, v0, vz); get_system1f3f(vz, vx, vy); copy3f(vz, vp); scale3f(vp, adj, vp); add3f(v0, vp, vp); for(b = 0; ok && b <= circumscribe; b++) { float xcos = (float) cos((b * 2 * cPI) / circumscribe); float ysin = (float) sin((b * 2 * cPI) / circumscribe); float xcosr = xcos * radius; float ysinr = ysin * radius; int flag = true, i; v[0] = vp[0] + vx[0] * xcosr + vy[0] * ysinr; v[1] = vp[1] + vx[1] * xcosr + vy[1] * ysinr; v[2] = vp[2] + vx[2] * xcosr + vy[2] * ysinr; i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; while(ok && j >= 0) { SurfaceJobAtomInfo *j_atom_info = atom_info + j; if((!present) || present[j]) if((j != a) && (j != jj)) { int skip_flag = false; if(a_atom_info->vdw == j_atom_info->vdw) { /* handle singularities */ float *v1 = coord + 3 * j; if((v0[0] == v1[0]) && (v0[1] == v1[1]) && (v0[2] == v1[2])) skip_flag = true; } if(jj_atom_info->vdw == j_atom_info->vdw) { /* handle singularities */ float *v1 = coord + 3 * j; if((v2[0] == v1[0]) && (v2[1] == v1[1]) && (v2[2] == v1[2])) skip_flag = true; } if(!skip_flag) if(within3f(coord + 3 * j, v,j_atom_info->vdw + probe_radius)) { flag = false; break; } } j = map->EList[i++]; ok &= !G->Interrupt; } } if(ok && flag && (*dotCnt < stopDot)) { float vt0[3], vt2[3]; subtract3f(v0, v, vt0); subtract3f(v2, v, vt2); normalize3f(vt0); normalize3f(vt2); add3f(vt0, vt2, n); invert3f(n); normalize3f(n); /* n[0] = vx[0] * xcos + vy[0] * ysin; n[1] = vx[1] * xcos + vy[1] * ysin; n[2] = vx[2] * xcos + vy[2] * ysin; */ I->dotCode[I->nDot] = 1; /* mark as exempt */ (*dotCnt)++; v += 3; n += 3; I->nDot++; } } return ok; } static int SolventDotMarkDotsWithinCutoff(PyMOLGlobals * G, SolventDot *I, MapType *map, float *I_dot, int nDot, float *cavityDot, int *dot_flag, float cutoff){ int ok = true, *p = dot_flag; float *v = I->dot; int a; for(a = 0; ok && a < I->nDot; a++) { int i = *(MapLocusEStart(map, v)); if(i && map->EList) { int j = map->EList[i++]; while(j >= 0) { if(within3f(cavityDot + (3 * j), v, cutoff)) { *p = true; break; } j = map->EList[i++]; } } v += 3; p++; if(G->Interrupt) { ok = false; } } return ok; } static void SolventDotSlideDotsAndInfo(PyMOLGlobals * G, SolventDot *I, int *dot_flag, int flag_value){ float *v0 = I->dot; float *n0 = I->dotNormal; int *dc0 = I->dotCode; int *p = dot_flag; int c = I->nDot; float *n = n0; float *v = v0; int *dc = dc0; int a; I->nDot = 0; for(a = 0; a < c; a++) { if(flag_value ? *(p++) : !*(p++)) { *(v0++) = *(v++); *(n0++) = *(n++); *(v0++) = *(v++); *(n0++) = *(n++); *(v0++) = *(v++); *(n0++) = *(n++); *(dc0++) = *(dc++); I->nDot++; } else { v += 3; n += 3; } } PRINTFD(G, FB_RepSurface) " SolventDotNew-DEBUG: %d->%d\n", c, I->nDot ENDFD; } static int SolventDotMarkDotsWithinProbeRadius(PyMOLGlobals * G, SolventDot *I, MapType *map, int cavity_cull, float probe_radius_plus, int *dot_flag, int *flag){ int ok = true; int *p = dot_flag; float *v = I->dot; int a; for(a = 0; ok && a < I->nDot; a++) { if(!dot_flag[a]) { int i = *(MapLocusEStart(map, v)); int cnt = 0; if(i && map->EList) { int j = map->EList[i++]; while(j >= 0) { if(j != a) { if(within3f(I->dot + (3 * j), v, probe_radius_plus)) { if(dot_flag[j]) { *p = true; (*flag) = true; break; } cnt++; if(cnt > cavity_cull) { *p = true; (*flag) = true; break; } } } j = map->EList[i++]; } } } v += 3; p++; ok &= !G->Interrupt; } ok &= !G->Interrupt; return ok; } static SolventDot *SolventDotNew(PyMOLGlobals * G, float *coord, SurfaceJobAtomInfo * atom_info, float probe_radius, SphereRec * sp, int *present, int circumscribe, int surface_mode, int surface_solvent, int cavity_cull, int all_visible_flag, float max_vdw, int cavity_mode, float cavity_radius, float cavity_cutoff) { int ok = true; int stopDot; int n_coord = VLAGetSize(atom_info); OOCalloc(G, SolventDot); CHECKOK(ok, I); /* printf("%p %p %p %f %p %p %p %d %d %d %d %d %f\n", G, coord, atom_info, probe_radius,sp, extent,present, circumscribe, surface_mode, surface_solvent, cavity_cull, all_visible_flag, max_vdw); */ stopDot = n_coord * sp->nDot + 2 * circumscribe; if (ok) I->dot = VLAlloc(float, (stopDot + 1) * 3); CHECKOK(ok, I->dot); if (ok){ I->dotNormal = VLAlloc(float, (stopDot + 1) * 3); CHECKOK(ok, I->dotNormal); } if (ok){ I->dotCode = VLACalloc(int, stopDot + 1); CHECKOK(ok, I->dotCode); } I->nDot = 0; if (ok) { int dotCnt = 0; MapType *map = MapNewFlagged(G, max_vdw + probe_radius, coord, n_coord, NULL, present); CHECKOK(ok, map); ok &= !G->Interrupt; if(map && ok) { ok &= MapSetupExpress(map); if (ok) { int a; int skip_flag; SurfaceJobAtomInfo *a_atom_info = atom_info; for(a = 0; ok && a < n_coord; a++) { OrthoBusyFast(G, a, n_coord * 5); if((!present) || (present[a])) { skip_flag = false; ok = SolventDotFilterOutSameXYZ(G, map, atom_info, a_atom_info, coord, a, present, &skip_flag); if(ok && !skip_flag) { ok = SolventDotGetDotsAroundVertexInSphere(G, I, map, atom_info, a_atom_info, coord, a, present, sp, probe_radius, &dotCnt, stopDot, I->dot, I->dotNormal, &I->nDot); } } a_atom_info++; } } /* for each pair of proximal atoms, circumscribe a circle for their intersection */ if (ok) { MapType *map2 = NULL; if(circumscribe && (!surface_solvent)){ map2 = MapNewFlagged(G, 2 * (max_vdw + probe_radius), coord, n_coord, NULL, present); CHECKOK(ok, map2); } ok &= !G->Interrupt; if(ok && map2) { int a; int skip_flag; SurfaceJobAtomInfo *a_atom_info = atom_info; ok &= MapSetupExpress(map2); for(a = 0; ok && a < n_coord; a++) { if((!present) || present[a]) { float *v0 = coord + 3 * a; skip_flag = false; ok = SolventDotFilterOutSameXYZ(G, map2, atom_info, a_atom_info, coord, a, present, &skip_flag); if(ok && !skip_flag) { int ii = *(MapLocusEStart(map2, v0)); if(ii) { int jj = map2->EList[ii++]; float vdw[3]; vdw[0] = a_atom_info->vdw + probe_radius; vdw[1] = vdw[0] * vdw[0]; while(ok && jj >= 0) { SurfaceJobAtomInfo *jj_atom_info = atom_info + jj; float dist; if(jj > a) /* only check if this is atom trails */ if((!present) || present[jj]) { float *v2 = coord + 3 * jj; vdw[2] = jj_atom_info->vdw + probe_radius; dist = (float) diff3f(v0, v2); if((dist > R_SMALL4) && (dist < (vdw[0] + vdw[2]))) { ok = SolventDotCircumscribeAroundVertex(G, I, map, vdw, dist, v0, v2, circumscribe, atom_info, a_atom_info, jj_atom_info, present, a, jj, coord, probe_radius, &dotCnt, stopDot); } } jj = map2->EList[ii++]; } } } } a_atom_info++; ok &= !G->Interrupt; } } MapFree(map2); } } MapFree(map); } if(ok && cavity_mode) { int nCavityDot = 0; int dotCnt = 0; float *cavityDot = VLAlloc(float, (stopDot + 1) * 3); CHECKOK(ok, cavityDot); if(cavity_radius<0.0F) { cavity_radius = - probe_radius * cavity_radius; } if(cavity_cutoff<0.0F) { cavity_cutoff = cavity_radius - cavity_cutoff * probe_radius; } { MapType *map = MapNewFlagged(G, max_vdw + cavity_radius, coord, n_coord, NULL, present); CHECKOK(ok, map); if(G->Interrupt) ok = false; if(ok && map) { ok &= MapSetupExpress(map); if (ok) { int a; int skip_flag; SurfaceJobAtomInfo *a_atom_info = atom_info; for(a = 0; a < n_coord; a++) { if((!present) || (present[a])) { skip_flag = false; ok = SolventDotFilterOutSameXYZ(G, map, atom_info, a_atom_info, coord, a, present, &skip_flag); if(ok && !skip_flag) { ok = SolventDotGetDotsAroundVertexInSphere(G, I, map, atom_info, a_atom_info, coord, a, present, sp, cavity_radius, &dotCnt, stopDot, cavityDot, NULL, &nCavityDot); } } a_atom_info++; } } } MapFree(map); } { int *dot_flag = Calloc(int, I->nDot); ErrChkPtr(G, dot_flag); { MapType *map = MapNew(G, cavity_cutoff, cavityDot, nCavityDot, NULL); if(map) { MapSetupExpress(map); ok = SolventDotMarkDotsWithinCutoff(G, I, map, I->dot, I->nDot, cavityDot, dot_flag, cavity_cutoff); } MapFree(map); } SolventDotSlideDotsAndInfo(G, I, dot_flag, false); FreeP(dot_flag); } VLAFreeP(cavityDot); } if(ok && (cavity_mode != 1) && (cavity_cull > 0) && (probe_radius > 0.75F) && (!surface_solvent)) { int *dot_flag = Calloc(int, I->nDot); float probe_radius_plus; probe_radius_plus = probe_radius * 1.5F; ErrChkPtr(G, dot_flag); { MapType *map = MapNew(G, probe_radius_plus, I->dot, I->nDot, NULL); if(map) { int flag = true; MapSetupExpress(map); while(ok && flag) { flag = false; ok = SolventDotMarkDotsWithinProbeRadius(G, I, map, cavity_cull, probe_radius_plus, dot_flag, &flag); } } MapFree(map); } if (ok) { SolventDotSlideDotsAndInfo(G, I, dot_flag, true); } FreeP(dot_flag); } if(!ok) { SolventDotFree(I); I = NULL; } return I; }