Files
pymol-open-source/layer1/CGOGL.cpp

2574 lines
79 KiB
C++

#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<const cgo::draw::line*>(*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<const cgo::draw::splitline*>(*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<const cgo::draw::arrays*>(*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<IndexBufferGL>(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<std::size_t>(nindices)});
}
static void CGO_gl_draw_buffers_indexed(CCGORenderer* I, CGO_op_data pc)
{
auto sp = reinterpret_cast<const cgo::draw::buffers_indexed*>(*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<VertexBufferGL>(vboid);
auto* ibo = I->G->ShaderMgr->getGPUBuffer<IndexBufferGL>(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<VertexBufferGL>(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<VertexBufferGL>(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<decltype(sp)>(*pc);
int mode = sp->mode;
auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
if (!shaderPrg) {
return;
}
auto* vbo = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<VertexBufferGL>(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<VertexBufferGL>(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<decltype(sp)>(*pc);
auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
if (!shaderPrg) {
return;
}
auto* vbo = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<decltype(sp)>(*pc);
auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
if (!shaderPrg) {
return;
}
auto* vbo = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<decltype(sp)>(*pc);
auto shaderPrg = I->G->ShaderMgr->Get_Current_Shader();
if (!shaderPrg) {
return;
}
auto* vbo = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(sp->vboid);
if (!vbo)
return;
IndexBufferGL* ibo = nullptr;
if (sp->iboid) {
ibo = I->G->ShaderMgr->getGPUBuffer<IndexBufferGL>(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<VertexBufferGL>(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<decltype(sp)>(*pc);
int num_spheres = sp->num_spheres;
int attr_color;
auto* vbo = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(sp->vboid);
auto* pickvbo =
I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<const cgo::draw::bezier_buffers*>(*cgo_data);
const auto vbo = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<decltype(sp)>(*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<VertexBufferGL>(sp->vboid);
auto* ibo = I->G->ShaderMgr->getGPUBuffer<IndexBufferGL>(sp->iboid);
auto* pickvbo =
I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<decltype(sp)>(*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<VertexBufferGL>(sp->vboid);
auto* pickvbo =
I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<decltype(sp)>(*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<VertexBufferGL>(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<decltype(sp)>(*pc);
int ntextures = sp->ntextures;
auto* vbo = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<decltype(sp)>(*pc);
int nverts = sp->nverts;
CShaderPrg* shaderPrg;
shaderPrg = I->G->ShaderMgr->Get_ScreenShader();
if (!shaderPrg) {
return;
}
auto* vb = I->G->ShaderMgr->getGPUBuffer<VertexBufferGL>(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<const cgo::draw::vertex_attribute_3f*>(*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<const cgo::draw::vertex_attribute_4ub*>(*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<const cgo::draw::vertex_attribute_4ub_if_picking*>(
*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<const cgo::draw::vertex_attribute_1f*>(*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<cgo::draw::cls*>(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<decltype(sp)>(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<float*>(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<decltype(sp)>(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<decltype(sp)>(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<decltype(sp)>(pc);
nverts = sp->nverts;
pickvbo = sp->pickvboid;
pca = sp->floatdata;
} break;
case CGO_DRAW_SPHERE_BUFFERS: {
const cgo::draw::sphere_buffers* sp =
reinterpret_cast<decltype(sp)>(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<decltype(sp)>(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<decltype(sp)>(pc);
nverts = sp->ntextures * 6;
pca = sp->floatdata;
} break;
case CGO_DRAW_LABELS: {
const cgo::draw::labels* sp;
sp = reinterpret_cast<decltype(sp)>(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<VertexBufferGL>(pickvbo);
auto pickPtrBytes =
reinterpret_cast<const std::byte*>(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<decltype(sp)>(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<int>(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<int>((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
}
}
}