#include "CGOGL.h" #include "CGO.h" #include "CGOGL.h" #include "CGORenderer.h" #include "CoordSet.h" #include "Feedback.h" #include "GLVertexBuffer.h" #include "GraphicsUtil.h" #include "Scene.h" #include "SceneDef.h" #include "ShaderMgr.h" #include "Sphere.h" #include "os_gl.h" #include "os_gl_cgo.h" #define VAR_FOR_NORMAL pl #define VERTEX_NORMAL_SIZE 3 #define VAR_FOR_NORMAL_CNT_PLUS #ifdef PURE_OPENGL_ES_2 #define glVertexAttrib4ubv(loc, data) glVertexAttrib4f(loc, \ (data)[0] / 255.f, (data)[1] / 255.f, (data)[2] / 255.f, (data)[3] / 255.f); #endif constexpr unsigned VERTEX_PICKCOLOR_RGBA_SIZE = 1; // 4 unsigned bytes constexpr unsigned VERTEX_PICKCOLOR_INDEX_SIZE = 2; // index + bond constexpr unsigned VERTEX_PICKCOLOR_SIZE = VERTEX_PICKCOLOR_RGBA_SIZE + // VERTEX_PICKCOLOR_INDEX_SIZE; constexpr unsigned VERTEX_ACCESSIBILITY_SIZE = 1; /* ======== GL Rendering ======== */ static int CGO_gl_begin_WARNING_CALLED = false, CGO_gl_end_WARNING_CALLED = false, CGO_gl_vertex_WARNING_CALLED = false; const float g_ones4f[4] = {1.f, 1.f, 1.f, 1.f}; static int CGOConvertDebugMode(int debug, int modeArg) { int mode = modeArg; if (debug == 1) { switch (mode) { case GL_TRIANGLES: mode = GL_LINES; break; case GL_TRIANGLE_STRIP: mode = GL_LINE_STRIP; break; case GL_TRIANGLE_FAN: mode = GL_LINES; break; } } else { mode = GL_POINTS; } return mode; } static void CGO_gl_begin(CCGORenderer* I, CGO_op_data pc) { #ifndef PURE_OPENGL_ES_2 if (I->use_shader) { #endif if (!CGO_gl_begin_WARNING_CALLED) { PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_begin() is called but not implemented in OpenGLES\n" ENDFB(I->G); CGO_gl_begin_WARNING_CALLED = true; } #ifndef PURE_OPENGL_ES_2 } else { int mode = CGO_get_int(*pc); if (I->debug) mode = CGOConvertDebugMode(I->debug, mode); glBegin(mode); } #endif } static void CGO_gl_end(CCGORenderer* I, CGO_op_data) { #ifndef PURE_OPENGL_ES_2 if (I->use_shader) { #endif if (!CGO_gl_end_WARNING_CALLED) { PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_end() is called but not implemented in OpenGLES\n" ENDFB(I->G); CGO_gl_end_WARNING_CALLED = true; } #ifndef PURE_OPENGL_ES_2 } else { glEnd(); } #endif } static void CGO_gl_vertex(CCGORenderer* I, CGO_op_data v) { #ifndef PURE_OPENGL_ES_2 if (I->use_shader) { #endif if (!CGO_gl_vertex_WARNING_CALLED) { PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_vertex() is called but not implemented in OpenGLES\n" ENDFB( I->G); CGO_gl_vertex_WARNING_CALLED = true; } #ifndef PURE_OPENGL_ES_2 } else { glVertex3fv(*v); } #endif } static void CGO_gl_vertex_cross(CCGORenderer* I, CGO_op_data v) { #ifndef PURE_OPENGL_ES_2 if (I->use_shader) { #endif if (!CGO_gl_vertex_WARNING_CALLED) { PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_vertex() is called but not implemented in OpenGLES\n" ENDFB( I->G); CGO_gl_vertex_WARNING_CALLED = true; } #ifndef PURE_OPENGL_ES_2 } else { CSetting *set1 = nullptr, *set2 = nullptr; if (I->rep && I->rep->cs) set1 = I->rep->cs->Setting.get(); if (I->rep && I->rep->obj) set2 = I->rep->obj->Setting.get(); float nonbonded_size = SettingGet_f(I->G, set1, set2, cSetting_nonbonded_size); float pt[3]; copy3f(*v, pt); pt[0] -= nonbonded_size; glVertex3fv(pt); pt[0] += 2 * nonbonded_size; glVertex3fv(pt); copy3f(*v, pt); pt[1] -= nonbonded_size; glVertex3fv(pt); pt[1] += 2 * nonbonded_size; glVertex3fv(pt); copy3f(*v, pt); pt[2] -= nonbonded_size; glVertex3fv(pt); pt[2] += 2 * nonbonded_size; glVertex3fv(pt); } #endif } static void CGO_gl_line(CCGORenderer* I, CGO_op_data v) { #ifndef PURE_OPENGL_ES_2 if (!I->use_shader) { auto line = reinterpret_cast(*v); glVertex3fv(line->vertex1); glVertex3fv(line->vertex2); } #endif } static void CGO_gl_splitline(CCGORenderer* I, CGO_op_data v) { #ifndef PURE_OPENGL_ES_2 if (!I->use_shader) { auto splitline = reinterpret_cast(*v); bool interpolation = splitline->flags & cgo::draw::splitline::interpolation; bool equal_colors = splitline->flags & cgo::draw::splitline::equal_colors; bool no_split_for_pick = splitline->flags & cgo::draw::splitline::no_split_for_pick; if (I->isPicking) { if (no_split_for_pick) { glVertex3fv(splitline->vertex1); glVertex3fv(splitline->vertex2); } else { float h[3]; average3f(splitline->vertex1, splitline->vertex2, h); glVertex3fv(splitline->vertex1); glVertex3fv(h); unsigned char col[4]; AssignNewPickColor(nullptr, I->info->pick, col, &I->rep->context, splitline->index, splitline->bond); glColor4ubv(col); glVertex3fv(h); glVertex3fv(splitline->vertex2); } } else if (interpolation || equal_colors) { glVertex3fv(splitline->vertex1); if (!equal_colors) glColor4ub(splitline->color2[0], splitline->color2[1], splitline->color2[2], CLIP_COLOR_VALUE(I->alpha)); glVertex3fv(splitline->vertex2); } else { float h[3]; average3f(splitline->vertex1, splitline->vertex2, h); glVertex3fv(splitline->vertex1); glVertex3fv(h); glColor4ub(splitline->color2[0], splitline->color2[1], splitline->color2[2], CLIP_COLOR_VALUE(I->alpha)); glVertex3fv(h); glVertex3fv(splitline->vertex2); } } #endif } static void CGO_gl_normal(CCGORenderer* I, CGO_op_data varg) { const float* v = *varg; #ifdef PURE_OPENGL_ES_2 if (I->use_shader) { glVertexAttrib3fv(VERTEX_NORMAL, v); } else { #else { #endif #ifndef PURE_OPENGL_ES_2 glNormal3f(v[0], v[1], v[2]); #endif } } static void CGO_gl_draw_arrays(CCGORenderer* I, CGO_op_data pc) { auto sp = reinterpret_cast(*pc); int mode = sp->mode, arrays = sp->arraybits, narrays = sp->narrays, nverts = sp->nverts; const float* data = sp->floatdata; (void) narrays; #ifndef PURE_OPENGL_ES_2 if (I->use_shader) { #endif #ifdef _WEBGL uint buffers[3] = {0, 0, 0}; glGenBuffers(3, buffers); #endif if (arrays & CGO_VERTEX_ARRAY) glEnableVertexAttribArray(VERTEX_POS); if (arrays & CGO_NORMAL_ARRAY) glEnableVertexAttribArray(VERTEX_NORMAL); if (I->isPicking) { if (arrays & CGO_PICK_COLOR_ARRAY) { glEnableVertexAttribArray(VERTEX_COLOR); } } else { if (arrays & CGO_COLOR_ARRAY) glEnableVertexAttribArray(VERTEX_COLOR); } if (arrays & CGO_VERTEX_ARRAY) { #ifdef _WEBGL glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glBufferData( GL_ARRAY_BUFFER, sizeof(float) * nverts * 3, data, GL_STATIC_DRAW); glVertexAttribPointer( VERTEX_POS, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, 0, 0); #else glVertexAttribPointer( VERTEX_POS, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, 0, data); #endif data += nverts * VERTEX_POS_SIZE; } if (arrays & CGO_NORMAL_ARRAY) { #ifdef _WEBGL glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); glBufferData( GL_ARRAY_BUFFER, sizeof(float) * nverts * 3, data, GL_STATIC_DRAW); glVertexAttribPointer( VERTEX_NORMAL, VERTEX_NORMAL_SIZE, GL_FLOAT, GL_FALSE, 0, 0); #else glVertexAttribPointer( VERTEX_NORMAL, VERTEX_NORMAL_SIZE, GL_FLOAT, GL_FALSE, 0, data); #endif data += nverts * VERTEX_NORMAL_SIZE; } if (I->isPicking) { if (arrays & CGO_COLOR_ARRAY) { data += nverts * VERTEX_COLOR_SIZE; } if (arrays & CGO_PICK_COLOR_ARRAY) { #ifdef _WEBGL glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); glBufferData(GL_ARRAY_BUFFER, nverts * 4, data, GL_STATIC_DRAW); glVertexAttribPointer( VERTEX_COLOR, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0); #else glVertexAttribPointer( VERTEX_COLOR, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_FALSE, 0, data); #endif data += nverts * VERTEX_PICKCOLOR_SIZE; } } else { if (arrays & CGO_COLOR_ARRAY) { #ifdef _WEBGL glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); glBufferData( GL_ARRAY_BUFFER, sizeof(float) * nverts * 4, data, GL_STATIC_DRAW); glVertexAttribPointer( VERTEX_COLOR, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, 0, 0); #else glVertexAttribPointer( VERTEX_COLOR, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, 0, data); #endif data += nverts * VERTEX_COLOR_SIZE; } if (arrays & CGO_PICK_COLOR_ARRAY) { data += nverts * VERTEX_PICKCOLOR_SIZE; } } if (I->debug) { mode = CGOConvertDebugMode(I->debug, mode); } glDrawArrays(mode, 0, nverts); if (I->isPicking) { if (arrays & CGO_PICK_COLOR_ARRAY) { glDisableVertexAttribArray(VERTEX_COLOR); } } else { if (arrays & CGO_COLOR_ARRAY) glDisableVertexAttribArray(VERTEX_COLOR); } if (arrays & CGO_VERTEX_ARRAY) glDisableVertexAttribArray(VERTEX_POS); if (arrays & CGO_NORMAL_ARRAY) glDisableVertexAttribArray(VERTEX_NORMAL); #ifdef _WEBGL glDeleteBuffers(3, buffers); #endif #ifndef PURE_OPENGL_ES_2 } else { int pl, pla, plc; const float* vertexVals = nullptr; const float *colorVals = 0, *normalVals = 0, *tmp_ptr; const uchar *pickColorVals = 0, *tmp_pc_ptr; float alpha = I->alpha; if (arrays & CGO_VERTEX_ARRAY) { vertexVals = data; data += nverts * VERTEX_POS_SIZE; } if (arrays & CGO_NORMAL_ARRAY) { normalVals = data; data += nverts * VERTEX_NORMAL_SIZE; } if (I->isPicking) { alpha = 1.f; if (arrays & CGO_COLOR_ARRAY) { data += nverts * VERTEX_COLOR_SIZE; } if (arrays & CGO_PICK_COLOR_ARRAY) { pickColorVals = (uchar*) data; data += nverts * VERTEX_PICKCOLOR_SIZE; } } else { if (arrays & CGO_COLOR_ARRAY) { colorVals = data; data += nverts * 4; } if (arrays & CGO_PICK_COLOR_ARRAY) { data += nverts * VERTEX_PICKCOLOR_SIZE; } } if (arrays & CGO_ACCESSIBILITY_ARRAY) { data += nverts * VERTEX_ACCESSIBILITY_SIZE; } if (I->debug) { mode = CGOConvertDebugMode(I->debug, mode); } glBegin(mode); for (pl = 0, pla = 0, plc = 0; pl < nverts; pl++, pla += 3, plc += 4) { if (pickColorVals) { tmp_pc_ptr = &pickColorVals[plc]; /* the pick colors are saved with rgba */ glColor4ub(tmp_pc_ptr[0], tmp_pc_ptr[1], tmp_pc_ptr[2], tmp_pc_ptr[3]); } else { if (colorVals) { tmp_ptr = &colorVals[plc]; glColor4f(tmp_ptr[0], tmp_ptr[1], tmp_ptr[2], alpha); } if (normalVals) { tmp_ptr = &normalVals[pla]; glNormal3fv(&normalVals[pla]); } } if (vertexVals) { tmp_ptr = &vertexVals[pla]; glVertex3fv(&vertexVals[pla]); } } glEnd(); } #endif } /* TransparentInfoSortIX - This function sorts all n_tri triangle * centroids in the array sum by: * 1) computing z-value in array z_value * 2) bin sorting z_values and placing indices in ix array (using Util.cpp) * * - uses sort_mem as pre-allocated memory to sort * - t_mode - either forward (1) or backwards (0) sort */ void TransparentInfoSortIX(PyMOLGlobals* G, float* sum, float* z_value, int* ix, int n_tri, int* sort_mem, int t_mode) { float* zv; float* sv; float matrix[16]; int idx; #ifdef PURE_OPENGL_ES_2 copy44f(SceneGetModelViewMatrixPtr(G), matrix); #else glGetFloatv(GL_MODELVIEW_MATRIX, matrix); #endif zv = z_value; sv = sum; /* for each triangle, computes the z */ for (idx = 0; idx < n_tri; ++idx) { *(zv++) = matrix[2] * sv[0] + matrix[6] * sv[1] + matrix[10] * sv[2]; sv += 3; } UtilZeroMem(sort_mem, sizeof(int) * (n_tri + 256)); switch (t_mode) { case 1: UtilSemiSortFloatIndexWithNBinsImpl( sort_mem, n_tri, 256, z_value, ix, true); // front to back /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZOrderFn); */ break; default: UtilSemiSortFloatIndexWithNBinsImpl( sort_mem, n_tri, 256, z_value, ix, false); // back to front /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZRevOrderFn); */ break; } } /** * CGOReorderIndicesWithTransparentInfo : This function * takes the triangle index array ix (result from TransparentInfoSortIX) * and sets the vertices (vertexIndices) for each triangle from the original * indices (vertexIndicesOriginal), then uses glBufferData to set the * GL_ELEMENT_ARRAY_BUFFER to these indices. * */ static void CGOReorderIndicesWithTransparentInfo(PyMOLGlobals* G, int nindices, size_t vbuf, int n_tri, int* ix, VertexIndex_t* vertexIndicesOriginal, VertexIndex_t* vertexIndices) { int c, pl, idx; auto* ibo = G->ShaderMgr->getGPUBuffer(vbuf); if (!vertexIndices) { PRINTFB(G, FB_RepSurface, FB_Errors) "ERROR: RepSurfaceRender() vertexIndices is not set, nindices=%d\n", nindices ENDFB(G); } /* updates the vertexIndices from the ix array */ for (c = 0, pl = 0; c < n_tri; c++) { idx = ix[c] * 3; vertexIndices[pl++] = vertexIndicesOriginal[idx]; vertexIndices[pl++] = vertexIndicesOriginal[idx + 1]; vertexIndices[pl++] = vertexIndicesOriginal[idx + 2]; } ibo->copyFrom(pymol::span{vertexIndices, static_cast(nindices)}); } static void CGO_gl_draw_buffers_indexed(CCGORenderer* I, CGO_op_data pc) { auto sp = reinterpret_cast(*pc); int mode = sp->mode, nindices = sp->nindices, nverts = sp->nverts, n_data = sp->n_data; size_t vboid = sp->vboid, iboid = sp->iboid; auto* vbo = I->G->ShaderMgr->getGPUBuffer(vboid); auto* ibo = I->G->ShaderMgr->getGPUBuffer(iboid); CheckGLErrorOK(I->G, "beginning of CGO_gl_draw_buffers_indexed err=%d\n"); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } if (I->isPicking) { int attr_a_Color = shaderPrg->GetAttribLocation("a_Color"); vbo->maskAttributes({attr_a_Color}); shaderPrg->Set1i("fog_enabled", 0); shaderPrg->Set1i("lighting_enabled", 0); if (I->use_shader) { if (sp->pickvboid) { auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); pickvbo->bind(shaderPrg->id, I->pick_pass()); } else { glEnableVertexAttribArray(attr_a_Color); glVertexAttribPointer(attr_a_Color, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_TRUE, 0, sp->floatdata); } } } if (n_data) { // if transparency data, then sort it int n_tri = nindices / 3; float* sum = sp->floatdata + nverts * 3; float* z_value = sum + (nindices * 3); int* ix = (int*) (z_value + n_tri); int* sort_mem = ix + n_tri; int t_mode; CSetting *set1 = nullptr, *set2 = nullptr; if (I->rep && I->rep->cs) set1 = I->rep->cs->Setting.get(); if (I->rep && I->rep->obj) set2 = I->rep->obj->Setting.get(); t_mode = SettingGet_i(I->G, set1, set2, cSetting_transparency_mode); if (t_mode != 3) { auto vertexIndicesOriginalTI = (VertexIndex_t*) (sort_mem + n_tri + 256); auto vertexIndicesTI = vertexIndicesOriginalTI + nindices; TransparentInfoSortIX(I->G, sum, z_value, ix, n_tri, sort_mem, t_mode); CGOReorderIndicesWithTransparentInfo(I->G, nindices, iboid, n_tri, ix, vertexIndicesOriginalTI, vertexIndicesTI); } } if (I->debug) { mode = CGOConvertDebugMode(I->debug, mode); } vbo->bind(shaderPrg->id); ibo->bind(); CheckGLErrorOK( I->G, "CGO_gl_draw_buffers_indexed: before glDrawElements err=%d\n"); glDrawElements(mode, nindices, VertexIndex_GL_ENUM, 0); CheckGLErrorOK( I->G, "CGO_gl_draw_buffers_indexed: after glDrawElements err=%d\n"); vbo->unbind(); ibo->unbind(); if (I->isPicking) { auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); if (pickvbo) pickvbo->unbind(); } CheckGLErrorOK(I->G, "CGO_gl_draw_buffers_indexed: end err=%d\n"); } static void CGO_gl_draw_buffers_not_indexed(CCGORenderer* I, CGO_op_data pc) { const cgo::draw::buffers_not_indexed* sp = reinterpret_cast(*pc); int mode = sp->mode; auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); if (!vbo) return; if (I->isPicking) { int attr_a_Color = shaderPrg->GetAttribLocation("a_Color"); vbo->maskAttributes({attr_a_Color}); shaderPrg->Set1i("fog_enabled", 0); shaderPrg->Set1i("lighting_enabled", 0); if (I->use_shader) { if (sp->pickvboid) { auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); pickvbo->bind(shaderPrg->id, I->pick_pass()); } else { glEnableVertexAttribArray(attr_a_Color); glVertexAttribPointer(attr_a_Color, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_TRUE, 0, sp->floatdata); } } } if (I->debug) { mode = CGOConvertDebugMode(I->debug, mode); } vbo->bind(shaderPrg->id); glDrawArrays(mode, 0, sp->nverts); vbo->unbind(); if (I->isPicking) { auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); if (pickvbo) pickvbo->unbind(); } } static void CGO_gl_mask_attribute_if_picking(CCGORenderer* I, CGO_op_data pc) { if (I->isPicking) { const cgo::draw::mask_attribute_if_picking* sp = reinterpret_cast(*pc); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); if (!vbo) return; int loc = shaderPrg->GetAttribLocation( I->G->ShaderMgr->GetAttributeName(sp->attr_lookup_idx)); vbo->maskAttribute(loc); } } static void CGO_gl_bind_vbo_for_picking(CCGORenderer* I, CGO_op_data pc) { if (I->isPicking) { const cgo::draw::bind_vbo_for_picking* sp = reinterpret_cast(*pc); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); if (!vbo) return; vbo->bind( shaderPrg->id, sp->which_attr_idx + sp->npickattrs * I->pick_pass()); } } static void CGO_gl_draw_custom(CCGORenderer* I, CGO_op_data pc) { const cgo::draw::custom* sp = reinterpret_cast(*pc); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); if (!vbo) return; IndexBufferGL* ibo = nullptr; if (sp->iboid) { ibo = I->G->ShaderMgr->getGPUBuffer(sp->iboid); } vbo->bind(shaderPrg->id); if (ibo) { ibo->bind(); glDrawElements(sp->mode, sp->nindices, VertexIndex_GL_ENUM, 0); } else { glDrawArrays(sp->mode, 0, sp->nverts); } vbo->unbind(); if (sp->pickvboid) { auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); if (pickvbo) pickvbo->unbind(); } if (ibo) ibo->unbind(); } static void CGO_gl_draw_sphere_buffers(CCGORenderer* I, CGO_op_data pc) { const cgo::draw::sphere_buffers* sp = reinterpret_cast(*pc); int num_spheres = sp->num_spheres; int attr_color; auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); CShaderPrg* shaderPrg; int pickable = 0; shaderPrg = I->G->ShaderMgr->Get_DefaultSphereShader( I->info ? I->info->pass : RenderPass::Antialias); if (!shaderPrg) { return; } attr_color = shaderPrg->GetAttribLocation("a_Color"); if (I->isPicking) { vbo->maskAttributes({attr_color}); pickable = SettingGet_i(I->G, I->set1, I->set2, cSetting_pickable); shaderPrg->Set1i("lighting_enabled", 0); if (pickable) { pickvbo->bind(shaderPrg->id, I->pick_pass()); } else { assert(I->info->pick); unsigned char nopick[4] = {}; I->info->pick->colorNoPick(nopick); glVertexAttrib4ubv(attr_color, nopick); } } vbo->bind(shaderPrg->id); #if defined(PURE_OPENGL_ES_2) glDrawArrays(GL_TRIANGLES, 0, num_spheres * VerticesPerSphere()); #else glDrawArrays(GL_QUADS, 0, num_spheres * 4); #endif vbo->unbind(); } static void CGO_gl_draw_bezier_buffers(CCGORenderer* I, CGO_op_data cgo_data) { const auto bezier = reinterpret_cast(*cgo_data); const auto vbo = I->G->ShaderMgr->getGPUBuffer(bezier->vboid); auto shaderPrg = I->G->ShaderMgr->Get_BezierShader(); if (!shaderPrg) { return; } vbo->bind(shaderPrg->id); glDrawArrays(GL_PATCHES, 0, 4); vbo->unbind(); } static void CGO_gl_draw_cylinder_buffers(CCGORenderer* I, CGO_op_data pc) { const cgo::draw::cylinder_buffers* sp = reinterpret_cast(*pc); int num_cyl = sp->num_cyl; int min_alpha = sp->alpha; int attr_colors, attr_colors2; CShaderPrg* shaderPrg; int pickable = 0; auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); auto* ibo = I->G->ShaderMgr->getGPUBuffer(sp->iboid); auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); shaderPrg = I->G->ShaderMgr->Get_CylinderShader( I->info ? I->info->pass : RenderPass::Antialias); if (!shaderPrg) { return; } attr_colors = shaderPrg->GetAttribLocation("a_Color"); attr_colors2 = shaderPrg->GetAttribLocation("a_Color2"); if (I->isPicking) { pickable = SettingGet_i(I->G, I->set1, I->set2, cSetting_pickable); shaderPrg->Set1i("lighting_enabled", 0); } if (I->isPicking) { vbo->maskAttributes({attr_colors, attr_colors2}); if (pickable) { // in first pass: 1st half of vbo, in second pass: 2nd half of vbo // first color (offset 0) pickvbo->bind(shaderPrg->id, I->pick_pass()); // second color (offset 4) pickvbo->bind(shaderPrg->id, I->pick_pass() + SHADER_PICKING_PASSES_MAX); } else { assert(I->info->pick); unsigned char nopick[4] = {}; I->info->pick->colorNoPick(nopick); glVertexAttrib4ubv(attr_colors, nopick); glVertexAttrib4ubv(attr_colors2, nopick); } } vbo->bind(shaderPrg->id); ibo->bind(); if (min_alpha < 255) { glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDrawElements(GL_TRIANGLES, num_cyl * NumTotalVerticesPerCylinder(), VertexIndex_GL_ENUM, 0); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthFunc(GL_LEQUAL); } glDrawElements(GL_TRIANGLES, num_cyl * NumTotalVerticesPerCylinder(), VertexIndex_GL_ENUM, 0); if (min_alpha < 255) { glDepthFunc(GL_LESS); } ibo->unbind(); vbo->unbind(); if (I->isPicking) pickvbo->unbind(); } #include "Texture.h" static void CGO_gl_draw_labels(CCGORenderer* I, CGO_op_data pc) { const cgo::draw::labels* sp = reinterpret_cast(*pc); CShaderPrg* shaderPrg; int t_mode = SettingGetGlobal_i(I->G, cSetting_transparency_mode); if (t_mode == 3 && I->info && I->info->pass != RenderPass::Transparent) { // in transparency_mode=3, labels are drawn in the transparency pass=-1 return; } shaderPrg = I->G->ShaderMgr->Get_LabelShader( I->info ? I->info->pass : RenderPass::Antialias); if (I->rep) { float label_size; CSetting *set1 = nullptr, *set2 = nullptr; if (I->rep->cs) set1 = I->rep->cs->Setting.get(); if (I->rep->obj) set2 = I->rep->obj->Setting.get(); label_size = SettingGet_f(I->G, set1, set2, cSetting_label_size); shaderPrg->Set1f("scaleByVertexScale", label_size < 0.f ? 1.f : 0.f); if (label_size < 0.f) { shaderPrg->Set1f("labelTextureSize", (float) -2.f * I->info->texture_font_size / label_size); } } if (!shaderPrg) { return; } auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); auto* pickvbo = I->G->ShaderMgr->getGPUBuffer(sp->pickvboid); if (I->isPicking) { pickvbo->bind(shaderPrg->id, I->pick_pass()); } if (!vbo) return; vbo->bind(shaderPrg->id); glDrawArrays(GL_TRIANGLES, 0, sp->ntextures * 6); vbo->unbind(); pickvbo->unbind(); } static void CGO_gl_draw_connectors(CCGORenderer* I, CGO_op_data pc) { int use_geometry_shaders = SettingGetGlobal_b(I->G, cSetting_use_geometry_shaders); const cgo::draw::connectors* sp = reinterpret_cast(*pc); GLenum mode = GL_LINES; int factor = 2; float lineWidth; if (I->isPicking) { return; } CheckGLErrorOK(I->G, "ERROR: CGO_gl_draw_connectors begin returns err=%d\n"); if (use_geometry_shaders) { mode = GL_POINTS; factor = 1; } else { factor = 4; } auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } if (I->rep) { float label_size; CSetting *set1 = nullptr, *set2 = nullptr; float v_scale = SceneGetScreenVertexScale(I->G, nullptr); if (I->rep->cs) set1 = I->rep->cs->Setting.get(); if (I->rep->obj) set2 = I->rep->obj->Setting.get(); label_size = SettingGet_f(I->G, set1, set2, cSetting_label_size); shaderPrg->Set1f("scaleByVertexScale", label_size < 0.f ? 1.f : 0.f); lineWidth = SettingGet_f(I->G, set1, set2, cSetting_label_connector_width); if (label_size < 0.f) { shaderPrg->Set1f("textureToLabelSize", v_scale * (float) I->info->texture_font_size / label_size); } else { shaderPrg->Set1f("textureToLabelSize", 1.f); } } else { lineWidth = SettingGetGlobal_f(I->G, cSetting_label_connector_width); } #ifndef _WEBGL if (!use_geometry_shaders) glLineWidth(lineWidth); #endif auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); if (!vbo) return; vbo->bind(shaderPrg->id); glDrawArrays(mode, 0, sp->nconnectors * factor); vbo->unbind(); CheckGLErrorOK(I->G, "ERROR: CGO_gl_draw_connectors end returns err=%d\n"); } static void CGO_gl_draw_textures(CCGORenderer* I, CGO_op_data pc) { const cgo::draw::textures* sp = reinterpret_cast(*pc); int ntextures = sp->ntextures; auto* vbo = I->G->ShaderMgr->getGPUBuffer(sp->vboid); CShaderPrg* shaderPrg; int attr_pickcolor = 0; shaderPrg = I->G->ShaderMgr->Get_LabelShader( I->info ? I->info->pass : RenderPass::Antialias); if (!shaderPrg) { return; } if (I->isPicking) { attr_pickcolor = shaderPrg->GetAttribLocation("attr_pickcolor"); } if (attr_pickcolor) { glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(attr_pickcolor); glVertexAttribPointer(attr_pickcolor, VERTEX_COLOR_SIZE, GL_UNSIGNED_BYTE, GL_TRUE, 0, sp->floatdata); } vbo->bind(shaderPrg->id); glDrawArrays(GL_TRIANGLES, 0, ntextures * 6); vbo->unbind(); if (attr_pickcolor) { glDisableVertexAttribArray(attr_pickcolor); } } static void CGO_gl_draw_screen_textures_and_polygons( CCGORenderer* I, CGO_op_data pc) { const cgo::draw::screen_textures* sp = reinterpret_cast(*pc); int nverts = sp->nverts; CShaderPrg* shaderPrg; shaderPrg = I->G->ShaderMgr->Get_ScreenShader(); if (!shaderPrg) { return; } auto* vb = I->G->ShaderMgr->getGPUBuffer(sp->vboid); if (!vb) return; vb->bind(shaderPrg->id); glDrawArrays(GL_TRIANGLES, 0, nverts); vb->unbind(); } static void CGO_gl_draw_trilines(CCGORenderer* I, CGO_op_data pc) { int nverts = CGO_get_int(*pc); int buffer = CGO_get_int(*pc + 1); int a_vertex, a_othervertex, a_uv, a_color, a_color2; auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } a_vertex = 0; // a_Vertex is bound to 0 (see ShaderMgr) // CShaderPrg_GetAttribLocation(shaderPrg, "a_Vertex"); a_othervertex = shaderPrg->GetAttribLocation("a_OtherVertex"); a_uv = shaderPrg->GetAttribLocation("a_UV"); a_color = shaderPrg->GetAttribLocation("a_Color"); a_color2 = shaderPrg->GetAttribLocation("a_Color2"); glEnableVertexAttribArray(a_vertex); glEnableVertexAttribArray(a_othervertex); glEnableVertexAttribArray(a_uv); glEnableVertexAttribArray(a_color); glEnableVertexAttribArray(a_color2); glBindBuffer(GL_ARRAY_BUFFER, buffer); glVertexAttribPointer(a_vertex, 3, GL_FLOAT, GL_FALSE, 32, (const void*) 0); glVertexAttribPointer( a_othervertex, 3, GL_FLOAT, GL_FALSE, 32, (const void*) 12); glVertexAttribPointer(a_uv, 1, GL_FLOAT, GL_FALSE, 32, (const void*) 24); glVertexAttribPointer( a_color, 4, GL_UNSIGNED_BYTE, GL_TRUE, 32, (const void*) 28); glVertexAttribPointer( a_color2, 4, GL_UNSIGNED_BYTE, GL_TRUE, 32, (const void*) 28); glDrawArrays(GL_TRIANGLES, 0, nverts); glDisableVertexAttribArray(a_vertex); glDisableVertexAttribArray(a_othervertex); glDisableVertexAttribArray(a_uv); glDisableVertexAttribArray(a_color); glDisableVertexAttribArray(a_color2); } /* CGO_gl_uniform3f - this is the implementation for the * CGOUniform3f/CGO_UNIFORM3F operation. From the uniform_id * it looks up the uniform location from the current shader, * and sets it to the values in this op. * */ static void CGO_gl_uniform3f(CCGORenderer* I, CGO_op_data pc) { int uniform_id = CGO_get_int(*pc); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (!shaderPrg) { return; } int loc = shaderPrg->GetUniformLocation( shaderPrg->uniformLocations[uniform_id].c_str()); const float* pcp = *pc + 1; glUniform3f(loc, pcp[0], pcp[1], pcp[2]); } static void CGO_gl_linewidth(CCGORenderer* I, CGO_op_data pc) { #ifndef _WEBGL glLineWidth(**pc); #endif } /** * call glLineWidth and set the "line_width" uniform */ static void glLineWidthAndUniform( float line_width, CShaderPrg* shaderPrg = nullptr) { #ifndef _WEBGL glLineWidth(line_width); #endif if (shaderPrg && shaderPrg->name == "trilines") shaderPrg->Set1f("line_width", line_width); } /* CGO_gl_special - this is the implementation function for CGOSpecial/CGO_SPECIAL. Each op has its own implementation. */ static void CGO_gl_special(CCGORenderer* I, CGO_op_data pc) { int mode = CGO_get_int(*pc); bool openVR = SceneGetStereo(I->G) == cStereo_openvr; char varwidth = 0; float vScale = (I->info ? I->info->vertex_scale : SceneGetScreenVertexScale(I->G, nullptr)); CSetting *csSetting = nullptr, *objSetting = nullptr; auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (I->rep && I->rep->cs) { csSetting = I->rep->cs->Setting.get(); } if (I->rep && I->rep->obj) { objSetting = I->rep->obj->Setting.get(); } switch (mode) { case LINEWIDTH_DYNAMIC_WITH_SCALE_RIBBON: { float line_width = SettingGet_f(I->G, nullptr, nullptr, cSetting_ribbon_width); if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width); if (I->info && I->info->width_scale_flag) { line_width *= I->info->width_scale; } glLineWidthAndUniform(line_width, shaderPrg); } break; case LINEWIDTH_DYNAMIC_WITH_SCALE_DASH: { float line_width = SettingGet_f(I->G, nullptr, nullptr, cSetting_dash_width); if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width); if (I->info && I->info->width_scale_flag) { line_width *= I->info->width_scale; } glLineWidthAndUniform(line_width, shaderPrg); } break; case LINEWIDTH_DYNAMIC_WITH_SCALE: { float line_width = SettingGet_f(I->G, nullptr, nullptr, cSetting_line_width); if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width); if (I->info && I->info->width_scale_flag) { line_width *= I->info->width_scale; } glLineWidthAndUniform(line_width, shaderPrg); } break; case LINEWIDTH_WITH_SCALE: { float line_width = SettingGet_f(I->G, nullptr, nullptr, cSetting_line_width); if (I->info && I->info->width_scale_flag) { line_width *= I->info->width_scale; } glLineWidthAndUniform(line_width, shaderPrg); } break; case LINEWIDTH_DYNAMIC_MESH: { float line_width; if (I->rep) { line_width = SettingGet_f(I->G, I->rep->cs->Setting.get(), I->rep->obj->Setting.get(), cSetting_mesh_width); } else { line_width = SettingGet_f(I->G, nullptr, nullptr, cSetting_mesh_width); } if (!openVR) line_width = SceneGetDynamicLineWidth(I->info, line_width); glLineWidthAndUniform(line_width, shaderPrg); } break; case POINTSIZE_DYNAMIC_DOT_WIDTH: { float ps; if (I->info && I->info->width_scale_flag) { ps = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width) * I->info->width_scale; } else { ps = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width); } #ifdef PURE_OPENGL_ES_2 if (I->G->ShaderMgr->current_shader) { shaderPrg->Set1f("g_pointSize", ps); } #else glPointSize(ps); #endif break; } case CYLINDERWIDTH_DYNAMIC_MESH: { CSetting* setting = nullptr; float mesh_width; if (I && I->rep && I->rep->obj) { setting = I->rep->obj->Setting.get(); } mesh_width = SettingGet_f(I->G, setting, nullptr, cSetting_mesh_width); if (shaderPrg) { const float* color = I->color ? I->color : g_ones4f; shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylinders(I->G, I->info, mesh_width)); shaderPrg->SetAttrib4fLocation( "a_Color", color[0], color[1], color[2], I->alpha); shaderPrg->SetAttrib4fLocation( "a_Color2", color[0], color[1], color[2], I->alpha); } } break; case DOTSIZE_WITH_SPHERESCALE: { float radius = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width); radius *= vScale; if (shaderPrg) shaderPrg->Set1f("sphere_size_scale", fabs(radius)); } break; case MESH_WIDTH_FOR_SURFACES: { float mesh_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_mesh_width); if (shaderPrg) shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylinders(I->G, I->info, mesh_width)); } break; case CYLINDER_WIDTH_FOR_DISTANCES: { float line_width, radius; int round_ends; round_ends = SettingGet_b(I->G, csSetting, objSetting, cSetting_dash_round_ends); line_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_dash_width); radius = SettingGet_f(I->G, csSetting, objSetting, cSetting_dash_radius); line_width = SceneGetDynamicLineWidth(I->info, line_width); if (shaderPrg) { if (radius == 0.0F) { float dash_size = SettingGet_f(I->G, csSetting, objSetting, cSetting_dash_width); shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylindersStatic(I->G, I->info, line_width, dash_size)); } else { shaderPrg->Set1f("uni_radius", radius); } if (!round_ends) { shaderPrg->Set1i("no_flat_caps", 0); } } } break; case CYLINDER_WIDTH_FOR_RIBBONS: { float pixel_scale_value = SettingGetGlobal_f(I->G, cSetting_ray_pixel_scale); float line_width, radius; line_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_ribbon_width); radius = SettingGet_f(I->G, csSetting, objSetting, cSetting_ribbon_radius); line_width = SceneGetDynamicLineWidth(I->info, line_width); if (pixel_scale_value < 0) pixel_scale_value = 1.0F; if (shaderPrg) { if (radius == 0.0F) { shaderPrg->Set1f( "uni_radius", vScale * pixel_scale_value * line_width / 2.f); } else { shaderPrg->Set1f("uni_radius", radius); } } } break; case DOT_WIDTH_FOR_DOTS: { float dot_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width); float radius; if (I->info && I->info->width_scale_flag) radius = (dot_width * I->info->width_scale); else radius = dot_width; if (shaderPrg) shaderPrg->Set1f("g_PointSize", radius); glPointSize(radius); } break; case DOT_WIDTH_FOR_DOT_SPHERES: { float dotSize = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_radius); float dot_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_dot_width); float radius; if (I->info && dotSize <= 0.0F) { if (I->info->width_scale_flag) radius = dot_width * I->info->width_scale * I->info->vertex_scale / 1.4142F; else radius = dot_width * I->info->vertex_scale; } else { radius = dotSize; } if (shaderPrg) shaderPrg->Set1f("sphere_size_scale", fabs(radius)); } break; case CYLINDER_WIDTH_FOR_NONBONDED: { if (shaderPrg) { float line_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_line_width); shaderPrg->Set1f("uni_radius", SceneGetLineWidthForCylindersStatic(I->G, I->info, line_width, line_width)); } } break; case CYLINDER_WIDTH_FOR_REPWIRE_VARWIDTH: varwidth = 1; case CYLINDER_WIDTH_FOR_REPWIRE: { float radius = SettingGet_f(I->G, csSetting, objSetting, cSetting_line_radius); if (radius < R_SMALL8) { float line_width = SettingGet_f(I->G, csSetting, objSetting, cSetting_line_width); float pixel_scale_value = SettingGetGlobal_f(I->G, cSetting_ray_pixel_scale); float vertex_scale = vScale; float scale_bound = SettingGetGlobal_f(I->G, cSetting_field_of_view) * cPI / 180.0f * 0.018f; if (!varwidth) { line_width = SceneGetDynamicLineWidth(I->info, line_width); } if (vertex_scale < scale_bound) { vertex_scale = scale_bound; } if (pixel_scale_value < 0) pixel_scale_value = 1.0F; radius = vertex_scale * pixel_scale_value * line_width / 2.f; } if (shaderPrg) { shaderPrg->Set1f("uni_radius", radius); } } break; case ENABLE_BACK_FACES_IF_NOT_TWO_SIDED: { int two_sided_lighting = SettingGet_i(I->G, csSetting, objSetting, cSetting_two_sided_lighting) > 0; if (!two_sided_lighting) { glCullFace(GL_BACK); glEnable(GL_CULL_FACE); } } break; case DISABLE_BACK_FACES_IF_NOT_TWO_SIDED: { int two_sided_lighting = SettingGet_i(I->G, csSetting, objSetting, cSetting_two_sided_lighting) > 0; if (!two_sided_lighting) { glDisable(GL_CULL_FACE); } } break; case SET_SURFACE_UNIFORMS: { float ambient_occlusion_scale = 0.f; int ambient_occlusion_mode = SettingGet_i( I->G, csSetting, objSetting, cSetting_ambient_occlusion_mode); if (ambient_occlusion_mode) { ambient_occlusion_scale = SettingGet_f( I->G, csSetting, objSetting, cSetting_ambient_occlusion_scale); } if (shaderPrg) shaderPrg->Set1f("ambient_occlusion_scale", ambient_occlusion_scale); } break; case SET_ALIGNMENT_UNIFORMS_ATTRIBS: { float linewidth = SettingGet_f(I->G, csSetting, objSetting, cSetting_cgo_line_width); float lineradius = SettingGet_f(I->G, csSetting, objSetting, cSetting_cgo_line_radius); float pixel_scale_value = SettingGetGlobal_f(I->G, cSetting_ray_pixel_scale); if (linewidth < 0.f) { linewidth = 1.f; } if (pixel_scale_value < 0) pixel_scale_value = 1.0F; if (lineradius < 0.f) { lineradius = linewidth * vScale * pixel_scale_value / 2.f; } shaderPrg->Set1f("uni_radius", lineradius); if (I->color) { shaderPrg->SetAttrib4fLocation( "a_Color", I->color[0], I->color[1], I->color[2], 1.f); shaderPrg->SetAttrib4fLocation( "a_Color2", I->color[0], I->color[1], I->color[2], 1.f); } glLineWidthAndUniform(lineradius * 2.f / vScale, shaderPrg); } break; case LINEWIDTH_FOR_LINES: { float line_width = SceneGetDynamicLineWidth( I->info, SettingGet_f(I->G, nullptr, nullptr, cSetting_line_width)); if (I->info && I->info->width_scale_flag) { line_width *= I->info->width_scale; } glLineWidthAndUniform(line_width, shaderPrg); } break; case SET_LABEL_SCALE_UNIFORMS: { if (I->rep) { float label_size; CSetting *set1 = nullptr, *set2 = nullptr; if (I->rep->cs) set1 = I->rep->cs->Setting.get(); if (I->rep->obj) set2 = I->rep->obj->Setting.get(); label_size = SettingGet_f(I->G, set1, set2, cSetting_label_size); shaderPrg->Set1f("scaleByVertexScale", label_size < 0.f ? 1.f : 0.f); if (label_size < 0.f) { shaderPrg->Set1f("labelTextureSize", (float) -2.f * I->info->texture_font_size / label_size); } } } break; default: PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_special(): bad mode=%d\n", mode ENDFB(I->G); } } /* CGO_gl_special_with_arg - this is the implementation function for CGOSpecialWithArg/CGO_SPECIAL_WITH_ARG. Each op has its own implementation. */ static void CGO_gl_special_with_arg(CCGORenderer* I, CGO_op_data pc) { #ifndef PURE_OPENGL_ES_2 int mode = CGO_get_int(*pc); float argval = *((*pc) + 1); bool use_shaders = SettingGetGlobal_b(I->G, cSetting_use_shaders); bool sphere_use_shaders = use_shaders && SettingGetGlobal_b(I->G, cSetting_use_shaders); switch (mode) { case LINEWIDTH_FOR_LINES: { if (!use_shaders) { glEnd(); glLineWidth(argval); glBegin(GL_LINES); } } break; case LINE_LIGHTING: if (!I->isPicking && !SettingGetGlobal_b(I->G, cSetting_use_shaders)) { if (!I->info->line_lighting) { bool enableLighting = (int) argval; if (enableLighting) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING); } } break; case SPHERE_MODE_OPS: { float pixel_scale = 1.0F / I->info->vertex_scale; int sphere_mode = (int) fabs(argval); bool enable = argval > 0.f; if (enable) { float pointSize; if ((sphere_mode == 1) || (sphere_mode == 6)) { pointSize = SettingGet_f(I->G, I->set1, I->set2, cSetting_sphere_point_size); glDisable(GL_POINT_SMOOTH); glDisable(GL_ALPHA_TEST); if (!I->isPicking && !sphere_use_shaders) { glEnable(GL_LIGHTING); glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST); } } else { float sphere_scale = SettingGet_f(I->G, I->set1, I->set2, cSetting_sphere_scale); if ((sphere_mode == 3) || (sphere_mode == 8)) { glEnable(GL_POINT_SMOOTH); glAlphaFunc(GL_GREATER, 0.5F); glEnable(GL_ALPHA_TEST); glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); pointSize = sphere_scale * pixel_scale * 2.0F; } else { glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST); glDisable(GL_POINT_SMOOTH); glDisable(GL_ALPHA_TEST); pointSize = sphere_scale * pixel_scale * 1.4F; } } if (!I->isPicking && ((sphere_mode == 7) || (sphere_mode == 8))) glEnable(GL_LIGHTING); glPointSize(pointSize); } else { if (sphere_mode == 3) { glDisable(GL_POINT_SMOOTH); glAlphaFunc(GL_GREATER, 0.05F); } else { glEnable(GL_ALPHA_TEST); } } } } #endif } static void CGO_gl_dotwidth(CCGORenderer* I, CGO_op_data pc) { #ifndef PURE_OPENGL_ES_2 glPointSize(**pc); #endif } static void CGO_gl_enable(CCGORenderer* I, CGO_op_data pc) { GLenum mode = CGO_get_int(*pc); CShaderMgr* shaderMgr = I->G->ShaderMgr; CShaderPrg* shaderPrg = shaderMgr->Get_Current_Shader(); if (I->use_shader) { if (true) { switch (mode) { case CGO_GL_LIGHTING: { if (shaderPrg) { shaderPrg->SetLightingEnabled(1); } } break; case GL_SHADER_LIGHTING: if (!I->isPicking) { if (shaderPrg) { shaderPrg->SetLightingEnabled(1); } } break; case GL_TWO_SIDED_LIGHTING: { if (shaderPrg) { shaderPrg->Set1i("two_sided_lighting_enabled", 1); } } break; case GL_MESH_LIGHTING: { int lighting = SettingGet_i(I->G, I->set1, I->set2, cSetting_mesh_lighting); if (shaderPrg) { shaderPrg->SetLightingEnabled(lighting); } } break; case GL_DOT_LIGHTING: { int lighting = SettingGet_i(I->G, I->set1, I->set2, cSetting_dot_lighting); if (shaderPrg && !I->isPicking) { shaderPrg->SetLightingEnabled(lighting); shaderPrg->Set1i("two_sided_lighting_enabled", 0); } } break; case GL_LABEL_FLOAT_TEXT: { int float_text = SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels); if (float_text) { glDisable(GL_DEPTH_TEST); } } break; case GL_DASH_TRANSPARENCY_DEPTH_TEST: { float dash_transparency = SettingGet_f(I->G, I->set1, I->set2, cSetting_dash_transparency); short dash_transparency_enabled; bool t_mode_3 = SettingGet_i(I->G, I->set1, I->set2, cSetting_transparency_mode) == 3; dash_transparency = (dash_transparency < 0.f ? 0.f : (dash_transparency > 1.f ? 1.f : dash_transparency)); dash_transparency_enabled = (dash_transparency > 0.f); if (dash_transparency_enabled && !t_mode_3 && !I->isPicking) { glDisable(GL_DEPTH_TEST); } } break; case GL_DEFAULT_SHADER: shaderMgr->Enable_DefaultShader( I->info ? I->info->pass : RenderPass::Antialias); break; case GL_LINE_SHADER: shaderMgr->Enable_LineShader( I->info ? I->info->pass : RenderPass::Antialias); break; case GL_SURFACE_SHADER: shaderMgr->Enable_SurfaceShader( I->info ? I->info->pass : RenderPass::Antialias); break; case GL_CYLINDER_SHADER: shaderMgr->Enable_CylinderShader( I->info ? I->info->pass : RenderPass::Antialias); break; case GL_SPHERE_SHADER: shaderMgr->Enable_DefaultSphereShader( I->info ? I->info->pass : RenderPass::Antialias); break; case GL_RAMP_SHADER: shaderMgr->Enable_RampShader(); break; case GL_DEFAULT_SHADER_WITH_SETTINGS: shaderMgr->Enable_DefaultShaderWithSettings( I->set1, I->set2, I->info ? I->info->pass : RenderPass::Antialias); break; case GL_BACKGROUND_SHADER: shaderMgr->Enable_BackgroundShader(); break; case GL_LABEL_SHADER: shaderMgr->Enable_LabelShader( I->info ? I->info->pass : RenderPass::Antialias); break; case GL_CONNECTOR_SHADER: shaderMgr->Enable_ConnectorShader( I->info ? I->info->pass : RenderPass::Antialias); break; case GL_SCREEN_SHADER: shaderMgr->Enable_ScreenShader(); break; case GL_TRILINES_SHADER: shaderMgr->Enable_TriLinesShader(); break; #ifndef _PYMOL_NO_AA_SHADERS case GL_FXAA_SHADER: shaderMgr->Enable_FXAAShader(); break; case GL_SMAA1_SHADER: shaderMgr->Enable_SMAA1Shader(); break; case GL_SMAA2_SHADER: shaderMgr->Enable_SMAA2Shader(); break; case GL_SMAA3_SHADER: shaderMgr->Enable_SMAA3Shader(); break; #endif case GL_OIT_SHADER: shaderMgr->Enable_OITShader(); break; case GL_OIT_COPY_SHADER: shaderMgr->Enable_OITCopyShader(); break; case GL_BACK_FACE_CULLING: glCullFace(GL_BACK); glEnable(GL_CULL_FACE); break; case GL_DEPTH_TEST: glEnable(mode); break; case GL_DEPTH_TEST_IF_FLOATING: { int float_text = SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels); if (float_text) glEnable(GL_DEPTH_TEST); } break; case GL_BEZIER_SHADER: shaderMgr->Enable_BezierShader(); break; } } } else { #ifndef PURE_OPENGL_ES_2 if (!I->isPicking) { if (mode == CGO_GL_LIGHTING) { glEnable(GL_LIGHTING); } } #endif } } static void CGO_gl_disable(CCGORenderer* I, CGO_op_data pc) { GLenum mode = CGO_get_int(*pc); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (I->use_shader) { switch (mode) { case GL_SHADER_LIGHTING: { if (shaderPrg) { shaderPrg->SetLightingEnabled(0); } } break; case GL_CYLINDER_SHADER: glDisable(GL_CULL_FACE); case GL_RAMP_SHADER: case GL_SCREEN_SHADER: case GL_LABEL_SHADER: case GL_CONNECTOR_SHADER: case GL_DEFAULT_SHADER: case GL_SURFACE_SHADER: case GL_SPHERE_SHADER: case GL_TRILINES_SHADER: case GL_OIT_COPY_SHADER: case GL_LINE_SHADER: I->G->ShaderMgr->Disable_Current_Shader(); break; case GL_LABEL_FLOAT_TEXT: { int float_text = SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels); if (float_text) { glEnable(GL_DEPTH_TEST); } } break; case GL_DASH_TRANSPARENCY_DEPTH_TEST: { float dash_transparency = SettingGet_f(I->G, I->set1, I->set2, cSetting_dash_transparency); short dash_transparency_enabled; bool t_mode_3 = SettingGet_i(I->G, I->set1, I->set2, cSetting_transparency_mode) == 3; dash_transparency = (dash_transparency < 0.f ? 0.f : (dash_transparency > 1.f ? 1.f : dash_transparency)); dash_transparency_enabled = (dash_transparency > 0.f); if (dash_transparency_enabled && !t_mode_3 && !I->isPicking) { glEnable(GL_DEPTH_TEST); } } break; case CGO_GL_LIGHTING: { if (shaderPrg) { shaderPrg->SetLightingEnabled(0); } } break; case GL_TWO_SIDED_LIGHTING: { if (shaderPrg) { shaderPrg->Set1i("two_sided_lighting_enabled", 0); } } break; #if !defined(PURE_OPENGL_ES_2) || defined(_WEBGL) case GL_OIT_SHADER: case GL_SMAA1_SHADER: case GL_SMAA2_SHADER: I->G->ShaderMgr->setDrawBuffer(I->G->ShaderMgr->topLevelConfig); break; #endif case GL_BACK_FACE_CULLING: glDisable(GL_CULL_FACE); break; case GL_DEPTH_TEST: glDisable(mode); break; case GL_DEPTH_TEST_IF_FLOATING: { int float_text = SettingGet_i(I->G, I->set1, I->set2, cSetting_float_labels); if (float_text) glDisable(GL_DEPTH_TEST); } break; } #ifndef PURE_OPENGL_ES_2 } else { if (mode != CGO_GL_LIGHTING || !I->isPicking) { if (mode == CGO_GL_LIGHTING) mode = GL_LIGHTING; glDisable(mode); } } #else } #endif } static void CGO_gl_alpha(CCGORenderer* I, CGO_op_data pc) { I->alpha = **pc; } static void CGO_gl_reset_normal(CCGORenderer* I, CGO_op_data pc) { SceneResetNormalUseShader(I->G, CGO_get_int(*pc), I->use_shader); } static void CGO_gl_null(CCGORenderer* I, CGO_op_data pc) {} static void CGO_gl_error(CCGORenderer* I, CGO_op_data pc) { PRINTFB(I->G, FB_CGO, FB_Warnings) " CGO_gl_error() is not suppose to be called op=%d\n", CGO_get_int((*pc) - 1) ENDFB(I->G); } static void CGO_gl_color(CCGORenderer* I, CGO_op_data varg) { auto* v = *varg; if (I->use_shader) { auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (shaderPrg) { int attr_a_Color = shaderPrg->GetAttribLocation("a_Color"); glVertexAttrib4f(attr_a_Color, v[0], v[1], v[2], I->alpha); } } else { #ifndef PURE_OPENGL_ES_2 glColor4f(v[0], v[1], v[2], I->alpha); #endif } } static void CGO_gl_sphere(CCGORenderer* I, CGO_op_data varg) { auto* v = *varg; if (I->isPicking) { SphereRender(I->G, 0, v, I->color, I->alpha, v[3]); } else { SphereRender(I->G, I->sphere_quality, v, nullptr, I->alpha, v[3]); } } static void CGO_gl_vertex_attribute_3f(CCGORenderer* I, CGO_op_data varg) { auto vertex_attr = reinterpret_cast(*varg); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); int loc = shaderPrg->GetAttribLocation( I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx)); if (loc >= 0) glVertexAttrib3fv(loc, vertex_attr->values); } static void CGO_gl_vertex_attribute_4ub(CCGORenderer* I, CGO_op_data varg) { auto vertex_attr = reinterpret_cast(*varg); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); int loc = shaderPrg->GetAttribLocation( I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx)); if (loc >= 0) glVertexAttrib4ubv(loc, vertex_attr->ubdata); } static void CGO_gl_vertex_attribute_4ub_if_picking( CCGORenderer* I, CGO_op_data varg) { if (I->isPicking) { auto vertex_attr = reinterpret_cast( *varg); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); int loc = shaderPrg->GetAttribLocation( I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx)); if (loc >= 0) glVertexAttrib4ubv(loc, vertex_attr->ubdata); } } static void CGO_gl_vertex_attribute_1f(CCGORenderer* I, CGO_op_data varg) { auto vertex_attr = reinterpret_cast(*varg); auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); const char* name = I->G->ShaderMgr->GetAttributeName(vertex_attr->attr_lookup_idx); int loc = shaderPrg->GetAttribLocation(name); if (loc >= 0) glVertexAttrib1f(loc, vertex_attr->value); } /* dispatch table for OpenGL */ CGO_op_fn CGO_gl[] = {CGO_gl_null, /* 0x00 */ CGO_gl_null, /* 0x01 */ CGO_gl_begin, /* 0x02 */ CGO_gl_end, /* 0x03 */ CGO_gl_vertex, /* 0x04 */ CGO_gl_normal, /* 0x05 */ CGO_gl_color, /* 0x06 */ CGO_gl_sphere, /* 0x07 */ CGO_gl_null, /* 0x08 */ CGO_gl_null, /* 0x09 */ CGO_gl_linewidth, /* 0x0A */ CGO_gl_null, /* 0x0B */ CGO_gl_enable, /* 0x0C */ CGO_gl_disable, /* 0x0D */ CGO_gl_null, /* 0x0E */ CGO_gl_null, /* 0x0F */ CGO_gl_dotwidth, /* 0X10 */ CGO_gl_null, /* 0x11 */ CGO_gl_null, /* 0x12 */ CGO_gl_null, /* 0X13 */ CGO_gl_null, /* 0X14 */ CGO_gl_null, /* 0x15 */ CGO_gl_null, /* 0x16 */ CGO_gl_null, /* 0X17 */ CGO_gl_null, /* 0X18 */ CGO_gl_alpha, /* 0x19 */ CGO_gl_null, /* 0x1A */ CGO_gl_null, /* 0X1B */ CGO_gl_draw_arrays, /* 0x1C DrawArrays() */ CGO_gl_null, /* 0x1D */ CGO_gl_reset_normal, /* 0x1E */ CGO_gl_null, /* pick color 0X1F */ CGO_gl_null, /* 0x20 draw buffers REMOVED */ CGO_gl_draw_buffers_indexed, /* 0x21 draw buffers indexed */ CGO_gl_null, /* 0x22 bounding box */ CGO_gl_draw_buffers_not_indexed, /* 0x23 draw buffers not indexed */ CGO_gl_special, /* 0x24 special */ CGO_gl_draw_cylinder_buffers, /* 0x25 draw GLSL cylinders */ CGO_gl_null, /* 0x26 shader cylinder */ CGO_gl_null, /* 0x27 shader cylinder with 2nd color */ CGO_gl_draw_sphere_buffers, /* 0x28 draw sphere buffers */ CGO_gl_null, /* 0x29 accessibility used for ambient occlusion */ CGO_gl_error, /* 0x2A draw texture */ CGO_gl_draw_textures, /* 0x2B draw textures */ CGO_gl_draw_screen_textures_and_polygons, /* 0x2C draw screen textures and polygons */ CGO_gl_error, CGO_gl_error, CGO_gl_draw_labels, CGO_gl_error, CGO_gl_draw_connectors, CGO_gl_draw_trilines, CGO_gl_uniform3f, CGO_gl_special_with_arg, CGO_gl_line, CGO_gl_splitline, CGO_gl_draw_custom, CGO_gl_vertex_attribute_3f, CGO_gl_vertex_attribute_4ub, CGO_gl_vertex_attribute_1f, CGO_gl_mask_attribute_if_picking, CGO_gl_bind_vbo_for_picking, CGO_gl_vertex, CGO_gl_null, // interpolated CGO_gl_vertex_cross, // CGO_VERTEX_CROSS CGO_gl_vertex_attribute_4ub_if_picking, CGO_gl_null, // custom cylinder alpha CGO_gl_null, // bezier CGO_gl_draw_bezier_buffers, CGO_gl_error}; #if 0 static void SetUCColorToPrev(uchar *color){ color[0] = color[-4]; color[1] = color[-3]; color[2] = color[-2]; color[3] = color[-1]; } static void SetUCColorToPrev8(uchar *color){ color[0] = color[-8]; color[1] = color[-7]; color[2] = color[-6]; color[3] = color[-5]; } #endif static void SetUCColorToPrevN(int n, uchar* color) { color[0] = color[-n * 4]; color[1] = color[-n * 4 + 1]; color[2] = color[-n * 4 + 2]; color[3] = color[-n * 4 + 3]; } static int* get_pickcolorsset_ptr(int op, float* pc) { #define RETURN_PICKCOLORSETPTR_CASE(cls) \ case cgo::draw::cls::op_code: \ return &(reinterpret_cast(pc)->pickcolorsset) switch (op) { RETURN_PICKCOLORSETPTR_CASE(buffers_indexed); RETURN_PICKCOLORSETPTR_CASE(buffers_not_indexed); RETURN_PICKCOLORSETPTR_CASE(labels); RETURN_PICKCOLORSETPTR_CASE(sphere_buffers); RETURN_PICKCOLORSETPTR_CASE(cylinder_buffers); RETURN_PICKCOLORSETPTR_CASE(custom); } return nullptr; } void CGORenderGLPicking(CGO* I, RenderInfo* info, PickContext* context, CSetting* set1, CSetting* set2, Rep* rep) { PyMOLGlobals* G = I->G; if (!G->ValidContext) return; if (!I->c) return; CCGORenderer* R = G->CGORenderer; bool pickable = (!I->no_pick) && SettingGet_b(G, set1, set2, cSetting_pickable); auto pick = info->pick; bool reset_colors = !pick->pickColorsValid(); R->use_shader = I->use_shader; R->isPicking = true; R->set1 = set1; R->set2 = set2; R->info = info; R->rep = rep; #ifndef _WEBGL glLineWidth(SettingGet_f(G, set1, set2, cSetting_cgo_line_width)); #endif for (auto it = I->begin(); !it.is_stop(); ++it) { const auto op = it.op_code(); CGO_OP_DATA_CONST float* pc = it.data(); switch (op) { case CGO_COLOR: continue; case CGO_PICK_COLOR: if (reset_colors) { // only if picking info is invalid unsigned char col[4]; AssignNewPickColor(I, pick, col, context, CGO_get_uint(pc), pickable ? CGO_get_int(pc + 1) : cPickableNoPick); #ifndef PURE_OPENGL_ES_2 if (!I->use_shader) { glColor4ubv(col); } #endif } else { PRINTFB(G, FB_CGO, FB_Warnings) " %s: unexpected CGO_PICK_COLOR with !reset_colors\n", __func__ ENDFB(G); } continue; case CGO_DRAW_ARRAYS: { const cgo::draw::arrays* sp = reinterpret_cast(pc); int arrays = sp->arraybits; if (reset_colors && arrays & CGO_PICK_COLOR_ARRAY) { // only if picking info is invalid int nverts = sp->nverts, v, idx = -1, bnd = -1; float* pca = sp->floatdata; if (arrays & CGO_VERTEX_ARRAY) { pca += nverts * VERTEX_POS_SIZE; } if (arrays & CGO_NORMAL_ARRAY) { pca += nverts * VERTEX_NORMAL_SIZE; } if (arrays & CGO_COLOR_ARRAY) { pca += nverts * VERTEX_COLOR_SIZE; } auto pickColorValsUC = (uchar*) pca; auto pickColorVals = (int*) (pca + nverts * VERTEX_PICKCOLOR_RGBA_SIZE); for (v = 0; v < nverts; v++) { bnd = pickable ? pickColorVals[v * 2 + 1] : cPickableNoPick; idx = pickColorVals[v * 2]; AssignNewPickColor( I, pick, pickColorValsUC + (v * 4), context, idx, bnd); } } } break; case CGO_DRAW_BUFFERS_INDEXED: case CGO_DRAW_BUFFERS_NOT_INDEXED: case CGO_DRAW_TEXTURES: case CGO_DRAW_LABELS: case CGO_DRAW_SPHERE_BUFFERS: case CGO_DRAW_CYLINDER_BUFFERS: case CGO_DRAW_CUSTOM: { int pickcolors_are_set = true; int* pickcolors_are_set_ptr = get_pickcolorsset_ptr(op, const_cast(pc)); if (!pickcolors_are_set_ptr) pickcolors_are_set_ptr = &pickcolors_are_set; // TODO remove `pickcolorsset` fields from CGOs // This assert can fail during "Roving Detail" demo. However, I still // question the need of the `pickcolorsset` fields. // assert(reset_colors || *pickcolors_are_set_ptr); if (reset_colors || !*pickcolors_are_set_ptr) { // only if picking info is invalid int nverts = 0; int nvertsperfrag = 1; int v, pl; int bnd = cPickableNoPick; unsigned int idx = 0; int srcp; float* pca = nullptr; int* pickDataSrc; uchar* pickColorDestUC = nullptr; bool free_pick_color_dest = false; int destOffset = 0, bufsizemult = 1; size_t pickvbo = 0; switch (op) { case CGO_DRAW_CUSTOM: { const cgo::draw::custom* sp = reinterpret_cast(pc); nverts = sp->nverts; pickvbo = sp->pickvboid; if (!pickvbo) continue; pca = sp->floatdata; nvertsperfrag = sp->vertsperpickinfo; bufsizemult = sp->npickbufs; pickColorDestUC = new uchar[bufsizemult * nverts * 4]; } break; case CGO_DRAW_BUFFERS_INDEXED: { const cgo::draw::buffers_indexed* sp = reinterpret_cast(pc); nverts = sp->nverts; pickvbo = sp->pickvboid; pca = sp->floatdata; } break; case CGO_DRAW_BUFFERS_NOT_INDEXED: { const cgo::draw::buffers_not_indexed* sp = reinterpret_cast(pc); nverts = sp->nverts; pickvbo = sp->pickvboid; pca = sp->floatdata; } break; case CGO_DRAW_SPHERE_BUFFERS: { const cgo::draw::sphere_buffers* sp = reinterpret_cast(pc); nverts = sp->num_spheres * VerticesPerSphere(); nvertsperfrag = VerticesPerSphere(); pickvbo = sp->pickvboid; pca = sp->floatdata; pickColorDestUC = new uchar[nverts * 4]; } break; case CGO_DRAW_CYLINDER_BUFFERS: { const cgo::draw::cylinder_buffers* sp = reinterpret_cast(pc); nverts = sp->num_cyl * NumVerticesPerCylinder(); nvertsperfrag = NumVerticesPerCylinder(); pickvbo = sp->pickvboid; pca = sp->floatdata; bufsizemult = 2; pickColorDestUC = new uchar[bufsizemult * nverts * 4]; } break; case CGO_DRAW_TEXTURES: { const cgo::draw::textures* sp = reinterpret_cast(pc); nverts = sp->ntextures * 6; pca = sp->floatdata; } break; case CGO_DRAW_LABELS: { const cgo::draw::labels* sp; sp = reinterpret_cast(pc); nverts = sp->ntextures * 6; pca = sp->floatdata; pickvbo = sp->pickvboid; } break; } if (pickColorDestUC) { free_pick_color_dest = true; pickDataSrc = (int*) (pca); } else { pickColorDestUC = (uchar*) pca; pickDataSrc = (int*) (pca + nverts); } destOffset = R->pick_pass() * sizeof(float) * nverts * bufsizemult; if (!pickable) { for (int i = 0; i < nverts * bufsizemult; ++i) { pick->colorNoPick(pickColorDestUC + 4 * i); } } else { int npickbufs = bufsizemult; int ploffsetforbuf = 0; if (op == CGO_DRAW_CYLINDER_BUFFERS) { // disabled 2016-07-19 TH: code looks almost identical to // else branch and CGO_DRAW_CYLINDER_BUFFERS seem to be // not used anymore. PRINTFB(I->G, FB_CGO, FB_Errors) " FIXME: SUPPOSEDLY UNUSED CODE EXECUTED in " "CGORenderPicking(!\n" ENDFB(I->G); } else { if (op == CGO_DRAW_CUSTOM) { ploffsetforbuf = sizeof(float) * nverts; // for multiple picking attributes } for (v = 0, pl = 0; v < nverts; v++, pl += 4) { if (v % nvertsperfrag) { // if same fragment, same color for (int pi = 0; pi < npickbufs; ++pi) { int ploffset = ploffsetforbuf * pi; SetUCColorToPrevN(1, &pickColorDestUC[pl + ploffset]); } continue; } int frag = (int) (v / nvertsperfrag); for (int pi = 0; pi < npickbufs; ++pi) { int ploffset = ploffsetforbuf * pi; srcp = 2 * ((npickbufs * frag) + pi); idx = pickDataSrc[srcp]; bnd = pickDataSrc[srcp + 1]; AssignNewPickColor(I, pick, &pickColorDestUC[pl + ploffset], context, idx, bnd); } } } } if (pickvbo) { // reload entire vbo auto* vbo = I->G->ShaderMgr->getGPUBuffer(pickvbo); auto pickPtrBytes = reinterpret_cast(pickColorDestUC); auto dataSpan = pymol::span{pickPtrBytes, sizeof(float) * nverts * bufsizemult}; vbo->bufferReplaceData(destOffset, dataSpan); (*pickcolors_are_set_ptr) = true; } if (free_pick_color_dest) { delete[] pickColorDestUC; pickColorDestUC = nullptr; free_pick_color_dest = false; } } } break; } CGO_gl[op](R, &pc); } R->isPicking = false; } void CGORenderGL(CGO* I, const float* color, CSetting* set1, CSetting* set2, RenderInfo* info, Rep* rep) /* this should be as fast as you can make it... * the ASM loop is about 2X long as raw looped GL calls, * but hopefully superscaler processors won't care */ { PyMOLGlobals* G = I->G; const float zee[] = {0.f, 0.f, 1.f}; const float color_tmp[] = {1.f, 1.f, 1.f}; if (I->render_alpha) { // for now, the render_alpha_only flag calls CGOSetZVector/CGORenderGLAlpha auto modMatrix = SceneGetModelViewMatrixPtr(G); CGOSetZVector(I, modMatrix[2], modMatrix[6], modMatrix[10]); CGORenderAlpha(I, info, 1); if (I->render_alpha == 1) // right now, render_alpha 1: renders alpha only, // 2: renders both alpha and rest return; } if (!G->ValidContext) { return; } if (!I->c) { return; } { CCGORenderer* R = G->CGORenderer; R->info = info; R->use_shader = I->use_shader; R->debug = I->debug; R->sphere_quality = I->sphere_quality; R->rep = rep; R->color = color; R->alpha = 1.0F - SettingGet_f(G, set1, set2, cSetting_cgo_transparency); R->set1 = set1; R->set2 = set2; // normals should be initialized to the view vector // (changed BB 9/14 from SceneResetNormalUseShader(), to // CScene->LinesNormal, which was arbitrary, I believe) SceneResetNormalToViewVector(I->G, I->use_shader); if (!color) { color = color_tmp; } { auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader(); if (shaderPrg && I->use_shader) { shaderPrg->SetAttrib4fLocation( "a_Color", color[0], color[1], color[2], R->alpha); } #ifndef PURE_OPENGL_ES_2 else { glColor4f(color[0], color[1], color[2], R->alpha); } #endif } #ifndef PURE_OPENGL_ES_2 const float width_scale = (info && info->width_scale_flag) ? info->width_scale : 1.f; glLineWidth( SettingGet_f(G, set1, set2, cSetting_cgo_line_width) * width_scale); glPointSize( SettingGet_f(G, set1, set2, cSetting_cgo_dot_width) * width_scale); #endif if (!(info && info->alpha_cgo)) { // Regular CGO dispatch table rendering for (auto it = I->begin(); !it.is_stop(); ++it) { const auto op = it.op_code(); assert(op < CGO_sz_size()); CGO_OP_DATA_CONST float* const pc = it.data(); CGO_gl[op](R, &pc); } return; } /* we're sorting transparent triangles globally */ { { int mode = -1; int vc = 0; // triangle normals const float *n0 = nullptr, *n1 = nullptr, *n2 = nullptr; // triangle vertices const float *v0 = nullptr, *v1 = nullptr, *v2 = nullptr; // triangle colors const float *c0 = color, *c1 = nullptr, *c2 = nullptr; for (auto it = I->begin(); !it.is_stop(); ++it) { const auto op = it.op_code(); assert(op < CGO_sz_size()); CGO_OP_DATA_CONST float* const pc = it.data(); if ((R->alpha != 1.f)) { switch (op) { /* transparency */ case CGO_BEGIN: mode = CGO_get_int(pc); CGO_gl_begin(R, &pc); vc = 0; n0 = zee; break; case CGO_END: CGO_gl_end(R, &pc); mode = -1; break; case CGO_NORMAL: switch (mode) { case GL_TRIANGLES: case GL_TRIANGLE_STRIP: case GL_TRIANGLE_FAN: n0 = pc; break; default: CGO_gl_normal(R, &pc); } break; case CGO_COLOR: c0 = pc; CGO_gl_color(R, &pc); break; case CGO_TRIANGLE: CGOAlphaTriangle(info->alpha_cgo, pc, pc + 3, pc + 6, pc + 9, pc + 12, pc + 15, pc + 18, pc + 21, pc + 24, R->alpha, R->alpha, R->alpha, false); break; case CGO_DRAW_ARRAYS: { const cgo::draw::arrays* sp = reinterpret_cast(pc); int mode = sp->mode, arrays = sp->arraybits, nverts = sp->nverts; float *vertexVals = 0, *nxtVals = 0, *colorVals = 0, *normalVals; float *vertexVals_tmp = 0, *colorVals_tmp = 0, *normalVals_tmp = 0; int step; short nxtn = 3; nxtVals = vertexVals = vertexVals_tmp = sp->floatdata; if (arrays & CGO_NORMAL_ARRAY) { nxtVals = normalVals = normalVals_tmp = vertexVals + (nxtn * nverts); } if (arrays & CGO_COLOR_ARRAY) { nxtVals = colorVals = colorVals_tmp = nxtVals + (nxtn * nverts); nxtn = 4; } switch (mode) { case GL_TRIANGLES: { for (step = 0; step < nverts; step += 3) { if (colorVals_tmp) { c0 = colorVals_tmp; c1 = colorVals_tmp + 4; c2 = colorVals_tmp + 8; } else { c1 = c2 = c0; } if (normalVals_tmp) { n0 = normalVals_tmp; n1 = normalVals_tmp + 3; n2 = normalVals_tmp + 6; } else { n1 = n2 = n0; } CGOAlphaTriangle(info->alpha_cgo, vertexVals_tmp, vertexVals_tmp + 3, vertexVals_tmp + 6, n0, n1, n2, c0, c1, c2, R->alpha, R->alpha, R->alpha, false); vertexVals_tmp += 9; if (normalVals_tmp) { normalVals_tmp += 9; } if (colorVals_tmp) { colorVals_tmp += 12; } } } break; case GL_TRIANGLE_STRIP: { if (colorVals_tmp) { c1 = colorVals_tmp; c2 = colorVals_tmp + 4; colorVals_tmp += 8; } else { c1 = c2 = c0; } if (normalVals_tmp) { n1 = normalVals_tmp; n2 = normalVals_tmp + 3; normalVals_tmp += 6; } else { n1 = n2 = n0; } vertexVals_tmp += 6; for (step = 2; step < nverts; step++) { if (colorVals_tmp) { c0 = c1; c1 = c2; c2 = colorVals_tmp; } if (normalVals_tmp) { n0 = n1; n1 = n2; n2 = normalVals_tmp; } CGOAlphaTriangle(info->alpha_cgo, vertexVals_tmp - 6, vertexVals_tmp - 3, vertexVals_tmp, n0, n1, n2, c0, c1, c2, R->alpha, R->alpha, R->alpha, false); vertexVals_tmp += 3; if (normalVals_tmp) { normalVals_tmp += 3; } if (colorVals_tmp) { colorVals_tmp += 4; } } } break; case GL_TRIANGLE_FAN: { float* firstVertex = vertexVals_tmp; if (colorVals_tmp) { c0 = colorVals_tmp; c2 = colorVals_tmp + 4; colorVals_tmp += 8; } else { c1 = c2 = c0; } if (normalVals_tmp) { n0 = normalVals_tmp; n2 = normalVals_tmp + 3; normalVals_tmp += 6; } vertexVals_tmp += 6; for (step = 2; step < nverts; step++) { if (colorVals_tmp) { c1 = c2; c2 = colorVals_tmp; } if (normalVals_tmp) { n1 = n2; n2 = normalVals_tmp; } CGOAlphaTriangle(info->alpha_cgo, firstVertex, vertexVals_tmp - 3, vertexVals_tmp, n0, n1, n2, c0, c1, c2, R->alpha, R->alpha, R->alpha, false); vertexVals_tmp += 3; if (normalVals_tmp) { normalVals_tmp += 3; } if (colorVals_tmp) { colorVals_tmp += 4; } } } break; } } break; case CGO_VERTEX: v0 = pc; switch (mode) { case GL_TRIANGLES: if (3 * ((vc + 1) / 3) == vc + 1) { CGOAlphaTriangle(info->alpha_cgo, v0, v1, v2, n0, n1, n2, c0, c1, c2, R->alpha, R->alpha, R->alpha, true); } v2 = v1; c2 = c1; n2 = n1; v1 = v0; c1 = c0; n1 = n0; vc++; break; case GL_TRIANGLE_STRIP: if (vc > 1) { CGOAlphaTriangle(info->alpha_cgo, v0, v1, v2, n0, n1, n2, c0, c1, c2, R->alpha, R->alpha, R->alpha, !(vc & 0x1)); } v2 = v1; c2 = c1; n2 = n1; v1 = v0; c1 = c0; n1 = n0; vc++; break; case GL_TRIANGLE_FAN: if (vc > 1) { CGOAlphaTriangle(info->alpha_cgo, v0, v1, v2, n0, n1, n2, c0, c1, c2, R->alpha, R->alpha, R->alpha, false); } else if (!vc) { n2 = n0; v2 = v0; c2 = c0; } v1 = v0; c1 = c0; n1 = n0; vc++; break; default: CGO_gl_vertex(R, &pc); break; } break; default: CGO_gl[op](R, &pc); break; } } else { /* opaque */ switch (op) { case CGO_COLOR: /* Since CGO operations are done in sequence, alpha could happen after color is set. In this case, we still need to keep track of the color in case there is a transparent object */ c0 = pc; break; default: break; } CGO_gl[op](R, &pc); } } } } } } void CGORenderGLAlpha(CGO* I, RenderInfo* info, bool calcDepth) { PyMOLGlobals* G = I->G; if (G->ValidContext && I->c) { int mode = GL_TRIANGLES; if (I->debug) { mode = CGOConvertDebugMode(I->debug, GL_TRIANGLES); } #ifndef PURE_OPENGL_ES_2 // not sure why shader is set, but disable it for now, // since we are doing immediate mode rendering for global transparency G->ShaderMgr->Disable_Current_Shader(); #endif /* 1. transform and measure range (if not already known) 2. bin into linked lists based on Z-centers 3. render by layer */ if (I->z_flag) { if (!I->i_start) { I->i_size = 256; I->i_start = pymol::calloc(I->i_size); } else { UtilZeroMem(I->i_start, sizeof(int) * I->i_size); } { const int i_size = I->i_size; const float* base = I->op; int* start = I->i_start; int delta = 1, ntris = 0; /* bin the triangles */ if (calcDepth) { for (auto it = I->begin(); !it.is_stop(); ++it) { if (it.op_code() == CGO_ALPHA_TRIANGLE) { float* const pc = it.data(); const float z = dot_product3f(pc + 1, I->z_vector); if (z > I->z_max) I->z_max = z; if (z < I->z_min) I->z_min = z; pc[4] = z; ntris++; } } } const float range_factor = (0.9999F * i_size) / (I->z_max - I->z_min); for (auto it = I->begin(); !it.is_stop(); ++it) { if (it.op_code() == CGO_ALPHA_TRIANGLE) { float* const pc = it.data(); assert(base < pc && pc < I->op + I->c); auto i = std::clamp((pc[4] - I->z_min) * range_factor, 0, i_size); CGO_put_int(pc, start[i]); start[i] = (pc - base); /* NOTE: will always be > 0 since we have CGO_read_int'd */ } } // for single-layer transparency, render front-to-back if (SettingGetGlobal_i(G, cSetting_transparency_mode) == 2) { delta = -1; start += (i_size - 1); } /* now render by bin */ #ifndef PURE_OPENGL_ES_2 glBegin(mode); for (int i = 0; i < i_size; i++) { int ii = *start; start += delta; while (ii) { const float* pc = base + ii; glColor4fv(pc + 23); glNormal3fv(pc + 14); glVertex3fv(pc + 5); glColor4fv(pc + 27); glNormal3fv(pc + 17); glVertex3fv(pc + 8); glColor4fv(pc + 31); glNormal3fv(pc + 20); glVertex3fv(pc + 11); ii = CGO_get_int(pc); } } glEnd(); #endif } } else { #ifndef PURE_OPENGL_ES_2 glBegin(mode); for (auto it = I->begin(); !it.is_stop(); ++it) { if (it.op_code() == CGO_ALPHA_TRIANGLE) { float* const pc = it.data(); glColor4fv(pc + 23); glNormal3fv(pc + 14); glVertex3fv(pc + 5); glColor4fv(pc + 27); glNormal3fv(pc + 17); glVertex3fv(pc + 8); glColor4fv(pc + 31); glNormal3fv(pc + 20); glVertex3fv(pc + 11); } } glEnd(); #endif } } }