mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
10158 lines
321 KiB
C++
10158 lines
321 KiB
C++
|
|
/*
|
|
A* -------------------------------------------------------------------
|
|
B* This file contains source code for the PyMOL computer program
|
|
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
|
|
D* -------------------------------------------------------------------
|
|
E* It is unlawful to modify or remove this copyright notice.
|
|
F* -------------------------------------------------------------------
|
|
G* Please see the accompanying LICENSE file for further information.
|
|
H* --------------------------------------------------\-----------------
|
|
I* Additional authors of this source file include:
|
|
-*
|
|
-*
|
|
-*
|
|
Z* -------------------------------------------------------------------
|
|
*/
|
|
#include "os_python.h"
|
|
|
|
#include "os_gl.h"
|
|
#include "os_predef.h"
|
|
#include "os_std.h"
|
|
|
|
#include "Base.h"
|
|
#include "CGO.h"
|
|
#include "CGOGL.h"
|
|
#include "CGORenderer.h"
|
|
#include "CoordSet.h"
|
|
#include "Err.h"
|
|
#include "Feedback.h"
|
|
#include "GadgetSet.h"
|
|
#include "Matrix.h"
|
|
#include "ObjectGadgetRamp.h"
|
|
#include "P.h"
|
|
#include "PConv.h"
|
|
#include "Picking.h"
|
|
#include "PrintUtils.h"
|
|
#include "PyMOLGlobals.h"
|
|
#include "Ray.h"
|
|
#include "Rep.h"
|
|
#include "Scene.h"
|
|
#include "ScenePicking.h"
|
|
#include "Setting.h"
|
|
#include "ShaderMgr.h"
|
|
#include "Sphere.h"
|
|
#include "Triangle.h"
|
|
#include "Util.h"
|
|
#include "VFont.h"
|
|
#include "Vector.h"
|
|
#include "GraphicsUtil.h"
|
|
#include "GLVertexBuffer.h"
|
|
|
|
#include "pymol/algorithm.h"
|
|
|
|
#define VAR_FOR_NORMAL pl
|
|
#define VERTEX_NORMAL_SIZE 3
|
|
#define VAR_FOR_NORMAL_CNT_PLUS
|
|
|
|
#define VALUES_PER_IMPOSTER_SPACE_COORD 1
|
|
|
|
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;
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <iostream>
|
|
|
|
template <typename T> inline T CLAMPVALUE(T val, T minimum, T maximum)
|
|
{
|
|
return (val < minimum) ? minimum : (val > maximum) ? maximum : val;
|
|
}
|
|
|
|
inline cCylCap cap1_from_cyl_shader_bits(const unsigned char bits)
|
|
{
|
|
return (bits & 1) ? ((bits & cCylShaderCap1RoundBit) ? cCylCap::Round
|
|
: cCylCap::Flat)
|
|
: cCylCap::None;
|
|
}
|
|
inline cCylCap cap2_from_cyl_shader_bits(const unsigned char bits)
|
|
{
|
|
return (bits & 2) ? ((bits & cCylShaderCap2RoundBit) ? cCylCap::Round
|
|
: cCylCap::Flat)
|
|
: cCylCap::None;
|
|
}
|
|
inline unsigned char cyl_shader_bits_from_caps(
|
|
const cCylCap cap1, const cCylCap cap2)
|
|
{
|
|
return ((cap1 == cCylCap::Flat)
|
|
? cCylShaderCap1Flat
|
|
: ((cap1 == cCylCap::Round) ? cCylShaderCap1Round : 0)) |
|
|
((cap2 == cCylCap::Flat)
|
|
? cCylShaderCap2Flat
|
|
: ((cap2 == cCylCap::Round) ? cCylShaderCap2Round : 0));
|
|
}
|
|
|
|
#define WARN_UNEXPECTED_OPERATION(G, op) \
|
|
PRINTFB(G, FB_CGO, FB_Warnings) \
|
|
" %s-Warning: unexpected op=0x%x (line %d)\n", __func__, op, __LINE__ ENDFB(G)
|
|
|
|
// like g_return_val_if_fail from glib
|
|
#define RETURN_VAL_IF_FAIL(expr, val) \
|
|
{ \
|
|
if (!(expr)) \
|
|
return (val); \
|
|
}
|
|
|
|
static void set_current_pick_color(CGO* cgo, unsigned int idx, int bnd)
|
|
{
|
|
if (cgo) {
|
|
cgo->current_pick_color_index = idx;
|
|
cgo->current_pick_color_bond = bnd;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Assign a new pick color index for {context, index, bond} (only in the
|
|
* first pass) and convert that to an RGBA color for the current picking pass.
|
|
*
|
|
* @param[out] cgo Set the "current pick color" in this CGO
|
|
* @param[in,out] pickmgr Pick color manager
|
|
* @param[out] color RGBA pick color to use in this pass
|
|
* @param[in] context Object state identifier
|
|
* @param[in] index Primary index
|
|
* @param[in] bond Secondary index
|
|
*/
|
|
void AssignNewPickColor(CGO* cgo, PickColorManager* pickmgr,
|
|
unsigned char* color, const PickContext* context, unsigned int index,
|
|
int bond)
|
|
{
|
|
set_current_pick_color(cgo, index, bond);
|
|
pickmgr->colorNext(color, context, index, bond);
|
|
}
|
|
|
|
std::size_t CGO_sz[] = {
|
|
CGO_NULL_SZ, //
|
|
CGO_NULL_SZ, //
|
|
CGO_BEGIN_SZ, //
|
|
CGO_END_SZ, //
|
|
CGO_VERTEX_SZ, //
|
|
CGO_NORMAL_SZ, //
|
|
fsizeof<cgo::draw::color>(), //
|
|
fsizeof<cgo::draw::sphere>(), //
|
|
CGO_TRIANGLE_SZ, //
|
|
fsizeof<cgo::draw::cylinder>(), //
|
|
CGO_LINEWIDTH_SZ, //
|
|
CGO_WIDTHSCALE_SZ, //
|
|
CGO_ENABLE_SZ, //
|
|
CGO_DISABLE_SZ, //
|
|
fsizeof<cgo::draw::sausage>(), //
|
|
fsizeof<cgo::draw::custom_cylinder>(), //
|
|
CGO_DOTWIDTH_SZ, //
|
|
CGO_ALPHA_TRIANGLE_SZ, //
|
|
CGO_ELLIPSOID_SZ, //
|
|
CGO_FONT_SZ, //
|
|
CGO_FONT_SCALE_SZ, //
|
|
CGO_FONT_VERTEX_SZ, //
|
|
CGO_FONT_AXES_SZ, //
|
|
CGO_CHAR_SZ, //
|
|
CGO_INDENT_SZ, //
|
|
CGO_ALPHA_SZ, //
|
|
CGO_QUADRIC_SZ, //
|
|
CGO_CONE_SZ, //
|
|
fsizeof<cgo::draw::arrays>(), //
|
|
CGO_NULL_SZ, CGO_RESET_NORMAL_SZ, //
|
|
CGO_PICK_COLOR_SZ, //
|
|
CGO_NULL_SZ, // CGO_DRAW_BUFFERS_SZ no longer used
|
|
fsizeof<cgo::draw::buffers_indexed>(), //
|
|
CGO_BOUNDING_BOX_SZ, //
|
|
fsizeof<cgo::draw::buffers_not_indexed>(), //
|
|
CGO_SPECIAL_SZ, //
|
|
fsizeof<cgo::draw::cylinder_buffers>(), //
|
|
fsizeof<cgo::draw::shadercylinder>(), //
|
|
fsizeof<cgo::draw::shadercylinder2ndcolor>(), //
|
|
fsizeof<cgo::draw::sphere_buffers>(), //
|
|
CGO_ACCESSIBILITY_SZ, //
|
|
CGO_DRAW_TEXTURE_SZ, //
|
|
fsizeof<cgo::draw::textures>(), //
|
|
fsizeof<cgo::draw::screen_textures>(), //
|
|
CGO_TEX_COORD_SZ, //
|
|
fsizeof<cgo::draw::label>(), //
|
|
fsizeof<cgo::draw::labels>(), //
|
|
CGO_DRAW_CONNECTOR_SZ, //
|
|
fsizeof<cgo::draw::connectors>(), //
|
|
CGO_DRAW_TRILINES_SZ, //
|
|
CGO_UNIFORM3F_SZ, //
|
|
CGO_SPECIAL_WITH_ARG_SZ, //
|
|
fsizeof<cgo::draw::line>(), //
|
|
fsizeof<cgo::draw::splitline>(), //
|
|
fsizeof<cgo::draw::custom>(), //
|
|
fsizeof<cgo::draw::vertex_attribute_3f>(), //
|
|
fsizeof<cgo::draw::vertex_attribute_4ub>(), //
|
|
fsizeof<cgo::draw::vertex_attribute_1f>(), //
|
|
fsizeof<cgo::draw::mask_attribute_if_picking>(), //
|
|
fsizeof<cgo::draw::bind_vbo_for_picking>(), //
|
|
CGO_VERTEX_BEGIN_LINE_STRIP_SZ, //
|
|
CGO_INTERPOLATED_SZ, //
|
|
CGO_VERTEX_CROSS_SZ, //
|
|
fsizeof<cgo::draw::vertex_attribute_4ub_if_picking>(), //
|
|
fsizeof<cgo::draw::custom_cylinder_alpha>(), //
|
|
CGO_BEZIER_SZ, //
|
|
fsizeof<cgo::draw::bezier_buffers>(), //
|
|
CGO_NULL_SZ //
|
|
};
|
|
|
|
/**
|
|
* Get the number of elements in `CGO_sz`
|
|
*/
|
|
size_t CGO_sz_size()
|
|
{
|
|
return sizeof(CGO_sz) / sizeof(*CGO_sz);
|
|
}
|
|
|
|
static float* CGO_add(CGO* I, unsigned c);
|
|
static float* CGO_size(CGO* I, int sz);
|
|
static int CGOSimpleCylinder(CGO* I, const float* v1, const float* v2,
|
|
const float tube_size, const float* c1, const float* c2, float a1,
|
|
const float a2, const bool interp, const cCylCap cap1, const cCylCap cap2,
|
|
const Pickable* pickcolor2 = nullptr, const bool stick_round_nub = false);
|
|
template <typename CylinderT>
|
|
static int CGOSimpleCylinder(CGO* I, const CylinderT& cyl, const float a1,
|
|
const float a2, const bool interp, const cCylCap cap1, const cCylCap cap2,
|
|
const Pickable* pickcolor2 = nullptr, const bool stick_round_nub = false);
|
|
static int CGOSimpleEllipsoid(CGO* I, const float* v, float vdw,
|
|
const float* n0, const float* n1, const float* n2);
|
|
static int CGOSimpleQuadric(CGO* I, const float* v, float vdw, const float* q);
|
|
static int CGOSimpleSphere(
|
|
CGO* I, const float* v, float vdw, short sphere_quality);
|
|
static int CGOSimpleCone(CGO* I, const float* v1, const float* v2, float r1,
|
|
float r2, const float* c1, const float* c2, cCylCap cap1, cCylCap cap2);
|
|
int CGOSimpleCone(CGO* I, const cgo::draw::cone& cone);
|
|
|
|
/**
|
|
* Inverse function of CGOArrayFromPyListInPlace
|
|
*
|
|
* I: (input) Primitive CGO (may contain CGO_DRAW_ARRAYS)
|
|
*
|
|
* Return: All-float Python list primitive CGO
|
|
*/
|
|
static PyObject* CGOArrayAsPyList(const CGO* I)
|
|
{
|
|
std::vector<float> flat;
|
|
flat.reserve(I->c);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto op = it.op_code();
|
|
auto pc = it.data();
|
|
auto sz = CGO_sz[op];
|
|
|
|
flat.push_back(op);
|
|
|
|
switch (op) {
|
|
case CGO_BEGIN:
|
|
case CGO_ENABLE:
|
|
case CGO_DISABLE:
|
|
case CGO_SPECIAL:
|
|
// first member int
|
|
flat.push_back(*reinterpret_cast<const int*>(pc));
|
|
++pc;
|
|
--sz;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
assert(sz == 2);
|
|
flat.push_back(*reinterpret_cast<const int*>(pc++)); // atom index
|
|
flat.push_back(*reinterpret_cast<const int*>(pc++)); // bond index
|
|
sz -= 2;
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
auto sp = reinterpret_cast<const cgo::draw::arrays*>(pc);
|
|
flat.push_back(sp->mode);
|
|
flat.push_back(sp->arraybits);
|
|
flat.push_back(sp->narrays); // (redundant)
|
|
flat.push_back(sp->nverts);
|
|
pc = sp->get_data();
|
|
sz = sp->get_data_length();
|
|
}
|
|
}
|
|
|
|
// float members
|
|
for (; sz; --sz) {
|
|
flat.push_back(*(pc++));
|
|
}
|
|
}
|
|
|
|
return PConvToPyObject(flat);
|
|
}
|
|
|
|
PyObject* CGOAsPyList(CGO* I)
|
|
{
|
|
PyObject* result;
|
|
result = PyList_New(2);
|
|
PyObject* list = CGOArrayAsPyList(I);
|
|
PyList_SetItem(result, 0, PyInt_FromLong(PyList_Size(list)));
|
|
PyList_SetItem(result, 1, list);
|
|
return (result);
|
|
}
|
|
|
|
static float CPythonVal_PyFloat_AsDouble_From_List(
|
|
void* G, PyObject* list, size_t i)
|
|
{
|
|
float out;
|
|
PConvPyFloatToFloat(PyList_GetItem(list, i), &out);
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Inverse function of CGOArrayAsPyList
|
|
*
|
|
* list: (input) All-float Python list primitive CGO (may contain
|
|
* CGO_DRAW_ARRAYS) I: (output) empty CGO
|
|
*/
|
|
static int CGOArrayFromPyListInPlace(PyObject* list, CGO* I)
|
|
{
|
|
// sanity check
|
|
if (!list || !PyList_Check(list))
|
|
return false;
|
|
|
|
auto G = I->G;
|
|
|
|
#define GET_FLOAT(i) \
|
|
((float) CPythonVal_PyFloat_AsDouble_From_List(I->G, list, i))
|
|
#define GET_INT(i) ((int) CPythonVal_PyFloat_AsDouble_From_List(I->G, list, i))
|
|
|
|
for (int i = 0, l = PyList_Size(list); i < l;) {
|
|
unsigned op = GET_INT(i++);
|
|
ok_assert(1, op < CGO_sz_size());
|
|
int sz = CGO_sz[op];
|
|
float* fdata = I->add_to_buffer(sz + 1);
|
|
CGO_write_int(fdata, op);
|
|
|
|
switch (op) {
|
|
case CGO_STOP:
|
|
// don't increment size for null terminator
|
|
I->c -= 1;
|
|
return true;
|
|
case CGO_BEGIN:
|
|
I->has_begin_end = true;
|
|
case CGO_ENABLE:
|
|
case CGO_DISABLE:
|
|
case CGO_SPECIAL:
|
|
// first member int
|
|
ok_assert(1, i < l);
|
|
CGO_write_int(fdata, GET_INT(i++));
|
|
sz--;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
ok_assert(1, i + 1 < l);
|
|
CGO_write_int(fdata, GET_INT(i++)); // atom index
|
|
CGO_write_int(fdata, GET_INT(i++)); // bond index
|
|
sz -= 2;
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
// has abstract superclass, need to be constructed!
|
|
ok_assert(1, i + 3 < l);
|
|
auto sp = new (fdata)
|
|
cgo::draw::arrays(GET_INT(i), GET_INT(i + 1), GET_INT(i + 3));
|
|
|
|
// sanity check
|
|
int narrays_check = GET_INT(i + 2);
|
|
if (sp->narrays != narrays_check) {
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGO-Warning: narrays mismatch: %d != %d\n", sp->narrays,
|
|
narrays_check ENDFB(I->G);
|
|
}
|
|
|
|
// data
|
|
sz = sp->get_data_length();
|
|
sp->floatdata = fdata = I->allocate_in_data_heap(sz);
|
|
|
|
i += 4;
|
|
} break;
|
|
}
|
|
|
|
// float members
|
|
for (; sz; --sz) {
|
|
ok_assert(1, i < l);
|
|
*(fdata++) = GET_FLOAT(i++);
|
|
}
|
|
}
|
|
|
|
#undef GET_FLOAT
|
|
#undef GET_INT
|
|
|
|
return true;
|
|
|
|
ok_except1:
|
|
PRINTFB(G, FB_CGO, FB_Errors) " %s-Error: Corrupt data\n", __func__ ENDFB(G);
|
|
return false;
|
|
}
|
|
|
|
CGO* CGONewFromPyList(
|
|
PyMOLGlobals* G, PyObject* list, int version, bool shouldCombine)
|
|
{
|
|
int ok = true;
|
|
auto I = CGONew(G);
|
|
if (ok)
|
|
ok = (list != nullptr);
|
|
if (ok)
|
|
ok = PyList_Check(list);
|
|
/* TO ENABLE BACKWARDS COMPATIBILITY...
|
|
Always check ll when adding new PyList_GetItem's */
|
|
if ((version > 0) && (version <= 86)) {
|
|
if (ok)
|
|
ok = PConvFromPyListItem(G, list, 0, I->c);
|
|
if (ok)
|
|
VLACheck(I->op, float, I->c);
|
|
if (ok)
|
|
ok = PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 1), I->op, I->c);
|
|
} else {
|
|
if (ok)
|
|
ok = CGOArrayFromPyListInPlace(PyList_GetItem(list, 1), I);
|
|
}
|
|
if (!ok) {
|
|
CGOFree(I);
|
|
}
|
|
{
|
|
CGO* cgo = nullptr;
|
|
if (shouldCombine && I && I->has_begin_end) {
|
|
cgo = CGOCombineBeginEnd(I, 0);
|
|
CGOFree(I);
|
|
} else {
|
|
cgo = I;
|
|
}
|
|
return cgo;
|
|
}
|
|
}
|
|
|
|
CGO::CGO(PyMOLGlobals* G, int size)
|
|
: G(G)
|
|
{
|
|
op = VLACalloc(float, size + 32);
|
|
cgo_shader_ub_color = SettingGet<bool>(G, cSetting_cgo_shader_ub_color);
|
|
cgo_shader_ub_normal = SettingGet<bool>(G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
|
|
void CGOSetUseShader(CGO* I, int use_shader)
|
|
{
|
|
I->use_shader = use_shader;
|
|
if (use_shader) {
|
|
I->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color);
|
|
I->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal);
|
|
} else {
|
|
I->cgo_shader_ub_color = 0;
|
|
I->cgo_shader_ub_normal = 0;
|
|
}
|
|
}
|
|
void CGOReset(CGO* I)
|
|
{
|
|
I->c = 0;
|
|
I->z_flag = false;
|
|
I->alpha = 1.f;
|
|
I->has_begin_end = false;
|
|
I->has_draw_buffers = false;
|
|
I->has_draw_cylinder_buffers = false;
|
|
I->normal[0] = 0.f;
|
|
I->normal[1] = 0.f;
|
|
I->normal[2] = 1.f;
|
|
I->color[0] = 0.f;
|
|
I->color[1] = 0.f;
|
|
I->color[2] = 1.f;
|
|
I->pickColor[0] = 0;
|
|
I->pickColor[1] = 0;
|
|
I->pickColor[2] = 0;
|
|
I->pickColor[3] = 255;
|
|
I->current_accessibility = 1.f;
|
|
}
|
|
|
|
void CGOFree(CGO*& I, bool withVBOs)
|
|
{
|
|
if (I) {
|
|
if (!withVBOs) {
|
|
I->has_draw_buffers = false;
|
|
}
|
|
DeleteP(I);
|
|
}
|
|
}
|
|
|
|
CGO::~CGO()
|
|
{
|
|
if (has_draw_buffers) {
|
|
CGOFreeVBOs(this);
|
|
}
|
|
FreeP(i_start);
|
|
VLAFreeP(op);
|
|
}
|
|
|
|
static float* CGO_add(CGO* I, unsigned c)
|
|
{
|
|
float* at;
|
|
VLACheck(I->op, float, I->c + c);
|
|
if (!I->op) {
|
|
return nullptr;
|
|
}
|
|
at = I->op + I->c;
|
|
I->c += c;
|
|
return (at);
|
|
}
|
|
|
|
static float* CGO_size(CGO* I, int sz)
|
|
{
|
|
float* at;
|
|
VLASize(I->op, float, sz);
|
|
if (!I->op) {
|
|
return nullptr;
|
|
}
|
|
at = I->op + I->c;
|
|
I->c = sz;
|
|
return (at);
|
|
}
|
|
|
|
/*===== Object Creation Routines =======*/
|
|
|
|
int CGOFromFloatArray(CGO* I, const float* src, int len)
|
|
{
|
|
int iarg;
|
|
int ok;
|
|
int all_ok = true;
|
|
int bad_entry = 0;
|
|
int sz;
|
|
int a;
|
|
int cc = 0;
|
|
float val;
|
|
float *pc, *save_pc, *tf;
|
|
VLACheck(I->op, float, I->c + len + 32);
|
|
save_pc = I->op + I->c;
|
|
while (len-- > 0) {
|
|
cc++;
|
|
const auto op = static_cast<unsigned>(*(src++));
|
|
|
|
if (op >= CGO_sz_size()) {
|
|
bad_entry = cc;
|
|
break;
|
|
}
|
|
|
|
sz = CGO_sz[op];
|
|
if (len < sz)
|
|
break; /* discard short instruction */
|
|
len -= sz;
|
|
pc = save_pc;
|
|
CGO_write_int(pc, op);
|
|
ok = true;
|
|
for (a = 0; a < sz; a++) {
|
|
cc++;
|
|
val = *(src++);
|
|
if (std::abs(val) <= R_SMALL8) {
|
|
val = 0;
|
|
}
|
|
if ((FLT_MAX - val) > 0.0F) { /* make sure we have a real float */
|
|
*(pc++) = val;
|
|
} else {
|
|
*(pc++) = 0.0;
|
|
ok = false;
|
|
}
|
|
}
|
|
if (ok) {
|
|
switch (op) {
|
|
case CGO_END:
|
|
case CGO_VERTEX:
|
|
case CGO_BEGIN:
|
|
I->has_begin_end = true;
|
|
}
|
|
switch (op) { /* now convert any instructions with int arguments */
|
|
case CGO_BEGIN:
|
|
case CGO_ENABLE:
|
|
case CGO_DISABLE:
|
|
case CGO_SPECIAL:
|
|
tf = save_pc + 1;
|
|
iarg = (int) *(tf);
|
|
CGO_write_int(tf, iarg);
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
tf = save_pc + 1;
|
|
CGO_write_int(tf, int(save_pc[1])); // atom index
|
|
CGO_write_int(tf, int(save_pc[2])); // bond index
|
|
break;
|
|
}
|
|
save_pc = pc;
|
|
I->c += sz + 1;
|
|
} else { /* discard illegal instructions */
|
|
if (all_ok)
|
|
bad_entry = cc;
|
|
all_ok = false;
|
|
}
|
|
}
|
|
return (bad_entry);
|
|
}
|
|
|
|
int CGOBegin(CGO* I, int mode)
|
|
{
|
|
float* pc = CGO_add(I, CGO_BEGIN_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_BEGIN);
|
|
CGO_write_int(pc, mode);
|
|
I->has_begin_end = true;
|
|
I->texture[0] = 0.f;
|
|
I->texture[1] = 0.f;
|
|
return true;
|
|
}
|
|
|
|
int CGOEnd(CGO* I)
|
|
{
|
|
float* pc = CGO_add(I, CGO_END_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_END);
|
|
I->has_begin_end = true;
|
|
return true;
|
|
}
|
|
|
|
int CGOEnable(CGO* I, int mode)
|
|
{
|
|
float* pc = CGO_add(I, CGO_ENABLE_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_ENABLE);
|
|
CGO_write_int(pc, mode);
|
|
return true;
|
|
}
|
|
|
|
int CGODisable(CGO* I, int mode)
|
|
{
|
|
float* pc = CGO_add(I, CGO_DISABLE_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_DISABLE);
|
|
CGO_write_int(pc, mode);
|
|
return true;
|
|
}
|
|
|
|
int CGOLinewidth(CGO* I, float v)
|
|
{
|
|
float* pc = CGO_add(I, CGO_LINEWIDTH_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_LINEWIDTH);
|
|
*(pc++) = v;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* implements special-case operations inside a CGO
|
|
*
|
|
* v: lookup value defined for each special operation (see CGO.h)
|
|
*/
|
|
int CGOSpecial(CGO* I, int v)
|
|
{
|
|
float* pc = CGO_add(I, CGO_SPECIAL_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_SPECIAL);
|
|
CGO_write_int(pc, v);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* implements special-case operations with an argument
|
|
* inside a CGO
|
|
*
|
|
* v: lookup value defined for each special operation (see CGO.h)
|
|
* argval : argument value
|
|
*/
|
|
int CGOSpecialWithArg(CGO* I, int v, float argval)
|
|
{
|
|
float* pc = CGO_add(I, CGO_SPECIAL_WITH_ARG_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_SPECIAL_WITH_ARG);
|
|
CGO_write_int(pc, v);
|
|
*pc = argval;
|
|
return true;
|
|
}
|
|
|
|
int CGODotwidth(CGO* I, float v)
|
|
{
|
|
float* pc = CGO_add(I, CGO_DOTWIDTH_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_DOTWIDTH);
|
|
*(pc++) = v;
|
|
return true;
|
|
}
|
|
|
|
/* CGOUniform3f - specifies a 3f uniform variable and
|
|
its value. This function returns the offset of where
|
|
these values are stored inside the CGO float array
|
|
so that they can be accessed and changed from outside
|
|
the CGO.
|
|
|
|
*/
|
|
int CGOUniform3f(CGO* I, int uniform_id, const float* value)
|
|
{
|
|
float* pc = CGO_add(I, CGO_UNIFORM3F_SZ + 1);
|
|
if (!pc)
|
|
return 0;
|
|
CGO_write_int(pc, CGO_UNIFORM3F);
|
|
CGO_write_int(pc, uniform_id);
|
|
copy3f(value, pc);
|
|
return pc - I->op;
|
|
}
|
|
|
|
int CGOBoundingBox(CGO* I, const float* min, const float* max)
|
|
{
|
|
float* pc = CGO_add(I, CGO_BOUNDING_BOX_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_BOUNDING_BOX);
|
|
*(pc++) = *(min);
|
|
*(pc++) = *(min + 1);
|
|
*(pc++) = *(min + 2);
|
|
*(pc++) = *(max);
|
|
*(pc++) = *(max + 1);
|
|
*(pc++) = *(max + 2);
|
|
return true;
|
|
}
|
|
|
|
int CGOAccessibility(CGO* I, float a)
|
|
{
|
|
float* pc = CGO_add(I, CGO_ACCESSIBILITY_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_ACCESSIBILITY);
|
|
*(pc++) = a;
|
|
return true;
|
|
}
|
|
|
|
int CGODrawTexture(CGO* I, float* worldPos, float* screenMin, float* screenMax,
|
|
float* textExtent)
|
|
{
|
|
float* pc = CGO_add(I, CGO_DRAW_TEXTURE_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_DRAW_TEXTURE);
|
|
*(pc++) = worldPos[0];
|
|
*(pc++) = worldPos[1];
|
|
*(pc++) = worldPos[2];
|
|
*(pc++) = screenMin[0];
|
|
*(pc++) = screenMin[1];
|
|
*(pc++) = screenMin[2];
|
|
*(pc++) = screenMax[0];
|
|
*(pc++) = screenMax[1];
|
|
*(pc++) = screenMax[2];
|
|
*(pc++) = textExtent[0];
|
|
*(pc++) = textExtent[1];
|
|
*(pc++) = textExtent[2];
|
|
*(pc++) = textExtent[3];
|
|
return true;
|
|
}
|
|
|
|
int CGODrawConnector(CGO* I, float* targetPt3d, float* labelCenterPt3d,
|
|
float text_width, float text_height, float* indentFactor,
|
|
float* screenWorldOffset, float* connectorColor, short relativeMode,
|
|
int draw_flags, float bkgrd_transp, float* bkgrd_color,
|
|
float rel_ext_length, float connectorWidth)
|
|
{
|
|
float* pc = CGO_add(I, CGO_DRAW_CONNECTOR_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_DRAW_CONNECTOR);
|
|
*(pc++) = targetPt3d[0];
|
|
*(pc++) = targetPt3d[1];
|
|
*(pc++) = targetPt3d[2];
|
|
*(pc++) = labelCenterPt3d[0];
|
|
*(pc++) = labelCenterPt3d[1];
|
|
*(pc++) = labelCenterPt3d[2];
|
|
*(pc++) = indentFactor[0];
|
|
*(pc++) = indentFactor[1];
|
|
*(pc++) = rel_ext_length; /* place for ext_length relative to height (i.e.,
|
|
text_height which is total height */
|
|
*(pc++) = screenWorldOffset[0];
|
|
*(pc++) = screenWorldOffset[1];
|
|
*(pc++) = screenWorldOffset[2];
|
|
*(pc++) = text_width;
|
|
*(pc++) = text_height;
|
|
*(pc++) = connectorColor[0];
|
|
*(pc++) = connectorColor[1];
|
|
*(pc++) = connectorColor[2];
|
|
*(pc++) = (float) relativeMode;
|
|
*(pc++) = (float) draw_flags;
|
|
*(pc++) = bkgrd_color[0];
|
|
*(pc++) = bkgrd_color[1];
|
|
*(pc++) = bkgrd_color[2];
|
|
*(pc++) = bkgrd_transp;
|
|
*(pc++) = connectorWidth; // place for label_connector_width
|
|
return true;
|
|
}
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
int CGODrawLabel(CGO* I, int texture_id, float* targetPos, float* worldPos,
|
|
float* screenWorldOffset, float* screenMin, float* screenMax,
|
|
float* textExtent, short relativeMode)
|
|
{
|
|
float* pc = CGO_add(I, CGO_DRAW_LABEL_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_DRAW_LABEL);
|
|
*(pc++) = worldPos[0];
|
|
*(pc++) = worldPos[1];
|
|
*(pc++) = worldPos[2];
|
|
*(pc++) = screenWorldOffset[0];
|
|
*(pc++) = screenWorldOffset[1];
|
|
*(pc++) = screenWorldOffset[2];
|
|
*(pc++) = screenMin[0];
|
|
*(pc++) = screenMin[1];
|
|
*(pc++) = screenMin[2];
|
|
*(pc++) = screenMax[0];
|
|
*(pc++) = screenMax[1];
|
|
*(pc++) = screenMax[2];
|
|
*(pc++) = textExtent[0];
|
|
*(pc++) = textExtent[1];
|
|
*(pc++) = textExtent[2];
|
|
*(pc++) = textExtent[3];
|
|
*(pc++) = (float) relativeMode;
|
|
*(pc++) = targetPos[0];
|
|
*(pc++) = targetPos[1];
|
|
*(pc++) = targetPos[2];
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
int CGOConev(CGO* I, const float* p1, const float* p2, float r1, float r2,
|
|
const float* c1, const float* c2, float cap1, float cap2)
|
|
{
|
|
float* pc = CGO_add(I, CGO_CONE_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_CONE);
|
|
*(pc++) = *(p1++);
|
|
*(pc++) = *(p1++);
|
|
*(pc++) = *(p1++);
|
|
*(pc++) = *(p2++);
|
|
*(pc++) = *(p2++);
|
|
*(pc++) = *(p2++);
|
|
*(pc++) = r1;
|
|
*(pc++) = r2;
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = cap1;
|
|
*(pc++) = cap2;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
int CGOPickColor(CGO* I, unsigned int index, int bond)
|
|
{
|
|
// check if uchar is -1 since extrude does this for masked atoms
|
|
if (index == (unsigned int) -1) {
|
|
bond = cPickableNoPick;
|
|
}
|
|
if (I->current_pick_color_index == index &&
|
|
I->current_pick_color_bond == bond)
|
|
return true;
|
|
float* pc = CGO_add(I, CGO_PICK_COLOR_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_PICK_COLOR);
|
|
CGO_write_uint(pc, index);
|
|
CGO_write_int(pc, bond);
|
|
I->current_pick_color_index = index;
|
|
I->current_pick_color_bond = bond;
|
|
return true;
|
|
}
|
|
|
|
int CGOAlpha(CGO* I, float alpha)
|
|
{
|
|
float* pc = CGO_add(I, CGO_ALPHA_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_ALPHA);
|
|
*(pc++) = alpha;
|
|
I->alpha = alpha;
|
|
return true;
|
|
}
|
|
|
|
int CGOSphere(CGO* I, const float* v1, float r)
|
|
{
|
|
float* pc = CGO_add(I, CGO_SPHERE_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_SPHERE);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = r;
|
|
return true;
|
|
}
|
|
|
|
int CGOEllipsoid(CGO* I, const float* v1, float r, const float* n1,
|
|
const float* n2, const float* n3)
|
|
{
|
|
float* pc = CGO_add(I, CGO_ELLIPSOID_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_ELLIPSOID);
|
|
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = r;
|
|
*(pc++) = *(n1++);
|
|
*(pc++) = *(n1++);
|
|
*(pc++) = *(n1++);
|
|
*(pc++) = *(n2++);
|
|
*(pc++) = *(n2++);
|
|
*(pc++) = *(n2++);
|
|
*(pc++) = *(n3++);
|
|
*(pc++) = *(n3++);
|
|
*(pc++) = *(n3++);
|
|
return true;
|
|
}
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
int CGOQuadric(CGO* I, const float* v, float r, const float* q)
|
|
{
|
|
float* pc = CGO_add(I, CGO_QUADRIC_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_QUADRIC);
|
|
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = r;
|
|
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
*(pc++) = *(q++);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
void CGOSetZVector(CGO* I, float z0, float z1, float z2)
|
|
{
|
|
I->z_flag = true;
|
|
I->z_vector[0] = z0;
|
|
I->z_vector[1] = z1;
|
|
I->z_vector[2] = z2;
|
|
I->z_min = FLT_MAX;
|
|
I->z_max = -FLT_MAX;
|
|
}
|
|
|
|
const static float one_third = 1.0F / 3.0F;
|
|
const static float _0 = 0.0F;
|
|
|
|
int CGOAlphaTriangle(CGO* I, const float* v1, const float* v2, const float* v3,
|
|
const float* n1, const float* n2, const float* n3, const float* c1,
|
|
const float* c2, const float* c3, float a1, float a2, float a3, int reverse)
|
|
{
|
|
if (v1 && v2 && v3) {
|
|
float* pc = CGO_add(I, CGO_ALPHA_TRIANGLE_SZ + 1);
|
|
float z = _0;
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_ALPHA_TRIANGLE);
|
|
CGO_write_int(pc, 0); // this is the place for the next triangle in the bin
|
|
*(pc++) = (v1[0] + v2[0] + v3[0]) * one_third;
|
|
*(pc++) = (v1[1] + v2[1] + v3[1]) * one_third;
|
|
*(pc++) = (v1[2] + v2[2] + v3[2]) * one_third;
|
|
if (I->z_flag) {
|
|
float* zv = I->z_vector;
|
|
z = pc[-3] * zv[0] + pc[-2] * zv[1] + pc[-1] * zv[2];
|
|
if (z > I->z_max)
|
|
I->z_max = z;
|
|
if (z < I->z_min)
|
|
I->z_min = z;
|
|
}
|
|
*(pc++) = z;
|
|
|
|
if (reverse) {
|
|
*(pc++) = *(v2++); /* vertices @ +5 */
|
|
*(pc++) = *(v2++);
|
|
*(pc++) = *(v2++);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v1++);
|
|
} else {
|
|
*(pc++) = *(v1++); /* vertices @ +5 */
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v1++);
|
|
*(pc++) = *(v2++);
|
|
*(pc++) = *(v2++);
|
|
*(pc++) = *(v2++);
|
|
}
|
|
|
|
*(pc++) = *(v3++);
|
|
*(pc++) = *(v3++);
|
|
*(pc++) = *(v3++);
|
|
|
|
if (reverse) {
|
|
*(pc++) = *(n2++); /* normals @ +14 */
|
|
*(pc++) = *(n2++);
|
|
*(pc++) = *(n2++);
|
|
*(pc++) = *(n1++);
|
|
*(pc++) = *(n1++);
|
|
*(pc++) = *(n1++);
|
|
} else {
|
|
*(pc++) = *(n1++); /* normals @ +14 */
|
|
*(pc++) = *(n1++);
|
|
*(pc++) = *(n1++);
|
|
*(pc++) = *(n2++);
|
|
*(pc++) = *(n2++);
|
|
*(pc++) = *(n2++);
|
|
}
|
|
*(pc++) = *(n3++);
|
|
*(pc++) = *(n3++);
|
|
*(pc++) = *(n3++);
|
|
|
|
if (reverse) {
|
|
*(pc++) = *(c2++); /* colors @ +23 */
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = a2;
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = a1;
|
|
} else {
|
|
*(pc++) = *(c1++); /* colors @ +23 */
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = *(c1++);
|
|
*(pc++) = a1;
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = *(c2++);
|
|
*(pc++) = a2;
|
|
}
|
|
*(pc++) = *(c3++);
|
|
*(pc++) = *(c3++);
|
|
*(pc++) = *(c3++);
|
|
*(pc++) = a3;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int CGOVertex(CGO* I, float v1, float v2, float v3)
|
|
{
|
|
float* pc = CGO_add(I, CGO_VERTEX_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_VERTEX);
|
|
*(pc++) = v1;
|
|
*(pc++) = v2;
|
|
*(pc++) = v3;
|
|
return true;
|
|
}
|
|
|
|
int CGOVertexv(CGO* I, const float* v)
|
|
{
|
|
float* pc = CGO_add(I, CGO_VERTEX_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_VERTEX);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
return true;
|
|
}
|
|
|
|
int CGOVertexCrossv(CGO* I, const float* v)
|
|
{
|
|
float* pc = CGO_add(I, CGO_VERTEX_CROSS_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_VERTEX_CROSS);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
return true;
|
|
}
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
int CGOInterpolated(CGO* I, const bool interp)
|
|
{
|
|
float* pc = CGO_add(I, CGO_INTERPOLATED_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_INTERPOLATED);
|
|
*(pc++) = interp ? 1.f : 0.f;
|
|
I->interpolated = interp;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
int CGOColor(CGO* I, float v1, float v2, float v3)
|
|
{
|
|
float* pc = CGO_add(I, CGO_COLOR_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_COLOR);
|
|
*(pc++) = v1;
|
|
*(pc++) = v2;
|
|
*(pc++) = v3;
|
|
I->color[0] = v1;
|
|
I->color[1] = v2;
|
|
I->color[2] = v3;
|
|
return true;
|
|
}
|
|
|
|
int CGOColorv(CGO* I, const float* v)
|
|
{
|
|
return CGOColor(I, v[0], v[1], v[2]);
|
|
}
|
|
|
|
int CGOTexCoord2f(CGO* I, float v1, float v2)
|
|
{
|
|
float* pc = CGO_add(I, CGO_TEX_COORD_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_TEX_COORD);
|
|
*(pc++) = v1;
|
|
*(pc++) = v2;
|
|
I->texture[0] = v1;
|
|
I->texture[1] = v2;
|
|
return true;
|
|
}
|
|
|
|
int CGONormal(CGO* I, float v1, float v2, float v3)
|
|
{
|
|
float* pc = CGO_add(I, CGO_NORMAL_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_NORMAL);
|
|
*(pc++) = v1;
|
|
*(pc++) = v2;
|
|
*(pc++) = v3;
|
|
I->normal[0] = v1;
|
|
I->normal[1] = v2;
|
|
I->normal[2] = v3;
|
|
return true;
|
|
}
|
|
|
|
int CGOResetNormal(CGO* I, int mode)
|
|
{
|
|
float* pc = CGO_add(I, CGO_RESET_NORMAL_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_RESET_NORMAL);
|
|
CGO_write_int(pc, mode);
|
|
SceneGetResetNormal(I->G, I->normal, mode);
|
|
return true;
|
|
}
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
int CGOFontVertexv(CGO* I, const float* v)
|
|
{
|
|
float* pc = CGO_add(I, CGO_FONT_VERTEX_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_FONT_VERTEX);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
return true;
|
|
}
|
|
|
|
int CGOFontVertex(CGO* I, float x, float y, float z)
|
|
{
|
|
float* pc = CGO_add(I, CGO_FONT_VERTEX_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_FONT_VERTEX);
|
|
*(pc++) = x;
|
|
*(pc++) = y;
|
|
*(pc++) = z;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
int CGOFontScale(CGO* I, float v1, float v2)
|
|
{
|
|
float* pc = CGO_add(I, CGO_FONT_SCALE_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_FONT_SCALE);
|
|
*(pc++) = v1;
|
|
*(pc++) = v2;
|
|
return true;
|
|
}
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
int CGOChar(CGO* I, char c)
|
|
{
|
|
float* pc = CGO_add(I, CGO_CHAR_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_CHAR);
|
|
*(pc++) = (float) c;
|
|
return true;
|
|
}
|
|
|
|
int CGOIndent(CGO* I, char c, float dir)
|
|
{
|
|
float* pc = CGO_add(I, CGO_INDENT_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_INDENT);
|
|
*(pc++) = (float) c;
|
|
*(pc++) = dir;
|
|
return true;
|
|
}
|
|
|
|
int CGOWrite(CGO* I, const char* str)
|
|
{
|
|
float* pc;
|
|
|
|
while (*str) {
|
|
pc = CGO_add(I, CGO_CHAR_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_CHAR);
|
|
*(pc++) = (float) *(str++);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int CGOWriteLeft(CGO* I, const char* str)
|
|
{
|
|
float* pc;
|
|
const char* s = str;
|
|
while (*s) {
|
|
pc = CGO_add(I, CGO_INDENT_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_INDENT);
|
|
*(pc++) = (float) *(s++);
|
|
*(pc++) = -1.0F;
|
|
}
|
|
s = str;
|
|
while (*s) {
|
|
pc = CGO_add(I, CGO_CHAR_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_CHAR);
|
|
*(pc++) = (float) *(s++);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int CGOWriteIndent(CGO* I, const char* str, float indent)
|
|
{
|
|
float* pc;
|
|
const char* s = str;
|
|
while (*s) {
|
|
pc = CGO_add(I, CGO_INDENT_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_INDENT);
|
|
*(pc++) = (float) *(s++);
|
|
*(pc++) = indent;
|
|
}
|
|
s = str;
|
|
while (*s) {
|
|
pc = CGO_add(I, CGO_CHAR_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_CHAR);
|
|
*(pc++) = (float) *(s++);
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
int CGONormalv(CGO* I, const float* v)
|
|
{
|
|
float* pc = CGO_add(I, CGO_NORMAL_SZ + 1);
|
|
if (!pc)
|
|
return false;
|
|
CGO_write_int(pc, CGO_NORMAL);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
*(pc++) = *(v++);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Add a null terminator to the CGO buffer, but don't increment
|
|
* the size variable (CGO::c).
|
|
*/
|
|
int CGOStop(CGO* I)
|
|
{
|
|
#define CGO_STOP_ZEROS 1
|
|
|
|
float* pc = CGO_size(I, I->c + CGO_STOP_ZEROS);
|
|
if (!pc)
|
|
return false;
|
|
UtilZeroMem(pc, sizeof(float) * CGO_STOP_ZEROS);
|
|
I->c -= CGO_STOP_ZEROS;
|
|
return true;
|
|
}
|
|
|
|
int CGOCheckComplex(CGO* I)
|
|
{
|
|
int fc = 0;
|
|
const SphereRec* sp = I->G->Sphere->Sphere[1];
|
|
|
|
/* stick_quality needs to match *every* CGO? */
|
|
auto nEdge = SettingGet<int>(I->G, cSetting_stick_quality);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_CYLINDER:
|
|
case CGO_CONE:
|
|
case CGO_SAUSAGE:
|
|
case CGO_CUSTOM_CYLINDER:
|
|
case CGO_CUSTOM_CYLINDER_ALPHA:
|
|
fc += 3 * (3 + (nEdge + 1) * 9) + 9;
|
|
break;
|
|
case CGO_ELLIPSOID:
|
|
case CGO_QUADRIC:
|
|
case CGO_SPHERE:
|
|
fc += (sp->NVertTot * 6) + (sp->NStrip * 3) + 3;
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
cgo::draw::arrays* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
fc += sp->nverts;
|
|
} break;
|
|
case CGO_DRAW_BUFFERS_INDEXED: {
|
|
cgo::draw::buffers_indexed* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLES:
|
|
fc += sp->nindices / 3;
|
|
break;
|
|
case GL_LINES:
|
|
fc += sp->nindices / 2;
|
|
break;
|
|
}
|
|
} break;
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED: {
|
|
cgo::draw::buffers_not_indexed* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLES:
|
|
fc += sp->nverts / 3;
|
|
break;
|
|
case GL_LINES:
|
|
fc += sp->nverts / 2;
|
|
break;
|
|
}
|
|
} break;
|
|
case CGO_DRAW_SPHERE_BUFFERS: {
|
|
cgo::draw::sphere_buffers* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
fc += sp->num_spheres * VerticesPerSphere();
|
|
} break;
|
|
case CGO_DRAW_CYLINDER_BUFFERS: {
|
|
cgo::draw::cylinder_buffers* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
fc += sp->num_cyl * NumVerticesPerCylinder();
|
|
} break;
|
|
}
|
|
}
|
|
return (fc);
|
|
}
|
|
|
|
int CGOPreloadFonts(CGO* I)
|
|
{
|
|
int ok = true;
|
|
int font_seen = false;
|
|
int font_id;
|
|
|
|
auto blocked = PAutoBlock(I->G);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_FONT:
|
|
ok = ok && (VFontLoad(I->G, 1.0, 1, 1, true));
|
|
font_seen = true;
|
|
break;
|
|
case CGO_CHAR:
|
|
if (!font_seen) {
|
|
font_id = VFontLoad(I->G, 1.0, 1, 1, true);
|
|
ok = ok && font_id;
|
|
font_seen = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (blocked)
|
|
PUnblock(I->G);
|
|
return (ok);
|
|
}
|
|
|
|
int CGOCheckForText(CGO* I)
|
|
{
|
|
int fc = 0;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_FONT:
|
|
case CGO_FONT_AXES:
|
|
case CGO_FONT_SCALE:
|
|
fc++;
|
|
break;
|
|
case CGO_INDENT:
|
|
case CGO_FONT_VERTEX:
|
|
fc++;
|
|
break;
|
|
case CGO_CHAR:
|
|
fc += 3 + 2 * 3 * 10; /* est 10 lines per char */
|
|
break;
|
|
}
|
|
}
|
|
PRINTFD(I->G, FB_CGO)
|
|
" CGOCheckForText-Debug: %d\n", fc ENDFD;
|
|
|
|
return (fc);
|
|
}
|
|
|
|
CGO* CGODrawText(const CGO* I, int est, float* camera)
|
|
{ /* assumes blocked intepreter */
|
|
CGO* cgo;
|
|
int font_id = 0;
|
|
char text[2] = " ";
|
|
float pos[] = {0.0F, 0.0F, 0.0F};
|
|
float axes[] = {1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F};
|
|
float scale[2] = {1.0, 1.0};
|
|
|
|
cgo = CGONewSized(I->G, I->c + est);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_FONT:
|
|
break;
|
|
case CGO_FONT_AXES:
|
|
break;
|
|
case CGO_FONT_SCALE:
|
|
scale[0] = pc[0];
|
|
scale[1] = pc[1];
|
|
break;
|
|
case CGO_FONT_VERTEX:
|
|
copy3f(pc, pos);
|
|
break;
|
|
case CGO_INDENT:
|
|
text[0] = (unsigned char) *pc;
|
|
VFontIndent(I->G, font_id, text, pos, scale, axes, pc[1]);
|
|
break;
|
|
case CGO_CHAR:
|
|
if (!font_id) {
|
|
font_id = VFontLoad(I->G, 1.0, 1, 1, false);
|
|
}
|
|
text[0] = (unsigned char) *pc;
|
|
VFontWriteToCGO(I->G, font_id, cgo, text, pos, scale, axes, cgo->color);
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
CGOStop(cgo);
|
|
if (cgo && cgo->has_begin_end) {
|
|
/* this is mainly for VFontWriteToCGO() that still creates CGOBegin/CGOEnd
|
|
*/
|
|
if (cgo && cgo->has_begin_end) {
|
|
CGO* convertcgo = nullptr;
|
|
convertcgo = CGOCombineBeginEnd(cgo, 0);
|
|
CGOFree(cgo);
|
|
cgo = convertcgo;
|
|
}
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
static void CGOAddVertexToDrawArrays(CGO* cgo, int pl, int plc, int pla,
|
|
const float* vertex, short notHaveValue, float* vertexVals,
|
|
float* normalVals, float* colorVals, float* pickColorVals,
|
|
float* accessibilityVals)
|
|
{
|
|
float* tmp_ptr;
|
|
if (notHaveValue & CGO_NORMAL_ARRAY) {
|
|
if (pl) {
|
|
tmp_ptr = &normalVals[pl - 3];
|
|
copy3f(tmp_ptr, &normalVals[pl]);
|
|
} else {
|
|
copy3f(cgo->normal, &normalVals[pl]);
|
|
}
|
|
}
|
|
if (notHaveValue & CGO_COLOR_ARRAY) {
|
|
if (plc) {
|
|
tmp_ptr = &colorVals[plc - 4];
|
|
copy4f(tmp_ptr, &colorVals[plc]);
|
|
} else {
|
|
copy3f(&colorVals[plc], cgo->color);
|
|
colorVals[plc + 3] = cgo->alpha;
|
|
}
|
|
}
|
|
if (pickColorVals) {
|
|
CGO_put_uint(pickColorVals + pla * 2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + pla * 2 + 1, cgo->current_pick_color_bond);
|
|
}
|
|
if (accessibilityVals) {
|
|
accessibilityVals[pla] = cgo->current_accessibility;
|
|
}
|
|
copy3f(vertex, &vertexVals[pl]);
|
|
}
|
|
|
|
bool CGOCombineBeginEnd(CGO** I, bool do_not_split_lines)
|
|
{
|
|
CGO* cgo = CGOCombineBeginEnd(*I, 0, do_not_split_lines);
|
|
CGOFree(*I);
|
|
*I = cgo;
|
|
return (cgo != nullptr);
|
|
}
|
|
|
|
/**
|
|
* Converts Begin/End blocks into CGO_DRAW_ARRAYS operations.
|
|
*
|
|
* @param I Primitive CGO to convert
|
|
* @param est Output CGO size estimate (size of buffer to "reserve")
|
|
* @param do_not_split_lines ???
|
|
*
|
|
* @return New converted CGO with has_begin_end=false
|
|
*/
|
|
CGO* CGOCombineBeginEnd(const CGO* I, int est, bool do_not_split_lines)
|
|
{
|
|
CGO* cgo;
|
|
|
|
int ok = true;
|
|
if (!I)
|
|
return nullptr;
|
|
cgo = CGONewSized(I->G, 0);
|
|
ok &= cgo ? true : false;
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_END:
|
|
case CGO_VERTEX:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOCombineBeginEnd: op=0x%02x encountered without CGO_BEGIN\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_BEGIN: {
|
|
float firstColor[3], firstAlpha;
|
|
char hasFirstColor = 0, hasFirstAlpha = 0;
|
|
int nverts = 0, damode = CGO_VERTEX_ARRAY, err = 0;
|
|
|
|
// read int argument of the BEGIN operation
|
|
int mode = CGO_get_int(it.data());
|
|
++it;
|
|
|
|
// we want to iterate twice over the BEGIN/END block
|
|
auto it2 = it;
|
|
|
|
// first iteration over BEGIN/END block (consumes 'it')
|
|
for (; !err && it != CGO_END; ++it) {
|
|
auto pc = it.data();
|
|
switch (it.op_code()) {
|
|
case CGO_DRAW_ARRAYS:
|
|
case CGO_STOP:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
" CGO-Error: CGOCombineBeginEnd: invalid op=0x%02x inside "
|
|
"BEGIN/END\n",
|
|
it.op_code() ENDFB(I->G);
|
|
err = true;
|
|
continue;
|
|
case CGO_NORMAL:
|
|
damode |= CGO_NORMAL_ARRAY;
|
|
break;
|
|
case CGO_COLOR:
|
|
if (!nverts) {
|
|
hasFirstColor = 1;
|
|
copy3f(pc, firstColor);
|
|
} else {
|
|
hasFirstColor = 0;
|
|
damode |= CGO_COLOR_ARRAY;
|
|
}
|
|
copy3f(pc, cgo->color);
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
damode |= CGO_PICK_COLOR_ARRAY;
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
damode |= CGO_ACCESSIBILITY_ARRAY;
|
|
break;
|
|
case CGO_VERTEX:
|
|
nverts++;
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
if (!nverts) {
|
|
hasFirstAlpha = 1;
|
|
firstAlpha = cgo->alpha;
|
|
} else {
|
|
hasFirstAlpha = 0;
|
|
damode |= CGO_COLOR_ARRAY;
|
|
}
|
|
break;
|
|
case CGO_LINE:
|
|
nverts += 2;
|
|
break;
|
|
case CGO_SPLITLINE: {
|
|
auto splitline = reinterpret_cast<const cgo::draw::splitline*>(pc);
|
|
if (do_not_split_lines ||
|
|
(splitline->flags & cgo::draw::splitline::equal_colors)) {
|
|
nverts += 2;
|
|
} else {
|
|
nverts += 4;
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
if (nverts > 0 && !err) {
|
|
int pl = 0, plc = 0, pla = 0;
|
|
float *vertexVals = nullptr, *normalVals = nullptr,
|
|
*colorVals = nullptr, *pickColorVals = nullptr,
|
|
*accessibilityVals = nullptr;
|
|
|
|
if (hasFirstAlpha || hasFirstColor) {
|
|
if (hasFirstAlpha) {
|
|
CGOAlpha(cgo, firstAlpha);
|
|
}
|
|
if (hasFirstColor) {
|
|
CGOColorv(cgo, firstColor);
|
|
}
|
|
}
|
|
|
|
float* nxtVals = cgo->add<cgo::draw::arrays>(mode, damode, nverts);
|
|
ok &= nxtVals ? true : false;
|
|
if (!ok)
|
|
continue;
|
|
|
|
assert(damode & CGO_VERTEX_ARRAY);
|
|
vertexVals = nxtVals;
|
|
nxtVals += VERTEX_POS_SIZE * nverts;
|
|
|
|
if (damode & CGO_NORMAL_ARRAY) {
|
|
normalVals = nxtVals;
|
|
nxtVals += nverts * VERTEX_NORMAL_SIZE;
|
|
}
|
|
|
|
if (damode & CGO_COLOR_ARRAY) {
|
|
colorVals = nxtVals;
|
|
nxtVals += nverts * VERTEX_COLOR_SIZE;
|
|
}
|
|
|
|
if (damode & CGO_PICK_COLOR_ARRAY) {
|
|
pickColorVals = nxtVals + VERTEX_PICKCOLOR_RGBA_SIZE * nverts;
|
|
nxtVals += nverts * VERTEX_PICKCOLOR_SIZE;
|
|
}
|
|
|
|
if (damode & CGO_ACCESSIBILITY_ARRAY) {
|
|
accessibilityVals = nxtVals;
|
|
nxtVals += nverts * VERTEX_ACCESSIBILITY_SIZE;
|
|
}
|
|
|
|
auto notHaveValue = damode;
|
|
|
|
// second iteration (with copy of iterator, doesn't consume 'it')
|
|
for (; ok && it2 != CGO_END; ++it2) {
|
|
auto pc = it2.data();
|
|
switch (it2.op_code()) {
|
|
case CGO_NORMAL:
|
|
copy3f(pc, &normalVals[pl]);
|
|
notHaveValue &= ~CGO_NORMAL_ARRAY;
|
|
break;
|
|
case CGO_COLOR:
|
|
if (colorVals) {
|
|
copy3f(pc, &colorVals[plc]);
|
|
colorVals[plc + 3] = cgo->alpha;
|
|
notHaveValue &= ~CGO_COLOR_ARRAY;
|
|
}
|
|
copy3f(pc, cgo->color);
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
notHaveValue &= ~CGO_PICK_COLOR_ARRAY;
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
cgo->current_accessibility = pc[0];
|
|
break;
|
|
case CGO_SPLITLINE: {
|
|
auto splitline = reinterpret_cast<const cgo::draw::splitline*>(pc);
|
|
float color2[] = {CONVERT_COLOR_VALUE(splitline->color2[0]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[1]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[2])};
|
|
if (do_not_split_lines ||
|
|
(splitline->flags & cgo::draw::splitline::equal_colors)) {
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex1,
|
|
notHaveValue, vertexVals, normalVals, colorVals,
|
|
pickColorVals, accessibilityVals);
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
notHaveValue = damode;
|
|
|
|
if (!(splitline->flags & cgo::draw::splitline::equal_colors)) {
|
|
if (colorVals) {
|
|
copy3f(color2, &colorVals[plc]);
|
|
colorVals[plc + 3] = cgo->alpha;
|
|
notHaveValue = notHaveValue & ~CGO_COLOR_ARRAY;
|
|
}
|
|
copy3f(color2, cgo->color);
|
|
}
|
|
if (pickColorVals) {
|
|
cgo->current_pick_color_index = splitline->index;
|
|
cgo->current_pick_color_bond = splitline->bond;
|
|
notHaveValue = notHaveValue & ~CGO_PICK_COLOR_ARRAY;
|
|
}
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex2,
|
|
notHaveValue, vertexVals, normalVals, colorVals,
|
|
pickColorVals, accessibilityVals);
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
notHaveValue = damode;
|
|
} else {
|
|
float mid[3];
|
|
add3f(splitline->vertex1, splitline->vertex2, mid);
|
|
mult3f(mid, .5f, mid);
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex1,
|
|
notHaveValue, vertexVals, normalVals, colorVals,
|
|
pickColorVals, accessibilityVals);
|
|
notHaveValue = damode;
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, mid, notHaveValue,
|
|
vertexVals, normalVals, colorVals, pickColorVals,
|
|
accessibilityVals);
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
if (colorVals) {
|
|
copy3f(color2, &colorVals[plc]);
|
|
colorVals[plc + 3] = cgo->alpha;
|
|
notHaveValue = notHaveValue & ~CGO_COLOR_ARRAY;
|
|
}
|
|
copy3f(color2, cgo->color);
|
|
if (pickColorVals) {
|
|
cgo->current_pick_color_index = splitline->index;
|
|
cgo->current_pick_color_bond = splitline->bond;
|
|
notHaveValue = notHaveValue & ~CGO_PICK_COLOR_ARRAY;
|
|
}
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, mid, notHaveValue,
|
|
vertexVals, normalVals, colorVals, pickColorVals,
|
|
accessibilityVals);
|
|
notHaveValue = damode;
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, splitline->vertex2,
|
|
notHaveValue, vertexVals, normalVals, colorVals,
|
|
pickColorVals, accessibilityVals);
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
notHaveValue = damode;
|
|
}
|
|
} break;
|
|
case CGO_LINE: {
|
|
auto line = reinterpret_cast<const cgo::draw::line*>(pc);
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, line->vertex1,
|
|
notHaveValue, vertexVals, normalVals, colorVals, pickColorVals,
|
|
accessibilityVals);
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
notHaveValue = damode;
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, line->vertex2,
|
|
notHaveValue, vertexVals, normalVals, colorVals, pickColorVals,
|
|
accessibilityVals);
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
} break;
|
|
case CGO_VERTEX:
|
|
CGOAddVertexToDrawArrays(cgo, pl, plc, pla, pc, notHaveValue,
|
|
vertexVals, normalVals, colorVals, pickColorVals,
|
|
accessibilityVals);
|
|
pl += 3;
|
|
plc += 4;
|
|
pla++;
|
|
notHaveValue = damode;
|
|
break;
|
|
case CGO_ALPHA:
|
|
// in case we're before CGO_COLOR
|
|
cgo->alpha = *pc;
|
|
if (colorVals) {
|
|
// in case we're after CGO_COLOR
|
|
colorVals[plc + 3] = *pc;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
cgo->add_to_cgo(op, pc);
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
if (ok) {
|
|
ok &= CGOStop(cgo);
|
|
if (ok) {
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
}
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
/**
|
|
* Release all shader resources from this CGO
|
|
*/
|
|
void CGOFreeVBOs(CGO* I)
|
|
{
|
|
constexpr bool freevbos = true;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_DRAW_TRILINES: {
|
|
unsigned buf = it.cast<cgo::draw::trilines>()->buffer;
|
|
if (freevbos)
|
|
I->G->ShaderMgr->AddVBOToFree(buf);
|
|
} break;
|
|
case CGO_DRAW_CUSTOM: {
|
|
auto sp = it.cast<cgo::draw::custom>();
|
|
if (freevbos) {
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->iboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
|
|
}
|
|
} break;
|
|
case CGO_DRAW_SPHERE_BUFFERS: {
|
|
auto sp = it.cast<cgo::draw::sphere_buffers>();
|
|
if (freevbos) {
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
|
|
}
|
|
} break;
|
|
case CGO_DRAW_LABELS: {
|
|
auto sp = it.cast<cgo::draw::labels>();
|
|
if (freevbos) {
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
|
|
}
|
|
} break;
|
|
case CGO_DRAW_TEXTURES: {
|
|
auto sp = it.cast<cgo::draw::textures>();
|
|
if (freevbos)
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
} break;
|
|
case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS: {
|
|
auto sp = it.cast<cgo::draw::screen_textures>();
|
|
if (freevbos)
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
} break;
|
|
case CGO_DRAW_CYLINDER_BUFFERS: {
|
|
auto sp = it.cast<cgo::draw::cylinder_buffers>();
|
|
if (freevbos) {
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->iboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
|
|
}
|
|
} break;
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED: {
|
|
auto sp = it.cast<cgo::draw::buffers_not_indexed>();
|
|
if (freevbos) {
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->pickvboid);
|
|
}
|
|
} break;
|
|
case CGO_DRAW_BUFFERS_INDEXED: {
|
|
auto sp = it.cast<cgo::draw::buffers_indexed>();
|
|
if (freevbos) {
|
|
I->G->ShaderMgr->freeGPUBuffers({sp->vboid, sp->iboid, sp->pickvboid});
|
|
}
|
|
} break;
|
|
case CGO_DRAW_CONNECTORS: {
|
|
auto sp = it.cast<cgo::draw::connectors>();
|
|
if (freevbos)
|
|
I->G->ShaderMgr->freeGPUBuffer(sp->vboid);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define set_min_max(mn, mx, pt) \
|
|
{ \
|
|
if (mn[0] > *pt) \
|
|
mn[0] = *pt; \
|
|
if (mn[1] > *(pt + 1)) \
|
|
mn[1] = *(pt + 1); \
|
|
if (mn[2] > *(pt + 2)) \
|
|
mn[2] = *(pt + 2); \
|
|
if (mx[0] < *pt) \
|
|
mx[0] = *pt; \
|
|
if (mx[1] < *(pt + 1)) \
|
|
mx[1] = *(pt + 1); \
|
|
if (mx[2] < *(pt + 2)) \
|
|
mx[2] = *(pt + 2); \
|
|
}
|
|
|
|
struct CGOCount
|
|
{
|
|
int num_total_vertices{};
|
|
int num_total_indexes{};
|
|
int num_total_vertices_lines{};
|
|
int num_total_indexes_lines{};
|
|
int num_total_vertices_points{};
|
|
};
|
|
|
|
static CGOCount CGOCountNumVertices(const CGO* I);
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
void CGOCountNumVerticesDEBUG(const CGO* I)
|
|
{
|
|
auto count = CGOCountNumVertices(I);
|
|
CGOCountNumVertices(I);
|
|
printf("CGOCountNumVerticesDEBUG: num_total_vertices=%d num_total_indexes=%d "
|
|
"num_total_vertices_lines=%d num_total_indexes_lines=%d "
|
|
"num_total_vertices_points=%d\n",
|
|
count.num_total_vertices, count.num_total_indexes,
|
|
count.num_total_vertices_lines, count.num_total_indexes_lines,
|
|
count.num_total_vertices_points);
|
|
}
|
|
#endif
|
|
|
|
static CGOCount CGOCountNumVertices(const CGO* I)
|
|
{
|
|
CGOCount count{};
|
|
short err = 0;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
|
|
err = 0;
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS: {
|
|
const auto sp = it.cast<cgo::draw::arrays>();
|
|
short shouldCompress = false, shouldCompressLines = false,
|
|
shouldCompressPoints = false;
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLE_FAN:
|
|
case GL_TRIANGLE_STRIP:
|
|
case GL_TRIANGLES:
|
|
shouldCompress = true;
|
|
break;
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP:
|
|
case GL_LINE_LOOP:
|
|
shouldCompressLines = true;
|
|
break;
|
|
case GL_POINTS:
|
|
shouldCompressPoints = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (!shouldCompress && !shouldCompressLines && !shouldCompressPoints) {
|
|
// skipped
|
|
} else if (shouldCompressLines) {
|
|
count.num_total_vertices_lines += sp->nverts;
|
|
switch (sp->mode) {
|
|
case GL_LINE_LOOP:
|
|
count.num_total_indexes_lines += 2 * sp->nverts;
|
|
break;
|
|
case GL_LINE_STRIP:
|
|
count.num_total_indexes_lines += 2 * (sp->nverts - 1);
|
|
break;
|
|
case GL_LINES:
|
|
count.num_total_indexes_lines += sp->nverts;
|
|
break;
|
|
}
|
|
} else if (shouldCompress) {
|
|
count.num_total_vertices += sp->nverts;
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLE_FAN:
|
|
count.num_total_indexes += 3 * (sp->nverts - 2);
|
|
break;
|
|
case GL_TRIANGLE_STRIP:
|
|
count.num_total_indexes += 3 * (sp->nverts - 2);
|
|
break;
|
|
case GL_TRIANGLES:
|
|
count.num_total_indexes += sp->nverts;
|
|
break;
|
|
}
|
|
} else if (shouldCompressPoints) {
|
|
count.num_total_vertices_points += sp->nverts;
|
|
}
|
|
} break;
|
|
case CGO_END:
|
|
if (!err) {
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOCountNumVertices: CGO_END encountered, should call "
|
|
"CGOCombineBeginEnd before CGOCountNumVertices\n" ENDFB(I->G);
|
|
err = true;
|
|
}
|
|
case CGO_VERTEX:
|
|
if (!err) {
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOCountNumVertices: CGO_VERTEX encountered, should call "
|
|
"CGOCombineBeginEnd before CGOCountNumVertices\n" ENDFB(I->G);
|
|
err = true;
|
|
}
|
|
case CGO_BEGIN:
|
|
if (!err) {
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOCountNumVertices: CGO_BEGIN encountered, should call "
|
|
"CGOCombineBeginEnd before CGOCountNumVertices\n" ENDFB(I->G);
|
|
err = true;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @param[in] I Primitive CGO
|
|
* @return number of vertices, or -1 on error
|
|
*/
|
|
static int CGOCountNumVerticesForScreen(const CGO* I)
|
|
{
|
|
auto G = I->G;
|
|
constexpr int MODE_INVALID = -1;
|
|
int begin_mode = MODE_INVALID;
|
|
int num_total_indexes = 0;
|
|
int nverts = 0;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
|
|
// sanity check: non-primitive operations
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return -1;
|
|
}
|
|
|
|
if (begin_mode == MODE_INVALID) {
|
|
switch (op) {
|
|
case CGO_BEGIN:
|
|
begin_mode = it.cast<cgo::draw::begin>()->mode;
|
|
break;
|
|
case CGO_VERTEX:
|
|
case CGO_END:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return -1;
|
|
}
|
|
} else {
|
|
switch (op) {
|
|
case CGO_BEGIN:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return -1;
|
|
case CGO_VERTEX:
|
|
++nverts;
|
|
break;
|
|
case CGO_END:
|
|
switch (begin_mode) {
|
|
case GL_TRIANGLE_FAN:
|
|
case GL_TRIANGLE_STRIP:
|
|
num_total_indexes += 3 * (nverts - 2);
|
|
break;
|
|
case GL_TRIANGLES:
|
|
num_total_indexes += nverts;
|
|
break;
|
|
default:
|
|
// not implemented
|
|
assert(false);
|
|
}
|
|
begin_mode = MODE_INVALID;
|
|
nverts = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return num_total_indexes;
|
|
}
|
|
|
|
static void SetVertexValuesForVBO(PyMOLGlobals* G, CGO* cgo, int pl, int plc,
|
|
int cnt, int incr, const float* vertexValsDA, const float* normalValsDA,
|
|
const float* colorValsDA, const float* pickColorValsDA, float* vertexVals,
|
|
uchar* normalValsC, float* normalVals, uchar* colorValsUC, float* colorVals,
|
|
float* pickColorVals, float* accessibilityVals = nullptr,
|
|
const float* accessibilityValsDA = nullptr)
|
|
{
|
|
int pl2 = pl + 1, pl3 = pl + 2;
|
|
int pln1 = VAR_FOR_NORMAL, pln2 = VAR_FOR_NORMAL + 1,
|
|
pln3 = VAR_FOR_NORMAL + 2;
|
|
int plc2 = plc + 1, plc3 = plc + 2, plc4 = plc + 3;
|
|
int c, c2, c3;
|
|
int cc, cc2, cc3, cc4;
|
|
int pcc = incr * 2, pcco = cnt * 2;
|
|
c = cnt * 3;
|
|
c2 = c + 1;
|
|
c3 = c + 2;
|
|
cc = cnt * 4;
|
|
cc2 = cc + 1;
|
|
cc3 = cc + 2;
|
|
cc4 = cc + 3;
|
|
vertexVals[pl] = vertexValsDA[c];
|
|
vertexVals[pl2] = vertexValsDA[c2];
|
|
vertexVals[pl3] = vertexValsDA[c3];
|
|
|
|
if (normalValsC) {
|
|
if (normalValsDA) {
|
|
normalValsC[pln1] = CLIP_NORMAL_VALUE(normalValsDA[c]);
|
|
normalValsC[pln2] = CLIP_NORMAL_VALUE(normalValsDA[c2]);
|
|
normalValsC[pln3] = CLIP_NORMAL_VALUE(normalValsDA[c3]);
|
|
} else {
|
|
normalValsC[pln1] = CLIP_NORMAL_VALUE(cgo->normal[0]);
|
|
normalValsC[pln2] = CLIP_NORMAL_VALUE(cgo->normal[1]);
|
|
normalValsC[pln3] = CLIP_NORMAL_VALUE(cgo->normal[2]);
|
|
}
|
|
|
|
#ifdef ALIGN_VBOS_TO_4_BYTE_ARRAYS
|
|
normalValsC[pln3 + 1] = 127;
|
|
#endif
|
|
} else {
|
|
if (normalValsDA) {
|
|
normalVals[pln1] = normalValsDA[c];
|
|
normalVals[pln2] = normalValsDA[c2];
|
|
normalVals[pln3] = normalValsDA[c3];
|
|
} else {
|
|
normalVals[pln1] = cgo->normal[0];
|
|
normalVals[pln2] = cgo->normal[1];
|
|
normalVals[pln3] = cgo->normal[2];
|
|
}
|
|
}
|
|
|
|
if (colorValsUC) {
|
|
if (colorValsDA) {
|
|
colorValsUC[plc] = CLIP_COLOR_VALUE(colorValsDA[cc]);
|
|
colorValsUC[plc2] = CLIP_COLOR_VALUE(colorValsDA[cc2]);
|
|
colorValsUC[plc3] = CLIP_COLOR_VALUE(colorValsDA[cc3]);
|
|
colorValsUC[plc4] = CLIP_COLOR_VALUE(colorValsDA[cc4]);
|
|
} else {
|
|
colorValsUC[plc] = CLIP_COLOR_VALUE(cgo->color[0]);
|
|
colorValsUC[plc2] = CLIP_COLOR_VALUE(cgo->color[1]);
|
|
colorValsUC[plc3] = CLIP_COLOR_VALUE(cgo->color[2]);
|
|
colorValsUC[plc4] = CLIP_COLOR_VALUE(cgo->alpha);
|
|
}
|
|
} else {
|
|
if (colorValsDA) {
|
|
colorVals[plc] = colorValsDA[cc];
|
|
colorVals[plc2] = colorValsDA[cc2];
|
|
colorVals[plc3] = colorValsDA[cc3];
|
|
colorVals[plc4] = colorValsDA[cc4];
|
|
} else {
|
|
colorVals[plc] = cgo->color[0];
|
|
colorVals[plc2] = cgo->color[1];
|
|
colorVals[plc3] = cgo->color[2];
|
|
colorVals[plc4] = cgo->alpha;
|
|
}
|
|
}
|
|
if (pickColorValsDA) {
|
|
cgo->current_pick_color_index = CGO_get_uint(pickColorValsDA + pcco);
|
|
cgo->current_pick_color_bond = CGO_get_int(pickColorValsDA + pcco + 1);
|
|
}
|
|
CGO_put_uint(pickColorVals + pcc, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + pcc + 1, cgo->current_pick_color_bond);
|
|
if (accessibilityValsDA) {
|
|
accessibilityVals[pl / 3] = accessibilityValsDA[cnt];
|
|
}
|
|
}
|
|
|
|
struct NormalColorFormatSize {
|
|
VertexFormat normalFormat{};
|
|
VertexFormat colorFormat{};
|
|
std::size_t normalSize{};
|
|
std::size_t colorSize{};
|
|
};
|
|
|
|
static NormalColorFormatSize GetNormalColorFormatSize(PyMOLGlobals* G)
|
|
{
|
|
NormalColorFormatSize fmt{};
|
|
|
|
fmt.normalFormat =
|
|
VERTEX_NORMAL_SIZE == 3 ? VertexFormat::Float3 : VertexFormat::Float4;
|
|
if (SettingGet<int>(G, cSetting_cgo_shader_ub_normal)) {
|
|
fmt.normalFormat = VERTEX_NORMAL_SIZE == 3 ? VertexFormat::Byte3Norm
|
|
: VertexFormat::Byte4Norm;
|
|
}
|
|
fmt.normalSize = GetSizeOfVertexFormat(fmt.normalFormat);
|
|
|
|
fmt.colorFormat = VertexFormat::Float4;
|
|
if (SettingGet<int>(G, cSetting_cgo_shader_ub_color)) {
|
|
fmt.colorFormat = VertexFormat::UByte4Norm;
|
|
}
|
|
fmt.colorSize = GetSizeOfVertexFormat(fmt.colorFormat);
|
|
|
|
return fmt;
|
|
}
|
|
|
|
// TODO: The following functions are temporary until OpenGL is a backend
|
|
|
|
/**
|
|
* @brief Create a vertex buffer
|
|
* @param layout Vertex buffer layout
|
|
* @param memProperty Usage property for Vertex buffer
|
|
* @return Reference to created Vertex Buffer (no mem allocated)
|
|
*/
|
|
static VertexBuffer& CreateVertexBuffer(PyMOLGlobals* G,
|
|
VertexBufferLayout layout = VertexBufferLayout::Separate,
|
|
MemoryUsageProperty memProperty = MemoryUsageProperty::GpuOnly)
|
|
{
|
|
return *G->ShaderMgr->newGPUBuffer<VertexBufferGL>(layout, memProperty);
|
|
}
|
|
|
|
/**
|
|
* @brief Allocates GPU memory for vertex buffer and sets data
|
|
* @param dataDesc Buffer data description
|
|
* @param dataOverride Optional data override
|
|
*/
|
|
static void SetVertexBufferData(PyMOLGlobals* G, VertexBuffer& vertexBuffer,
|
|
BufferDataDesc&& dataDesc, pymol::span<const std::byte> dataOverride = {})
|
|
{
|
|
auto* vbo = static_cast<VertexBufferGL*>(&vertexBuffer);
|
|
vbo->bufferData(std::move(dataDesc));
|
|
}
|
|
|
|
/**
|
|
* @brief Allocates GPU memory for vertex buffer and sets data
|
|
* @param vertexBuffer Vertex buffer
|
|
* @param dataDesc Buffer data description
|
|
* @param bufferSize Size of the buffer
|
|
* @param dataOverride Optional data override
|
|
*/
|
|
static void SetVertexBufferDataSized(PyMOLGlobals* G,
|
|
VertexBuffer& vertexBuffer, BufferDataDesc&& dataDesc,
|
|
std::size_t bufferSize, std::byte* dataOverride = nullptr)
|
|
{
|
|
auto* vbo = static_cast<VertexBufferGL*>(&vertexBuffer);
|
|
vbo->bufferData(std::move(dataDesc), dataOverride, bufferSize);
|
|
}
|
|
|
|
/**
|
|
* @brief Create an index buffer
|
|
* @return Reference to created Index Buffer (no mem allocated)
|
|
*/
|
|
static IndexBuffer& CreateIndexBuffer(PyMOLGlobals* G)
|
|
{
|
|
return *G->ShaderMgr->newGPUBuffer<IndexBufferGL>();
|
|
}
|
|
|
|
/**
|
|
* @brief Allocates GPU memory for index buffer and sets data
|
|
* @param indexBuffer Index buffer
|
|
* @param data Index data
|
|
*/
|
|
static void SetIndexBufferData(PyMOLGlobals* G, IndexBuffer& indexBuffer,
|
|
pymol::span<const std::uint32_t> data)
|
|
{
|
|
auto* ibo = static_cast<IndexBufferGL*>(&indexBuffer);
|
|
ibo->copyFrom(data);
|
|
}
|
|
|
|
static int OptimizePointsToVBO(const CGO* I, CGO* cgo,
|
|
int num_total_vertices_points, float* min, float* max,
|
|
short* has_draw_buffer, bool addshaders)
|
|
{
|
|
auto G = I->G;
|
|
int pl = 0, plc = 0, idxpl = 0, vpl = 0, nxtn;
|
|
uchar* colorValsUC = 0;
|
|
uchar* normalValsC = 0;
|
|
bool has_normals = false, has_colors = false;
|
|
int ok = true;
|
|
|
|
cgo->alpha = 1.f;
|
|
cgo->color[0] = 1.f;
|
|
cgo->color[1] = 1.f;
|
|
cgo->color[2] = 1.f;
|
|
|
|
unsigned mul = VERTEX_POS_SIZE + VERTEX_PICKCOLOR_SIZE;
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_normal)
|
|
? 1
|
|
: VERTEX_NORMAL_SIZE;
|
|
mul +=
|
|
SettingGet<bool>(G, cSetting_cgo_shader_ub_color) ? 1 : VERTEX_COLOR_SIZE;
|
|
auto const tot = size_t(num_total_vertices_points) * mul;
|
|
|
|
std::vector<float> vertexValsVec(tot);
|
|
auto* vertexVals = vertexValsVec.data();
|
|
auto* normalVals = vertexVals + 3 * num_total_vertices_points;
|
|
nxtn = 3;
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)) {
|
|
normalValsC = (uchar*) normalVals;
|
|
nxtn = 1;
|
|
}
|
|
auto* colorVals = normalVals + nxtn * num_total_vertices_points;
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)) {
|
|
colorValsUC = (uchar*) colorVals;
|
|
nxtn = 1;
|
|
} else {
|
|
nxtn = 4;
|
|
}
|
|
auto* pickColorVals = (colorVals + nxtn * num_total_vertices_points);
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_BOUNDING_BOX:
|
|
case CGO_DRAW_SPHERE_BUFFERS:
|
|
case CGO_DRAW_LABELS:
|
|
case CGO_DRAW_TEXTURES:
|
|
case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
|
|
case CGO_DRAW_CYLINDER_BUFFERS:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"ERROR: OptimizePointsToVBO used with unsupported CGO ops" ENDFB(I->G);
|
|
return 0;
|
|
break;
|
|
case CGO_NORMAL:
|
|
cgo->normal[0] = *pc;
|
|
cgo->normal[1] = *(pc + 1);
|
|
cgo->normal[2] = *(pc + 2);
|
|
has_normals = true;
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
has_colors = true;
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
const auto sp = it.cast<cgo::draw::arrays>();
|
|
short shouldCompress = false;
|
|
switch (sp->mode) {
|
|
case GL_POINTS:
|
|
shouldCompress = true;
|
|
default:
|
|
break;
|
|
}
|
|
/* TODO : DO WE NEED TO COMPENSATE FOR THIS? if (!has_normals &&
|
|
arrays & CGO_NORMAL_ARRAY){ arrays = arrays ^ CGO_NORMAL_ARRAY; narrays
|
|
-= 1;
|
|
}*/
|
|
if (shouldCompress) {
|
|
int cnt, nxtn = VERTEX_POS_SIZE, incr = 0;
|
|
float *vertexValsDA = nullptr, *nxtVals = nullptr,
|
|
*colorValsDA = nullptr, *normalValsDA = nullptr;
|
|
float *pickColorValsDA = nullptr, *pickColorValsTMP;
|
|
|
|
nxtVals = vertexValsDA = sp->floatdata;
|
|
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt += 3) {
|
|
set_min_max(min, max, &vertexValsDA[cnt]);
|
|
}
|
|
if (sp->arraybits & CGO_NORMAL_ARRAY) {
|
|
has_normals = true;
|
|
nxtVals = normalValsDA = vertexValsDA + (nxtn * sp->nverts);
|
|
}
|
|
if (sp->arraybits & CGO_COLOR_ARRAY) {
|
|
has_colors = true;
|
|
nxtVals = colorValsDA = nxtVals + (nxtn * sp->nverts);
|
|
nxtn = VERTEX_COLOR_SIZE;
|
|
}
|
|
if (sp->arraybits & CGO_PICK_COLOR_ARRAY) {
|
|
nxtVals = nxtVals + (nxtn * sp->nverts);
|
|
pickColorValsDA = nxtVals + sp->nverts;
|
|
nxtn = VERTEX_PICKCOLOR_SIZE;
|
|
}
|
|
pickColorValsTMP = pickColorVals + (idxpl * 2);
|
|
switch (sp->mode) {
|
|
case GL_POINTS:
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
SetVertexValuesForVBO(I->G, cgo, pl, plc, cnt, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
}
|
|
break;
|
|
}
|
|
vpl += sp->nverts;
|
|
}
|
|
} break;
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
if (ok) {
|
|
short arrays = CGO_VERTEX_ARRAY | CGO_PICK_COLOR_ARRAY;
|
|
auto fmt = GetNormalColorFormatSize(I->G);
|
|
|
|
auto& vbo = CreateVertexBuffer(I->G);
|
|
|
|
BufferDataDesc bufData{{{"a_Vertex", VertexFormat::Float3,
|
|
sizeof(float) * num_total_vertices_points * 3, vertexVals}}};
|
|
|
|
if (has_normals) {
|
|
bufData.descs.push_back({"a_Normal", fmt.normalFormat,
|
|
num_total_vertices_points * fmt.normalSize, normalVals});
|
|
}
|
|
if (has_colors) {
|
|
bufData.descs.push_back({"a_Color", fmt.colorFormat,
|
|
num_total_vertices_points * fmt.colorSize, colorVals});
|
|
}
|
|
SetVertexBufferData(I->G, vbo, std::move(bufData));
|
|
|
|
if (ok && has_colors) {
|
|
arrays |= CGO_COLOR_ARRAY;
|
|
}
|
|
|
|
auto vboid = vbo.get_hash_id();
|
|
|
|
if (ok) {
|
|
float* newPickColorVals;
|
|
if (addshaders)
|
|
CGOEnable(cgo, GL_DEFAULT_SHADER);
|
|
newPickColorVals = cgo->add<cgo::draw::buffers_not_indexed>(
|
|
GL_POINTS, arrays, num_total_vertices_points, vboid);
|
|
CHECKOK(ok, newPickColorVals);
|
|
if (ok && addshaders)
|
|
ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
|
|
if (!newPickColorVals)
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
if (ok)
|
|
memcpy(newPickColorVals + num_total_vertices_points, pickColorVals,
|
|
num_total_vertices_points * 2 * sizeof(float));
|
|
*has_draw_buffer = true;
|
|
} else {
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
}
|
|
}
|
|
return ok;
|
|
/* END GL_POINTS */
|
|
// printf("num_total_vertices_points=%d\n", num_total_vertices_points);
|
|
}
|
|
|
|
static void FixPickColorsForLine(float* pick1, float* pick2)
|
|
{
|
|
unsigned int p1 = CGO_get_uint(pick1);
|
|
unsigned int p2 = CGO_get_uint(pick2);
|
|
int b1 = CGO_get_int(pick1 + 1);
|
|
int b2 = CGO_get_int(pick2 + 1);
|
|
if (p1 != p2 || b1 != b2) {
|
|
// if the pick colors are different, then pick the first one
|
|
CGO_put_uint(pick1, p2);
|
|
CGO_put_int(pick1 + 1, b2);
|
|
}
|
|
}
|
|
|
|
static void FixPickColorsForTriangle(float* pick1, float* pick2, float* pick3)
|
|
{
|
|
unsigned int p1 = CGO_get_uint(pick1);
|
|
unsigned int p2 = CGO_get_uint(pick2);
|
|
unsigned int p3 = CGO_get_uint(pick3);
|
|
int b1 = CGO_get_int(pick1 + 1);
|
|
int b2 = CGO_get_int(pick2 + 1);
|
|
int b3 = CGO_get_int(pick3 + 1);
|
|
if (p1 != p2 || p1 != p3 || p2 != p3 || b1 != b2 || b1 != b3 || b2 != b3) {
|
|
// right now, if the pick colors are different, then pick majority,
|
|
// otherwise, pick first one
|
|
if (p1 == p2 && b1 == b2) {
|
|
CGO_put_uint(pick3, p1);
|
|
CGO_put_int(pick3 + 1, b1);
|
|
} else if (p1 == p3 && b1 == b3) {
|
|
CGO_put_uint(pick2, p1);
|
|
CGO_put_int(pick2 + 1, b1);
|
|
} else if (p2 == p3 && b2 == b3) {
|
|
CGO_put_uint(pick1, p2);
|
|
CGO_put_int(pick1 + 1, b2);
|
|
} else {
|
|
CGO_put_uint(pick2, p1);
|
|
CGO_put_int(pick2 + 1, b1);
|
|
CGO_put_uint(pick3, p1);
|
|
CGO_put_int(pick3 + 1, b1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool CGOProcessCGOtoArrays(const CGO* I, CGO* cgo, CGO* addtocgo,
|
|
float* min, float* max, int* ambient_occlusion, float* vertexVals,
|
|
float* normalVals, uchar* normalValsC, float* colorVals, uchar* colorValsUC,
|
|
float* pickColorVals, float* accessibilityVals, bool& has_normals,
|
|
bool& has_colors, bool& has_accessibility)
|
|
{
|
|
auto G = I->G;
|
|
int idxpl = 0;
|
|
int pl = 0, plc = 0, vpl = 0;
|
|
int ok = true;
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_BOUNDING_BOX:
|
|
case CGO_DRAW_SPHERE_BUFFERS:
|
|
case CGO_DRAW_LABELS:
|
|
case CGO_DRAW_TEXTURES:
|
|
case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
|
|
case CGO_DRAW_CYLINDER_BUFFERS:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
case CGO_DRAW_BUFFERS_INDEXED: {
|
|
const float* newpc = pc;
|
|
if (addtocgo)
|
|
addtocgo->add_to_cgo(op, newpc);
|
|
} break;
|
|
case CGO_NORMAL:
|
|
cgo->normal[0] = *pc;
|
|
cgo->normal[1] = *(pc + 1);
|
|
cgo->normal[2] = *(pc + 2);
|
|
has_normals = true;
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
has_colors = true;
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
cgo->current_accessibility = pc[0];
|
|
has_accessibility = true;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
const auto sp = it.cast<cgo::draw::arrays>();
|
|
short shouldCompress = false;
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLE_FAN:
|
|
case GL_TRIANGLE_STRIP:
|
|
case GL_TRIANGLES:
|
|
shouldCompress = true;
|
|
default:
|
|
break;
|
|
}
|
|
if (shouldCompress) {
|
|
int cnt, incr = 0;
|
|
const float* nxtVals = sp->floatdata;
|
|
const float *vertexValsDA = nullptr, *colorValsDA = nullptr,
|
|
*normalValsDA = nullptr, *pickColorValsDA = nullptr,
|
|
*accessibilityValsDA = nullptr;
|
|
|
|
assert(sp->arraybits & CGO_VERTEX_ARRAY);
|
|
vertexValsDA = nxtVals;
|
|
nxtVals += sp->nverts * VERTEX_POS_SIZE;
|
|
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt += 3) {
|
|
set_min_max(min, max, &vertexValsDA[cnt]);
|
|
}
|
|
if (sp->arraybits & CGO_NORMAL_ARRAY) {
|
|
has_normals = true;
|
|
normalValsDA = nxtVals;
|
|
nxtVals += sp->nverts * VERTEX_NORMAL_SIZE;
|
|
}
|
|
|
|
if (sp->arraybits & CGO_COLOR_ARRAY) {
|
|
has_colors = true;
|
|
colorValsDA = nxtVals;
|
|
nxtVals += sp->nverts * VERTEX_COLOR_SIZE;
|
|
}
|
|
if (sp->arraybits & CGO_PICK_COLOR_ARRAY) {
|
|
nxtVals += VERTEX_PICKCOLOR_RGBA_SIZE * sp->nverts;
|
|
pickColorValsDA = nxtVals;
|
|
nxtVals += VERTEX_PICKCOLOR_INDEX_SIZE * sp->nverts;
|
|
}
|
|
float* pickColorValsTMP = pickColorVals + (idxpl * 2);
|
|
if (sp->arraybits & CGO_ACCESSIBILITY_ARRAY) {
|
|
if (!(*ambient_occlusion) && incr) {
|
|
for (cnt = 0; cnt < incr; cnt++) {
|
|
/* if ambient_occlusion, need to fill in the array */
|
|
accessibilityVals[cnt] = 1.f;
|
|
}
|
|
}
|
|
(*ambient_occlusion) = 1;
|
|
accessibilityValsDA = nxtVals;
|
|
nxtVals += VERTEX_ACCESSIBILITY_SIZE * sp->nverts;
|
|
has_accessibility = true;
|
|
} else {
|
|
if (*ambient_occlusion) {
|
|
for (cnt = incr; cnt < incr + sp->nverts; cnt++) {
|
|
/* if ambient_occlusion, need to fill in the array */
|
|
accessibilityVals[cnt] = 1.f;
|
|
}
|
|
}
|
|
}
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLES:
|
|
for (cnt = 0; ok && cnt < sp->nverts; cnt++) {
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP, accessibilityVals, accessibilityValsDA);
|
|
if (incr && (incr % 3) == 0) {
|
|
FixPickColorsForTriangle(pickColorValsTMP + (incr - 3) * 2,
|
|
pickColorValsTMP + (incr - 2) * 2,
|
|
pickColorValsTMP + (incr - 1) * 2);
|
|
}
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
ok &= !G->Interrupt;
|
|
}
|
|
break;
|
|
case GL_TRIANGLE_STRIP: {
|
|
short flip = 0;
|
|
for (cnt = 2; ok && cnt < sp->nverts; cnt++) {
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt - (flip ? 0 : 2), incr++,
|
|
vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
|
|
vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP, accessibilityVals, accessibilityValsDA);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt - 1, incr++,
|
|
vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
|
|
vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP, accessibilityVals, accessibilityValsDA);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt - (flip ? 2 : 0), incr++,
|
|
vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
|
|
vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP, accessibilityVals, accessibilityValsDA);
|
|
FixPickColorsForTriangle(pickColorValsTMP + (incr - 3) * 2,
|
|
pickColorValsTMP + (incr - 2) * 2,
|
|
pickColorValsTMP + (incr - 1) * 2);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
ok &= !G->Interrupt;
|
|
flip = !flip;
|
|
}
|
|
} break;
|
|
case GL_TRIANGLE_FAN:
|
|
for (cnt = 2; ok && cnt < sp->nverts; cnt++) {
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, 0, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP, accessibilityVals, accessibilityValsDA);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt - 1, incr++,
|
|
vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
|
|
vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP, accessibilityVals, accessibilityValsDA);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP, accessibilityVals, accessibilityValsDA);
|
|
FixPickColorsForTriangle(pickColorValsTMP + (incr - 3) * 2,
|
|
pickColorValsTMP + (incr - 2) * 2,
|
|
pickColorValsTMP + (incr - 1) * 2);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
ok &= !G->Interrupt;
|
|
}
|
|
break;
|
|
}
|
|
vpl += sp->nverts;
|
|
}
|
|
} break;
|
|
}
|
|
ok &= !G->Interrupt;
|
|
}
|
|
ok &= !G->Interrupt;
|
|
return ok;
|
|
}
|
|
|
|
static bool CGOProcessScreenCGOtoArrays(PyMOLGlobals* G, CGO* cgo,
|
|
float* vertexVals, float* texcoordVals, float* colorVals,
|
|
uchar* colorValsUC)
|
|
{
|
|
int pl = 0;
|
|
cgo->alpha = 1.f;
|
|
|
|
for (auto it = cgo->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_BOUNDING_BOX:
|
|
case CGO_DRAW_SPHERE_BUFFERS:
|
|
case CGO_DRAW_LABELS:
|
|
case CGO_DRAW_TEXTURES:
|
|
case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
|
|
case CGO_DRAW_CYLINDER_BUFFERS:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_ARRAYS:
|
|
case CGO_ACCESSIBILITY:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return false;
|
|
case CGO_NORMAL:
|
|
cgo->normal[0] = *pc;
|
|
cgo->normal[1] = *(pc + 1);
|
|
cgo->normal[2] = *(pc + 2);
|
|
break;
|
|
case CGO_TEX_COORD:
|
|
cgo->texture[0] = *pc;
|
|
cgo->texture[1] = *(pc + 1);
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_BEGIN: {
|
|
int mode = it.cast<cgo::draw::begin>()->mode;
|
|
int nverts = 0, ipl = 0;
|
|
cgo->texture[0] = cgo->texture[1] = 0.f;
|
|
|
|
for (++it;; ++it) {
|
|
if (it.is_stop()) {
|
|
WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
|
|
return false;
|
|
}
|
|
|
|
const auto op = it.op_code();
|
|
if (op == CGO_END) {
|
|
break;
|
|
}
|
|
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_TEX_COORD:
|
|
cgo->texture[0] = *pc;
|
|
cgo->texture[1] = *(pc + 1);
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_VERTEX: {
|
|
switch (mode) {
|
|
case GL_TRIANGLES: {
|
|
int vpl = pl * 3, tpl = pl * 2, cpl = pl * 4;
|
|
vertexVals[vpl] = *pc;
|
|
vertexVals[vpl + 1] = *(pc + 1);
|
|
vertexVals[vpl + 2] = *(pc + 2);
|
|
texcoordVals[tpl] = cgo->texture[0];
|
|
texcoordVals[tpl + 1] = cgo->texture[1];
|
|
if (colorValsUC) {
|
|
colorValsUC[cpl] = CLIP_COLOR_VALUE(cgo->color[0]);
|
|
colorValsUC[cpl + 1] = CLIP_COLOR_VALUE(cgo->color[1]);
|
|
colorValsUC[cpl + 2] = CLIP_COLOR_VALUE(cgo->color[2]);
|
|
colorValsUC[cpl + 3] = CLIP_COLOR_VALUE(cgo->alpha);
|
|
} else {
|
|
colorVals[cpl] = cgo->color[0];
|
|
colorVals[cpl + 1] = cgo->color[1];
|
|
colorVals[cpl + 2] = cgo->color[2];
|
|
colorVals[cpl + 3] = cgo->alpha;
|
|
}
|
|
pl++;
|
|
} break;
|
|
case GL_TRIANGLE_STRIP: {
|
|
int vpl = pl * 3, tpl = pl * 2, cpl = pl * 4;
|
|
if (ipl < 3) {
|
|
vertexVals[vpl] = *pc;
|
|
vertexVals[vpl + 1] = *(pc + 1);
|
|
vertexVals[vpl + 2] = *(pc + 2);
|
|
texcoordVals[tpl] = cgo->texture[0];
|
|
texcoordVals[tpl + 1] = cgo->texture[1];
|
|
if (colorValsUC) {
|
|
colorValsUC[cpl] = CLIP_COLOR_VALUE(cgo->color[0]);
|
|
colorValsUC[cpl + 1] = CLIP_COLOR_VALUE(cgo->color[1]);
|
|
colorValsUC[cpl + 2] = CLIP_COLOR_VALUE(cgo->color[2]);
|
|
colorValsUC[cpl + 3] = CLIP_COLOR_VALUE(cgo->alpha);
|
|
} else {
|
|
colorVals[cpl] = cgo->color[0];
|
|
colorVals[cpl + 1] = cgo->color[1];
|
|
colorVals[cpl + 2] = cgo->color[2];
|
|
colorVals[cpl + 3] = cgo->alpha;
|
|
}
|
|
pl++;
|
|
ipl++;
|
|
} else {
|
|
vertexVals[vpl] = vertexVals[vpl - 6];
|
|
vertexVals[vpl + 1] = vertexVals[vpl - 5];
|
|
vertexVals[vpl + 2] = vertexVals[vpl - 4];
|
|
texcoordVals[tpl] = texcoordVals[tpl - 4];
|
|
texcoordVals[tpl + 1] = texcoordVals[tpl - 3];
|
|
if (colorValsUC) {
|
|
colorValsUC[cpl] = colorValsUC[cpl - 8];
|
|
colorValsUC[cpl + 1] = colorValsUC[cpl - 7];
|
|
colorValsUC[cpl + 2] = colorValsUC[cpl - 6];
|
|
colorValsUC[cpl + 3] = colorValsUC[cpl - 5];
|
|
} else {
|
|
colorVals[cpl] = colorVals[cpl - 8];
|
|
colorVals[cpl + 1] = colorVals[cpl - 7];
|
|
colorVals[cpl + 2] = colorVals[cpl - 6];
|
|
colorVals[cpl + 3] = colorVals[cpl - 5];
|
|
}
|
|
pl++;
|
|
vpl += 3;
|
|
tpl += 2;
|
|
cpl += 4;
|
|
ipl++;
|
|
vertexVals[vpl] = vertexVals[vpl - 6];
|
|
vertexVals[vpl + 1] = vertexVals[vpl - 5];
|
|
vertexVals[vpl + 2] = vertexVals[vpl - 4];
|
|
texcoordVals[tpl] = texcoordVals[tpl - 4];
|
|
texcoordVals[tpl + 1] = texcoordVals[tpl - 3];
|
|
if (colorValsUC) {
|
|
colorValsUC[cpl] = colorValsUC[cpl - 8];
|
|
colorValsUC[cpl + 1] = colorValsUC[cpl - 7];
|
|
colorValsUC[cpl + 2] = colorValsUC[cpl - 6];
|
|
colorValsUC[cpl + 3] = colorValsUC[cpl - 5];
|
|
} else {
|
|
colorVals[cpl] = colorVals[cpl - 8];
|
|
colorVals[cpl + 1] = colorVals[cpl - 7];
|
|
colorVals[cpl + 2] = colorVals[cpl - 6];
|
|
colorVals[cpl + 3] = colorVals[cpl - 5];
|
|
}
|
|
pl++;
|
|
vpl += 3;
|
|
tpl += 2;
|
|
cpl += 4;
|
|
ipl++;
|
|
vertexVals[vpl] = *pc;
|
|
vertexVals[vpl + 1] = *(pc + 1);
|
|
vertexVals[vpl + 2] = *(pc + 2);
|
|
texcoordVals[tpl] = cgo->texture[0];
|
|
texcoordVals[tpl + 1] = cgo->texture[1];
|
|
if (colorValsUC) {
|
|
colorValsUC[cpl] = CLIP_COLOR_VALUE(cgo->color[0]);
|
|
colorValsUC[cpl + 1] = CLIP_COLOR_VALUE(cgo->color[1]);
|
|
colorValsUC[cpl + 2] = CLIP_COLOR_VALUE(cgo->color[2]);
|
|
colorValsUC[cpl + 3] = CLIP_COLOR_VALUE(cgo->alpha);
|
|
} else {
|
|
colorVals[cpl] = cgo->color[0];
|
|
colorVals[cpl + 1] = cgo->color[1];
|
|
colorVals[cpl + 2] = cgo->color[2];
|
|
colorVals[cpl + 3] = cgo->alpha;
|
|
}
|
|
pl++;
|
|
ipl++;
|
|
}
|
|
} break;
|
|
case GL_TRIANGLE_FAN:
|
|
default:
|
|
printf(
|
|
"CGOProcessScreenCGOtoArrays: WARNING: mode=%d not implemented "
|
|
"yet GL_LINES=%d GL_LINE_STRIP=%d GL_LINE_LOOP=%d\n",
|
|
mode, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP);
|
|
break;
|
|
}
|
|
nverts++;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool OptimizeVertsToVBONotIndexed(const CGO* I, CGO* cgo,
|
|
const CGOCount& count, float* min, float* max, short* has_draw_buffer,
|
|
bool addshaders)
|
|
{
|
|
auto G = I->G;
|
|
bool ok = true;
|
|
uchar* colorValsUC = 0;
|
|
uchar* normalValsC = 0;
|
|
|
|
cgo->alpha = 1.f;
|
|
cgo->color[0] = 1.f;
|
|
cgo->color[1] = 1.f;
|
|
cgo->color[2] = 1.f;
|
|
|
|
unsigned mul =
|
|
VERTEX_POS_SIZE + VERTEX_PICKCOLOR_SIZE + VERTEX_ACCESSIBILITY_SIZE;
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_normal)
|
|
? 1
|
|
: VERTEX_NORMAL_SIZE;
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_color)
|
|
? 1
|
|
: VERTEX_COLOR_SIZE;
|
|
auto const tot = size_t(count.num_total_indexes) * mul;
|
|
|
|
std::vector<float> vertexValsVec(tot);
|
|
auto* vertexVals = vertexValsVec.data();
|
|
auto* normalVals = vertexVals + 3 * count.num_total_indexes;
|
|
unsigned nxtn = VERTEX_NORMAL_SIZE;
|
|
if (SettingGet<int>(G, cSetting_cgo_shader_ub_normal)) {
|
|
normalValsC = (uchar*) normalVals;
|
|
nxtn = 1;
|
|
}
|
|
auto* colorVals = normalVals + nxtn * count.num_total_indexes;
|
|
if (SettingGet<int>(G, cSetting_cgo_shader_ub_color)) {
|
|
colorValsUC = (uchar*) colorVals;
|
|
nxtn = 1;
|
|
} else {
|
|
nxtn = 4;
|
|
}
|
|
auto* pickColorVals = (colorVals + nxtn * count.num_total_indexes);
|
|
nxtn = 3;
|
|
auto* accessibilityVals = pickColorVals + nxtn * count.num_total_indexes;
|
|
|
|
bool has_normals = false, has_colors = false, has_accessibility = false;
|
|
int ambient_occlusion{};
|
|
ok = CGOProcessCGOtoArrays(I, cgo, cgo, min, max, &ambient_occlusion,
|
|
vertexVals, normalVals, normalValsC, colorVals, colorValsUC,
|
|
pickColorVals, accessibilityVals, has_normals, has_colors,
|
|
has_accessibility);
|
|
if (!ok) {
|
|
if (!G->Interrupt)
|
|
PRINTFB(G, FB_CGO, FB_Errors)
|
|
"ERROR: CGOProcessCGOtoArrays() could not allocate enough "
|
|
"memory\n" ENDFB(G);
|
|
return false;
|
|
}
|
|
if (ok) {
|
|
auto fmt = GetNormalColorFormatSize(G);
|
|
|
|
auto& vbo = CreateVertexBuffer(
|
|
G, VertexBufferLayout::Sequential);
|
|
BufferDataDesc bufData{{{"a_Vertex", VertexFormat::Float3,
|
|
sizeof(float) * count.num_total_indexes * 3, vertexVals}}};
|
|
if (has_normals) {
|
|
bufData.descs.push_back({"a_Normal", fmt.normalFormat,
|
|
count.num_total_indexes * fmt.normalSize, normalVals});
|
|
}
|
|
if (has_colors) {
|
|
bufData.descs.push_back({"a_Color", fmt.colorFormat,
|
|
count.num_total_indexes * fmt.colorSize, colorVals});
|
|
}
|
|
if (has_accessibility) {
|
|
bufData.descs.push_back({"a_Accessibility", VertexFormat::Float,
|
|
sizeof(float) * count.num_total_indexes, accessibilityVals});
|
|
}
|
|
SetVertexBufferData(I->G, vbo, std::move(bufData));
|
|
|
|
auto vboid = vbo.get_hash_id();
|
|
// picking VBO: generate a buffer twice the size needed, for each picking
|
|
// pass
|
|
auto& pickvbo = CreateVertexBuffer(
|
|
G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu);
|
|
BufferDataDesc pickDataDesc{{//
|
|
BufferDesc{"a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes},
|
|
BufferDesc{"a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes}}};
|
|
SetVertexBufferData(G, pickvbo, std::move(pickDataDesc));
|
|
auto pickvboid = pickvbo.get_hash_id();
|
|
|
|
if (ok) {
|
|
float* newPickColorVals;
|
|
int arrays = CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY | CGO_COLOR_ARRAY |
|
|
CGO_PICK_COLOR_ARRAY;
|
|
if (ambient_occlusion) {
|
|
arrays |= CGO_ACCESSIBILITY_ARRAY;
|
|
}
|
|
if (addshaders)
|
|
CGOEnable(cgo, GL_DEFAULT_SHADER_WITH_SETTINGS);
|
|
newPickColorVals = cgo->add<cgo::draw::buffers_not_indexed>(
|
|
GL_TRIANGLES, arrays, count.num_total_indexes, vboid, pickvboid);
|
|
if (ok && addshaders)
|
|
ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
|
|
CHECKOK(ok, newPickColorVals);
|
|
if (!newPickColorVals) {
|
|
G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
G->ShaderMgr->freeGPUBuffer(vboid);
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_CGO, FB_Errors)
|
|
"CGOOptimizeToVBONotIndexed: ERROR: "
|
|
"CGODrawBuffersNotIndexed() could not allocate enough memory\n" ENDFB(
|
|
G);
|
|
return false;
|
|
}
|
|
memcpy(newPickColorVals + count.num_total_indexes, pickColorVals,
|
|
count.num_total_indexes * 2 * sizeof(float));
|
|
*has_draw_buffer = true;
|
|
} else {
|
|
G->ShaderMgr->freeGPUBuffer(vboid);
|
|
G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static bool OptimizeLinesToVBONotIndexed(const CGO* I, CGO* cgo,
|
|
const CGOCount& count, float* min, float* max, short* has_draw_buffer,
|
|
bool addshaders)
|
|
{
|
|
auto G = I->G;
|
|
bool ok = true;
|
|
bool has_color = false, has_normals = false;
|
|
int pl = 0, plc = 0, idxpl = 0, vpl = 0, nxtn;
|
|
uchar* colorValsUC = 0;
|
|
uchar* normalValsC = 0;
|
|
|
|
cgo->alpha = 1.f;
|
|
cgo->color[0] = 1.f;
|
|
cgo->color[1] = 1.f;
|
|
cgo->color[2] = 1.f;
|
|
|
|
unsigned mul = VERTEX_POS_SIZE + VERTEX_PICKCOLOR_SIZE;
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_normal)
|
|
? 1
|
|
: VERTEX_NORMAL_SIZE;
|
|
mul +=
|
|
SettingGet<bool>(G, cSetting_cgo_shader_ub_color) ? 1 : VERTEX_COLOR_SIZE;
|
|
auto const tot = size_t(count.num_total_indexes_lines) * mul;
|
|
|
|
std::vector<float> vertexValsVec(tot);
|
|
auto* vertexVals = vertexValsVec.data();
|
|
auto* normalVals = vertexVals + 3 * count.num_total_indexes_lines;
|
|
nxtn = 3;
|
|
if (SettingGet<int>(G, cSetting_cgo_shader_ub_normal)) {
|
|
normalValsC = (uchar*) normalVals;
|
|
nxtn = 1;
|
|
}
|
|
|
|
auto* colorVals = normalVals + nxtn * count.num_total_indexes_lines;
|
|
if (SettingGet<int>(G, cSetting_cgo_shader_ub_color)) {
|
|
colorValsUC = (uchar*) colorVals;
|
|
nxtn = 1;
|
|
} else {
|
|
nxtn = 4;
|
|
}
|
|
auto* pickColorVals = (colorVals + nxtn * count.num_total_indexes_lines);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_SPECIAL:
|
|
case CGO_RESET_NORMAL: {
|
|
const float* newpc = pc;
|
|
cgo->add_to_cgo(op, newpc);
|
|
} break;
|
|
case CGO_NORMAL:
|
|
has_normals = true;
|
|
cgo->normal[0] = *pc;
|
|
cgo->normal[1] = *(pc + 1);
|
|
cgo->normal[2] = *(pc + 2);
|
|
break;
|
|
case CGO_COLOR:
|
|
has_color = true;
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
cgo->current_accessibility = pc[0];
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
auto sp = it.cast<cgo::draw::arrays>();
|
|
short shouldCompress = false;
|
|
switch (sp->mode) {
|
|
case GL_LINE_LOOP:
|
|
case GL_LINE_STRIP:
|
|
case GL_LINES:
|
|
shouldCompress = true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (shouldCompress) {
|
|
int cnt, incr = 0;
|
|
const float* nxtVals = sp->floatdata;
|
|
const float *vertexValsDA = nullptr, *colorValsDA = nullptr,
|
|
*normalValsDA = nullptr, *pickColorValsDA = nullptr;
|
|
|
|
assert(sp->arraybits & CGO_VERTEX_ARRAY);
|
|
vertexValsDA = nxtVals;
|
|
nxtVals += sp->nverts * VERTEX_POS_SIZE;
|
|
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt += 3) {
|
|
set_min_max(min, max, &vertexValsDA[cnt]);
|
|
}
|
|
if (sp->arraybits & CGO_NORMAL_ARRAY) {
|
|
has_normals = true;
|
|
normalValsDA = nxtVals;
|
|
nxtVals += sp->nverts * VERTEX_NORMAL_SIZE;
|
|
}
|
|
|
|
if (sp->arraybits & CGO_COLOR_ARRAY) {
|
|
has_color = true;
|
|
colorValsDA = nxtVals;
|
|
nxtVals += sp->nverts * VERTEX_COLOR_SIZE;
|
|
}
|
|
if (sp->arraybits & CGO_PICK_COLOR_ARRAY) {
|
|
nxtVals += VERTEX_PICKCOLOR_RGBA_SIZE * sp->nverts;
|
|
pickColorValsDA = nxtVals;
|
|
nxtVals += VERTEX_PICKCOLOR_INDEX_SIZE * sp->nverts;
|
|
}
|
|
float* pickColorValsTMP = pickColorVals + (idxpl * 2);
|
|
switch (sp->mode) {
|
|
case GL_LINES:
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
if (incr && (incr % 2) == 0) {
|
|
FixPickColorsForLine(pickColorValsTMP + (incr - 2) * 2,
|
|
pickColorValsTMP + (incr - 1) * 2);
|
|
}
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
}
|
|
break;
|
|
case GL_LINE_STRIP:
|
|
for (cnt = 1; cnt < sp->nverts; cnt++) {
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt - 1, incr++,
|
|
vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
|
|
vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
FixPickColorsForLine(pickColorValsTMP + (incr - 2) * 2,
|
|
pickColorValsTMP + (incr - 1) * 2);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
}
|
|
break;
|
|
case GL_LINE_LOOP:
|
|
for (cnt = 1; cnt < sp->nverts; cnt++) {
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt - 1, incr++,
|
|
vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
|
|
vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, cnt, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
FixPickColorsForLine(pickColorValsTMP + (incr - 2) * 2,
|
|
pickColorValsTMP + (incr - 1) * 2);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
}
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, 0, incr++, vertexValsDA,
|
|
normalValsDA, colorValsDA, pickColorValsDA, vertexVals,
|
|
normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
SetVertexValuesForVBO(G, cgo, pl, plc, sp->nverts - 1, incr++,
|
|
vertexValsDA, normalValsDA, colorValsDA, pickColorValsDA,
|
|
vertexVals, normalValsC, normalVals, colorValsUC, colorVals,
|
|
pickColorValsTMP);
|
|
FixPickColorsForLine(pickColorValsTMP + (incr - 2) * 2,
|
|
pickColorValsTMP + (incr - 1) * 2);
|
|
idxpl++;
|
|
pl += 3;
|
|
plc += 4;
|
|
break;
|
|
}
|
|
|
|
// pl += 3 * nverts;
|
|
// plc += 4 * nverts;
|
|
vpl += sp->nverts;
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
auto fmt = GetNormalColorFormatSize(G);
|
|
|
|
auto& vbo = CreateVertexBuffer(G, VertexBufferLayout::Sequential);
|
|
BufferDataDesc bufData{{{"a_Vertex", VertexFormat::Float3,
|
|
sizeof(float) * count.num_total_indexes_lines * 3, vertexVals}}};
|
|
|
|
if (has_normals) {
|
|
bufData.descs.push_back({"a_Normal", fmt.normalFormat,
|
|
count.num_total_indexes_lines * fmt.normalSize, normalVals});
|
|
}
|
|
if (has_color) {
|
|
bufData.descs.push_back({"a_Color", fmt.colorFormat,
|
|
count.num_total_indexes_lines * fmt.colorSize, colorVals});
|
|
}
|
|
SetVertexBufferData(I->G, vbo, std::move(bufData));
|
|
auto vboid = vbo.get_hash_id();
|
|
|
|
// picking VBO: generate a buffer twice the size needed, for each picking
|
|
// pass
|
|
auto& pickvbo = CreateVertexBuffer(
|
|
G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu);
|
|
BufferDataDesc pickDataDesc{{//
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes_lines),
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes_lines)}};
|
|
SetVertexBufferData(G, pickvbo, std::move(pickDataDesc));
|
|
auto pickvboid = pickvbo.get_hash_id();
|
|
|
|
if (ok) {
|
|
float* newPickColorVals;
|
|
if (addshaders)
|
|
CGOEnable(cgo, GL_DEFAULT_SHADER_WITH_SETTINGS);
|
|
CGODisable(cgo, GL_SHADER_LIGHTING);
|
|
newPickColorVals = cgo->add<cgo::draw::buffers_not_indexed>(GL_LINES,
|
|
CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY | CGO_COLOR_ARRAY |
|
|
CGO_PICK_COLOR_ARRAY,
|
|
count.num_total_indexes_lines, vboid, pickvboid);
|
|
if (ok && addshaders)
|
|
ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
|
|
CHECKOK(ok, newPickColorVals);
|
|
if (!ok) {
|
|
PRINTFB(G, FB_CGO, FB_Errors)
|
|
"CGOOptimizeToVBONotIndexed: ERROR: "
|
|
"CGODrawBuffersNotIndexed() could not allocate enough memory\n" ENDFB(G);
|
|
if (!newPickColorVals) {
|
|
G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
G->ShaderMgr->freeGPUBuffer(vboid);
|
|
}
|
|
return false;
|
|
}
|
|
memcpy(newPickColorVals + count.num_total_indexes_lines, pickColorVals,
|
|
count.num_total_indexes_lines * 2 * sizeof(float));
|
|
*has_draw_buffer = true;
|
|
} else {
|
|
G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
G->ShaderMgr->freeGPUBuffer(vboid);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool CGOOptimizeToVBONotIndexed(CGO** I)
|
|
{
|
|
CGO* cgo = CGOOptimizeToVBONotIndexed(*I, 0, true);
|
|
CGOFree(*I);
|
|
*I = cgo;
|
|
return (cgo != nullptr);
|
|
}
|
|
|
|
/**
|
|
* Converts primitive operations into VBO operations.
|
|
*
|
|
* If the input CGO has sortable alpha triangles, you should use
|
|
* CGOOptimizeToVBOIndexed() instead.
|
|
*
|
|
* @param I Primitive CGO to convert
|
|
* @param est Output CGO size estimate (size of buffer to "reserve")
|
|
* @param addshaders Add Enable/Disable shader operations
|
|
*
|
|
* @return New converted CGO with use_shader=true
|
|
*/
|
|
CGO* CGOOptimizeToVBONotIndexed(const CGO* I, int est, bool addshaders)
|
|
{
|
|
auto G = I->G;
|
|
|
|
std::unique_ptr<CGO> I_begin_end_combined;
|
|
if (I->has_begin_end) {
|
|
I_begin_end_combined.reset(CGOCombineBeginEnd(I));
|
|
I = I_begin_end_combined.get();
|
|
assert(!I->has_begin_end);
|
|
}
|
|
|
|
short has_draw_buffer = false;
|
|
float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX},
|
|
max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
|
|
int ok = true;
|
|
auto cgo = CGONew(G);
|
|
|
|
auto count = CGOCountNumVertices(I);
|
|
if (count.num_total_vertices_points > 0) {
|
|
if (!OptimizePointsToVBO(I, cgo, count.num_total_vertices_points, min, max,
|
|
&has_draw_buffer, addshaders)) {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (count.num_total_indexes > 0) {
|
|
if (!OptimizeVertsToVBONotIndexed(
|
|
I, cgo, count, min, max, &has_draw_buffer, addshaders)) {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (ok && count.num_total_indexes_lines > 0) {
|
|
if (!OptimizeLinesToVBONotIndexed(
|
|
I, cgo, count, min, max, &has_draw_buffer, addshaders)) {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (ok && (count.num_total_vertices > 0 || count.num_total_vertices_lines > 0 ||
|
|
count.num_total_vertices_points > 0)) {
|
|
ok &= CGOBoundingBox(cgo, min, max);
|
|
}
|
|
|
|
if (ok)
|
|
ok &= CGOStop(cgo);
|
|
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
|
|
cgo->has_draw_buffers |= has_draw_buffer;
|
|
cgo->use_shader = true;
|
|
cgo->cgo_shader_ub_color = SettingGet<int>(G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal = SettingGet<int>(G, cSetting_cgo_shader_ub_normal);
|
|
return (cgo);
|
|
}
|
|
|
|
static bool OptimizeVertsToVBOIndexed(const CGO* I, CGO* cgo,
|
|
const CGOCount& count, float* min, float* max, short* has_draw_buffer,
|
|
bool addshaders, bool embedTransparencyInfo)
|
|
{
|
|
auto G = I->G;
|
|
bool ok = true;
|
|
int pl = 0, plc = 0, idxpl = 0, vpl = 0, nxtn;
|
|
uchar* colorValsUC = 0;
|
|
uchar* normalValsC = 0;
|
|
short ambient_occlusion = 0;
|
|
float* sumarray = nullptr;
|
|
int n_data = 0;
|
|
|
|
if (embedTransparencyInfo) {
|
|
int n_tri = count.num_total_indexes / 3;
|
|
int bytes_to_allocate =
|
|
2 * count.num_total_indexes *
|
|
sizeof(VertexIndex_t) + // vertexIndicesOriginal, vertexIndices
|
|
3 * count.num_total_indexes * sizeof(float) + // 3 * for sum
|
|
n_tri * sizeof(float) +
|
|
2 * n_tri * sizeof(int) +
|
|
256 * sizeof(int); // z_value (float * n_tri), ix (n_tri * int),
|
|
// sort_mem ((n_tri + 256) * int)
|
|
// round to 4 byte words for the length of the CGO
|
|
n_data = bytes_to_allocate / 4 + (((bytes_to_allocate % 4) == 0) ? 0 : 1);
|
|
}
|
|
std::vector<VertexIndex_t> vertexIndices(count.num_total_indexes);
|
|
|
|
unsigned mul =
|
|
VERTEX_POS_SIZE + VERTEX_PICKCOLOR_SIZE + VERTEX_ACCESSIBILITY_SIZE;
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_normal)
|
|
? 1
|
|
: VERTEX_NORMAL_SIZE;
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_color)
|
|
? 1
|
|
: VERTEX_COLOR_SIZE;
|
|
auto const tot = size_t(count.num_total_vertices) * mul;
|
|
|
|
std::vector<float> vertexValsVec(tot);
|
|
auto* vertexVals = vertexValsVec.data();
|
|
auto* normalVals = vertexVals + 3 * count.num_total_vertices;
|
|
nxtn = 3;
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)) {
|
|
normalValsC = (uchar*) normalVals;
|
|
nxtn = 1;
|
|
}
|
|
|
|
auto* colorVals = normalVals + nxtn * count.num_total_vertices;
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)) {
|
|
colorValsUC = (uchar*) colorVals;
|
|
nxtn = 1;
|
|
} else {
|
|
nxtn = 4;
|
|
}
|
|
auto* pickColorVals = (colorVals + nxtn * count.num_total_vertices);
|
|
auto* accessibilityVals = pickColorVals + 3 * count.num_total_vertices;
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto pc = it.data();
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_NORMAL:
|
|
cgo->normal[0] = *pc;
|
|
cgo->normal[1] = *(pc + 1);
|
|
cgo->normal[2] = *(pc + 2);
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
cgo->current_accessibility = *pc;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
auto sp = it.cast<cgo::draw::arrays>();
|
|
short shouldCompress = false;
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLE_FAN:
|
|
case GL_TRIANGLE_STRIP:
|
|
case GL_TRIANGLES:
|
|
shouldCompress = true;
|
|
default:
|
|
break;
|
|
}
|
|
if (shouldCompress) {
|
|
int cnt, nxtn = 3;
|
|
float *vertexValsDA = 0, *nxtVals = 0, *colorValsDA = 0,
|
|
*normalValsDA, *accessibilityValsDA;
|
|
float *pickColorValsDA, *pickColorValsTMP, *accessibilityValsTMP;
|
|
|
|
nxtVals = vertexValsDA = sp->floatdata;
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt += 3) {
|
|
set_min_max(min, max, &vertexValsDA[cnt]);
|
|
}
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
vertexVals[pl + cnt] = vertexValsDA[cnt];
|
|
}
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)) {
|
|
if (sp->arraybits & CGO_NORMAL_ARRAY) {
|
|
nxtVals = normalValsDA = vertexValsDA + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
normalValsC[VAR_FOR_NORMAL + cnt VAR_FOR_NORMAL_CNT_PLUS] =
|
|
CLIP_NORMAL_VALUE(normalValsDA[cnt]);
|
|
}
|
|
} else {
|
|
uchar norm[3] = {CLIP_NORMAL_VALUE(cgo->normal[0]),
|
|
CLIP_NORMAL_VALUE(cgo->normal[1]),
|
|
CLIP_NORMAL_VALUE(cgo->normal[2])};
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
normalValsC[VAR_FOR_NORMAL + cnt VAR_FOR_NORMAL_CNT_PLUS] =
|
|
norm[cnt % 3];
|
|
}
|
|
}
|
|
} else {
|
|
if (sp->arraybits & CGO_NORMAL_ARRAY) {
|
|
nxtVals = normalValsDA = vertexValsDA + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
normalVals[pl + cnt] = normalValsDA[cnt];
|
|
}
|
|
} else {
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
normalVals[pl + cnt] = cgo->normal[cnt % 3];
|
|
}
|
|
}
|
|
}
|
|
nxtn = 3;
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)) {
|
|
if (sp->arraybits & CGO_COLOR_ARRAY) {
|
|
nxtVals = colorValsDA = nxtVals + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt += 4) {
|
|
colorValsUC[plc + cnt] = CLIP_COLOR_VALUE(colorValsDA[cnt]);
|
|
colorValsUC[plc + cnt + 1] =
|
|
CLIP_COLOR_VALUE(colorValsDA[cnt + 1]);
|
|
colorValsUC[plc + cnt + 2] =
|
|
CLIP_COLOR_VALUE(colorValsDA[cnt + 2]);
|
|
colorValsUC[plc + cnt + 3] =
|
|
CLIP_COLOR_VALUE(colorValsDA[cnt + 3]);
|
|
}
|
|
nxtn = VERTEX_COLOR_SIZE;
|
|
} else {
|
|
uchar col[4] = {CLIP_COLOR_VALUE(cgo->color[0]),
|
|
CLIP_COLOR_VALUE(cgo->color[1]),
|
|
CLIP_COLOR_VALUE(cgo->color[2]),
|
|
CLIP_COLOR_VALUE(cgo->alpha)};
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt++) {
|
|
colorValsUC[plc + cnt] = col[cnt % 4];
|
|
}
|
|
}
|
|
} else {
|
|
if (sp->arraybits & CGO_COLOR_ARRAY) {
|
|
nxtVals = colorValsDA = nxtVals + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt += 4) {
|
|
colorVals[plc + cnt] = colorValsDA[cnt];
|
|
colorVals[plc + cnt + 1] = colorValsDA[cnt + 1];
|
|
colorVals[plc + cnt + 2] = colorValsDA[cnt + 2];
|
|
colorVals[plc + cnt + 3] = colorValsDA[cnt + 3];
|
|
}
|
|
nxtn = VERTEX_COLOR_SIZE;
|
|
} else {
|
|
float col[4] = {
|
|
cgo->color[0], cgo->color[1], cgo->color[2], cgo->alpha};
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt++) {
|
|
colorVals[plc + cnt] = col[cnt % 4];
|
|
}
|
|
}
|
|
}
|
|
if (sp->arraybits & CGO_PICK_COLOR_ARRAY) {
|
|
nxtVals = nxtVals + (nxtn * sp->nverts);
|
|
pickColorValsDA = nxtVals + sp->nverts;
|
|
pickColorValsTMP = pickColorVals + (vpl * 2);
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
|
|
CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
|
|
}
|
|
nxtn = VERTEX_PICKCOLOR_SIZE;
|
|
} else {
|
|
pickColorValsTMP = pickColorVals + (vpl * 2);
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
CGO_put_uint(pickColorValsTMP++, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorValsTMP++, cgo->current_pick_color_bond);
|
|
}
|
|
}
|
|
if (sp->arraybits & CGO_ACCESSIBILITY_ARRAY) {
|
|
if (!ambient_occlusion) {
|
|
for (cnt = 0; cnt < vpl; cnt++) {
|
|
accessibilityVals[cnt] = 1.f;
|
|
}
|
|
}
|
|
ambient_occlusion = 1;
|
|
nxtVals = nxtVals + (nxtn * sp->nverts);
|
|
accessibilityValsDA = nxtVals;
|
|
accessibilityValsTMP = accessibilityVals + vpl;
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
accessibilityValsTMP[cnt] = accessibilityValsDA[cnt];
|
|
}
|
|
} else {
|
|
if (ambient_occlusion) {
|
|
accessibilityValsTMP = accessibilityVals + vpl;
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
accessibilityValsTMP[cnt] = 1.f;
|
|
}
|
|
}
|
|
}
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLES:
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
vertexIndices[idxpl++] = vpl + cnt;
|
|
}
|
|
break;
|
|
case GL_TRIANGLE_STRIP: {
|
|
short flip = 0;
|
|
for (cnt = 2; cnt < sp->nverts; cnt++) {
|
|
vertexIndices[idxpl++] = vpl + cnt - (flip ? 0 : 2);
|
|
vertexIndices[idxpl++] = vpl + cnt - 1;
|
|
vertexIndices[idxpl++] = vpl + cnt - (flip ? 2 : 0);
|
|
flip = !flip;
|
|
}
|
|
} break;
|
|
case GL_TRIANGLE_FAN:
|
|
for (cnt = 2; cnt < sp->nverts; cnt++) {
|
|
vertexIndices[idxpl++] = vpl;
|
|
vertexIndices[idxpl++] = vpl + cnt - 1;
|
|
vertexIndices[idxpl++] = vpl + cnt;
|
|
}
|
|
break;
|
|
}
|
|
pl += 3 * sp->nverts;
|
|
plc += 4 * sp->nverts;
|
|
vpl += sp->nverts;
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
if (sumarray) {
|
|
for (idxpl = 0; idxpl < count.num_total_indexes; idxpl += 3) {
|
|
add3f(&vertexVals[3 * vertexIndices[idxpl]],
|
|
&vertexVals[3 * vertexIndices[idxpl + 1]], sumarray);
|
|
add3f(&vertexVals[3 * vertexIndices[idxpl + 2]], sumarray, sumarray);
|
|
sumarray += 3;
|
|
}
|
|
}
|
|
if (ok) {
|
|
auto fmt = GetNormalColorFormatSize(I->G);
|
|
|
|
auto& vbo = CreateVertexBuffer(I->G);
|
|
BufferDataDesc dataDesc{{//
|
|
BufferDesc{"a_Vertex", VertexFormat::Float3,
|
|
sizeof(float) * count.num_total_vertices * 3, vertexVals},
|
|
BufferDesc{"a_Normal", fmt.normalFormat,
|
|
count.num_total_vertices * fmt.normalSize, normalVals},
|
|
BufferDesc{"a_Color", fmt.colorFormat,
|
|
count.num_total_vertices * fmt.colorSize, colorVals},
|
|
BufferDesc{"a_Accessibility", VertexFormat::Float,
|
|
sizeof(float) * count.num_total_vertices, accessibilityVals}}};
|
|
SetVertexBufferData(I->G, vbo, std::move(dataDesc));
|
|
|
|
auto& ibo = CreateIndexBuffer(I->G);
|
|
SetIndexBufferData(I->G, ibo, vertexIndices);
|
|
|
|
auto vboid = vbo.get_hash_id();
|
|
auto iboid = ibo.get_hash_id();
|
|
|
|
auto& pickvbo = CreateVertexBuffer(
|
|
I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu);
|
|
BufferDataDesc pickDataDesc{{//
|
|
BufferDesc{"a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes},
|
|
BufferDesc{"a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes}}};
|
|
SetVertexBufferData(I->G, pickvbo, std::move(pickDataDesc));
|
|
auto pickvboid = pickvbo.get_hash_id();
|
|
|
|
if (ok) {
|
|
float* newPickColorVals;
|
|
int arrays = CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY | CGO_COLOR_ARRAY |
|
|
CGO_PICK_COLOR_ARRAY;
|
|
if (ambient_occlusion) {
|
|
arrays |= CGO_ACCESSIBILITY_ARRAY;
|
|
}
|
|
if (addshaders)
|
|
CGOEnable(cgo, GL_DEFAULT_SHADER);
|
|
newPickColorVals = cgo->add<cgo::draw::buffers_indexed>(GL_TRIANGLES,
|
|
arrays, count.num_total_indexes, count.num_total_vertices, vboid, iboid, n_data,
|
|
pickvboid);
|
|
if (embedTransparencyInfo) {
|
|
int n_tri = count.num_total_indexes / 3;
|
|
float* sumarray;
|
|
float* sum = sumarray = newPickColorVals + count.num_total_vertices * 3;
|
|
float* z_value = sum + (count.num_total_indexes * 3);
|
|
int* ix = (int*) z_value + n_tri;
|
|
int* sort_mem = ix + n_tri;
|
|
auto vertexIndicesOriginalTI =
|
|
(VertexIndex_t*) (sort_mem + n_tri + 256);
|
|
|
|
for (idxpl = 0; idxpl < count.num_total_indexes; idxpl += 3) {
|
|
add3f(&vertexVals[3 * vertexIndices[idxpl]],
|
|
&vertexVals[3 * vertexIndices[idxpl + 1]], sumarray);
|
|
add3f(
|
|
&vertexVals[3 * vertexIndices[idxpl + 2]], sumarray, sumarray);
|
|
sumarray += 3;
|
|
}
|
|
memcpy(vertexIndicesOriginalTI, vertexIndices.data(),
|
|
sizeof(VertexIndex_t) * count.num_total_indexes);
|
|
}
|
|
|
|
if (addshaders && ok)
|
|
ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
|
|
CHECKOK(ok, newPickColorVals);
|
|
if (!newPickColorVals) {
|
|
I->G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(iboid);
|
|
}
|
|
if (ok)
|
|
memcpy(newPickColorVals + count.num_total_vertices, pickColorVals,
|
|
count.num_total_vertices * 2 * sizeof(float));
|
|
*has_draw_buffer = true;
|
|
} else {
|
|
I->G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(iboid);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static bool OptimizeLinesToVBOIndexed(const CGO* I, CGO* cgo,
|
|
const CGOCount& count, float* min, float* max, short* has_draw_buffer,
|
|
bool addshaders)
|
|
{
|
|
auto G = I->G;
|
|
bool ok = true;
|
|
uchar* colorValsUC = 0;
|
|
uchar* normalValsC = 0;
|
|
int pl = 0, plc = 0, idxpl = 0, vpl = 0, sz;
|
|
bool hasNormals = 0;
|
|
|
|
hasNormals = !CGOHasAnyLineVerticesWithoutNormals(I);
|
|
std::vector<VertexIndex_t> vertexIndexes(count.num_total_indexes_lines);
|
|
|
|
unsigned mul = VERTEX_POS_SIZE + VERTEX_PICKCOLOR_SIZE;
|
|
if (hasNormals) {
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_normal)
|
|
? 1
|
|
: VERTEX_NORMAL_SIZE;
|
|
}
|
|
mul += SettingGet<bool>(G, cSetting_cgo_shader_ub_color)
|
|
? 1
|
|
: VERTEX_COLOR_SIZE;
|
|
auto const tot = size_t(count.num_total_vertices_lines) * mul;
|
|
|
|
std::vector<float> vertexValsVec(tot);
|
|
auto* vertexVals = vertexValsVec.data();
|
|
auto* nxtVals = vertexVals + VERTEX_POS_SIZE * count.num_total_vertices_lines;
|
|
float* normalVals = nullptr;
|
|
if (hasNormals) {
|
|
normalVals = nxtVals;
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)) {
|
|
normalValsC = (uchar*) normalVals;
|
|
sz = 1;
|
|
} else {
|
|
sz = VERTEX_NORMAL_SIZE;
|
|
}
|
|
nxtVals += sz * count.num_total_vertices_lines;
|
|
}
|
|
|
|
auto* colorVals = nxtVals;
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)) {
|
|
colorValsUC = (uchar*) colorVals;
|
|
sz = 1;
|
|
} else {
|
|
sz = VERTEX_COLOR_SIZE;
|
|
}
|
|
nxtVals += sz * count.num_total_vertices_lines;
|
|
|
|
auto* pickColorVals = nxtVals;
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto pc = it.data();
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_NORMAL:
|
|
cgo->normal[0] = *pc;
|
|
cgo->normal[1] = *(pc + 1);
|
|
cgo->normal[2] = *(pc + 2);
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
cgo->current_accessibility = *pc;
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
auto sp = it.cast<cgo::draw::arrays>();
|
|
short shouldCompress = false;
|
|
switch (sp->mode) {
|
|
case GL_LINE_LOOP:
|
|
case GL_LINE_STRIP:
|
|
case GL_LINES:
|
|
shouldCompress = true;
|
|
default:
|
|
break;
|
|
}
|
|
if (shouldCompress) {
|
|
int cnt, nxtn = 3;
|
|
float *vertexValsDA = 0, *nxtVals2 = 0, *colorValsDA = 0,
|
|
*normalValsDA = 0;
|
|
float *pickColorValsDA = 0, *pickColorValsTMP;
|
|
|
|
nxtVals2 = vertexValsDA = sp->floatdata;
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt += 3) {
|
|
set_min_max(min, max, &vertexValsDA[cnt]);
|
|
}
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
vertexVals[pl + cnt] = vertexValsDA[cnt];
|
|
}
|
|
if (normalVals) {
|
|
if (sp->arraybits & CGO_NORMAL_ARRAY) {
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_normal)) {
|
|
nxtVals2 = normalValsDA = nxtVals2 + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
normalValsC[VAR_FOR_NORMAL + cnt VAR_FOR_NORMAL_CNT_PLUS] =
|
|
CLIP_NORMAL_VALUE(normalValsDA[cnt]);
|
|
}
|
|
} else {
|
|
nxtVals2 = normalValsDA = nxtVals2 + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 3; cnt++) {
|
|
normalVals[VAR_FOR_NORMAL + cnt] = normalValsDA[cnt];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)) {
|
|
if (sp->arraybits & CGO_COLOR_ARRAY) {
|
|
nxtVals2 = colorValsDA = nxtVals2 + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt++) {
|
|
colorValsUC[plc + cnt] = CLIP_COLOR_VALUE(colorValsDA[cnt]);
|
|
}
|
|
nxtn = 4;
|
|
} else {
|
|
uchar col[4] = {CLIP_COLOR_VALUE(cgo->color[0]),
|
|
CLIP_COLOR_VALUE(cgo->color[1]),
|
|
CLIP_COLOR_VALUE(cgo->color[2]),
|
|
CLIP_COLOR_VALUE(cgo->alpha)};
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt++) {
|
|
colorValsUC[plc + cnt] = col[cnt % 4];
|
|
}
|
|
}
|
|
} else {
|
|
if (sp->arraybits & CGO_COLOR_ARRAY) {
|
|
nxtVals2 = colorValsDA = nxtVals2 + (nxtn * sp->nverts);
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt++) {
|
|
colorVals[plc + cnt] = colorValsDA[cnt];
|
|
}
|
|
nxtn = 4;
|
|
} else {
|
|
float col[4] = {
|
|
cgo->color[0], cgo->color[1], cgo->color[2], cgo->alpha};
|
|
for (cnt = 0; cnt < sp->nverts * 4; cnt++) {
|
|
colorVals[plc + cnt] = col[cnt % 4];
|
|
}
|
|
}
|
|
}
|
|
if (sp->arraybits & CGO_PICK_COLOR_ARRAY) {
|
|
nxtVals2 = nxtVals2 + (nxtn * sp->nverts);
|
|
pickColorValsDA = nxtVals2 + sp->nverts;
|
|
pickColorValsTMP = pickColorVals + (vpl * 2);
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
|
|
CGO_put_int(pickColorValsTMP++, CGO_get_int(pickColorValsDA++));
|
|
}
|
|
nxtn = VERTEX_PICKCOLOR_SIZE;
|
|
} else {
|
|
pickColorValsTMP = pickColorVals + (vpl * 2);
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
CGO_put_uint(pickColorValsTMP++, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorValsTMP++, cgo->current_pick_color_bond);
|
|
}
|
|
}
|
|
if (idxpl + sp->nverts > count.num_total_indexes_lines) {
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"ERROR: CGOOptimizeToVBOIndexed() num_total_indexes_lines=%d "
|
|
"mode=%d nverts=%d idxpl=%d\n",
|
|
count.num_total_indexes_lines, sp->mode, sp->nverts,
|
|
idxpl ENDFB(I->G);
|
|
}
|
|
switch (sp->mode) {
|
|
case GL_LINES:
|
|
for (cnt = 0; cnt < sp->nverts; cnt++) {
|
|
vertexIndexes[idxpl++] = vpl + cnt;
|
|
}
|
|
break;
|
|
case GL_LINE_STRIP:
|
|
for (cnt = 1; cnt < sp->nverts; cnt++) {
|
|
vertexIndexes[idxpl++] = vpl + cnt - 1;
|
|
vertexIndexes[idxpl++] = vpl + cnt;
|
|
}
|
|
break;
|
|
case GL_LINE_LOOP:
|
|
for (cnt = 1; cnt < sp->nverts; cnt++) {
|
|
vertexIndexes[idxpl++] = vpl + cnt - 1;
|
|
vertexIndexes[idxpl++] = vpl + cnt;
|
|
}
|
|
vertexIndexes[idxpl++] = vpl;
|
|
vertexIndexes[idxpl++] = vpl + sp->nverts - 1;
|
|
break;
|
|
}
|
|
|
|
pl += 3 * sp->nverts;
|
|
plc += 4 * sp->nverts;
|
|
vpl += sp->nverts;
|
|
}
|
|
} break;
|
|
case CGO_SPECIAL:
|
|
CGOSpecial(cgo, CGO_get_int(pc));
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
if (ok) {
|
|
auto fmt = GetNormalColorFormatSize(I->G);
|
|
|
|
auto& vbo = CreateVertexBuffer(I->G);
|
|
BufferDataDesc dataDesc{{//
|
|
BufferDesc{"a_Vertex", VertexFormat::Float3,
|
|
sizeof(float) * count.num_total_vertices_lines * 3, vertexVals},
|
|
BufferDesc{"a_Normal", fmt.normalFormat,
|
|
count.num_total_vertices_lines * fmt.normalSize, normalVals},
|
|
BufferDesc{"a_Color", fmt.colorFormat,
|
|
count.num_total_vertices_lines * fmt.colorSize, colorVals},
|
|
}};
|
|
SetVertexBufferData(I->G, vbo, std::move(dataDesc));
|
|
|
|
auto& ibo = CreateIndexBuffer(I->G);
|
|
SetIndexBufferData(I->G, ibo, vertexIndexes);
|
|
|
|
auto vboid = vbo.get_hash_id();
|
|
auto iboid = ibo.get_hash_id();
|
|
|
|
auto& pickvbo = CreateVertexBuffer(
|
|
I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu);
|
|
BufferDataDesc pickDataDesc{{//
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes),
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * count.num_total_indexes)}};
|
|
SetVertexBufferData(I->G, pickvbo, std::move(pickDataDesc));
|
|
auto pickvboid = pickvbo.get_hash_id();
|
|
|
|
if (ok) {
|
|
if (addshaders) {
|
|
CGOEnable(cgo, GL_DEFAULT_SHADER);
|
|
// TODO: Check if this next line is supposed to be in this
|
|
// if-statement. VBONotIndexed has it otherwise.
|
|
CGODisable(cgo, GL_SHADER_LIGHTING);
|
|
}
|
|
auto newPickColorVals = cgo->add<cgo::draw::buffers_indexed>(GL_LINES,
|
|
CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY | CGO_COLOR_ARRAY |
|
|
CGO_PICK_COLOR_ARRAY,
|
|
count.num_total_indexes_lines, count.num_total_vertices_lines, vboid,
|
|
iboid, 0, pickvboid);
|
|
CHECKOK(ok, newPickColorVals);
|
|
if (addshaders && ok)
|
|
ok &= CGODisable(cgo, GL_DEFAULT_SHADER);
|
|
if (!newPickColorVals) {
|
|
I->G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(iboid);
|
|
}
|
|
if (ok)
|
|
memcpy(newPickColorVals + count.num_total_vertices_lines, pickColorVals,
|
|
count.num_total_vertices_lines * 2 * sizeof(float));
|
|
*has_draw_buffer = true;
|
|
} else {
|
|
I->G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(iboid);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* Similar to CGOOptimizeToVBONotIndexed(), but works with sortable alpha
|
|
* triangles.
|
|
*
|
|
* @param I Primitive CGO to convert
|
|
* @param est Output CGO size estimate (size of buffer to "reserve")
|
|
* @param color Initial color to use until a CGO_COLOR operation is observed
|
|
* @param addshaders Add Enable/Disable shader operations
|
|
* @param embedTransparencyInfo ???
|
|
*
|
|
* @return New converted CGO with use_shader=true
|
|
*/
|
|
CGO* CGOOptimizeToVBOIndexed(const CGO* I, int est, const float* color,
|
|
bool addshaders, bool embedTransparencyInfo)
|
|
{
|
|
CGO* cgo;
|
|
|
|
std::unique_ptr<CGO> I_begin_end_combined;
|
|
if (I->has_begin_end) {
|
|
I_begin_end_combined.reset(CGOCombineBeginEnd(I));
|
|
I = I_begin_end_combined.get();
|
|
assert(!I->has_begin_end);
|
|
}
|
|
|
|
short has_draw_buffer = false;
|
|
float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX},
|
|
max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
|
|
int ok = true;
|
|
|
|
auto count = CGOCountNumVertices(I);
|
|
|
|
cgo = CGONewSized(I->G, I->c + est);
|
|
CHECKOK(ok, cgo);
|
|
if (ok) {
|
|
if (color) {
|
|
cgo->color[0] = color[0];
|
|
cgo->color[1] = color[1];
|
|
cgo->color[2] = color[2];
|
|
cgo->alpha = color[3];
|
|
} else {
|
|
cgo->color[0] = 1.f;
|
|
cgo->color[1] = 1.f;
|
|
cgo->color[2] = 1.f;
|
|
cgo->alpha = 1.f;
|
|
}
|
|
}
|
|
|
|
if (count.num_total_vertices_points > 0) {
|
|
/* This does not need to be indexed (for now) */
|
|
if (!OptimizePointsToVBO(I, cgo, count.num_total_vertices_points, min, max,
|
|
&has_draw_buffer, addshaders)) {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (count.num_total_vertices > 0) {
|
|
if (!OptimizeVertsToVBOIndexed(I, cgo, count, min, max, &has_draw_buffer,
|
|
addshaders, embedTransparencyInfo)) {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (ok && count.num_total_vertices_lines > 0) {
|
|
if (!OptimizeLinesToVBOIndexed(
|
|
I, cgo, count, min, max, &has_draw_buffer, addshaders)) {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (ok && (count.num_total_vertices > 0 || count.num_total_vertices_lines > 0)) {
|
|
ok &= CGOBoundingBox(cgo, min, max);
|
|
}
|
|
|
|
if (ok)
|
|
ok &= CGOStop(cgo);
|
|
if (ok) {
|
|
if (has_draw_buffer) {
|
|
cgo->has_draw_buffers = true;
|
|
}
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
struct OptimizeSphereData {
|
|
std::vector<float> vert;
|
|
std::vector<unsigned char> color;
|
|
std::vector<int> pickColor;
|
|
std::vector<unsigned char> rightUpFlagsUB;
|
|
std::vector<float> rightUpFlags;
|
|
float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
|
|
float max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
|
|
int total_vert = 0;
|
|
int total_spheres = 0;
|
|
};
|
|
|
|
/**
|
|
* @param[in] I input CGO
|
|
* @param[out] cgo output CGO
|
|
* @param[in] num_total_spheres Number of spheres to optimize
|
|
* @param[out] leftOverCGO CGO data not relevant to spheres.
|
|
* @pre cgo must not be nullptr
|
|
*
|
|
* Note: To use leftOverCGO, it must already be preallocated.
|
|
*/
|
|
|
|
static OptimizeSphereData GetOptimizeSphereData(
|
|
const CGO* I, CGO*& cgo, int num_total_spheres, CGO* leftOverCGO)
|
|
{
|
|
OptimizeSphereData sphereData;
|
|
const int tot = VerticesPerSphere() * 4 * num_total_spheres;
|
|
#if defined(PURE_OPENGL_ES_2)
|
|
int rightup_flags[6] = {0, 1, 3, 3, 2, 0};
|
|
#else
|
|
int rightup_flags[4] = {0, 1, 3, 2};
|
|
#endif
|
|
|
|
auto cgo_shader_ub_flags =
|
|
SettingGet<bool>(cgo->G, cSetting_cgo_shader_ub_flags);
|
|
|
|
sphereData.vert.resize(tot);
|
|
auto vertVals = sphereData.vert.data();
|
|
|
|
sphereData.color.resize(tot);
|
|
auto colorValsUB = sphereData.color.data();
|
|
|
|
unsigned char* rightUpFlagValsUB = nullptr;
|
|
float* rightUpFlagVals = nullptr;
|
|
if (cgo_shader_ub_flags) {
|
|
sphereData.rightUpFlagsUB.resize(VALUES_PER_IMPOSTER_SPACE_COORD *
|
|
VerticesPerSphere() * num_total_spheres);
|
|
rightUpFlagValsUB = sphereData.rightUpFlagsUB.data();
|
|
} else {
|
|
sphereData.rightUpFlags.resize(VALUES_PER_IMPOSTER_SPACE_COORD *
|
|
VerticesPerSphere() * num_total_spheres);
|
|
rightUpFlagVals = sphereData.rightUpFlags.data();
|
|
}
|
|
|
|
bool has_picking = CGOHasOperationsOfType(I, CGO_PICK_COLOR);
|
|
int* pickcolorVals = nullptr;
|
|
if (has_picking) {
|
|
// atom/bond info for picking, 2 ints for each sphere
|
|
sphereData.pickColor.resize(num_total_spheres * 2 * 4);
|
|
pickcolorVals = sphereData.pickColor.data();
|
|
}
|
|
|
|
cgo->alpha = 1.f;
|
|
float min_alpha = 1.f;
|
|
bool copyNormalToLeftOver = false;
|
|
bool copyColorToLeftOver = false;
|
|
bool copyPickColorToLeftOver = false;
|
|
bool copyAlphaToLeftOver = false;
|
|
bool ok = true;
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_NORMAL:
|
|
cgo->normal[0] = *pc;
|
|
cgo->normal[1] = *(pc + 1);
|
|
cgo->normal[2] = *(pc + 2);
|
|
copyNormalToLeftOver = true;
|
|
break;
|
|
case CGO_COLOR:
|
|
cgo->color[0] = *pc;
|
|
cgo->color[1] = *(pc + 1);
|
|
cgo->color[2] = *(pc + 2);
|
|
copyColorToLeftOver = true;
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
if (cgo->alpha < min_alpha)
|
|
min_alpha = cgo->alpha;
|
|
copyAlphaToLeftOver = true;
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
copyPickColorToLeftOver = true;
|
|
break;
|
|
case CGO_SPHERE:
|
|
for (int vv = 0; vv < VerticesPerSphere();
|
|
vv++) { // generate eight vertices of a bounding box for each
|
|
// cylinder
|
|
vertVals[0] = *(pc);
|
|
vertVals[1] = *(pc + 1);
|
|
vertVals[2] = *(pc + 2);
|
|
vertVals[3] = *(pc + 3);
|
|
set_min_max(sphereData.min, sphereData.max, vertVals);
|
|
if (cgo_shader_ub_flags) {
|
|
rightUpFlagValsUB[0] = rightup_flags[vv];
|
|
rightUpFlagValsUB++;
|
|
} else {
|
|
rightUpFlagVals[0] = rightup_flags[vv];
|
|
rightUpFlagVals++;
|
|
}
|
|
colorValsUB[0] = CLIP_COLOR_VALUE(cgo->color[0]);
|
|
colorValsUB[1] = CLIP_COLOR_VALUE(cgo->color[1]);
|
|
colorValsUB[2] = CLIP_COLOR_VALUE(cgo->color[2]);
|
|
colorValsUB[3] = CLIP_COLOR_VALUE(cgo->alpha);
|
|
colorValsUB += 4;
|
|
vertVals += 4;
|
|
sphereData.total_vert++;
|
|
}
|
|
if (has_picking) {
|
|
*(pickcolorVals++) = cgo->current_pick_color_index;
|
|
*(pickcolorVals++) = cgo->current_pick_color_bond;
|
|
}
|
|
sphereData.total_spheres++;
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeSpheresToVBONonIndexed() CGO_DRAW_BUFFERS_INDEXED "
|
|
"or CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeCylindersToVBO() "
|
|
"CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS encountered op=0x%X\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_LABELS:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeCylindersToVBO() CGO_DRAW_LABELS encountered "
|
|
"op=0x%X\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_TEXTURES:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeCylindersToVBO() CGO_DRAW_TEXTURES encountered "
|
|
"op=0x%X\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_ARRAYS:
|
|
default:
|
|
if (!leftOverCGO)
|
|
break;
|
|
if (copyAlphaToLeftOver) {
|
|
copyAlphaToLeftOver = false;
|
|
CGOAlpha(leftOverCGO, cgo->alpha);
|
|
}
|
|
if (copyColorToLeftOver) {
|
|
copyColorToLeftOver = false;
|
|
CGOColor(leftOverCGO, cgo->color[0], cgo->color[1], cgo->color[2]);
|
|
}
|
|
if (copyNormalToLeftOver) {
|
|
CGONormalv(leftOverCGO, cgo->normal);
|
|
}
|
|
if (copyPickColorToLeftOver) {
|
|
copyPickColorToLeftOver = false;
|
|
CGOPickColor(leftOverCGO, cgo->current_pick_color_index,
|
|
cgo->current_pick_color_bond);
|
|
}
|
|
leftOverCGO->add_to_cgo(op, pc);
|
|
}
|
|
#ifndef _WEBGL
|
|
ok &= !I->G->Interrupt;
|
|
#endif
|
|
}
|
|
return sphereData;
|
|
}
|
|
|
|
/**
|
|
* Creates and fills OpenGL buffers with optimized sphere Data
|
|
* @param[in] I input CGO
|
|
* @param[out] cgo output optimized CGO
|
|
* @param[in] addshaders adds shader call to CGO
|
|
* @param[in] num_total_spheres number of total spheres to optimize
|
|
* @param[in] sphereData Optimized Sphere Data
|
|
* @pre cgo must not be nullptr
|
|
*/
|
|
|
|
static bool PopulateBufferOptimizedSphereData(const CGO* I, CGO* cgo,
|
|
bool addshaders, int num_total_spheres,
|
|
const OptimizeSphereData& sphereData)
|
|
{
|
|
bool ok = true;
|
|
auto rtp = VertexFormat::Float;
|
|
short rsz = sizeof(float);
|
|
const void* radiusptr = sphereData.rightUpFlags.data();
|
|
auto cgo_shader_ub_flags =
|
|
SettingGet<bool>(cgo->G, cSetting_cgo_shader_ub_flags);
|
|
if (cgo_shader_ub_flags) {
|
|
rtp = VertexFormat::UByte;
|
|
rsz = sizeof(std::uint8_t);
|
|
radiusptr = sphereData.rightUpFlagsUB.data();
|
|
}
|
|
|
|
auto& vbo = CreateVertexBuffer(I->G);
|
|
BufferDataDesc vertDescs{{//
|
|
BufferDesc("a_vertex_radius", VertexFormat::Float4,
|
|
sizeof(float) * sphereData.total_vert * 4, sphereData.vert.data()),
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * sphereData.total_vert, sphereData.color.data()),
|
|
BufferDesc("a_rightUpFlags", rtp,
|
|
rsz * sphereData.total_vert * VALUES_PER_IMPOSTER_SPACE_COORD,
|
|
radiusptr),
|
|
}};
|
|
SetVertexBufferData(I->G, vbo, std::move(vertDescs));
|
|
auto vboid = vbo.get_hash_id();
|
|
|
|
auto& pickvbo = CreateVertexBuffer(
|
|
I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu);
|
|
BufferDataDesc pickDataDesc{{//
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm, 0),
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * sphereData.total_vert)}};
|
|
auto size = sizeof(float) * sphereData.total_vert * 2;
|
|
SetVertexBufferDataSized(I->G, pickvbo, std::move(pickDataDesc), size);
|
|
|
|
auto pickvboid = pickvbo.get_hash_id();
|
|
|
|
cgo->has_draw_buffers = true;
|
|
cgo->has_draw_sphere_buffers = true;
|
|
|
|
auto freebuffers = [vboid, pickvboid, I]() {
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
};
|
|
if (ok) {
|
|
if (addshaders)
|
|
CGOEnable(cgo, GL_SPHERE_SHADER);
|
|
auto pickcolor_data = (int*) cgo->add<cgo::draw::sphere_buffers>(
|
|
sphereData.total_spheres, (cgo_shader_ub_flags ? 3 : 1), vboid,
|
|
pickvboid); // always cgo_shader_ub_color
|
|
CHECKOK(ok, pickcolor_data);
|
|
if (ok && !sphereData.pickColor.empty()) {
|
|
memcpy(pickcolor_data, sphereData.pickColor.data(),
|
|
num_total_spheres * 2 * 4);
|
|
}
|
|
if (ok && addshaders)
|
|
ok &= CGODisable(cgo, GL_SPHERE_SHADER);
|
|
if (!ok) {
|
|
freebuffers();
|
|
}
|
|
} else {
|
|
freebuffers();
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* Optimizes Sphere CGO and populates data into GPU buffers
|
|
* @param[in] I input CGO
|
|
* @param[in] est estimated size of output CGO
|
|
* @param[in] addshaders adds shader call to CGO
|
|
* @param[out] leftOverCGO CGO data not relevant to spheres.
|
|
* @return optimized CGO
|
|
*
|
|
* Note: If estimated size is unknown beforehand, provide 0 to est.
|
|
* Note: To use leftOverCGO, it must already be preallocated.
|
|
*/
|
|
|
|
CGO* CGOOptimizeSpheresToVBONonIndexed(
|
|
const CGO* I, int est, bool addshaders, CGO* leftOverCGO)
|
|
{
|
|
bool ok = true;
|
|
int num_total_spheres = CGOCountNumberOfOperationsOfType(I, CGO_SPHERE);
|
|
|
|
if (num_total_spheres <= 0) {
|
|
return nullptr;
|
|
}
|
|
auto cgo = CGONewSized(I->G, I->c + est);
|
|
auto sphereData =
|
|
GetOptimizeSphereData(I, cgo, num_total_spheres, leftOverCGO);
|
|
|
|
if (ok && sphereData.total_spheres > 0) {
|
|
ok = PopulateBufferOptimizedSphereData(
|
|
I, cgo, addshaders, num_total_spheres, sphereData);
|
|
}
|
|
|
|
if (ok && num_total_spheres > 0) {
|
|
ok &= CGOBoundingBox(cgo, sphereData.min, sphereData.max);
|
|
}
|
|
|
|
if (ok)
|
|
ok &= CGOStop(cgo);
|
|
|
|
if (ok) {
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color = true;
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGet<bool>(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
CGO* CGOOptimizeBezier(const CGO* I)
|
|
{
|
|
auto cgo = std::make_unique<CGO>(I->G);
|
|
int num_splines = CGOCountNumberOfOperationsOfType(I, CGO_BEZIER);
|
|
auto& vbo = CreateVertexBuffer(I->G);
|
|
std::vector<float> vertData;
|
|
vertData.reserve(num_splines * CGO_BEZIER_SZ);
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_BEZIER: {
|
|
vertData.resize(vertData.size() + CGO_BEZIER_SZ);
|
|
std::copy_n(pc, CGO_BEZIER_SZ, vertData.end() - CGO_BEZIER_SZ);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
std::size_t numDimensions = 3;
|
|
std::size_t numVerts = 4;
|
|
BufferDataDesc dataDesc{{
|
|
BufferDesc("position", VertexFormat::Float3,
|
|
sizeof(float) * numVerts * numDimensions, vertData.data()),
|
|
}};
|
|
SetVertexBufferData(I->G, vbo, std::move(dataDesc));
|
|
auto vboid = vbo.get_hash_id();
|
|
|
|
CGOEnable(cgo.get(), GL_BEZIER_SHADER);
|
|
cgo->add<cgo::draw::bezier_buffers>(vboid);
|
|
CGODisable(cgo.get(), GL_BEZIER_SHADER);
|
|
cgo->use_shader = true;
|
|
return cgo.release();
|
|
}
|
|
|
|
/**
|
|
* converts a CGO that has primitives into pure geometry,
|
|
* and converts CGO_BEGIN/CGO_END blocks into CGO_DRAW_ARRAYS
|
|
* operations, similar to what CGOCombineBeginEnd() does.
|
|
*
|
|
* I: input CGO
|
|
* est: initial size of the newly allocated CGO array
|
|
* that is returned by this function
|
|
* sphere_quality: the quality of the spheres generated by this function
|
|
* (if -1, defaults to cgo_sphere_quality)
|
|
* stick_round_nub: if true, a round cap is generated, otherwise, it generates
|
|
* the old "pointed" caps
|
|
*/
|
|
CGO* CGOSimplify(
|
|
const CGO* I, int est, short sphere_quality, bool stick_round_nub)
|
|
{
|
|
auto G = I->G;
|
|
int ok = true;
|
|
if (sphere_quality < 0) {
|
|
sphere_quality =
|
|
SettingGet_i(I->G, nullptr, nullptr, cSetting_cgo_sphere_quality);
|
|
}
|
|
|
|
std::unique_ptr<CGO> cgo_managed(CGONew(G, I->c + est));
|
|
auto* const cgo = cgo_managed.get();
|
|
RETURN_VAL_IF_FAIL(cgo, nullptr);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_COLOR:
|
|
copy3f(pc, cgo->color);
|
|
CGOColorv(cgo, pc);
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
CGOPickColor(cgo, CGO_get_uint(pc), CGO_get_int(pc + 1));
|
|
break;
|
|
case CGO_SHADER_CYLINDER: {
|
|
float v2[3];
|
|
int cap = CGO_get_int(pc + 7);
|
|
cCylCap fcap = cap1_from_cyl_shader_bits(cap);
|
|
cCylCap bcap = cap2_from_cyl_shader_bits(cap);
|
|
add3f(pc, pc + 3, v2);
|
|
ok &= CGOSimpleCylinder(cgo, pc, v2, *(pc + 6), 0, 0, cgo->alpha,
|
|
cgo->alpha, (cap & cCylShaderInterpColor), fcap, bcap, nullptr,
|
|
stick_round_nub);
|
|
} break;
|
|
case CGO_SHADER_CYLINDER_WITH_2ND_COLOR: {
|
|
auto cyl = it.cast<cgo::draw::shadercylinder2ndcolor>();
|
|
float v1[3];
|
|
int cap = cyl->cap;
|
|
cCylCap fcap = cap1_from_cyl_shader_bits(cap);
|
|
cCylCap bcap = cap2_from_cyl_shader_bits(cap);
|
|
Pickable pickcolor2 = {cyl->pick_color_index, cyl->pick_color_bond};
|
|
float color1[3] = {cgo->color[0], cgo->color[1], cgo->color[2]};
|
|
add3f(pc, pc + 3, v1);
|
|
float mid[3];
|
|
mult3f(cyl->axis, .5f, mid);
|
|
add3f(cyl->origin, mid, mid);
|
|
float alpha2 = cyl->alpha >= 0.f ? cyl->alpha : cgo->alpha;
|
|
if (cap & cCylShaderInterpColor) {
|
|
ok &= CGOSimpleCylinder(cgo, cyl->origin, v1, cyl->tube_size, color1,
|
|
cyl->color2, cgo->alpha, alpha2, true, bcap, fcap, &pickcolor2,
|
|
stick_round_nub);
|
|
} else {
|
|
ok &= CGOColorv(cgo, color1);
|
|
ok &= CGOSimpleCylinder(cgo, cyl->origin, mid, cyl->tube_size, color1,
|
|
nullptr, cgo->alpha, alpha2, false, fcap, cCylCap::None, nullptr,
|
|
stick_round_nub);
|
|
ok &= CGOColorv(cgo, cyl->color2);
|
|
ok &= CGOPickColor(cgo, pickcolor2.index, pickcolor2.bond);
|
|
ok &= CGOSimpleCylinder(cgo, mid, v1, cyl->tube_size, cyl->color2,
|
|
nullptr, cgo->alpha, alpha2, false, cCylCap::None, bcap, nullptr,
|
|
stick_round_nub);
|
|
}
|
|
} break;
|
|
case CGO_CYLINDER: {
|
|
auto cyl = it.cast<cgo::draw::cylinder>();
|
|
ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true,
|
|
cCylCap::Flat, cCylCap::Flat, nullptr, stick_round_nub);
|
|
} break;
|
|
case CGO_CONE: {
|
|
auto cone = it.cast<cgo::draw::cone>();
|
|
ok &= CGOSimpleCone(cgo, *cone);
|
|
} break;
|
|
case CGO_SAUSAGE:
|
|
ok &= CGOSimpleCylinder(cgo, pc, pc + 3, *(pc + 6), pc + 7, pc + 10,
|
|
cgo->alpha, cgo->alpha, true, cCylCap::Round, cCylCap::Round, nullptr,
|
|
stick_round_nub);
|
|
break;
|
|
case CGO_CUSTOM_CYLINDER: {
|
|
auto cyl = it.cast<cgo::draw::custom_cylinder>();
|
|
ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true,
|
|
cyl->get_cap1(), cyl->get_cap2(), nullptr, stick_round_nub);
|
|
} break;
|
|
case CGO_CUSTOM_CYLINDER_ALPHA: {
|
|
auto cyl = it.cast<cgo::draw::custom_cylinder_alpha>();
|
|
ok &= CGOSimpleCylinder(cgo, *cyl, cyl->color1[3], cyl->color2[3], true,
|
|
cyl->get_cap1(), cyl->get_cap2(), nullptr, stick_round_nub);
|
|
} break;
|
|
case CGO_SPHERE:
|
|
ok &= CGOSimpleSphere(cgo, pc, *(pc + 3), sphere_quality);
|
|
break;
|
|
case CGO_ELLIPSOID:
|
|
ok &= CGOSimpleEllipsoid(cgo, pc, *(pc + 3), pc + 4, pc + 7, pc + 10);
|
|
break;
|
|
case CGO_QUADRIC:
|
|
ok &= CGOSimpleQuadric(cgo, pc, *(pc + 3), pc + 4);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
case CGO_DRAW_SPHERE_BUFFERS:
|
|
case CGO_DRAW_CYLINDER_BUFFERS:
|
|
case CGO_DRAW_LABELS:
|
|
case CGO_DRAW_TEXTURES:
|
|
case CGO_END:
|
|
case CGO_VERTEX:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return nullptr;
|
|
case CGO_BEGIN: {
|
|
float firstColor[3], firstAlpha;
|
|
char hasFirstColor = 0, hasFirstAlpha = 0;
|
|
int nverts = 0, damode = CGO_VERTEX_ARRAY, err = 0;
|
|
int mode = it.cast<cgo::draw::begin>()->mode;
|
|
|
|
// remember for a second iteration
|
|
auto it2 = it;
|
|
|
|
for (++it;; ++it) {
|
|
if (it.is_stop()) {
|
|
WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
|
|
return nullptr;
|
|
}
|
|
|
|
const auto op = it.op_code();
|
|
if (op == CGO_END) {
|
|
break;
|
|
}
|
|
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return nullptr;
|
|
case CGO_NORMAL:
|
|
damode |= CGO_NORMAL_ARRAY;
|
|
break;
|
|
case CGO_COLOR:
|
|
if (!nverts) {
|
|
hasFirstColor = 1;
|
|
firstColor[0] = pc[0];
|
|
firstColor[1] = pc[1];
|
|
firstColor[2] = pc[2];
|
|
} else {
|
|
hasFirstColor = 0;
|
|
damode |= CGO_COLOR_ARRAY;
|
|
}
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
damode |= CGO_PICK_COLOR_ARRAY;
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
damode |= CGO_ACCESSIBILITY_ARRAY;
|
|
break;
|
|
case CGO_VERTEX:
|
|
nverts++;
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
if (!nverts) {
|
|
hasFirstAlpha = 1;
|
|
firstAlpha = cgo->alpha;
|
|
} else {
|
|
hasFirstAlpha = 0;
|
|
damode |= CGO_COLOR_ARRAY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nverts > 0 && !err) {
|
|
int pl = 0, plc = 0, pla = 0;
|
|
float *vertexVals, *tmp_ptr;
|
|
float *normalVals = 0, *colorVals = 0, *nxtVals = 0, *pickColorVals = 0,
|
|
*accessibilityVals = 0;
|
|
short notHaveValue = 0, nxtn = 3;
|
|
if (hasFirstAlpha || hasFirstColor) {
|
|
if (hasFirstAlpha) {
|
|
CGOAlpha(cgo, firstAlpha);
|
|
}
|
|
if (hasFirstColor) {
|
|
CGOColorv(cgo, firstColor);
|
|
}
|
|
}
|
|
nxtVals = vertexVals =
|
|
cgo->add<cgo::draw::arrays>(mode, damode, nverts);
|
|
RETURN_VAL_IF_FAIL(vertexVals, nullptr);
|
|
if (damode & CGO_NORMAL_ARRAY) {
|
|
nxtVals = normalVals = vertexVals + (nxtn * nverts);
|
|
}
|
|
if (damode & CGO_COLOR_ARRAY) {
|
|
nxtVals = colorVals = nxtVals + (nxtn * nverts);
|
|
nxtn = VERTEX_COLOR_SIZE;
|
|
}
|
|
if (damode & CGO_PICK_COLOR_ARRAY) {
|
|
nxtVals = nxtVals + (nxtn * nverts);
|
|
pickColorVals = nxtVals + nverts;
|
|
nxtn = VERTEX_PICKCOLOR_SIZE;
|
|
}
|
|
if (damode & CGO_ACCESSIBILITY_ARRAY) {
|
|
nxtVals = nxtVals + (nxtn * nverts);
|
|
accessibilityVals = nxtVals;
|
|
nxtn = 1;
|
|
}
|
|
notHaveValue = damode;
|
|
bool skiptoend = false;
|
|
|
|
// second iteration
|
|
for (++it2; !skiptoend; ++it2) {
|
|
const auto op = it2.op_code();
|
|
if (op == CGO_END) {
|
|
break;
|
|
}
|
|
|
|
const auto pc = it2.data();
|
|
|
|
switch (op) {
|
|
case CGO_NORMAL:
|
|
normalVals[pl] = pc[0];
|
|
normalVals[pl + 1] = pc[1];
|
|
normalVals[pl + 2] = pc[2];
|
|
notHaveValue &= ~CGO_NORMAL_ARRAY;
|
|
break;
|
|
case CGO_COLOR:
|
|
if (colorVals) {
|
|
colorVals[plc] = pc[0];
|
|
colorVals[plc + 1] = pc[1];
|
|
colorVals[plc + 2] = pc[2];
|
|
colorVals[plc + 3] = cgo->alpha;
|
|
notHaveValue &= ~CGO_COLOR_ARRAY;
|
|
}
|
|
break;
|
|
case CGO_PICK_COLOR:
|
|
CGOPickColor(cgo, CGO_get_uint(pc), CGO_get_int(pc + 1));
|
|
notHaveValue &= ~CGO_PICK_COLOR_ARRAY;
|
|
break;
|
|
case CGO_ACCESSIBILITY:
|
|
cgo->current_accessibility = pc[0];
|
|
break;
|
|
case CGO_VERTEX:
|
|
if (notHaveValue & CGO_NORMAL_ARRAY) {
|
|
if (pl) {
|
|
tmp_ptr = &normalVals[pl - 3];
|
|
normalVals[pl] = tmp_ptr[0];
|
|
normalVals[pl + 1] = tmp_ptr[1];
|
|
normalVals[pl + 2] = tmp_ptr[2];
|
|
} else {
|
|
copy3f(cgo->normal, &normalVals[pl]);
|
|
}
|
|
}
|
|
if (notHaveValue & CGO_COLOR_ARRAY) {
|
|
if (plc) {
|
|
tmp_ptr = &colorVals[plc - 4];
|
|
colorVals[plc] = tmp_ptr[0];
|
|
colorVals[plc + 1] = tmp_ptr[1];
|
|
colorVals[plc + 2] = tmp_ptr[2];
|
|
colorVals[plc + 3] = tmp_ptr[3];
|
|
} else {
|
|
copy3f(cgo->color, &colorVals[plc]);
|
|
colorVals[plc + 3] = cgo->alpha;
|
|
}
|
|
}
|
|
if (pickColorVals) {
|
|
CGO_put_uint(
|
|
pickColorVals + pla * 2, cgo->current_pick_color_index);
|
|
CGO_put_int(
|
|
pickColorVals + pla * 2 + 1, cgo->current_pick_color_bond);
|
|
}
|
|
if (accessibilityVals) {
|
|
accessibilityVals[pla] = cgo->current_accessibility;
|
|
}
|
|
vertexVals[pl++] = pc[0];
|
|
vertexVals[pl++] = pc[1];
|
|
vertexVals[pl++] = pc[2];
|
|
plc += 4;
|
|
pla++;
|
|
if (pla >= nverts) // anything past the last vertex is ignored
|
|
skiptoend = true;
|
|
notHaveValue = damode;
|
|
break;
|
|
case CGO_ALPHA:
|
|
// in case we're before CGO_COLOR
|
|
cgo->alpha = *pc;
|
|
if (colorVals) {
|
|
// in case we're after CGO_COLOR
|
|
colorVals[plc + 3] = *pc;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
|
|
if (G->Interrupt) {
|
|
return nullptr;
|
|
}
|
|
|
|
RETURN_VAL_IF_FAIL(ok, nullptr);
|
|
}
|
|
|
|
CGOStop(cgo);
|
|
return cgo_managed.release();
|
|
}
|
|
|
|
/**
|
|
* converts a CGO that has primitives into pure geomtry, just like CGOSimplify
|
|
* but without converting the CGO_BEGIN/CGO_END blocks.
|
|
*
|
|
*/
|
|
CGO* CGOSimplifyNoCompress(
|
|
const CGO* I, int est, short sphere_quality, bool stick_round_nub)
|
|
{
|
|
CGO* cgo;
|
|
|
|
int ok = true;
|
|
if (sphere_quality < 0) {
|
|
sphere_quality =
|
|
SettingGet_i(I->G, nullptr, nullptr, cSetting_cgo_sphere_quality);
|
|
}
|
|
|
|
cgo = CGONewSized(I->G, I->c + est);
|
|
CHECKOK(ok, cgo);
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_PICK_COLOR:
|
|
CGOPickColor(cgo, CGO_get_uint(pc), CGO_get_int(pc + 1));
|
|
break;
|
|
case CGO_SHADER_CYLINDER: {
|
|
float v2[3];
|
|
int cap = CGO_get_int(pc + 7);
|
|
cCylCap fcap = cap1_from_cyl_shader_bits(cap);
|
|
cCylCap bcap = cap2_from_cyl_shader_bits(cap);
|
|
add3f(pc, pc + 3, v2);
|
|
ok &= CGOSimpleCylinder(cgo, pc, v2, *(pc + 6), 0, 0, cgo->alpha,
|
|
cgo->alpha, (cap & cCylShaderInterpColor), fcap, bcap, nullptr,
|
|
stick_round_nub);
|
|
} break;
|
|
case CGO_SHADER_CYLINDER_WITH_2ND_COLOR: {
|
|
auto cyl = it.cast<cgo::draw::shadercylinder2ndcolor>();
|
|
float v1[3];
|
|
int cap = cyl->cap;
|
|
cCylCap fcap = cap1_from_cyl_shader_bits(cap);
|
|
cCylCap bcap = cap2_from_cyl_shader_bits(cap);
|
|
Pickable pickcolor2 = {cyl->pick_color_index, cyl->pick_color_bond};
|
|
float color1[3] = {cgo->color[0], cgo->color[1], cgo->color[2]};
|
|
add3f(cyl->origin, cyl->axis, v1);
|
|
float mid[3];
|
|
mult3f(cyl->axis, .5f, mid);
|
|
add3f(cyl->origin, mid, mid);
|
|
float alpha2 = cyl->alpha >= 0.f ? cyl->alpha : cgo->alpha;
|
|
if (cap & cCylShaderInterpColor) {
|
|
ok &= CGOSimpleCylinder(cgo, cyl->origin, v1, cyl->tube_size, color1,
|
|
cyl->color2, cgo->alpha, alpha2, true, bcap, fcap, &pickcolor2,
|
|
stick_round_nub);
|
|
} else {
|
|
ok &= CGOColorv(cgo, color1);
|
|
ok &= CGOSimpleCylinder(cgo, cyl->origin, mid, cyl->tube_size, color1,
|
|
nullptr, cgo->alpha, alpha2, false, fcap, cCylCap::None, nullptr,
|
|
stick_round_nub);
|
|
ok &= CGOColorv(cgo, cyl->color2);
|
|
ok &= CGOPickColor(cgo, pickcolor2.index, pickcolor2.bond);
|
|
ok &= CGOSimpleCylinder(cgo, mid, v1, cyl->tube_size, cyl->color2,
|
|
nullptr, cgo->alpha, alpha2, false, cCylCap::None, bcap, nullptr,
|
|
stick_round_nub);
|
|
}
|
|
} break;
|
|
case CGO_CYLINDER: {
|
|
auto cyl = it.cast<cgo::draw::cylinder>();
|
|
ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true,
|
|
cCylCap::Flat, cCylCap::Flat, nullptr, stick_round_nub);
|
|
} break;
|
|
case CGO_CONE: {
|
|
auto cone = it.cast<cgo::draw::cone>();
|
|
ok &= CGOSimpleCone(cgo, *cone);
|
|
} break;
|
|
case CGO_SAUSAGE:
|
|
ok &= CGOSimpleCylinder(cgo, pc, pc + 3, *(pc + 6), pc + 7, pc + 10,
|
|
cgo->alpha, cgo->alpha, true, cCylCap::Round, cCylCap::Round, nullptr,
|
|
stick_round_nub);
|
|
break;
|
|
case CGO_CUSTOM_CYLINDER: {
|
|
auto cyl = it.cast<cgo::draw::custom_cylinder>();
|
|
ok &= CGOSimpleCylinder(cgo, *cyl, cgo->alpha, cgo->alpha, true,
|
|
cyl->get_cap1(), cyl->get_cap2(), nullptr, stick_round_nub);
|
|
} break;
|
|
case CGO_CUSTOM_CYLINDER_ALPHA: {
|
|
auto cyl = it.cast<cgo::draw::custom_cylinder_alpha>();
|
|
ok &= CGOSimpleCylinder(cgo, *cyl, cyl->color1[3], cyl->color2[3], true,
|
|
cyl->get_cap1(), cyl->get_cap2(), nullptr, stick_round_nub);
|
|
} break;
|
|
case CGO_SPHERE:
|
|
ok &= CGOSimpleSphere(cgo, pc, *(pc + 3), sphere_quality);
|
|
break;
|
|
case CGO_ELLIPSOID:
|
|
ok &= CGOSimpleEllipsoid(cgo, pc, *(pc + 3), pc + 4, pc + 7, pc + 10);
|
|
break;
|
|
case CGO_QUADRIC:
|
|
ok &= CGOSimpleQuadric(cgo, pc, *(pc + 3), pc + 4);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"CGOSimplifyNoCompress-Error: CGO_DRAW_BUFFERS_INDEXED "
|
|
"encountered\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"CGOSimplifyNoCompress-Error: CGO_DRAW_BUFFERS_NOT_INDEXED "
|
|
"encountered\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_SPHERE_BUFFERS:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"CGOSimplifyNoCompress-Error: CGO_DRAW_SPHERE_BUFFERS "
|
|
"encountered\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_CYLINDER_BUFFERS:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"CGOSimplifyNoCompress-Error: CGO_DRAW_CYLINDER_BUFFERS "
|
|
"encountered\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_LABELS:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"CGOSimplifyNoCompress-Error: CGO_DRAW_LABELS encountered\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_TEXTURES:
|
|
PRINTFB(I->G, FB_CGO, FB_Errors)
|
|
"CGOSimplifyNoCompress-Error: CGO_DRAW_TEXTURES encountered\n" ENDFB(
|
|
I->G);
|
|
break;
|
|
case CGO_BEGIN:
|
|
cgo->has_begin_end = true;
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
if (ok) {
|
|
ok &= CGOStop(cgo);
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
CGO* CGOOptimizeTextures(const CGO* I, int est)
|
|
{
|
|
CGO* cgo = nullptr;
|
|
int ok = true;
|
|
auto num_total_textures = CGOCountNumberOfOperationsOfType(I, CGO_DRAW_TEXTURE);
|
|
// printf("CGOOptimizeTextures: num_total_textures=%d\n",
|
|
// num_total_textures);
|
|
if (num_total_textures) {
|
|
int place3 = 0, place2 = 0;
|
|
std::vector<float> worldPos(num_total_textures * 18);
|
|
std::vector<float> screenValues(num_total_textures * 18);
|
|
std::vector<float> textExtents(num_total_textures * 12);
|
|
std::vector<float> pickColorValsVec(num_total_textures * 12); /* pick index and bond */
|
|
auto* pickColorVals = pickColorValsVec.data();
|
|
|
|
cgo = CGONewSized(I->G, 0);
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeTextures() CGO_DRAW_BUFFERS_INDEXED or "
|
|
"CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_TEXTURE: {
|
|
float screenMin[3], screenMax[3], textExtent[4];
|
|
copy3f(pc, &worldPos[place3]);
|
|
copy3f(pc, &worldPos[place3 + 3]);
|
|
copy3f(pc, &worldPos[place3 + 6]);
|
|
copy3f(pc, &worldPos[place3 + 9]);
|
|
copy3f(pc, &worldPos[place3 + 12]);
|
|
copy3f(pc, &worldPos[place3 + 15]);
|
|
copy3f(pc + 3, screenMin);
|
|
copy3f(pc + 6, screenMax);
|
|
copy4f(pc + 9, textExtent);
|
|
copy3f(screenMin, &screenValues[place3]);
|
|
copy3f(screenMin, &screenValues[place3 + 3]);
|
|
copy3f(screenMin, &screenValues[place3 + 6]);
|
|
copy3f(screenMin, &screenValues[place3 + 9]);
|
|
copy3f(screenMin, &screenValues[place3 + 12]);
|
|
copy3f(screenMax, &screenValues[place3 + 15]);
|
|
screenValues[place3 + 4] = screenMax[1];
|
|
screenValues[place3 + 6] = screenMax[0];
|
|
screenValues[place3 + 10] = screenMax[1];
|
|
screenValues[place3 + 12] = screenMax[0];
|
|
screenValues[place3 + 17] = screenMin[2];
|
|
place3 += 18;
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[0];
|
|
textExtents[place2++] = textExtent[1];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[0];
|
|
textExtents[place2++] = textExtent[3];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[2];
|
|
textExtents[place2++] = textExtent[1];
|
|
CGO_put_int(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_uint(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[0];
|
|
textExtents[place2++] = textExtent[3];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[2];
|
|
textExtents[place2++] = textExtent[1];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[2];
|
|
textExtents[place2++] = textExtent[3];
|
|
} break;
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
if (ok) {
|
|
auto& vbo = CreateVertexBuffer(I->G, VertexBufferLayout::Sequential);
|
|
BufferDataDesc dataDesc{{
|
|
BufferDesc("attr_worldpos", VertexFormat::Float3,
|
|
sizeof(float) * num_total_textures * 18, worldPos.data()),
|
|
BufferDesc("attr_screenoffset", VertexFormat::Float3,
|
|
sizeof(float) * num_total_textures * 18, screenValues.data()),
|
|
BufferDesc("attr_texcoords", VertexFormat::Float3,
|
|
sizeof(float) * num_total_textures * 18, textExtents.data())}};
|
|
SetVertexBufferData(I->G, vbo, std::move(dataDesc));
|
|
auto vboid = vbo.get_hash_id();
|
|
|
|
if (ok) {
|
|
float* pickArray =
|
|
cgo->add<cgo::draw::textures>(num_total_textures, vboid);
|
|
CHECKOK(ok, pickArray);
|
|
if (!pickArray)
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
if (ok)
|
|
memcpy(pickArray + num_total_textures * 6, pickColorVals,
|
|
num_total_textures * 12 * sizeof(float));
|
|
if (ok)
|
|
ok &= CGOStop(cgo);
|
|
} else {
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
}
|
|
}
|
|
return cgo;
|
|
}
|
|
|
|
CGO* CGOConvertToLabelShader(const CGO* I, CGO* addTo)
|
|
{
|
|
/* Lines that pass in two vertices per line */
|
|
PyMOLGlobals* G = I->G;
|
|
|
|
AttribDataOp world_pos_op = {{CGO_DRAW_LABEL, 1, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::label, world_pos), 0}};
|
|
AttribDataOp screen_offset_op = {{CGO_DRAW_LABEL, 2, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::label, screen_world_offset), 0}};
|
|
AttribDataOp screen_min_op = {{CGO_DRAW_LABEL, 3, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::label, screen_min), 0}};
|
|
AttribDataOp screen_max_op = {{CGO_DRAW_LABEL, 4, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::label, screen_max), 0}};
|
|
AttribDataOp text_extent_op = {{CGO_DRAW_LABEL, 5, FLOAT2_TO_FLOAT2,
|
|
offsetof(cgo::draw::label, text_extent), 0}};
|
|
AttribDataOp relative_mode_op = {{CGO_DRAW_LABEL, 6, FLOAT_TO_FLOAT,
|
|
offsetof(cgo::draw::label, relative_mode), 0}};
|
|
AttribDataOp target_pos_op = {{CGO_DRAW_LABEL, 7, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::label, target_pos), 6}};
|
|
|
|
AttribDataDesc attrDesc = {
|
|
{"attr_worldpos", VertexFormat::Float3, world_pos_op},
|
|
{"attr_targetpos", VertexFormat::Float3, target_pos_op},
|
|
{"attr_screenoffset", VertexFormat::Float3, screen_min_op},
|
|
{"attr_texcoords", VertexFormat::Float2, text_extent_op},
|
|
{"attr_screenworldoffset", VertexFormat::Float3, screen_offset_op},
|
|
{"attr_relative_mode", VertexFormat::Float, relative_mode_op}};
|
|
|
|
auto ComputeScreenValues = [](void* varData, const float* pc,
|
|
void* screenData, int idx) {
|
|
auto sp = reinterpret_cast<const cgo::draw::label*>(pc);
|
|
const auto& smin = sp->screen_min;
|
|
const auto& smax = sp->screen_max;
|
|
float* v = reinterpret_cast<float*>(varData);
|
|
switch (idx) {
|
|
case 0:
|
|
v[0] = smin[0];
|
|
v[1] = smin[1];
|
|
v[2] = smin[2];
|
|
break;
|
|
case 1:
|
|
v[0] = smin[0];
|
|
v[1] = smax[1];
|
|
v[2] = smin[2];
|
|
break;
|
|
case 2:
|
|
v[0] = smax[0];
|
|
v[1] = smin[1];
|
|
v[2] = smin[2];
|
|
break;
|
|
case 3:
|
|
v[0] = smin[0];
|
|
v[1] = smax[1];
|
|
v[2] = smin[2];
|
|
break;
|
|
case 4:
|
|
v[0] = smax[0];
|
|
v[1] = smin[1];
|
|
v[2] = smin[2];
|
|
break;
|
|
case 5:
|
|
v[0] = smax[0];
|
|
v[1] = smax[1];
|
|
v[2] = smin[2];
|
|
break;
|
|
};
|
|
};
|
|
|
|
auto ComputeTexCoords = [](void* varData, const float* pc, void* discard,
|
|
int idx) {
|
|
auto sp = reinterpret_cast<const cgo::draw::label*>(pc);
|
|
float* v = reinterpret_cast<float*>(varData);
|
|
const auto& te = sp->text_extent;
|
|
static struct {
|
|
int x, y;
|
|
} const idxs[6] = {{0, 1}, {0, 3}, {2, 1}, {0, 3}, {2, 1}, {2, 3}};
|
|
v[0] = te[idxs[idx].x];
|
|
v[1] = te[idxs[idx].y];
|
|
};
|
|
|
|
attrDesc[1].attrOps[0].funcDataConversions.push_back(
|
|
{ComputeScreenValues, nullptr, "attr_screenoffset"});
|
|
attrDesc[1].attrOps[0].funcDataConversions.push_back(
|
|
{ComputeTexCoords, nullptr, "attr_texcoords"});
|
|
|
|
uchar pickdata[4] = {0, 0, 0, 0};
|
|
addTo->add<cgo::draw::vertex_attribute_4ub>(
|
|
G->ShaderMgr->GetAttributeUID("attr_pickcolor"), pickdata);
|
|
|
|
AttribDataOp pickOp = {{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0}};
|
|
AttribDataDesc pickDesc = {
|
|
{"attr_pickcolor", VertexFormat::UByte4Norm, pickOp}};
|
|
return CGOConvertToShader(
|
|
I, attrDesc, pickDesc, GL_TRIANGLES, VertexBufferLayout::Interleaved, true);
|
|
}
|
|
|
|
CGO* CGOOptimizeLabels(const CGO* I, int est, bool addshaders)
|
|
{
|
|
CGO* cgo = nullptr;
|
|
int ok = true;
|
|
auto num_total_labels = CGOCountNumberOfOperationsOfType(I, CGO_DRAW_LABEL);
|
|
if (num_total_labels) {
|
|
int place3 = 0, place2 = 0, place = 0;
|
|
std::vector<float> worldPos(num_total_labels * 6 * 17);
|
|
auto* screenValues = worldPos.data() + (num_total_labels * 18);
|
|
auto* targetPos = screenValues + (num_total_labels * 18);
|
|
auto* screenWorldValues = targetPos + (num_total_labels * 18);
|
|
auto* textExtents = screenWorldValues + (num_total_labels * 18);
|
|
auto* pickColorVals =
|
|
textExtents + (num_total_labels * 12); /* pick index and bond */
|
|
auto relativeMode = (float*) (pickColorVals + (num_total_labels * 12));
|
|
cgo = CGONewSized(I->G, 0);
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeLabels() CGO_DRAW_BUFFERS_INDEXED or "
|
|
"CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_LABEL: {
|
|
float screenWorldOffset[3], screenMin[3], screenMax[3], textExtent[4];
|
|
copy3f(pc, &worldPos[place3]);
|
|
copy3f(pc, &worldPos[place3 + 3]);
|
|
copy3f(pc, &worldPos[place3 + 6]);
|
|
copy3f(pc, &worldPos[place3 + 9]);
|
|
copy3f(pc, &worldPos[place3 + 12]);
|
|
copy3f(pc, &worldPos[place3 + 15]);
|
|
copy3f(pc + 3, screenWorldOffset);
|
|
copy3f(pc + 6, screenMin);
|
|
copy3f(pc + 9, screenMax);
|
|
copy4f(pc + 12, textExtent);
|
|
copy3f(screenWorldOffset, &screenWorldValues[place3]);
|
|
copy3f(&screenWorldValues[place3], &screenWorldValues[place3 + 3]);
|
|
copy3f(&screenWorldValues[place3], &screenWorldValues[place3 + 6]);
|
|
copy3f(&screenWorldValues[place3], &screenWorldValues[place3 + 9]);
|
|
copy3f(&screenWorldValues[place3], &screenWorldValues[place3 + 12]);
|
|
copy3f(&screenWorldValues[place3], &screenWorldValues[place3 + 15]);
|
|
copy3f(screenMin, &screenValues[place3]);
|
|
copy3f(screenMin, &screenValues[place3 + 3]);
|
|
copy3f(screenMin, &screenValues[place3 + 6]);
|
|
copy3f(screenMin, &screenValues[place3 + 9]);
|
|
copy3f(screenMin, &screenValues[place3 + 12]);
|
|
copy3f(screenMax, &screenValues[place3 + 15]);
|
|
screenValues[place3 + 4] = screenMax[1];
|
|
screenValues[place3 + 6] = screenMax[0];
|
|
screenValues[place3 + 10] = screenMax[1];
|
|
screenValues[place3 + 12] = screenMax[0];
|
|
screenValues[place3 + 17] = screenMin[2];
|
|
copy3f(pc + 17, &targetPos[place3]);
|
|
copy3f(pc + 17, &targetPos[place3 + 3]);
|
|
copy3f(pc + 17, &targetPos[place3 + 6]);
|
|
copy3f(pc + 17, &targetPos[place3 + 9]);
|
|
copy3f(pc + 17, &targetPos[place3 + 12]);
|
|
copy3f(pc + 17, &targetPos[place3 + 15]);
|
|
place3 += 18;
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[0];
|
|
textExtents[place2++] = textExtent[1];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[0];
|
|
textExtents[place2++] = textExtent[3];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[2];
|
|
textExtents[place2++] = textExtent[1];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[0];
|
|
textExtents[place2++] = textExtent[3];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[2];
|
|
textExtents[place2++] = textExtent[1];
|
|
CGO_put_uint(pickColorVals + place2, cgo->current_pick_color_index);
|
|
CGO_put_int(pickColorVals + place2 + 1, cgo->current_pick_color_bond);
|
|
textExtents[place2++] = textExtent[2];
|
|
textExtents[place2++] = textExtent[3];
|
|
{
|
|
uchar rM = (uchar) * (pc + 16);
|
|
relativeMode[place++] = rM;
|
|
relativeMode[place++] = rM;
|
|
relativeMode[place++] = rM;
|
|
relativeMode[place++] = rM;
|
|
relativeMode[place++] = rM;
|
|
relativeMode[place++] = rM;
|
|
}
|
|
} break;
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
if (ok) {
|
|
// Static Vertex Data
|
|
auto& vbo = CreateVertexBuffer(I->G, VertexBufferLayout::Sequential);
|
|
BufferDataDesc dataDesc{{
|
|
BufferDesc("attr_worldpos", VertexFormat::Float3,
|
|
sizeof(float) * num_total_labels * 18, worldPos.data()),
|
|
BufferDesc("attr_targetpos", VertexFormat::Float3,
|
|
sizeof(float) * num_total_labels * 18, targetPos),
|
|
BufferDesc("attr_screenoffset", VertexFormat::Float3,
|
|
sizeof(float) * num_total_labels * 18, screenValues),
|
|
BufferDesc("attr_texcoords", VertexFormat::Float2,
|
|
sizeof(float) * num_total_labels * 12, textExtents),
|
|
BufferDesc("attr_screenworldoffset", VertexFormat::Float3,
|
|
sizeof(float) * num_total_labels * 18, screenWorldValues),
|
|
BufferDesc("attr_relative_mode", VertexFormat::Float,
|
|
sizeof(float) * num_total_labels * 6, relativeMode)}};
|
|
SetVertexBufferData(I->G, vbo, std::move(dataDesc));
|
|
auto vboid = vbo.get_hash_id();
|
|
|
|
auto& pickvbo = CreateVertexBuffer(
|
|
I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu);
|
|
BufferDataDesc pickDataDesc{{
|
|
BufferDesc("attr_pickcolor", VertexFormat::UByteNorm, 0),
|
|
BufferDesc("attr_pickcolor", VertexFormat::UByteNorm,
|
|
sizeof(float) * num_total_labels * 6)}};
|
|
std::size_t bufferSize = sizeof(float) * num_total_labels * 12;
|
|
SetVertexBufferDataSized(
|
|
I->G, pickvbo, std::move(pickDataDesc), bufferSize);
|
|
auto pickvboid = pickvbo.get_hash_id();
|
|
|
|
auto freebuffers = [vboid, pickvboid, I]() {
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
I->G->ShaderMgr->freeGPUBuffer(pickvboid);
|
|
};
|
|
|
|
if (ok) {
|
|
float* pickArray = nullptr;
|
|
if (addshaders) {
|
|
CGOEnable(cgo, GL_LABEL_SHADER);
|
|
}
|
|
pickArray =
|
|
cgo->add<cgo::draw::labels>(num_total_labels, vboid, pickvboid);
|
|
if (addshaders) {
|
|
CGODisable(cgo, GL_LABEL_SHADER);
|
|
}
|
|
CHECKOK(ok, pickArray);
|
|
if (!pickArray) {
|
|
freebuffers();
|
|
}
|
|
if (ok)
|
|
memcpy(pickArray + num_total_labels * 6, pickColorVals,
|
|
num_total_labels * 12 * sizeof(float));
|
|
if (ok)
|
|
ok &= CGOStop(cgo);
|
|
} else {
|
|
freebuffers();
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
}
|
|
}
|
|
return cgo;
|
|
}
|
|
|
|
CGO* CGOOptimizeConnectors(const CGO* I, int est)
|
|
{
|
|
CGO* cgo = nullptr;
|
|
int num_total_connectors;
|
|
int ok = true;
|
|
int use_geometry_shaders =
|
|
SettingGetGlobal_b(I->G, cSetting_use_geometry_shaders);
|
|
int factor = (use_geometry_shaders ? 1 : 4);
|
|
num_total_connectors =
|
|
CGOCountNumberOfOperationsOfType(I, CGO_DRAW_CONNECTOR);
|
|
|
|
if (num_total_connectors) {
|
|
uchar* isCenterPt = nullptr;
|
|
int place3 = 0, place2 = 0, place = 0;
|
|
std::vector<float> targetPt3d(num_total_connectors * 20 *
|
|
factor, 0); /* too much, relativeMode only needs 1
|
|
byte per vertex, instead of 1 float */
|
|
auto* labelCenterPt3d = targetPt3d.data() + (num_total_connectors * 3 * factor);
|
|
auto* indentFactor = labelCenterPt3d + (num_total_connectors * 3 * factor);
|
|
auto* screenWorldOffset = indentFactor + (num_total_connectors * 2 * factor);
|
|
auto* connectorColor = screenWorldOffset + (num_total_connectors * 3 * factor);
|
|
auto* textSize = connectorColor + (num_total_connectors * factor);
|
|
auto* relativeMode = (uchar*) (textSize + (num_total_connectors * 2 * factor));
|
|
auto* drawBkgrd = (uchar*) (relativeMode + (num_total_connectors * factor));
|
|
auto* bkgrdColor = (float*) (drawBkgrd + (num_total_connectors * factor));
|
|
auto* relExtLength = (float*) (bkgrdColor + (num_total_connectors * factor));
|
|
auto* connectorWidth = (float*) (relExtLength + (num_total_connectors * factor));
|
|
if (!use_geometry_shaders)
|
|
isCenterPt = (uchar*) (connectorWidth + (num_total_connectors * factor));
|
|
else
|
|
isCenterPt = nullptr;
|
|
cgo = CGONewSized(I->G, 0);
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeConnectors() CGO_DRAW_BUFFERS_INDEXED or "
|
|
"CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_CONNECTOR: {
|
|
uchar* uc;
|
|
int f;
|
|
if (!use_geometry_shaders) {
|
|
isCenterPt[place] = 0;
|
|
isCenterPt[place + 1] = 2;
|
|
isCenterPt[place + 2] = 2;
|
|
isCenterPt[place + 3] = 1;
|
|
}
|
|
copy3f(pc, &targetPt3d[place3]);
|
|
copy3f(pc + 3, &labelCenterPt3d[place3]);
|
|
copy2f(pc + 6, &indentFactor[place2]);
|
|
copy3f(pc + 9, &screenWorldOffset[place3]);
|
|
copy2f(pc + 12, &textSize[place2]);
|
|
relativeMode[place] = (uchar) (int) pc[17];
|
|
drawBkgrd[place] = (uchar) (int) pc[18];
|
|
uc = (uchar*) &bkgrdColor[place];
|
|
uc[0] = CLIP_COLOR_VALUE(*(pc + 19));
|
|
uc[1] = CLIP_COLOR_VALUE(*(pc + 20));
|
|
uc[2] = CLIP_COLOR_VALUE(*(pc + 21));
|
|
uc[3] = CLIP_COLOR_VALUE(*(pc + 22));
|
|
uc = (uchar*) &connectorColor[place];
|
|
uc[0] = CLIP_COLOR_VALUE(*(pc + 14));
|
|
uc[1] = CLIP_COLOR_VALUE(*(pc + 15));
|
|
uc[2] = CLIP_COLOR_VALUE(*(pc + 16));
|
|
uc[3] = 255;
|
|
relExtLength[place] = *(pc + 8);
|
|
connectorWidth[place] = *(pc + 23);
|
|
place3 += 3;
|
|
place2 += 2;
|
|
place += 1;
|
|
for (f = 1; f < factor; f++) {
|
|
copy3f(pc, &targetPt3d[place3]);
|
|
copy3f(pc + 3, &labelCenterPt3d[place3]);
|
|
copy2f(pc + 6, &indentFactor[place2]);
|
|
copy3f(pc + 9, &screenWorldOffset[place3]);
|
|
copy2f(pc + 12, &textSize[place2]);
|
|
relativeMode[place] = (uchar) (int) pc[17];
|
|
drawBkgrd[place] = (uchar) (int) pc[18];
|
|
relExtLength[place] = *(pc + 8);
|
|
connectorWidth[place] = *(pc + 23);
|
|
uc = (uchar*) &bkgrdColor[place];
|
|
uc[0] = uc[-4];
|
|
uc[1] = uc[-3];
|
|
uc[2] = uc[-2];
|
|
uc[3] = uc[-1];
|
|
uc = (uchar*) &connectorColor[place];
|
|
uc[0] = uc[-4];
|
|
uc[1] = uc[-3];
|
|
uc[2] = uc[-2];
|
|
uc[3] = uc[-1];
|
|
place3 += 3;
|
|
place2 += 2;
|
|
place += 1;
|
|
}
|
|
} break;
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
if (ok) {
|
|
const size_t quant = factor * num_total_connectors;
|
|
auto& vbo = CreateVertexBuffer(I->G);
|
|
BufferDataDesc bufferDataDesc{{//
|
|
BufferDesc("a_target_pt3d", VertexFormat::Float3,
|
|
sizeof(float) * 3 * quant, targetPt3d.data()),
|
|
BufferDesc("a_center_pt3d", VertexFormat::Float3,
|
|
sizeof(float) * 3 * quant, labelCenterPt3d),
|
|
BufferDesc("a_indentFactor", VertexFormat::Float2,
|
|
sizeof(float) * 2 * quant, indentFactor),
|
|
BufferDesc("a_screenWorldOffset", VertexFormat::Float3,
|
|
sizeof(float) * 3 * quant, screenWorldOffset),
|
|
BufferDesc("a_textSize", VertexFormat::Float2,
|
|
sizeof(float) * 2 * quant, textSize),
|
|
BufferDesc("a_Color", VertexFormat::UByte4Norm, sizeof(float) * quant,
|
|
connectorColor),
|
|
BufferDesc("a_relative_mode", VertexFormat::UByte,
|
|
sizeof(std::uint8_t) * quant, relativeMode),
|
|
BufferDesc("a_draw_flags", VertexFormat::UByte,
|
|
sizeof(std::uint8_t) * quant, drawBkgrd),
|
|
BufferDesc("a_bkgrd_color", VertexFormat::UByte4Norm,
|
|
sizeof(float) * quant, bkgrdColor),
|
|
BufferDesc("a_rel_ext_length", VertexFormat::Float,
|
|
sizeof(float) * quant, relExtLength),
|
|
BufferDesc("a_con_width", VertexFormat::Float, sizeof(float) * quant,
|
|
connectorWidth),
|
|
BufferDesc("a_isCenterPt", VertexFormat::UByte,
|
|
sizeof(std::uint8_t) * quant, isCenterPt)}};
|
|
SetVertexBufferData(I->G, vbo, std::move(bufferDataDesc));
|
|
auto vboid = vbo.get_hash_id();
|
|
if (ok) {
|
|
cgo->add<cgo::draw::connectors>(num_total_connectors, vboid);
|
|
if (ok)
|
|
ok &= CGOStop(cgo);
|
|
}
|
|
if (!ok) {
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
CGOFree(cgo);
|
|
}
|
|
}
|
|
}
|
|
CheckGLErrorOK(I->G, "ERROR: CGOOptimizeConnectors() end returns err=%d\n");
|
|
return cgo;
|
|
}
|
|
|
|
CGO* CGOExpandDrawTextures(const CGO* I, int est)
|
|
{
|
|
CGO* cgo = CGONew(I->G);
|
|
int ok = true;
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
"WARNING: CGOOptimizeTextures() CGO_DRAW_BUFFERS_INDEXED or "
|
|
"CGO_DRAW_BUFFERS_INDEXED encountered op=%d\n",
|
|
op ENDFB(I->G);
|
|
break;
|
|
case CGO_DRAW_TEXTURE: {
|
|
float screenMin[3], screenMax[3], textExtent[4];
|
|
float alpha = cgo->alpha;
|
|
CGOAlpha(cgo, 0.f);
|
|
CGOColor(cgo, 0.f, 0.f, 0.f);
|
|
copy3f(pc + 3, screenMin);
|
|
copy3f(pc + 6, screenMax);
|
|
copy4f(pc + 9, textExtent);
|
|
CGOBegin(cgo, GL_TRIANGLES);
|
|
CGOTexCoord2f(cgo, textExtent[0], textExtent[1]);
|
|
CGOVertexv(cgo, screenMin);
|
|
CGOTexCoord2f(cgo, textExtent[0], textExtent[3]);
|
|
CGOVertex(cgo, screenMin[0], screenMax[1], screenMin[2]);
|
|
CGOTexCoord2f(cgo, textExtent[2], textExtent[1]);
|
|
CGOVertex(cgo, screenMax[0], screenMin[1], screenMin[2]);
|
|
CGOTexCoord2f(cgo, textExtent[0], textExtent[3]);
|
|
CGOVertex(cgo, screenMin[0], screenMax[1], screenMin[2]);
|
|
CGOTexCoord2f(cgo, textExtent[2], textExtent[1]);
|
|
CGOVertex(cgo, screenMax[0], screenMin[1], screenMin[2]);
|
|
CGOTexCoord2f(cgo, textExtent[2], textExtent[3]);
|
|
CGOVertex(cgo, screenMax[0], screenMax[1], screenMin[2]);
|
|
CGOEnd(cgo);
|
|
CGOAlpha(cgo, alpha);
|
|
} break;
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
CGOStop(cgo);
|
|
return cgo;
|
|
}
|
|
|
|
/* ======== Raytrace Renderer ======== */
|
|
|
|
int CGOGetExtent(const CGO* I, float* mn, float* mx)
|
|
{
|
|
int result = false;
|
|
|
|
#define check_extent(v, r) \
|
|
{ \
|
|
if (!result) { \
|
|
mn[0] = ((*(v)) - r); \
|
|
mx[0] = ((*(v)) + r); \
|
|
mn[1] = ((*(v + 1)) - r); \
|
|
mx[1] = ((*(v + 1)) + r); \
|
|
mn[2] = ((*(v + 2)) - r); \
|
|
mx[2] = ((*(v + 2)) + r); \
|
|
result = true; \
|
|
} else { \
|
|
if (mn[0] > ((*(v)) - r)) \
|
|
mn[0] = ((*(v)) - r); \
|
|
if (mx[0] < ((*(v)) + r)) \
|
|
mx[0] = ((*(v)) + r); \
|
|
if (mn[1] > ((*((v) + 1)) - r)) \
|
|
mn[1] = ((*((v) + 1)) - r); \
|
|
if (mx[1] < ((*((v) + 1)) + r)) \
|
|
mx[1] = ((*((v) + 1)) + r); \
|
|
if (mn[2] > ((*((v) + 2)) - r)) \
|
|
mn[2] = ((*((v) + 2)) - r); \
|
|
if (mx[2] < ((*((v) + 2)) + r)) \
|
|
mx[2] = ((*((v) + 2)) + r); \
|
|
} \
|
|
}
|
|
|
|
#define check_extent4(v, r) \
|
|
{ \
|
|
if (!result) { \
|
|
mn[0] = ((*(v)) - r); \
|
|
mx[0] = ((*(v)) + r); \
|
|
mn[1] = ((*(v + 1)) - r); \
|
|
mx[1] = ((*(v + 1)) + r); \
|
|
mn[2] = ((*(v + 2)) - r); \
|
|
mx[2] = ((*(v + 2)) + r); \
|
|
mn[3] = ((*(v + 3)) - r); \
|
|
mx[3] = ((*(v + 3)) + r); \
|
|
result = true; \
|
|
} else { \
|
|
if (mn[0] > ((*(v)) - r)) \
|
|
mn[0] = ((*(v)) - r); \
|
|
if (mx[0] < ((*(v)) + r)) \
|
|
mx[0] = ((*(v)) + r); \
|
|
if (mn[1] > ((*((v) + 1)) - r)) \
|
|
mn[1] = ((*((v) + 1)) - r); \
|
|
if (mx[1] < ((*((v) + 1)) + r)) \
|
|
mx[1] = ((*((v) + 1)) + r); \
|
|
if (mn[2] > ((*((v) + 2)) - r)) \
|
|
mn[2] = ((*((v) + 2)) - r); \
|
|
if (mx[2] < ((*((v) + 2)) + r)) \
|
|
mx[2] = ((*((v) + 2)) + r); \
|
|
if (mn[3] > ((*((v) + 3)) - r)) \
|
|
mn[3] = ((*((v) + 3)) - r); \
|
|
if (mx[3] < ((*((v) + 3)) + r)) \
|
|
mx[3] = ((*((v) + 3)) + r); \
|
|
} \
|
|
}
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto pc = it.data();
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_VERTEX:
|
|
check_extent(pc, 0);
|
|
break;
|
|
case CGO_SPHERE:
|
|
case CGO_ELLIPSOID:
|
|
check_extent(pc, *(pc + 3));
|
|
break;
|
|
case CGO_CYLINDER:
|
|
case CGO_CONE:
|
|
case CGO_SAUSAGE:
|
|
case CGO_CUSTOM_CYLINDER:
|
|
case CGO_CUSTOM_CYLINDER_ALPHA:
|
|
check_extent(pc, *(pc + 6));
|
|
check_extent(pc + 3, *(pc + 6));
|
|
break;
|
|
case CGO_TRIANGLE:
|
|
check_extent(pc, 0);
|
|
check_extent(pc + 3, 0);
|
|
check_extent(pc + 6, 0);
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
const cgo::draw::arrays* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
const float* pct = sp->floatdata;
|
|
int pl;
|
|
|
|
if (sp->arraybits & CGO_VERTEX_ARRAY) {
|
|
for (pl = 0; pl < sp->nverts; pl++) {
|
|
check_extent(pct, 0);
|
|
pct += VERTEX_POS_SIZE;
|
|
}
|
|
}
|
|
} break;
|
|
case CGO_BOUNDING_BOX: {
|
|
if (!result) {
|
|
mn[0] = (*pc);
|
|
mn[1] = *(pc + 1);
|
|
mn[2] = *(pc + 2);
|
|
mx[0] = *(pc + 3);
|
|
mx[1] = *(pc + 4);
|
|
mx[2] = *(pc + 5);
|
|
result = true;
|
|
} else {
|
|
if (mn[0] > *pc)
|
|
mn[0] = (*pc);
|
|
if (mn[1] > *(pc + 1))
|
|
mn[1] = *(pc + 1);
|
|
if (mn[2] > *(pc + 2))
|
|
mn[2] = *(pc + 2);
|
|
if (mx[0] < *(pc + 3))
|
|
mx[0] = *(pc + 3);
|
|
if (mx[1] < *(pc + 4))
|
|
mx[1] = *(pc + 4);
|
|
if (mx[2] < *(pc + 5))
|
|
mx[2] = *(pc + 5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
int CGOHasNormals(const CGO* I)
|
|
{
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
switch (it.op_code()) {
|
|
case CGO_NORMAL:
|
|
case CGO_SPHERE:
|
|
case CGO_ELLIPSOID:
|
|
case CGO_CYLINDER:
|
|
case CGO_CONE:
|
|
case CGO_SAUSAGE:
|
|
case CGO_CUSTOM_CYLINDER:
|
|
case CGO_CUSTOM_CYLINDER_ALPHA:
|
|
return true;
|
|
case CGO_DRAW_ARRAYS:
|
|
if (it.cast<cgo::draw::arrays>()->arraybits & CGO_NORMAL_ARRAY) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int CGOQuadricToEllipsoid(const float* v, float r, const float* q,
|
|
float* r_el, float* n0, float* n1, float* n2)
|
|
{
|
|
int ok = false;
|
|
double inp_matrix[16];
|
|
double e_val[4];
|
|
double e_vec[16];
|
|
double inverse[16];
|
|
|
|
inp_matrix[0] = q[0];
|
|
inp_matrix[1] = q[3];
|
|
inp_matrix[2] = q[5];
|
|
inp_matrix[3] = q[6];
|
|
inp_matrix[4] = q[3];
|
|
inp_matrix[5] = q[1];
|
|
inp_matrix[6] = q[4];
|
|
inp_matrix[7] = q[7];
|
|
inp_matrix[8] = q[5];
|
|
inp_matrix[9] = q[4];
|
|
inp_matrix[10] = q[2];
|
|
inp_matrix[11] = q[8];
|
|
inp_matrix[12] = q[6];
|
|
inp_matrix[13] = q[7];
|
|
inp_matrix[14] = q[8];
|
|
inp_matrix[15] = q[9];
|
|
|
|
if (xx_matrix_invert(inverse, inp_matrix, 4)) {
|
|
|
|
/* inverse now contains Uij coefficients */
|
|
float pradius = sqrt1f(-1 / inverse[15]);
|
|
int n_rot;
|
|
|
|
if (xx_matrix_jacobi_solve(e_vec, e_val, &n_rot, inverse, 4)) {
|
|
float mag[3];
|
|
float scale[3];
|
|
float mx;
|
|
n0[0] = e_vec[0];
|
|
n0[1] = e_vec[4];
|
|
n0[2] = e_vec[8];
|
|
n1[0] = e_vec[1];
|
|
n1[1] = e_vec[5];
|
|
n1[2] = e_vec[9];
|
|
n2[0] = e_vec[2];
|
|
n2[1] = e_vec[6];
|
|
n2[2] = e_vec[10];
|
|
|
|
normalize3f(n0);
|
|
normalize3f(n1);
|
|
normalize3f(n2);
|
|
mag[0] = sqrt1f(e_val[0]);
|
|
mag[1] = sqrt1f(e_val[1]);
|
|
mag[2] = sqrt1f(e_val[2]);
|
|
|
|
mx = mag[0];
|
|
if (mx < mag[1])
|
|
mx = mag[1];
|
|
if (mx < mag[2])
|
|
mx = mag[2];
|
|
|
|
scale[0] = mag[0] / mx;
|
|
scale[1] = mag[1] / mx;
|
|
scale[2] = mag[2] / mx;
|
|
|
|
scale3f(n0, scale[0], n0);
|
|
scale3f(n1, scale[1], n1);
|
|
scale3f(n2, scale[2], n2);
|
|
|
|
*r_el = mx * pradius;
|
|
ok = true;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static int CGORenderQuadricRay(CRay* ray, float* v, float r, float* q)
|
|
{
|
|
float r_el, n0[3], n1[3], n2[3];
|
|
int ok = true;
|
|
if (CGOQuadricToEllipsoid(v, r, q, &r_el, n0, n1, n2))
|
|
ok &= ray->ellipsoid3fv(v, r_el, n0, n1, n2);
|
|
return ok;
|
|
}
|
|
|
|
/* ======== Raytrace Renderer ======== */
|
|
|
|
int CGORenderRay(CGO* I, CRay* ray, RenderInfo* info, const float* color,
|
|
ObjectGadgetRamp* ramp, CSetting* set1, CSetting* set2)
|
|
{
|
|
#ifdef _PYMOL_NO_RAY
|
|
return 0;
|
|
#else
|
|
int vc = 0;
|
|
float linewidth = 1.0F;
|
|
float widthscale = 0.15F;
|
|
float lineradius, dotradius, dotwidth;
|
|
float white[] = {1.0, 1.0, 1.0};
|
|
float zee[] = {0.0, 0.0, 1.0};
|
|
int ok = true;
|
|
const float *n0 = nullptr, *n1 = nullptr, *n2 = nullptr, *v0 = nullptr,
|
|
*v1 = nullptr, *v2 = nullptr, *c0 = nullptr, *c1 = nullptr,
|
|
*c2 = nullptr;
|
|
float rampc0[3], rampc1[3], rampc2[3];
|
|
int mode = -1;
|
|
/* workaround; multi-state ray-trace bug */
|
|
if (!I) {
|
|
assert("TODO investigate" && false);
|
|
return 0; /* not sure if it should return 0 or 1, 0 - fails, but is it a
|
|
memory issue? might not be since the arg is nullptr */
|
|
}
|
|
|
|
I->G->CGORenderer->alpha =
|
|
1.0F - SettingGet_f(I->G, set1, set2, cSetting_cgo_transparency);
|
|
|
|
widthscale = SettingGet_f(I->G, set1, set2, cSetting_cgo_ray_width_scale);
|
|
|
|
/* printf("debug %8.9f\n",SceneGetScreenVertexScale(I->G,zee)); */
|
|
linewidth = SettingGet_f(I->G, set1, set2, cSetting_cgo_line_width);
|
|
if (linewidth < 0.0F)
|
|
linewidth = 1.0F;
|
|
lineradius = SettingGet_f(I->G, set1, set2, cSetting_cgo_line_radius);
|
|
dotwidth = SettingGet_f(I->G, set1, set2, cSetting_cgo_dot_width);
|
|
dotradius = SettingGet_f(I->G, set1, set2, cSetting_cgo_dot_radius);
|
|
if (lineradius < 0.0F)
|
|
lineradius = linewidth * ray->PixelRadius / 2.0F;
|
|
if (dotradius < 0.0F)
|
|
dotradius = dotwidth * ray->PixelRadius / 2.0F;
|
|
if (widthscale < 0.0F)
|
|
widthscale = ray->PixelRadius / 2.0F;
|
|
if (color)
|
|
c0 = color;
|
|
else
|
|
c0 = white;
|
|
ray->transparentf(1.0F - I->G->CGORenderer->alpha);
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto pc = it.data();
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_BEGIN:
|
|
mode = CGO_get_int(pc);
|
|
vc = 0;
|
|
n0 = zee;
|
|
break;
|
|
case CGO_END:
|
|
switch (mode) {
|
|
case GL_LINE_LOOP:
|
|
if (vc > 1)
|
|
ok &= ray->sausage3fv(v0, v2, lineradius, c0, c2);
|
|
break;
|
|
}
|
|
mode = -1;
|
|
break;
|
|
case CGO_WIDTHSCALE:
|
|
widthscale = *pc;
|
|
lineradius = widthscale * linewidth;
|
|
dotradius = widthscale * dotwidth;
|
|
break;
|
|
case CGO_DOTWIDTH:
|
|
dotwidth = *pc;
|
|
dotradius = widthscale * dotwidth;
|
|
break;
|
|
case CGO_LINEWIDTH:
|
|
linewidth = *pc;
|
|
lineradius = widthscale * linewidth;
|
|
break;
|
|
case CGO_NORMAL:
|
|
n0 = pc;
|
|
break;
|
|
case CGO_SPECIAL_WITH_ARG: {
|
|
float argval = *(pc + 1);
|
|
switch (*(int*) pc) {
|
|
case LINEWIDTH_FOR_LINES:
|
|
linewidth = argval;
|
|
lineradius = widthscale * linewidth;
|
|
}
|
|
} break;
|
|
case CGO_SPECIAL: {
|
|
switch ((*(int*) pc)) {
|
|
case LINEWIDTH_DYNAMIC_WITH_SCALE_RIBBON: {
|
|
float radius = SettingGet_f(I->G, set1, set2, cSetting_ribbon_radius);
|
|
if (radius == 0.0F) {
|
|
float ribbon_width =
|
|
SettingGet_f(I->G, set1, set2, cSetting_ribbon_width);
|
|
float line_width = SceneGetDynamicLineWidth(info, ribbon_width);
|
|
SceneGetDynamicLineWidth(info, line_width);
|
|
radius = ray->PixelRadius * line_width / 2.0F;
|
|
}
|
|
lineradius = radius;
|
|
} break;
|
|
case LINEWIDTH_FOR_LINES: {
|
|
float radius = SettingGet_f(I->G, set1, set2, cSetting_line_radius);
|
|
if (radius <= 0.0F) {
|
|
float line_width =
|
|
SettingGet_f(I->G, set1, set2, cSetting_line_width);
|
|
line_width = SceneGetDynamicLineWidth(info, line_width);
|
|
radius = ray->PixelRadius * line_width / 2.0F;
|
|
}
|
|
lineradius = radius;
|
|
} break;
|
|
case CYLINDER_WIDTH_FOR_NONBONDED:
|
|
case LINEWIDTH_WITH_SCALE: {
|
|
float line_width = SettingGet_f(I->G, set1, set2, cSetting_line_width);
|
|
line_width = SceneGetDynamicLineWidth(info, line_width);
|
|
lineradius = widthscale * line_width / 2.f;
|
|
} break;
|
|
}
|
|
} break;
|
|
case CGO_COLOR:
|
|
c0 = pc;
|
|
ray->color3fv(c0);
|
|
break;
|
|
case CGO_ALPHA:
|
|
I->G->CGORenderer->alpha = *pc;
|
|
ray->transparentf(1.0F - *pc);
|
|
break;
|
|
case CGO_LINE: {
|
|
auto line = reinterpret_cast<cgo::draw::line*>(pc);
|
|
ok &= ray->sausage3fv(line->vertex1, line->vertex2, lineradius, c0, c0);
|
|
} break;
|
|
case CGO_SPLITLINE: {
|
|
auto splitline = reinterpret_cast<cgo::draw::splitline*>(pc);
|
|
float color2[] = {CONVERT_COLOR_VALUE(splitline->color2[0]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[1]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[2])};
|
|
if (splitline->flags & cgo::draw::splitline::interpolation) {
|
|
ok &= ray->sausage3fv(
|
|
splitline->vertex1, splitline->vertex2, lineradius, c0, color2);
|
|
} else {
|
|
float mid[3];
|
|
add3f(splitline->vertex1, splitline->vertex2, mid);
|
|
mult3f(mid, .5f, mid);
|
|
ok &= ray->customCylinder3fv(splitline->vertex1, mid, lineradius, c0,
|
|
c0, cCylCap::Round, cCylCap::None);
|
|
ok &= ray->customCylinder3fv(mid, splitline->vertex2, lineradius,
|
|
color2, color2, cCylCap::None, cCylCap::Round);
|
|
}
|
|
} break;
|
|
case CGO_VERTEX_CROSS: {
|
|
float pt1[3], pt2[3];
|
|
float nonbonded_size =
|
|
SettingGet_f(I->G, set1, set2, cSetting_nonbonded_size);
|
|
copy3f(pc, pt1);
|
|
copy3f(pc, pt2);
|
|
pt1[0] -= nonbonded_size;
|
|
pt2[0] += nonbonded_size;
|
|
ok &= ray->sausage3fv(pt1, pt2, lineradius, c0, c0);
|
|
|
|
copy3f(pc, pt1);
|
|
copy3f(pc, pt2);
|
|
pt1[1] -= nonbonded_size;
|
|
pt2[1] += nonbonded_size;
|
|
ok &= ray->sausage3fv(pt1, pt2, lineradius, c0, c0);
|
|
|
|
copy3f(pc, pt1);
|
|
copy3f(pc, pt2);
|
|
pt1[2] -= nonbonded_size;
|
|
pt2[2] += nonbonded_size;
|
|
ok &= ray->sausage3fv(pt1, pt2, lineradius, c0, c0);
|
|
} break;
|
|
case CGO_VERTEX_BEGIN_LINE_STRIP:
|
|
case CGO_VERTEX:
|
|
v0 = pc;
|
|
|
|
if (ramp) {
|
|
if (!ObjectGadgetRampInterVertex(ramp, v0, rampc0, -1)) {
|
|
copy3f(white, rampc0);
|
|
}
|
|
c0 = rampc0;
|
|
}
|
|
switch (mode) {
|
|
case GL_POINTS:
|
|
ok &= ray->sphere3fv(v0, dotradius);
|
|
break;
|
|
case GL_LINES:
|
|
if (vc & 0x1)
|
|
ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
|
|
v1 = v0;
|
|
if (!ramp) {
|
|
c1 = c0;
|
|
}
|
|
break;
|
|
case GL_LINE_STRIP:
|
|
if (vc) {
|
|
ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
|
|
}
|
|
v1 = v0;
|
|
if (!ramp) {
|
|
c1 = c0;
|
|
}
|
|
break;
|
|
case GL_LINE_LOOP:
|
|
if (vc)
|
|
ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
|
|
else {
|
|
v2 = v0;
|
|
c2 = c0;
|
|
}
|
|
v1 = v0;
|
|
if (!ramp)
|
|
c1 = c0;
|
|
break;
|
|
case GL_TRIANGLES:
|
|
if (((vc + 1) % 3) == 0)
|
|
ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
|
|
v2 = v1;
|
|
n2 = n1;
|
|
v1 = v0;
|
|
n1 = n0;
|
|
if (!ramp) {
|
|
c2 = c1;
|
|
c1 = c0;
|
|
}
|
|
break;
|
|
case GL_TRIANGLE_STRIP:
|
|
if (vc > 1)
|
|
ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
|
|
v2 = v1;
|
|
n2 = n1;
|
|
v1 = v0;
|
|
n1 = n0;
|
|
if (!ramp) {
|
|
c2 = c1;
|
|
c1 = c0;
|
|
}
|
|
break;
|
|
case GL_TRIANGLE_FAN:
|
|
if (vc > 1)
|
|
ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
|
|
else if (!vc) {
|
|
n2 = n0;
|
|
v2 = v0;
|
|
if (!ramp)
|
|
c2 = c0;
|
|
}
|
|
v1 = v0;
|
|
n1 = n0;
|
|
if (!ramp)
|
|
c1 = c0;
|
|
break;
|
|
}
|
|
if (ramp) {
|
|
switch (mode) {
|
|
case GL_TRIANGLES:
|
|
case GL_TRIANGLE_STRIP:
|
|
case GL_TRIANGLE_FAN:
|
|
copy3f(rampc1, rampc2);
|
|
c2 = rampc2;
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP:
|
|
case GL_LINE_LOOP:
|
|
copy3f(rampc0, rampc1);
|
|
c1 = rampc1;
|
|
break;
|
|
}
|
|
}
|
|
vc++;
|
|
break;
|
|
case CGO_SPHERE:
|
|
ray->color3fv(c0);
|
|
ok &= ray->sphere3fv(pc, *(pc + 3));
|
|
break;
|
|
case CGO_ELLIPSOID:
|
|
ray->color3fv(c0);
|
|
ok &= ray->ellipsoid3fv(pc, *(pc + 3), pc + 4, pc + 7, pc + 10);
|
|
break;
|
|
case CGO_QUADRIC:
|
|
ray->color3fv(c0);
|
|
ok &= CGORenderQuadricRay(ray, pc, *(pc + 3), pc + 4);
|
|
break;
|
|
case CGO_CONE:
|
|
ok &= ray->cone3fv(pc, pc + 3, *(pc + 6), *(pc + 7), pc + 8, pc + 11,
|
|
static_cast<cCylCap>(int(pc[14])), static_cast<cCylCap>(int(pc[15])));
|
|
break;
|
|
case CGO_CUSTOM_CYLINDER: {
|
|
auto cyl = reinterpret_cast<cgo::draw::custom_cylinder*>(pc);
|
|
ok &= ray->customCylinder3fv(*cyl);
|
|
} break;
|
|
case CGO_CUSTOM_CYLINDER_ALPHA: {
|
|
auto cyl = reinterpret_cast<cgo::draw::custom_cylinder_alpha*>(pc);
|
|
ok &= ray->customCylinderAlpha3fv(*cyl);
|
|
} break;
|
|
case CGO_SHADER_CYLINDER: {
|
|
float p2[3];
|
|
int cap = CGO_get_int(pc + 7);
|
|
const cCylCap cap1 = cap1_from_cyl_shader_bits(cap);
|
|
const cCylCap cap2 = cap2_from_cyl_shader_bits(cap);
|
|
add3f(pc, pc + 3, p2);
|
|
ok &= ray->customCylinder3fv(
|
|
pc, p2, *(pc + 6), ray->CurColor, ray->CurColor, cap1, cap2);
|
|
} break;
|
|
case CGO_SHADER_CYLINDER_WITH_2ND_COLOR: {
|
|
auto cyl = reinterpret_cast<cgo::draw::shadercylinder2ndcolor*>(pc);
|
|
float v1[3];
|
|
int cap = cyl->cap;
|
|
const cCylCap fcap = cap1_from_cyl_shader_bits(cap);
|
|
const cCylCap bcap = cap2_from_cyl_shader_bits(cap);
|
|
int colorinterp = cap & cCylShaderInterpColor;
|
|
const float* color1 = c0;
|
|
const float* color2 = cyl->color2;
|
|
add3f(cyl->origin, cyl->axis, v1);
|
|
float alpha1 = I->G->CGORenderer->alpha;
|
|
float alpha2 = cyl->alpha >= 0.f ? cyl->alpha : alpha1;
|
|
if (colorinterp || equal3f(color1, color2)) {
|
|
ok &= ray->customCylinder3fv(
|
|
pc, v1, cyl->tube_size, color1, color2, fcap, bcap, alpha1, alpha2);
|
|
} else {
|
|
float mid[3];
|
|
mult3f(cyl->axis, .5f, mid);
|
|
add3f(cyl->origin, mid, mid);
|
|
|
|
ray->color3fv(c0);
|
|
ok &= ray->customCylinder3fv(cyl->origin, mid, cyl->tube_size, color1,
|
|
color1, fcap, cCylCap::None, alpha1, alpha2);
|
|
ray->color3fv(cyl->color2);
|
|
ok &= ray->customCylinder3fv(mid, v1, cyl->tube_size, color2, color2,
|
|
cCylCap::None, bcap, alpha1, alpha2);
|
|
}
|
|
} break;
|
|
case CGO_CYLINDER: {
|
|
auto* cyl = reinterpret_cast<cgo::draw::cylinder*>(pc);
|
|
ok &= ray->cylinder3fv(*cyl);
|
|
} break;
|
|
case CGO_SAUSAGE:
|
|
ok &= ray->sausage3fv(pc, pc + 3, *(pc + 6), pc + 7, pc + 10);
|
|
break;
|
|
case CGO_TRIANGLE:
|
|
ok &= ray->triangle3fv(pc, pc + 3, pc + 6, pc + 9, pc + 12, pc + 15,
|
|
pc + 18, pc + 21, pc + 24);
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
cgo::draw::arrays* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
int const mode = sp->mode, arrays = sp->arraybits, nverts = sp->nverts;
|
|
float* vertexVals = sp->floatdata;
|
|
float *normalVals = 0, *colorVals = 0;
|
|
int offset = 0;
|
|
if (arrays & CGO_VERTEX_ARRAY) {
|
|
vertexVals = sp->floatdata;
|
|
offset += nverts * VERTEX_POS_SIZE;
|
|
}
|
|
if (arrays & CGO_NORMAL_ARRAY) {
|
|
normalVals = sp->floatdata + offset;
|
|
offset += nverts * VERTEX_NORMAL_SIZE;
|
|
}
|
|
if (arrays & CGO_COLOR_ARRAY) {
|
|
colorVals = sp->floatdata + offset;
|
|
offset += nverts * VERTEX_COLOR_SIZE;
|
|
}
|
|
vc = 0;
|
|
for (int v = 0, pl = 0, plc = 0; ok && v < nverts;
|
|
v++, pl += 3, plc += 4) {
|
|
if (normalVals) {
|
|
n0 = &normalVals[pl];
|
|
}
|
|
if (colorVals) {
|
|
c0 = &colorVals[plc];
|
|
ray->color3fv(c0);
|
|
ray->transparentf(1.0f - c0[3]);
|
|
}
|
|
if (vertexVals) {
|
|
v0 = &vertexVals[pl];
|
|
}
|
|
switch (mode) {
|
|
case GL_POINTS:
|
|
ok &= ray->sphere3fv(v0, dotradius);
|
|
break;
|
|
case GL_LINES:
|
|
if (vc & 0x1)
|
|
ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
|
|
v1 = v0;
|
|
c1 = c0;
|
|
break;
|
|
case GL_LINE_STRIP:
|
|
if (vc)
|
|
ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
|
|
v1 = v0;
|
|
c1 = c0;
|
|
break;
|
|
case GL_LINE_LOOP:
|
|
if (vc)
|
|
ok &= ray->sausage3fv(v0, v1, lineradius, c0, c1);
|
|
else {
|
|
v2 = v0;
|
|
c2 = c0;
|
|
}
|
|
v1 = v0;
|
|
c1 = c0;
|
|
break;
|
|
case GL_TRIANGLES:
|
|
if (((vc + 1) % 3) == 0)
|
|
ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
|
|
v2 = v1;
|
|
c2 = c1;
|
|
n2 = n1;
|
|
v1 = v0;
|
|
c1 = c0;
|
|
n1 = n0;
|
|
break;
|
|
case GL_TRIANGLE_STRIP:
|
|
if (vc > 1)
|
|
ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
|
|
v2 = v1;
|
|
c2 = c1;
|
|
n2 = n1;
|
|
v1 = v0;
|
|
c1 = c0;
|
|
n1 = n0;
|
|
break;
|
|
case GL_TRIANGLE_FAN:
|
|
if (vc > 1)
|
|
ok &= ray->triangle3fv(v0, v1, v2, n0, n1, n2, c0, c1, c2);
|
|
else if (!vc) {
|
|
n2 = n0;
|
|
v2 = v0;
|
|
c2 = c0;
|
|
}
|
|
v1 = v0;
|
|
c1 = c0;
|
|
n1 = n0;
|
|
break;
|
|
}
|
|
vc++;
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ok)
|
|
ray->transparentf(0.0F);
|
|
return ok;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Add axes-aligned cube to CGO with BEGIN/END GL_TRIANGLE_STRIP.
|
|
*
|
|
* @param center XYZ center position
|
|
* @param radius Radius of a sphere with the same volume as the cube
|
|
*/
|
|
void CGOSimpleCube(CGO* I, const float* center, float radius)
|
|
{
|
|
float const x = center[0];
|
|
float const y = center[1];
|
|
float const z = center[2];
|
|
|
|
// match volume of sphere
|
|
float const r = radius * 0.805996 /* (M_PI / 6)^(1/3) */;
|
|
|
|
CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
CGONormal(I, 0., 0., 1.);
|
|
CGOVertex(I, x + r, y + r, z + r);
|
|
CGOVertex(I, x - r, y + r, z + r);
|
|
CGOVertex(I, x + r, y - r, z + r);
|
|
CGOVertex(I, x - r, y - r, z + r);
|
|
CGOEnd(I);
|
|
CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
CGONormal(I, 1., 0., 0.);
|
|
CGOVertex(I, x + r, y - r, z - r);
|
|
CGOVertex(I, x + r, y + r, z - r);
|
|
CGOVertex(I, x + r, y - r, z + r);
|
|
CGOVertex(I, x + r, y + r, z + r);
|
|
CGOEnd(I);
|
|
CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
CGONormal(I, 0., 1., 0.);
|
|
CGOVertex(I, x + r, y + r, z - r);
|
|
CGOVertex(I, x - r, y + r, z - r);
|
|
CGOVertex(I, x + r, y + r, z + r);
|
|
CGOVertex(I, x - r, y + r, z + r);
|
|
CGOEnd(I);
|
|
CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
CGONormal(I, 0., 0., -1.);
|
|
CGOVertex(I, x - r, y - r, z - r);
|
|
CGOVertex(I, x - r, y + r, z - r);
|
|
CGOVertex(I, x + r, y - r, z - r);
|
|
CGOVertex(I, x + r, y + r, z - r);
|
|
CGOEnd(I);
|
|
CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
CGONormal(I, -1., 0., 0.);
|
|
CGOVertex(I, x - r, y + r, z + r);
|
|
CGOVertex(I, x - r, y + r, z - r);
|
|
CGOVertex(I, x - r, y - r, z + r);
|
|
CGOVertex(I, x - r, y - r, z - r);
|
|
CGOEnd(I);
|
|
CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
CGONormal(I, 0., -1., 0.);
|
|
CGOVertex(I, x - r, y - r, z + r);
|
|
CGOVertex(I, x - r, y - r, z - r);
|
|
CGOVertex(I, x + r, y - r, z + r);
|
|
CGOVertex(I, x + r, y - r, z - r);
|
|
CGOEnd(I);
|
|
}
|
|
|
|
/**
|
|
* Add axes-aligned tetrahedron to CGO with BEGIN/END GL_TRIANGLES.
|
|
*
|
|
* @param center XYZ center position
|
|
* @param radius Distance between center and (each) edge
|
|
*/
|
|
void CGOSimpleTetrahedron(CGO* I, const float* center, float radius)
|
|
{
|
|
float vertices[][3] = {
|
|
{1.f, 1.f, 1.f},
|
|
{-1.f, -1.f, 1.f},
|
|
{1.f, -1.f, -1.f},
|
|
{-1.f, 1.f, -1.f},
|
|
};
|
|
|
|
for (float* const v : vertices) {
|
|
scale3f(v, radius, v);
|
|
add3f(v, center, v);
|
|
}
|
|
|
|
constexpr float n = 0.57735027;
|
|
|
|
CGOBegin(I, GL_TRIANGLES);
|
|
CGONormal(I, n, -n, n);
|
|
CGOVertexv(I, vertices[0]);
|
|
CGOVertexv(I, vertices[1]);
|
|
CGOVertexv(I, vertices[2]);
|
|
CGONormal(I, n, n, -n);
|
|
CGOVertexv(I, vertices[0]);
|
|
CGOVertexv(I, vertices[2]);
|
|
CGOVertexv(I, vertices[3]);
|
|
CGONormal(I, -n, n, n);
|
|
CGOVertexv(I, vertices[0]);
|
|
CGOVertexv(I, vertices[3]);
|
|
CGOVertexv(I, vertices[1]);
|
|
CGONormal(I, -n, -n, -n);
|
|
CGOVertexv(I, vertices[1]);
|
|
CGOVertexv(I, vertices[3]);
|
|
CGOVertexv(I, vertices[2]);
|
|
CGOEnd(I);
|
|
}
|
|
|
|
/* translation function which turns cylinders and spheres into triangles */
|
|
|
|
static int CGOSimpleSphere(
|
|
CGO* I, const float* v, float vdw, short sphere_quality)
|
|
{
|
|
SphereRec* sp;
|
|
int *q, *s;
|
|
int b, c;
|
|
int ok = true;
|
|
/* cgo_sphere_quality is between 0 and (NUMBER_OF_SPHERE_LEVELS-1) */
|
|
|
|
sp = I->G->Sphere->Sphere[CLAMPVALUE<short>(
|
|
sphere_quality, 0, (NUMBER_OF_SPHERE_LEVELS - 1))];
|
|
|
|
q = sp->Sequence;
|
|
s = sp->StripLen;
|
|
|
|
for (b = 0; b < sp->NStrip; b++) {
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
for (c = 0; ok && c < (*s); c++) {
|
|
ok &= CGONormalv(I, sp->dot[*q]);
|
|
if (ok)
|
|
ok &= CGOVertex(I, v[0] + vdw * sp->dot[*q][0],
|
|
v[1] + vdw * sp->dot[*q][1], v[2] + vdw * sp->dot[*q][2]);
|
|
q++;
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
s++;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static int CGOSimpleQuadric(CGO* I, const float* v, float r, const float* q)
|
|
{
|
|
float r_el, n0[3], n1[3], n2[3];
|
|
int ok = true;
|
|
if (CGOQuadricToEllipsoid(v, r, q, &r_el, n0, n1, n2))
|
|
ok &= CGOSimpleEllipsoid(I, v, r_el, n0, n1, n2);
|
|
return ok;
|
|
}
|
|
|
|
static int CGOSimpleEllipsoid(CGO* I, const float* v, float vdw,
|
|
const float* n0, const float* n1, const float* n2)
|
|
{
|
|
SphereRec* sp;
|
|
int *q, *s;
|
|
int b, c;
|
|
int ds;
|
|
float nn0[3], nn1[3], nn2[3];
|
|
float scale[3], scale_sq[3];
|
|
int ok = true;
|
|
|
|
normalize23f(n0, nn0);
|
|
normalize23f(n1, nn1);
|
|
normalize23f(n2, nn2);
|
|
|
|
scale[0] = (float) length3f(n0);
|
|
scale[1] = (float) length3f(n1);
|
|
scale[2] = (float) length3f(n2);
|
|
|
|
scale_sq[0] = scale[0] * scale[0];
|
|
scale_sq[1] = scale[1] * scale[1];
|
|
scale_sq[2] = scale[2] * scale[2];
|
|
|
|
ds = SettingGet_i(I->G, nullptr, nullptr, cSetting_cgo_ellipsoid_quality);
|
|
if (ds < 0)
|
|
ds = SettingGet_i(I->G, nullptr, nullptr, cSetting_ellipsoid_quality);
|
|
if (ds < 0)
|
|
ds = 0;
|
|
if (ds > 3)
|
|
ds = 3;
|
|
sp = I->G->Sphere->Sphere[ds];
|
|
|
|
q = sp->Sequence;
|
|
s = sp->StripLen;
|
|
|
|
for (b = 0; b < sp->NStrip; b++) {
|
|
ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
for (c = 0; ok && c < (*s); c++) {
|
|
float* sp_dot_q = sp->dot[*q];
|
|
float s0 = vdw * sp_dot_q[0];
|
|
float s1 = vdw * sp_dot_q[1];
|
|
float s2 = vdw * sp_dot_q[2];
|
|
float d0[3], d1[3], d2[3], vv[3], direction[3];
|
|
float dd0, dd1, dd2, ss0, ss1, ss2;
|
|
float comp0[3], comp1[3], comp2[3];
|
|
float surfnormal[3];
|
|
int i;
|
|
|
|
scale3f(n0, s0, d0);
|
|
scale3f(n1, s1, d1);
|
|
scale3f(n2, s2, d2);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
vv[i] = d0[i] + d1[i] + d2[i];
|
|
}
|
|
normalize23f(vv, direction);
|
|
add3f(v, vv, vv);
|
|
|
|
dd0 = dot_product3f(direction, nn0);
|
|
dd1 = dot_product3f(direction, nn1);
|
|
dd2 = dot_product3f(direction, nn2);
|
|
|
|
if (scale[0] > R_SMALL8) {
|
|
ss0 = dd0 / scale_sq[0];
|
|
} else {
|
|
ss0 = 0.0F;
|
|
}
|
|
if (scale[1] > R_SMALL8) {
|
|
ss1 = dd1 / scale_sq[1];
|
|
} else {
|
|
ss1 = 0.0F;
|
|
}
|
|
|
|
if (scale[2] > R_SMALL8) {
|
|
ss2 = dd2 / scale_sq[2];
|
|
} else {
|
|
ss2 = 0.0F;
|
|
}
|
|
|
|
scale3f(nn0, ss0, comp0);
|
|
scale3f(nn1, ss1, comp1);
|
|
scale3f(nn2, ss2, comp2);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
surfnormal[i] = comp0[i] + comp1[i] + comp2[i];
|
|
}
|
|
normalize3f(surfnormal);
|
|
|
|
ok &= CGONormalv(I, surfnormal);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, vv);
|
|
q++;
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
s++;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* Triangulated round cap (half-globe)
|
|
*/
|
|
void CGORoundNub(CGO* I,
|
|
const float* v1, // cap center
|
|
const float* p0, // normal along axis
|
|
const float* p1, // x coord in cap space
|
|
const float* p2, // y coord in cap space
|
|
int direction, // 1 or -1
|
|
int nEdge, // "quality"
|
|
float size)
|
|
{
|
|
const int cmax = (nEdge + 3) / 2;
|
|
const float PI_over_cmax = PI / ((cmax - 1) * 2);
|
|
const float PI_over_nEdge = (PI * 2) / nEdge;
|
|
float z2 = 1.f;
|
|
|
|
// z coord in cap space
|
|
float p3[3];
|
|
scale3f(p0, direction, p3);
|
|
|
|
CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
|
|
// from equator to pole (latitudinal)
|
|
for (int c = 1; c < cmax; c += 1) {
|
|
float z1 = z2;
|
|
z2 = cos(c * PI_over_cmax);
|
|
|
|
// around cylinder axis (longitudinal)
|
|
for (int d = (nEdge + 1) * (-direction); d; d += direction) {
|
|
float z3 = z1;
|
|
|
|
// 2 vertices
|
|
for (int e = -1; e < 1; ++e) {
|
|
float x = cos(d * PI_over_nEdge) * sin((c + e) * PI_over_cmax);
|
|
float y = sin(d * PI_over_nEdge) * sin((c + e) * PI_over_cmax);
|
|
float normal[3], vertex[3];
|
|
|
|
normal[0] = p1[0] * x + p2[0] * y + p3[0] * z3;
|
|
normal[1] = p1[1] * x + p2[1] * y + p3[1] * z3;
|
|
normal[2] = p1[2] * x + p2[2] * y + p3[2] * z3;
|
|
|
|
vertex[0] = v1[0] + normal[0] * size;
|
|
vertex[1] = v1[1] + normal[1] * size;
|
|
vertex[2] = v1[2] + normal[2] * size;
|
|
|
|
normalize3f(normal);
|
|
CGONormalv(I, normal);
|
|
CGOVertexv(I, vertex);
|
|
|
|
z3 = z2;
|
|
}
|
|
}
|
|
}
|
|
|
|
CGOEnd(I);
|
|
}
|
|
|
|
static int CGOSimpleCylinder(CGO* I, const float* v1, const float* v2,
|
|
const float tube_size, const float* c1, const float* c2, const float alpha1,
|
|
const float alpha2, const bool interp, const cCylCap cap1,
|
|
const cCylCap cap2, const Pickable* pickcolor2, const bool stick_round_nub)
|
|
{
|
|
#define MAX_EDGE 50
|
|
|
|
float d[3], t[3], p0[3], p1[3], p2[3], vv1[3], vv2[3], v_buf[9], *v;
|
|
float x[MAX_EDGE + 1], y[MAX_EDGE + 1];
|
|
float overlap;
|
|
float nub;
|
|
bool colorFlag, interpColorFlag;
|
|
int nEdge;
|
|
int c;
|
|
int ok = true;
|
|
float midcolor[3];
|
|
float midalpha{alpha1};
|
|
Pickable pickcolor[2];
|
|
pickcolor[0].index = I->current_pick_color_index;
|
|
pickcolor[0].bond = I->current_pick_color_bond;
|
|
if (pickcolor2) {
|
|
pickcolor[1].index = pickcolor2->index;
|
|
pickcolor[1].bond = pickcolor2->bond;
|
|
} else {
|
|
pickcolor[1].index = pickcolor[0].index;
|
|
pickcolor[1].bond = pickcolor[0].bond;
|
|
}
|
|
v = v_buf;
|
|
nEdge = SettingGetGlobal_i(I->G, cSetting_stick_quality);
|
|
overlap = tube_size * SettingGetGlobal_f(I->G, cSetting_stick_overlap);
|
|
nub = tube_size * SettingGetGlobal_f(I->G, cSetting_stick_nub);
|
|
|
|
if (nEdge > MAX_EDGE)
|
|
nEdge = MAX_EDGE;
|
|
subdivide(nEdge, x, y);
|
|
|
|
colorFlag = (c1 != c2) && c2;
|
|
colorFlag |= alpha1 != alpha2;
|
|
|
|
interpColorFlag = c2 && interp && pickcolor2;
|
|
if (interpColorFlag) {
|
|
average3f(c1, c2, midcolor);
|
|
midalpha = (alpha1 + alpha2) / 2.0f;
|
|
}
|
|
/* direction vector */
|
|
|
|
p0[0] = (v2[0] - v1[0]);
|
|
p0[1] = (v2[1] - v1[1]);
|
|
p0[2] = (v2[2] - v1[2]);
|
|
|
|
normalize3f(p0);
|
|
|
|
if (cap1 == cCylCapRound && !stick_round_nub) {
|
|
vv1[0] = v1[0] - p0[0] * overlap;
|
|
vv1[1] = v1[1] - p0[1] * overlap;
|
|
vv1[2] = v1[2] - p0[2] * overlap;
|
|
} else {
|
|
vv1[0] = v1[0];
|
|
vv1[1] = v1[1];
|
|
vv1[2] = v1[2];
|
|
}
|
|
if (cap2 == cCylCapRound && !stick_round_nub) {
|
|
vv2[0] = v2[0] + p0[0] * overlap;
|
|
vv2[1] = v2[1] + p0[1] * overlap;
|
|
vv2[2] = v2[2] + p0[2] * overlap;
|
|
} else {
|
|
vv2[0] = v2[0];
|
|
vv2[1] = v2[1];
|
|
vv2[2] = v2[2];
|
|
}
|
|
|
|
d[0] = (vv2[0] - vv1[0]);
|
|
d[1] = (vv2[1] - vv1[1]);
|
|
d[2] = (vv2[2] - vv1[2]);
|
|
if (pickcolor2) {
|
|
mult3f(d, .5f, d);
|
|
}
|
|
get_divergent3f(d, t);
|
|
cross_product3f(d, t, p1);
|
|
normalize3f(p1);
|
|
|
|
cross_product3f(d, p1, p2);
|
|
|
|
normalize3f(p2);
|
|
|
|
/* now we have a coordinate system */
|
|
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
for (c = nEdge; ok && c >= 0; c--) {
|
|
v[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
v[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
v[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
v[3] = vv1[0] + v[0] * tube_size;
|
|
v[4] = vv1[1] + v[1] * tube_size;
|
|
v[5] = vv1[2] + v[2] * tube_size;
|
|
|
|
v[6] = v[3] + d[0];
|
|
v[7] = v[4] + d[1];
|
|
v[8] = v[5] + d[2];
|
|
|
|
ok &= CGONormalv(I, v);
|
|
if (ok && (colorFlag || interpColorFlag)) {
|
|
ok &= CGOColorv(I, c1);
|
|
ok &= CGOAlpha(I, alpha1);
|
|
}
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
if (ok && interpColorFlag) {
|
|
ok &= CGOColorv(I, midcolor);
|
|
ok &= CGOAlpha(I, midalpha);
|
|
} else if (ok && colorFlag && !pickcolor2) {
|
|
ok &= CGOColorv(I, c2);
|
|
ok &= CGOAlpha(I, alpha2);
|
|
}
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 6);
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
if (pickcolor2) {
|
|
ok &= CGOColorv(I, c2);
|
|
ok &= CGOAlpha(I, alpha2);
|
|
CGOPickColor(I, pickcolor2->index, pickcolor2->bond);
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
for (c = nEdge; ok && c >= 0; c--) {
|
|
v[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
v[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
v[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
v[3] = vv1[0] + v[0] * tube_size + d[0];
|
|
v[4] = vv1[1] + v[1] * tube_size + d[1];
|
|
v[5] = vv1[2] + v[2] * tube_size + d[2];
|
|
|
|
v[6] = v[3] + d[0];
|
|
v[7] = v[4] + d[1];
|
|
v[8] = v[5] + d[2];
|
|
|
|
ok &= CGONormalv(I, v);
|
|
if (ok && interpColorFlag) {
|
|
ok &= CGOColorv(I, midcolor);
|
|
ok &= CGOAlpha(I, midalpha);
|
|
}
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
if (ok && interpColorFlag) {
|
|
ok &= CGOColorv(I, c2);
|
|
ok &= CGOAlpha(I, alpha2);
|
|
}
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 6);
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
}
|
|
|
|
if (ok && cap1 != cCylCap::None) {
|
|
if (ok && colorFlag && c1) {
|
|
ok &= CGOColorv(I, c1);
|
|
ok &= CGOAlpha(I, alpha1);
|
|
}
|
|
if (pickcolor2)
|
|
CGOPickColor(I, pickcolor[0].index, pickcolor[0].bond);
|
|
|
|
if (stick_round_nub && cap1 == cCylCapRound) {
|
|
CGORoundNub(I, v1, p0, p1, p2, -1, nEdge, tube_size);
|
|
} else {
|
|
v[0] = -p0[0];
|
|
v[1] = -p0[1];
|
|
v[2] = -p0[2];
|
|
|
|
if (cap1 == cCylCapRound) {
|
|
v[3] = vv1[0] - p0[0] * nub;
|
|
v[4] = vv1[1] - p0[1] * nub;
|
|
v[5] = vv1[2] - p0[2] * nub;
|
|
} else {
|
|
v[3] = vv1[0];
|
|
v[4] = vv1[1];
|
|
v[5] = vv1[2];
|
|
}
|
|
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_FAN);
|
|
if (ok)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
|
|
for (c = nEdge; ok && c >= 0; c--) {
|
|
v[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
v[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
v[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
v[3] = vv1[0] + v[0] * tube_size;
|
|
v[4] = vv1[1] + v[1] * tube_size;
|
|
v[5] = vv1[2] + v[2] * tube_size;
|
|
|
|
if (cap1 == cCylCapRound)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
}
|
|
}
|
|
|
|
if (ok && cap2 != cCylCap::None) {
|
|
if (ok && colorFlag && c2) {
|
|
ok &= CGOColorv(I, c2);
|
|
ok &= CGOAlpha(I, alpha2);
|
|
}
|
|
if (pickcolor2)
|
|
CGOPickColor(I, pickcolor2->index, pickcolor2->bond);
|
|
|
|
if (stick_round_nub && cap2 == cCylCapRound) {
|
|
CGORoundNub(I, v2, p0, p1, p2, 1, nEdge, tube_size);
|
|
} else {
|
|
v[0] = p0[0];
|
|
v[1] = p0[1];
|
|
v[2] = p0[2];
|
|
|
|
if (cap2 == cCylCapRound) {
|
|
v[3] = vv2[0] + p0[0] * nub;
|
|
v[4] = vv2[1] + p0[1] * nub;
|
|
v[5] = vv2[2] + p0[2] * nub;
|
|
} else {
|
|
v[3] = vv2[0];
|
|
v[4] = vv2[1];
|
|
v[5] = vv2[2];
|
|
}
|
|
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_FAN);
|
|
if (ok)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
|
|
for (c = 0; ok && c <= nEdge; c++) {
|
|
v[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
v[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
v[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
v[3] = vv2[0] + v[0] * tube_size;
|
|
v[4] = vv2[1] + v[1] * tube_size;
|
|
v[5] = vv2[2] + v[2] * tube_size;
|
|
|
|
if (cap2 == cCylCapRound)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
template <typename CylinderT>
|
|
static int CGOSimpleCylinder(CGO* I, const CylinderT& cyl, const float a1,
|
|
const float a2, const bool interp, const cCylCap cap1, const cCylCap cap2,
|
|
const Pickable* pickcolor2, const bool stick_round_nub)
|
|
{
|
|
return CGOSimpleCylinder(I, cyl.vertex1, cyl.vertex2, cyl.radius, cyl.color1,
|
|
cyl.color2, a1, a2, interp, cap1, cap2, pickcolor2, stick_round_nub);
|
|
}
|
|
|
|
static int CGOSimpleCone(CGO* I, const float* v1, const float* v2, float r1,
|
|
float r2, const float* c1, const float* c2, cCylCap cap1, cCylCap cap2)
|
|
{
|
|
#define MAX_EDGE 50
|
|
|
|
float d[3], t[3], p0[3], p1[3], p2[3], vv1[3], vv2[3], v_buf[9], *v;
|
|
float x[MAX_EDGE + 1], y[MAX_EDGE + 1], edge_normal[3 * (MAX_EDGE + 1)];
|
|
int colorFlag;
|
|
int nEdge;
|
|
int c;
|
|
int ok = true;
|
|
|
|
v = v_buf;
|
|
nEdge = SettingGetGlobal_i(I->G, cSetting_cone_quality);
|
|
|
|
if (nEdge > MAX_EDGE)
|
|
nEdge = MAX_EDGE;
|
|
subdivide(nEdge, x, y);
|
|
|
|
colorFlag = (c1 != c2) && c2;
|
|
|
|
ok &= CGOColorv(I, c1);
|
|
|
|
/* direction vector */
|
|
|
|
p0[0] = (v2[0] - v1[0]);
|
|
p0[1] = (v2[1] - v1[1]);
|
|
p0[2] = (v2[2] - v1[2]);
|
|
|
|
normalize3f(p0);
|
|
|
|
{
|
|
vv1[0] = v1[0];
|
|
vv1[1] = v1[1];
|
|
vv1[2] = v1[2];
|
|
}
|
|
|
|
{
|
|
vv2[0] = v2[0];
|
|
vv2[1] = v2[1];
|
|
vv2[2] = v2[2];
|
|
}
|
|
|
|
d[0] = (vv2[0] - vv1[0]);
|
|
d[1] = (vv2[1] - vv1[1]);
|
|
d[2] = (vv2[2] - vv1[2]);
|
|
|
|
get_divergent3f(d, t);
|
|
|
|
cross_product3f(d, t, p1);
|
|
|
|
normalize3f(p1);
|
|
|
|
cross_product3f(d, p1, p2);
|
|
|
|
normalize3f(p2);
|
|
|
|
/* now we have a coordinate system */
|
|
|
|
{
|
|
float len = diff3f(v1, v2);
|
|
float vt[3], nt[3];
|
|
float slope = 0.0F;
|
|
|
|
if (len) {
|
|
slope = (r1 - r2) / len;
|
|
}
|
|
for (c = nEdge; c >= 0; c--) {
|
|
vt[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
vt[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
vt[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
scale3f(p0, slope, nt);
|
|
add3f(nt, vt, vt);
|
|
normalize3f(vt);
|
|
copy3f(vt, edge_normal + 3 * c);
|
|
}
|
|
}
|
|
|
|
/* now we have normals */
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_STRIP);
|
|
for (c = nEdge; ok && c >= 0; c--) {
|
|
v[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
v[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
v[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
v[3] = vv1[0] + v[0] * r1;
|
|
v[4] = vv1[1] + v[1] * r1;
|
|
v[5] = vv1[2] + v[2] * r1;
|
|
|
|
v[6] = vv1[0] + v[0] * r2 + d[0];
|
|
v[7] = vv1[1] + v[1] * r2 + d[1];
|
|
v[8] = vv1[2] + v[2] * r2 + d[2];
|
|
|
|
ok &= CGONormalv(I, edge_normal + 3 * c);
|
|
if (ok && colorFlag)
|
|
CGOColorv(I, c1);
|
|
if (ok)
|
|
CGOVertexv(I, v + 3);
|
|
if (ok && colorFlag)
|
|
CGOColorv(I, c2);
|
|
if (ok)
|
|
CGOVertexv(I, v + 6);
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
|
|
if (ok && cap1 != cCylCap::None) {
|
|
v[0] = -p0[0];
|
|
v[1] = -p0[1];
|
|
v[2] = -p0[2];
|
|
|
|
{
|
|
v[3] = vv1[0];
|
|
v[4] = vv1[1];
|
|
v[5] = vv1[2];
|
|
}
|
|
|
|
if (colorFlag)
|
|
ok &= CGOColorv(I, c1);
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_FAN);
|
|
if (ok)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
|
|
for (c = nEdge; ok && c >= 0; c--) {
|
|
v[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
v[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
v[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
v[3] = vv1[0] + v[0] * r1;
|
|
v[4] = vv1[1] + v[1] * r1;
|
|
v[5] = vv1[2] + v[2] * r1;
|
|
|
|
if (cap1 == cCylCapRound)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
}
|
|
|
|
if (ok && cap2 != cCylCap::None) {
|
|
|
|
v[0] = p0[0];
|
|
v[1] = p0[1];
|
|
v[2] = p0[2];
|
|
|
|
{
|
|
v[3] = vv2[0];
|
|
v[4] = vv2[1];
|
|
v[5] = vv2[2];
|
|
}
|
|
|
|
if (colorFlag)
|
|
ok &= CGOColorv(I, c2);
|
|
if (ok)
|
|
ok &= CGOBegin(I, GL_TRIANGLE_FAN);
|
|
if (ok)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
|
|
for (c = 0; ok && c <= nEdge; c++) {
|
|
v[0] = p1[0] * x[c] + p2[0] * y[c];
|
|
v[1] = p1[1] * x[c] + p2[1] * y[c];
|
|
v[2] = p1[2] * x[c] + p2[2] * y[c];
|
|
|
|
v[3] = vv2[0] + v[0] * r2;
|
|
v[4] = vv2[1] + v[1] * r2;
|
|
v[5] = vv2[2] + v[2] * r2;
|
|
|
|
if (cap2 == cCylCapRound)
|
|
ok &= CGONormalv(I, v);
|
|
if (ok)
|
|
ok &= CGOVertexv(I, v + 3);
|
|
}
|
|
if (ok)
|
|
ok &= CGOEnd(I);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
int CGOSimpleCone(CGO* I, const cgo::draw::cone& cone)
|
|
{
|
|
return CGOSimpleCone(I, cone.vertex1, cone.vertex2, cone.radius1,
|
|
cone.radius2, cone.color1, cone.color2, static_cast<cCylCap>(cone.cap1),
|
|
static_cast<cCylCap>(cone.cap2));
|
|
}
|
|
|
|
/* CGOGetNextDrawBufferedIndex: This is used by RepSurface to */
|
|
/* get the data from the CGO_DRAW_BUFFERS_INDEXED operation so */
|
|
/* that it can update the indices for semi-transparent surfaces. */
|
|
const cgo::draw::buffers_not_indexed* CGOGetNextDrawBufferedNotIndex(
|
|
const CGO* cgo)
|
|
{
|
|
for (auto it = cgo->begin(); !it.is_stop(); ++it) {
|
|
if (it.op_code() == CGO_DRAW_BUFFERS_NOT_INDEXED) {
|
|
return it.cast<cgo::draw::buffers_not_indexed>();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool CGO::append(const CGO& source_, bool stopAtEnd)
|
|
{
|
|
const CGO* const source = &source_;
|
|
int ok = 1;
|
|
|
|
for (auto it = source->begin(); !it.is_stop(); ++it) {
|
|
add_to_cgo(it.op_code(), it.data());
|
|
}
|
|
|
|
if (stopAtEnd)
|
|
ok &= CGOStop(this);
|
|
has_draw_buffers |= source->has_draw_buffers;
|
|
has_draw_cylinder_buffers |= source->has_draw_cylinder_buffers;
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* Appends `src` to the end of this CGO. Takes ownership of data
|
|
* (incl. VBOs) and leaves `src` as a valid but empty CGO.
|
|
*/
|
|
void CGO::move_append(CGO&& src_)
|
|
{
|
|
CGO* const src = &src_;
|
|
if (!src->c)
|
|
return;
|
|
|
|
// copy buffer
|
|
VLACheck(op, float, c + src->c);
|
|
memcpy(op + c, src->op, src->c * sizeof(float));
|
|
|
|
// update sizes
|
|
c += src->c;
|
|
src->c = 0;
|
|
|
|
// null terminators (CGO_STOP)
|
|
*(op + c) = 0;
|
|
*(src->op) = 0;
|
|
|
|
// move heap data
|
|
for (auto& ref : src->_data_heap) {
|
|
_data_heap.emplace_back(std::move(ref));
|
|
}
|
|
src->_data_heap.clear();
|
|
|
|
// copy boolean flags
|
|
has_draw_buffers |= src->has_draw_buffers;
|
|
has_draw_cylinder_buffers |= src->has_draw_cylinder_buffers;
|
|
has_draw_sphere_buffers |= src->has_draw_sphere_buffers;
|
|
has_begin_end |= src->has_begin_end;
|
|
use_shader |= src->use_shader;
|
|
render_alpha |= src->render_alpha;
|
|
src->has_draw_buffers = false;
|
|
}
|
|
|
|
/**
|
|
* Appends `src` to the end of this CGO and then free's `src`
|
|
* and sets the pointer to nullptr.
|
|
*/
|
|
void CGO::free_append(CGO*& src)
|
|
{
|
|
free_append(std::move(src));
|
|
assert(src == nullptr);
|
|
}
|
|
void CGO::free_append(CGO*&& src)
|
|
{
|
|
if (!src)
|
|
return;
|
|
move_append(std::move(*src));
|
|
DeleteP(src);
|
|
}
|
|
|
|
int CGOAppend(CGO* dest, const CGO* source, bool stopAtEnd)
|
|
{
|
|
return dest->append(*source, stopAtEnd);
|
|
}
|
|
|
|
int CGOCountNumberOfOperationsOfType(const CGO* I, int optype)
|
|
{
|
|
std::set<int> ops = {optype};
|
|
return CGOCountNumberOfOperationsOfTypeN(I, ops);
|
|
}
|
|
|
|
int CGOCountNumberOfOperationsOfTypeN(const CGO* I, const std::set<int>& optype)
|
|
{
|
|
int numops = 0;
|
|
for (auto cgoit = I->begin(); !cgoit.is_stop(); ++cgoit) {
|
|
if (optype.count(cgoit.op_code()))
|
|
numops++;
|
|
}
|
|
return (numops);
|
|
}
|
|
|
|
int CGOCountNumberOfOperationsOfTypeN(
|
|
const CGO* I, const std::map<int, int>& optype)
|
|
{
|
|
int numops = 0;
|
|
for (auto cgoit = I->begin(); !cgoit.is_stop(); ++cgoit) {
|
|
auto it = optype.find(cgoit.op_code());
|
|
if (it != optype.end())
|
|
numops += it->second;
|
|
}
|
|
return (numops);
|
|
}
|
|
|
|
bool CGOHasOperationsOfType(const CGO* I, int optype)
|
|
{
|
|
std::set<int> ops = {optype};
|
|
return CGOHasOperationsOfTypeN(I, ops);
|
|
}
|
|
|
|
bool CGOHasOperations(const CGO* I)
|
|
{
|
|
return !I->begin().is_stop();
|
|
}
|
|
|
|
bool CGOHasOperationsOfTypeN(const CGO* I, const std::set<int>& optype)
|
|
{
|
|
if (!I->op)
|
|
return false;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
if (optype.count(it.op_code()))
|
|
return 1;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static bool CGOFilterOutOperationsOfTypeN(
|
|
const CGO* I, CGO* cgo, const std::set<int>& optype)
|
|
{
|
|
if (!I->op)
|
|
return false;
|
|
|
|
bool ret = false;
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto op = it.op_code();
|
|
if (optype.find(op) == optype.end()) {
|
|
auto pc = it.data();
|
|
cgo->add_to_cgo(op, pc);
|
|
} else {
|
|
ret = true; // returns if filtered anything
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool CGOFilterOutCylinderOperationsInto(const CGO* I, CGO* cgo)
|
|
{
|
|
static std::set<int> optypes = {CGO_SHADER_CYLINDER,
|
|
CGO_SHADER_CYLINDER_WITH_2ND_COLOR, CGO_SAUSAGE, CGO_CYLINDER,
|
|
CGO_CUSTOM_CYLINDER, CGO_CUSTOM_CYLINDER_ALPHA};
|
|
return CGOFilterOutOperationsOfTypeN(I, cgo, optypes);
|
|
}
|
|
|
|
bool CGOFilterOutBezierOperationsInto(const CGO* I, CGO* cgo)
|
|
{
|
|
static std::set<int> optypes = {CGO_BEZIER};
|
|
return CGOFilterOutOperationsOfTypeN(I, cgo, optypes);
|
|
}
|
|
|
|
bool CGOHasCylinderOperations(const CGO* I)
|
|
{
|
|
static std::set<int> optypes = {CGO_SHADER_CYLINDER,
|
|
CGO_SHADER_CYLINDER_WITH_2ND_COLOR, CGO_SAUSAGE, CGO_CYLINDER,
|
|
CGO_CUSTOM_CYLINDER, CGO_CUSTOM_CYLINDER_ALPHA};
|
|
return CGOHasOperationsOfTypeN(I, optypes);
|
|
}
|
|
|
|
bool CGOHasSphereOperations(const CGO* I)
|
|
{
|
|
static std::set<int> optypes = {CGO_SPHERE};
|
|
return CGOHasOperationsOfTypeN(I, optypes);
|
|
}
|
|
|
|
bool CGOHasBezierOperations(const CGO* I)
|
|
{
|
|
static std::set<int> optypes = {CGO_BEZIER};
|
|
return CGOHasOperationsOfTypeN(I, optypes);
|
|
}
|
|
|
|
bool CGOCheckWhetherToFree(PyMOLGlobals* G, CGO* I)
|
|
{
|
|
if (I->use_shader) {
|
|
if (I->cgo_shader_ub_color !=
|
|
SettingGetGlobal_i(G, cSetting_cgo_shader_ub_color) ||
|
|
I->cgo_shader_ub_normal !=
|
|
SettingGetGlobal_i(G, cSetting_cgo_shader_ub_normal)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CGO* CGOConvertLinesToShaderCylinders(const CGO* I, int est)
|
|
{
|
|
|
|
int tot_nverts = 0, tot_ncyls = 0;
|
|
|
|
CGO* cgo = CGONewSized(I->G, I->c + est);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS: {
|
|
const cgo::draw::arrays* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
float* vals =
|
|
cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
|
|
int nvals = sp->narrays * sp->nverts;
|
|
memcpy(vals, sp->floatdata, nvals);
|
|
} break;
|
|
case CGO_END:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOConvertLinesToShaderCylinders: CGO_END encountered without "
|
|
"CGO_BEGIN but skipped for OpenGLES\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_VERTEX:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOConvertLinesToShaderCylinders: CGO_VERTEX encountered without "
|
|
"CGO_BEGIN but skipped for OpenGLES\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_BEGIN: {
|
|
const float *last_vertex = nullptr, *last_color = nullptr,
|
|
*current_color = nullptr, *color = nullptr;
|
|
unsigned int last_pick_color_idx = 0;
|
|
int last_pick_color_bnd = cPickableNoPick;
|
|
int nverts = 0, err = 0;
|
|
int mode = CGO_get_int(pc);
|
|
|
|
for (++it; !err && it != CGO_END; ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_VERTEX:
|
|
if (last_vertex) {
|
|
switch (mode) {
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP: {
|
|
float axis[3];
|
|
bool pick_color_diff = false;
|
|
axis[0] = pc[0] - last_vertex[0];
|
|
axis[1] = pc[1] - last_vertex[1];
|
|
axis[2] = pc[2] - last_vertex[2];
|
|
pick_color_diff =
|
|
(cgo->current_pick_color_index != last_pick_color_idx ||
|
|
cgo->current_pick_color_bond != last_pick_color_bnd);
|
|
if (last_color && current_color &&
|
|
(!equal3f(last_color, current_color) || pick_color_diff)) {
|
|
CGOColorv(cgo, last_color);
|
|
if (pick_color_diff) {
|
|
Pickable pickcolor2 = {cgo->current_pick_color_index,
|
|
cgo->current_pick_color_bond};
|
|
CGOPickColor(cgo, last_pick_color_idx, last_pick_color_bnd);
|
|
cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, last_vertex,
|
|
axis, 1.f, cCylShaderBothCapsRound, current_color,
|
|
&pickcolor2);
|
|
CGOPickColor(cgo, pickcolor2.index, pickcolor2.bond);
|
|
} else {
|
|
cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, last_vertex,
|
|
axis, 1.f, cCylShaderBothCapsRound, current_color);
|
|
}
|
|
CGOColorv(cgo, current_color);
|
|
} else {
|
|
cgo->add<cgo::draw::shadercylinder>(
|
|
last_vertex, axis, 1.f, cCylShaderBothCapsRound);
|
|
}
|
|
last_vertex = pc;
|
|
last_pick_color_idx = cgo->current_pick_color_index;
|
|
last_pick_color_bnd = cgo->current_pick_color_bond;
|
|
tot_ncyls++;
|
|
}
|
|
if (mode == GL_LINES) {
|
|
last_vertex = nullptr;
|
|
last_color = nullptr;
|
|
}
|
|
}
|
|
} else {
|
|
last_vertex = pc;
|
|
current_color = color;
|
|
last_pick_color_idx = cgo->current_pick_color_index;
|
|
last_pick_color_bnd = cgo->current_pick_color_bond;
|
|
}
|
|
nverts++;
|
|
break;
|
|
case CGO_LINE: {
|
|
float axis[3];
|
|
auto line = reinterpret_cast<const cgo::draw::line*>(pc);
|
|
subtract3f(line->vertex2, line->vertex1, axis);
|
|
cgo->add<cgo::draw::shadercylinder>(
|
|
line->vertex1, axis, 1.f, cCylShaderBothCapsRound);
|
|
tot_ncyls++;
|
|
} break;
|
|
case CGO_SPLITLINE: {
|
|
float axis[3];
|
|
auto splitline = reinterpret_cast<const cgo::draw::splitline*>(pc);
|
|
Pickable pickcolor2 = {splitline->index, splitline->bond};
|
|
float color2[] = {CONVERT_COLOR_VALUE(splitline->color2[0]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[1]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[2])};
|
|
unsigned char flags = splitline->flags;
|
|
subtract3f(splitline->vertex2, splitline->vertex1, axis);
|
|
if ((flags & cgo::draw::splitline::equal_colors) &&
|
|
(flags & cgo::draw::splitline::no_split_for_pick)) {
|
|
cgo->add<cgo::draw::shadercylinder>(
|
|
splitline->vertex1, axis, 1., cCylShaderBothCapsRound);
|
|
} else {
|
|
int cap = cCylShaderBothCapsRound;
|
|
if (flags & splitline->flags &
|
|
cgo::draw::splitline::interpolation) {
|
|
cap |= cCylShaderInterpColor;
|
|
}
|
|
cgo->add<cgo::draw::shadercylinder2ndcolor>(
|
|
cgo, splitline->vertex1, axis, 1., cap, color2, &pickcolor2);
|
|
last_pick_color_idx = splitline->index;
|
|
last_pick_color_bnd = splitline->bond;
|
|
}
|
|
tot_ncyls++;
|
|
} break;
|
|
case CGO_COLOR:
|
|
if (op == CGO_COLOR) {
|
|
last_color = current_color;
|
|
current_color = pc;
|
|
color = pc;
|
|
}
|
|
case CGO_PICK_COLOR:
|
|
if (op == CGO_PICK_COLOR) {
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
}
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
|
|
tot_nverts += nverts;
|
|
} break;
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
CGOStop(cgo);
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
if (tot_ncyls) {
|
|
return (cgo);
|
|
} else {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
#ifdef WITH_UNUSED_FUNCTIONS
|
|
/* CGOSplitUpLinesForPicking: This operation goes through */
|
|
/* a CGO and returns a new CGO that has the same lines but */
|
|
/* a line that has two different pick colors will get split */
|
|
/* at its midpoint into two separate lines so that it can */
|
|
/* be used for picking */
|
|
CGO* CGOSplitUpLinesForPicking(const CGO* I)
|
|
{
|
|
auto G = I->G;
|
|
|
|
std::unique_ptr<CGO> cgo_managed(new CGO(G));
|
|
CGO* cgo = cgo_managed.get();
|
|
int tot_nverts = 0;
|
|
|
|
CGOBegin(cgo, GL_LINES);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_PICK_COLOR: {
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
} break;
|
|
case CGO_END:
|
|
case CGO_VERTEX:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return nullptr;
|
|
case CGO_BEGIN: {
|
|
const float *last_vertex = nullptr, *last_color = nullptr,
|
|
*current_color = nullptr, *color = nullptr;
|
|
unsigned int last_pick_color_idx = 0;
|
|
int last_pick_color_bnd = cPickableNoPick;
|
|
int nverts = 0;
|
|
const int mode = it.cast<cgo::draw::begin>()->mode;
|
|
|
|
for (++it;; ++it) {
|
|
if (it.is_stop()) {
|
|
WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
|
|
return nullptr;
|
|
}
|
|
|
|
const auto op = it.op_code();
|
|
if (op == CGO_END) {
|
|
break;
|
|
}
|
|
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_VERTEX:
|
|
if (last_vertex) {
|
|
switch (mode) {
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP: {
|
|
bool pick_color_diff = false;
|
|
pick_color_diff =
|
|
(cgo->current_pick_color_index != last_pick_color_idx ||
|
|
cgo->current_pick_color_bond != last_pick_color_bnd);
|
|
if (pick_color_diff ||
|
|
(last_color && current_color &&
|
|
(!equal3f(last_color, current_color)))) {
|
|
if (pick_color_diff) {
|
|
float haxis[3];
|
|
float mid[3];
|
|
uint curp_idx = cgo->current_pick_color_index;
|
|
int curp_bnd = cgo->current_pick_color_bond;
|
|
haxis[0] = .5f * (pc[0] - last_vertex[0]);
|
|
haxis[1] = .5f * (pc[1] - last_vertex[1]);
|
|
haxis[2] = .5f * (pc[2] - last_vertex[2]);
|
|
add3f(last_vertex, haxis, mid);
|
|
CGOPickColor(cgo, last_pick_color_idx, last_pick_color_bnd);
|
|
CGOVertexv(cgo, last_vertex);
|
|
CGOVertexv(cgo, mid);
|
|
CGOPickColor(cgo, curp_idx, curp_bnd);
|
|
CGOVertexv(cgo, mid);
|
|
CGOVertexv(cgo, pc);
|
|
} else {
|
|
CGOVertexv(cgo, last_vertex);
|
|
CGOVertexv(cgo, pc);
|
|
}
|
|
} else {
|
|
CGOVertexv(cgo, last_vertex);
|
|
CGOVertexv(cgo, pc);
|
|
}
|
|
last_vertex = pc;
|
|
last_pick_color_idx = cgo->current_pick_color_index;
|
|
last_pick_color_bnd = cgo->current_pick_color_bond;
|
|
}
|
|
if (mode == GL_LINES) {
|
|
last_vertex = nullptr;
|
|
last_color = nullptr;
|
|
}
|
|
}
|
|
} else {
|
|
last_vertex = pc;
|
|
current_color = color;
|
|
last_pick_color_idx = cgo->current_pick_color_index;
|
|
last_pick_color_bnd = cgo->current_pick_color_bond;
|
|
}
|
|
nverts++;
|
|
break;
|
|
case CGO_COLOR: {
|
|
last_color = current_color;
|
|
current_color = pc;
|
|
color = pc;
|
|
} break;
|
|
case CGO_PICK_COLOR: {
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
} break;
|
|
}
|
|
}
|
|
tot_nverts += nverts;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
if (!tot_nverts) {
|
|
return nullptr;
|
|
}
|
|
|
|
CGOEnd(cgo);
|
|
CGOStop(cgo);
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
|
|
return cgo_managed.release();
|
|
}
|
|
#endif
|
|
|
|
static void trilinesBufferAddVertex(float*& buffer,
|
|
const float* v1, // vertex
|
|
const float* v2, // vertex other end of line
|
|
const float* color, // RGB color
|
|
float alpha, // alpha
|
|
signed char uv) // uv
|
|
{
|
|
// vertex
|
|
(*buffer++) = v1[0];
|
|
(*buffer++) = v1[1];
|
|
(*buffer++) = v1[2];
|
|
|
|
// othervertex
|
|
(*buffer++) = v2[0];
|
|
(*buffer++) = v2[1];
|
|
(*buffer++) = v2[2];
|
|
|
|
(*buffer++) = (float) uv;
|
|
|
|
// RGBA
|
|
unsigned char* byte_view = (unsigned char*) (buffer++);
|
|
(*byte_view++) = CLIP_COLOR_VALUE(color[0]);
|
|
(*byte_view++) = CLIP_COLOR_VALUE(color[1]);
|
|
(*byte_view++) = CLIP_COLOR_VALUE(color[2]);
|
|
(*byte_view++) = CLIP_COLOR_VALUE(alpha);
|
|
}
|
|
|
|
static void trilinesBufferAddVertices(float*& buffer,
|
|
const float* v1, // vertex
|
|
const float* v2, // vertex other end of line
|
|
const float* color, // RGB color
|
|
float alpha) // alpha
|
|
{
|
|
// Vertex 1
|
|
trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 1); //-1, 1);
|
|
// Vertex 3
|
|
trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 3); // 1, 1);
|
|
// Vertex 2
|
|
trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 0); //-1, -1);
|
|
// Vertex 4
|
|
trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 3); // 1, 1);
|
|
// Vertex 3
|
|
trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 2); // 1, -1);
|
|
// Vertex 1
|
|
trilinesBufferAddVertex(buffer, v1, v2, color, alpha, 1); //-1, 1);
|
|
}
|
|
|
|
static void CGOTrilines_GetCurrentColor(const float*& current_color,
|
|
const float* colorv, const float* last_color, const float* cc)
|
|
{
|
|
if (!current_color) {
|
|
if (colorv) {
|
|
current_color = colorv;
|
|
} else if (last_color) {
|
|
current_color = last_color;
|
|
} else {
|
|
current_color = cc;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Changes
|
|
*
|
|
* CGO_ENABLE <frommode>
|
|
*
|
|
* to
|
|
*
|
|
* CGO_ENABLE <tomode>
|
|
*
|
|
* in place.
|
|
*/
|
|
void CGOChangeShadersTo(CGO* I, int frommode, int tomode)
|
|
{
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
if (it.op_code() == CGO_ENABLE) {
|
|
auto eo = it.cast<cgo::draw::enable>();
|
|
if (eo->mode == frommode) {
|
|
eo->mode = tomode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CGO* CGOOptimizeScreenTexturesAndPolygons(CGO* I, int est)
|
|
{
|
|
auto G = I->G;
|
|
int ok = true;
|
|
|
|
int num_total_indices = CGOCountNumVerticesForScreen(I);
|
|
if (num_total_indices <= 0)
|
|
return nullptr;
|
|
|
|
std::unique_ptr<CGO> cgo_managed(new CGO(G));
|
|
auto* const cgo = cgo_managed.get();
|
|
|
|
{
|
|
float *colorVals = 0, *texcoordVals;
|
|
int tot, nxtn;
|
|
uchar* colorValsUC = 0;
|
|
CGOAlpha(cgo, 1.f);
|
|
cgo->alpha = 1.f;
|
|
cgo->color[0] = 1.f;
|
|
cgo->color[1] = 1.f;
|
|
cgo->color[2] = 1.f;
|
|
|
|
{
|
|
int mul =
|
|
6; // 3 - screenoffset/vertex, 2 - texture coordinates, 1 - color
|
|
/*
|
|
if (SettingGetGlobal_i(I->G, cSetting_cgo_shader_ub_color)){
|
|
mul++;
|
|
} else {
|
|
mul += 4;
|
|
}*/
|
|
tot = num_total_indices * mul;
|
|
}
|
|
|
|
auto vertexVals_managed = std::vector<float>(tot);
|
|
float* vertexVals = vertexVals_managed.data();
|
|
texcoordVals = vertexVals + 3 * num_total_indices;
|
|
nxtn = 2;
|
|
colorVals = texcoordVals + nxtn * num_total_indices;
|
|
colorValsUC = (uchar*) colorVals;
|
|
nxtn = 1;
|
|
ok = CGOProcessScreenCGOtoArrays(
|
|
G, I, vertexVals, texcoordVals, colorVals, colorValsUC);
|
|
RETURN_VAL_IF_FAIL(ok && !G->Interrupt, nullptr);
|
|
if (ok) {
|
|
auto& vbo = CreateVertexBuffer(I->G);
|
|
BufferDataDesc dataDesc{{
|
|
BufferDesc("attr_screenoffset", VertexFormat::Float3,
|
|
sizeof(float) * num_total_indices * 3, vertexVals),
|
|
BufferDesc("attr_texcoords", VertexFormat::Float2,
|
|
sizeof(float) * num_total_indices * 2, texcoordVals),
|
|
BufferDesc("attr_backgroundcolor", VertexFormat::UByte4Norm,
|
|
sizeof(std::uint8_t) * num_total_indices * 4, colorValsUC)}};
|
|
SetVertexBufferData(I->G, vbo, std::move(dataDesc));
|
|
auto vboid = vbo.get_hash_id();
|
|
if (ok) {
|
|
CGOEnable(cgo, GL_SCREEN_SHADER);
|
|
cgo->add<cgo::draw::screen_textures>(num_total_indices, vboid);
|
|
if (ok)
|
|
ok &= CGODisable(cgo, GL_SCREEN_SHADER);
|
|
RETURN_VAL_IF_FAIL(ok, nullptr);
|
|
} else {
|
|
I->G->ShaderMgr->freeGPUBuffer(vboid);
|
|
}
|
|
}
|
|
cgo->use_shader = true;
|
|
}
|
|
|
|
return cgo_managed.release();
|
|
}
|
|
|
|
CGO* CGOColorByRamp(PyMOLGlobals* G, const CGO* I, ObjectGadgetRamp* ramp,
|
|
int state, CSetting* set1)
|
|
{
|
|
if (!I) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto cgo = CGONewSized(G, 0);
|
|
if (!cgo) {
|
|
return nullptr;
|
|
}
|
|
|
|
int ok = true;
|
|
float white[3] = {1.f, 1.f, 1.f};
|
|
float probe_radius = SettingGet_f(G, set1, nullptr, cSetting_solvent_radius);
|
|
float v_above[3], n0[3] = {0.f, 0.f, 0.f};
|
|
int ramp_above =
|
|
SettingGet_i(G, set1, nullptr, cSetting_surface_ramp_above_mode) == 1;
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
bool skipCopy = false;
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS: {
|
|
const cgo::draw::arrays* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
float* vals =
|
|
cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
|
|
int nvals = sp->narrays * sp->nverts;
|
|
ok &= vals ? true : false;
|
|
if (ok)
|
|
memcpy(vals, sp->floatdata, nvals);
|
|
skipCopy = true;
|
|
} break;
|
|
case CGO_NORMAL:
|
|
copy3f(pc, n0);
|
|
break;
|
|
case CGO_VERTEX: {
|
|
float color[3];
|
|
copy3f(white, color);
|
|
if (ramp_above) {
|
|
copy3f(n0, v_above);
|
|
scale3f(v_above, probe_radius, v_above);
|
|
add3f(pc, v_above, v_above);
|
|
} else {
|
|
copy3f(pc, v_above);
|
|
}
|
|
if (ObjectGadgetRampInterVertex(ramp, v_above, color, state)) {
|
|
CGOColorv(cgo, color);
|
|
} else {
|
|
CGOColorv(cgo, white);
|
|
}
|
|
} break;
|
|
}
|
|
if (!skipCopy) {
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
if (ok) {
|
|
ok &= CGOStop(cgo);
|
|
if (ok) {
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
}
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
/**
|
|
* FIXME: This function always returns true for `checkOpaque=ture`
|
|
*/
|
|
int CGOHasTransparency(const CGO* I, bool checkTransp, bool checkOpaque)
|
|
{
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
if (it.op_code() == CGO_ALPHA) {
|
|
const auto pc = it.data();
|
|
if (checkTransp && *pc < 1.f)
|
|
return 1;
|
|
if (checkOpaque && *pc == 1.f)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return checkOpaque;
|
|
}
|
|
|
|
CGO* CGOConvertTrianglesToAlpha(const CGO* I)
|
|
{
|
|
int tot_nverts = 0;
|
|
|
|
CGO* cgo = CGONewSized(I->G, I->c);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS: {
|
|
const cgo::draw::arrays* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
const int mode = sp->mode, arrays = sp->arraybits, nverts = sp->nverts;
|
|
const float* nxtVals = sp->floatdata;
|
|
|
|
assert(arrays & CGO_VERTEX_ARRAY);
|
|
const float* vertexValsDA = nxtVals;
|
|
nxtVals += VERTEX_POS_SIZE * nverts;
|
|
|
|
const float* normalValsDA = nullptr;
|
|
if (arrays & CGO_NORMAL_ARRAY) {
|
|
normalValsDA = nxtVals;
|
|
nxtVals += VERTEX_NORMAL_SIZE * nverts;
|
|
}
|
|
|
|
const float* colorValsDA = nullptr;
|
|
if (arrays & CGO_COLOR_ARRAY) {
|
|
colorValsDA = nxtVals;
|
|
nxtVals += VERTEX_COLOR_SIZE * nverts;
|
|
}
|
|
|
|
const float* const vertexVals0 = vertexValsDA;
|
|
const float* const normalVals0 = normalValsDA;
|
|
const float* const colorVals0 = colorValsDA;
|
|
|
|
switch (mode) {
|
|
case GL_TRIANGLES: {
|
|
for (int cnt = 0; cnt < nverts; cnt += 3) {
|
|
if (colorVals0) {
|
|
CGOAlphaTriangle(cgo, vertexValsDA, vertexValsDA + 3,
|
|
vertexValsDA + 6, normalValsDA, normalValsDA + 3,
|
|
normalValsDA + 6, colorValsDA, colorValsDA + 4, colorValsDA + 8,
|
|
*(colorValsDA + 3), *(colorValsDA + 7), *(colorValsDA + 11), 0);
|
|
} else {
|
|
CGOAlphaTriangle(cgo, vertexValsDA, vertexValsDA + 3,
|
|
vertexValsDA + 6, normalValsDA, normalValsDA + 3,
|
|
normalValsDA + 6, cgo->color, cgo->color, cgo->color,
|
|
cgo->alpha, cgo->alpha, cgo->alpha, 0);
|
|
}
|
|
vertexValsDA += 9;
|
|
normalValsDA += 9;
|
|
if (colorVals0)
|
|
colorValsDA += 12;
|
|
}
|
|
}
|
|
tot_nverts += nverts;
|
|
break;
|
|
case GL_TRIANGLE_STRIP: {
|
|
short flip = 0;
|
|
vertexValsDA += 6;
|
|
normalValsDA += 6;
|
|
if (colorVals0)
|
|
colorValsDA += 8;
|
|
|
|
for (int cnt = 2; cnt < nverts; cnt++) {
|
|
if (colorVals0) {
|
|
CGOAlphaTriangle(cgo, vertexValsDA - 6, vertexValsDA - 3,
|
|
vertexValsDA, normalValsDA - 6, normalValsDA - 3, normalValsDA,
|
|
colorValsDA - 8, colorValsDA - 4, colorValsDA,
|
|
*(colorValsDA - 5), *(colorValsDA - 1), *(colorValsDA + 3),
|
|
flip);
|
|
} else {
|
|
CGOAlphaTriangle(cgo, vertexValsDA - 6, vertexValsDA - 3,
|
|
vertexValsDA, normalValsDA - 6, normalValsDA - 3, normalValsDA,
|
|
cgo->color, cgo->color, cgo->color, cgo->alpha, cgo->alpha,
|
|
cgo->alpha, flip);
|
|
}
|
|
vertexValsDA += 3;
|
|
normalValsDA += 3;
|
|
if (colorVals0)
|
|
colorValsDA += 4;
|
|
flip = !flip;
|
|
}
|
|
}
|
|
tot_nverts += nverts;
|
|
break;
|
|
case GL_TRIANGLE_FAN: {
|
|
vertexValsDA += 6;
|
|
normalValsDA += 6;
|
|
if (colorVals0)
|
|
colorValsDA += 8;
|
|
for (int cnt = 2; cnt < nverts; cnt++) {
|
|
if (colorVals0) {
|
|
CGOAlphaTriangle(cgo, vertexVals0, vertexValsDA - 3, vertexValsDA,
|
|
normalVals0, normalValsDA - 3, normalValsDA, colorVals0,
|
|
colorValsDA - 4, colorValsDA, *(colorVals0 + 3),
|
|
*(colorValsDA - 1), *(colorValsDA + 3), 0);
|
|
} else {
|
|
CGOAlphaTriangle(cgo, vertexVals0, vertexValsDA - 3, vertexValsDA,
|
|
normalVals0, normalValsDA - 3, normalValsDA, cgo->color,
|
|
cgo->color, cgo->color, cgo->alpha, cgo->alpha, cgo->alpha, 0);
|
|
}
|
|
vertexValsDA += 3;
|
|
normalValsDA += 3;
|
|
if (colorVals0)
|
|
colorValsDA += 4;
|
|
}
|
|
}
|
|
tot_nverts += nverts;
|
|
break;
|
|
}
|
|
} break;
|
|
case CGO_END:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOConvertTrianglesToAlpha: CGO_END encountered without CGO_BEGIN but "
|
|
"skipped for OpenGLES\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_VERTEX:
|
|
PRINTFB(I->G, FB_CGO, FB_Warnings)
|
|
" CGOConvertTrianglesToAlpha: CGO_VERTEX encountered without CGO_BEGIN "
|
|
"but skipped for OpenGLES\n" ENDFB(I->G);
|
|
break;
|
|
case CGO_BEGIN: {
|
|
float vertices[3][3], colors[4][3], normals[4][3], alpha[4];
|
|
short verticespl = 2, colorspl = 2, normalspl = 2, alphapl = 2;
|
|
short hasShifted = 0;
|
|
int nverts = 0, err = 0;
|
|
int mode = CGO_get_int(pc);
|
|
short mode_is_triangles = 0, flip = 0, mode_is_fan = 0;
|
|
copy3f(cgo->color, colors[3]);
|
|
copy3f(cgo->normal, normals[3]);
|
|
alpha[3] = cgo->alpha;
|
|
switch (mode) {
|
|
case GL_TRIANGLE_FAN:
|
|
mode_is_fan = 1;
|
|
case GL_TRIANGLES:
|
|
case GL_TRIANGLE_STRIP:
|
|
mode_is_triangles = 1;
|
|
}
|
|
if (!mode_is_triangles) {
|
|
CGOBegin(cgo, mode);
|
|
}
|
|
|
|
for (++it; !err && it != CGO_END; ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
short add_to_cgo = 1;
|
|
switch (op) {
|
|
case CGO_VERTEX:
|
|
if (mode_is_triangles) {
|
|
if (!(hasShifted & 1)) { // colors
|
|
if (colorspl >= 0) {
|
|
copy3f(colors[colorspl + 1], colors[colorspl]);
|
|
colorspl--;
|
|
} else {
|
|
if (!mode_is_fan)
|
|
copy3f(colors[1], colors[2]);
|
|
copy3f(colors[0], colors[1]);
|
|
}
|
|
}
|
|
if (!(hasShifted & 2)) { // normals
|
|
if (normalspl >= 0) {
|
|
copy3f(normals[normalspl + 1], normals[normalspl]);
|
|
normalspl--;
|
|
} else {
|
|
if (!mode_is_fan)
|
|
copy3f(normals[1], normals[2]);
|
|
copy3f(normals[0], normals[1]);
|
|
}
|
|
}
|
|
if (!(hasShifted & 4)) { // alphas
|
|
if (alphapl >= 0) {
|
|
alpha[alphapl] = alpha[alphapl + 1];
|
|
alphapl--;
|
|
} else {
|
|
if (!mode_is_fan)
|
|
alpha[2] = alpha[1];
|
|
alpha[1] = alpha[0];
|
|
}
|
|
}
|
|
if (verticespl >= 0) {
|
|
copy3f(pc, vertices[verticespl]);
|
|
verticespl--;
|
|
} else {
|
|
if (!mode_is_fan)
|
|
copy3f(vertices[1], vertices[2]);
|
|
copy3f(vertices[0], vertices[1]);
|
|
copy3f(pc, vertices[0]);
|
|
}
|
|
|
|
nverts++;
|
|
switch (mode) {
|
|
case GL_TRIANGLES:
|
|
if (!(nverts % 3)) {
|
|
CGOAlphaTriangle(cgo, vertices[2], vertices[1], vertices[0],
|
|
normals[2], normals[1], normals[0], colors[2], colors[1],
|
|
colors[0], alpha[2], alpha[1], alpha[0], 0);
|
|
}
|
|
break;
|
|
case GL_TRIANGLE_STRIP:
|
|
if (verticespl < 0) {
|
|
int off0, off2;
|
|
if (flip) {
|
|
off0 = 0;
|
|
off2 = 2;
|
|
} else {
|
|
off0 = 2;
|
|
off2 = 0;
|
|
}
|
|
flip = !flip;
|
|
CGOAlphaTriangle(cgo, vertices[off0], vertices[1],
|
|
vertices[off2], normals[off0], normals[1], normals[off2],
|
|
colors[off0], colors[1], colors[off2], alpha[off0],
|
|
alpha[1], alpha[off2], 0);
|
|
}
|
|
break;
|
|
case GL_TRIANGLE_FAN:
|
|
if (verticespl < 0) {
|
|
CGOAlphaTriangle(cgo, vertices[2], vertices[1], vertices[0],
|
|
normals[2], normals[1], normals[0], colors[2], colors[1],
|
|
colors[0], alpha[2], alpha[1], alpha[0], 0);
|
|
}
|
|
}
|
|
add_to_cgo = !mode_is_triangles;
|
|
hasShifted = 0;
|
|
} else {
|
|
add_to_cgo = 1;
|
|
}
|
|
case CGO_COLOR:
|
|
if (op == CGO_COLOR) {
|
|
add_to_cgo = !mode_is_triangles;
|
|
if (mode_is_triangles) {
|
|
if (colorspl >= 0) {
|
|
copy3f(pc, colors[colorspl]);
|
|
colorspl--;
|
|
} else {
|
|
if (!mode_is_fan)
|
|
copy3f(colors[1], colors[2]);
|
|
copy3f(colors[0], colors[1]);
|
|
copy3f(pc, colors[0]);
|
|
}
|
|
hasShifted |= 1;
|
|
}
|
|
}
|
|
case CGO_NORMAL:
|
|
if (op == CGO_NORMAL) {
|
|
add_to_cgo = !mode_is_triangles;
|
|
if (mode_is_triangles) {
|
|
if (normalspl >= 0) {
|
|
copy3f(pc, normals[normalspl]);
|
|
normalspl--;
|
|
} else {
|
|
if (!mode_is_fan)
|
|
copy3f(normals[1], normals[2]);
|
|
copy3f(normals[0], normals[1]);
|
|
copy3f(pc, normals[0]);
|
|
}
|
|
hasShifted |= 2;
|
|
}
|
|
}
|
|
case CGO_ALPHA:
|
|
if (op == CGO_ALPHA) {
|
|
add_to_cgo = !mode_is_triangles;
|
|
if (mode_is_triangles) {
|
|
if (alphapl >= 0)
|
|
alpha[alphapl--] = *pc;
|
|
else {
|
|
if (!mode_is_fan)
|
|
alpha[2] = alpha[1];
|
|
alpha[1] = alpha[0];
|
|
alpha[0] = *pc;
|
|
}
|
|
hasShifted |= 4;
|
|
}
|
|
}
|
|
default:
|
|
if (add_to_cgo) {
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!mode_is_triangles) {
|
|
CGOEnd(cgo);
|
|
}
|
|
|
|
tot_nverts += nverts;
|
|
} break;
|
|
case CGO_COLOR:
|
|
if (op == CGO_COLOR) {
|
|
copy3f(pc, cgo->color);
|
|
}
|
|
case CGO_NORMAL:
|
|
if (op == CGO_NORMAL) {
|
|
copy3f(pc, cgo->normal);
|
|
}
|
|
case CGO_ALPHA:
|
|
if (op == CGO_ALPHA) {
|
|
cgo->alpha = *pc;
|
|
}
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
CGOStop(cgo);
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
if (tot_nverts) {
|
|
return (cgo);
|
|
} else {
|
|
CGOFree(cgo);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts TRIANGLE(_STRIP|_FAN) to TRIANGLES and generates
|
|
* normals for all triangles. Discards any existing normals for
|
|
* triangles.
|
|
*
|
|
* I: primitive CGO
|
|
* return: new primitive CGO with normals on triangles
|
|
*/
|
|
CGO* CGOGenerateNormalsForTriangles(const CGO* I)
|
|
{
|
|
auto G = I->G;
|
|
auto cgo = CGONewSized(G, I->c);
|
|
|
|
float vertices[3][3];
|
|
float current_color[3] = {0.f, 0.f, 0.f}, colors[3][3];
|
|
float current_normal[3];
|
|
float current_alpha = 0, alphas[3];
|
|
|
|
bool has_alpha = false;
|
|
bool has_color = false;
|
|
|
|
int mode = 0;
|
|
bool inside_begin_triangles = false;
|
|
int current_i = 0;
|
|
int vertex_count = 0;
|
|
bool flip = false;
|
|
bool emit;
|
|
|
|
const int indices_regular[] = {0, 1, 2};
|
|
const int indices_flipped[] = {0, 2, 1};
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
auto op = it.op_code();
|
|
|
|
if (op == CGO_BEGIN) {
|
|
mode = *reinterpret_cast<const int*>(pc);
|
|
|
|
switch (mode) {
|
|
case GL_TRIANGLE_STRIP:
|
|
case GL_TRIANGLE_FAN:
|
|
case GL_TRIANGLES:
|
|
current_i = 0;
|
|
vertex_count = 0;
|
|
flip = false;
|
|
inside_begin_triangles = true;
|
|
|
|
CGOBegin(cgo, GL_TRIANGLES);
|
|
continue; // for-loop, no add_to_cgo
|
|
}
|
|
|
|
inside_begin_triangles = false;
|
|
} else if (op == CGO_END) {
|
|
inside_begin_triangles = false;
|
|
}
|
|
|
|
if (!inside_begin_triangles) {
|
|
cgo->add_to_cgo(op, pc);
|
|
continue;
|
|
}
|
|
|
|
// handle operations inside BEGIN/END TRIANGLE(S|_STRIP|_FAN)
|
|
switch (op) {
|
|
case CGO_VERTEX:
|
|
copy3(reinterpret_cast<const float*>(pc), vertices[current_i]);
|
|
copy3(current_color, colors[current_i]);
|
|
alphas[current_i] = current_alpha;
|
|
|
|
++vertex_count;
|
|
|
|
switch (mode) {
|
|
case GL_TRIANGLE_STRIP:
|
|
current_i = vertex_count % 3;
|
|
emit = (vertex_count > 2);
|
|
break;
|
|
case GL_TRIANGLE_FAN:
|
|
current_i = ((vertex_count + 1) % 2) + 1;
|
|
emit = (vertex_count > 2);
|
|
break;
|
|
default:
|
|
current_i = vertex_count % 3;
|
|
emit = (current_i == 0);
|
|
}
|
|
|
|
if (emit) {
|
|
auto* indices = flip ? indices_flipped : indices_regular;
|
|
|
|
if (mode != GL_TRIANGLES) {
|
|
flip = !flip;
|
|
}
|
|
|
|
CalculateTriangleNormal(vertices[0], vertices[indices[1]],
|
|
vertices[indices[2]], current_normal);
|
|
|
|
CGONormalv(cgo, current_normal);
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
int k = indices[j];
|
|
if (has_color)
|
|
CGOColorv(cgo, colors[k]);
|
|
if (has_alpha)
|
|
CGOAlpha(cgo, alphas[k]);
|
|
CGOVertexv(cgo, vertices[k]);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case CGO_COLOR:
|
|
copy3(reinterpret_cast<const float*>(pc), current_color);
|
|
has_color = true;
|
|
break;
|
|
case CGO_ALPHA:
|
|
current_alpha = *reinterpret_cast<const float*>(pc);
|
|
has_alpha = true;
|
|
break;
|
|
case CGO_NORMAL:
|
|
// discard, we will generate new normals
|
|
break;
|
|
default:
|
|
PRINTFB(G, FB_CGO, FB_Warnings)
|
|
" CGO-Warning: CGOGenerateNormalsForTriangles: unhandled op=0x%02x "
|
|
"inside BEGIN/END\n",
|
|
op ENDFB(G);
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
|
|
CGOStop(cgo);
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
CGO* CGOTurnLightingOnLinesOff(const CGO* I, bool use_shader)
|
|
{
|
|
bool cur_mode_is_lines = false;
|
|
auto cgo = CGONewSized(I->G, I->c);
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS: {
|
|
const cgo::draw::arrays* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
float* vals;
|
|
int nvals = sp->narrays * sp->nverts;
|
|
switch (sp->mode) {
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP:
|
|
CGODisable(cgo, CGO_GL_LIGHTING);
|
|
cur_mode_is_lines = true;
|
|
}
|
|
vals = cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
|
|
memcpy(vals, sp->floatdata, nvals);
|
|
if (cur_mode_is_lines) {
|
|
CGOEnable(cgo, CGO_GL_LIGHTING);
|
|
cur_mode_is_lines = false;
|
|
}
|
|
} break;
|
|
case CGO_DRAW_BUFFERS_INDEXED: {
|
|
const cgo::draw::buffers_indexed* sp = reinterpret_cast<decltype(sp)>(pc);
|
|
int mode = sp->mode, mode_is_lines = 0;
|
|
switch (mode) {
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP:
|
|
mode_is_lines = true;
|
|
}
|
|
if (mode_is_lines) {
|
|
CGODisable(cgo, CGO_GL_LIGHTING);
|
|
}
|
|
cgo->copy_op_from<cgo::draw::buffers_indexed>(pc);
|
|
if (mode_is_lines) {
|
|
CGOEnable(cgo, CGO_GL_LIGHTING);
|
|
}
|
|
} break;
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED: {
|
|
const cgo::draw::buffers_not_indexed* sp =
|
|
reinterpret_cast<decltype(sp)>(pc);
|
|
int mode = sp->mode, mode_is_lines = 0;
|
|
switch (mode) {
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP:
|
|
mode_is_lines = true;
|
|
}
|
|
if (mode_is_lines) {
|
|
CGODisable(cgo, CGO_GL_LIGHTING);
|
|
}
|
|
cgo->copy_op_from<cgo::draw::buffers_not_indexed>(pc);
|
|
if (mode_is_lines) {
|
|
CGOEnable(cgo, CGO_GL_LIGHTING);
|
|
}
|
|
} break;
|
|
case CGO_END: {
|
|
CGOEnd(cgo);
|
|
if (cur_mode_is_lines) {
|
|
CGOEnable(cgo, CGO_GL_LIGHTING);
|
|
cur_mode_is_lines = 0;
|
|
}
|
|
} break;
|
|
case CGO_BEGIN: {
|
|
int mode = CGO_get_int(pc);
|
|
switch (mode) {
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP:
|
|
CGODisable(cgo, CGO_GL_LIGHTING);
|
|
cur_mode_is_lines = true;
|
|
break;
|
|
default:
|
|
if (!use_shader) { // no shaders, not lines, turn lighting on
|
|
CGOEnable(cgo, CGO_GL_LIGHTING);
|
|
}
|
|
}
|
|
CGOBegin(cgo, mode);
|
|
} break;
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
}
|
|
cgo->use_shader = use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
bool CGOHasAnyTriangleVerticesWithoutNormals(const CGO* I, bool checkTriangles)
|
|
{
|
|
bool inside = false;
|
|
bool hasNormal = false;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_BEGIN:
|
|
switch (CGO_get_int(pc)) {
|
|
case GL_TRIANGLE_FAN:
|
|
case GL_TRIANGLES:
|
|
case GL_TRIANGLE_STRIP:
|
|
if (checkTriangles)
|
|
inside = 1;
|
|
break;
|
|
case GL_LINE_STRIP:
|
|
case GL_LINES:
|
|
if (!checkTriangles)
|
|
inside = 1;
|
|
break;
|
|
}
|
|
break;
|
|
case CGO_END:
|
|
inside = 0;
|
|
break;
|
|
case CGO_NORMAL:
|
|
hasNormal = 1;
|
|
break;
|
|
case CGO_VERTEX:
|
|
if (inside && !hasNormal)
|
|
return 1;
|
|
break;
|
|
case CGO_DRAW_ARRAYS: {
|
|
const auto sp = it.cast<cgo::draw::arrays>();
|
|
switch (sp->mode) {
|
|
case GL_TRIANGLE_FAN:
|
|
case GL_TRIANGLES:
|
|
case GL_TRIANGLE_STRIP:
|
|
if (checkTriangles) {
|
|
if (!(sp->arraybits & CGO_NORMAL_ARRAY)) {
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
case GL_LINE_STRIP:
|
|
case GL_LINES:
|
|
if (!checkTriangles) {
|
|
if (!(sp->arraybits & CGO_NORMAL_ARRAY)) {
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CGO::add_to_cgo(int op, const float* pc)
|
|
{
|
|
switch (op) {
|
|
case CGO_STOP:
|
|
// only append to buffer, don't increment size
|
|
CGOStop(this);
|
|
break;
|
|
case CGO_DRAW_ARRAYS:
|
|
copy_op_from<cgo::draw::arrays>(pc);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
copy_op_from<cgo::draw::buffers_indexed>(pc);
|
|
break;
|
|
case CGO_DRAW_TEXTURES:
|
|
copy_op_from<cgo::draw::textures>(pc);
|
|
break;
|
|
case CGO_DRAW_SCREEN_TEXTURES_AND_POLYGONS:
|
|
copy_op_from<cgo::draw::screen_textures>(pc);
|
|
break;
|
|
case CGO_DRAW_LABELS:
|
|
copy_op_from<cgo::draw::labels>(pc);
|
|
break;
|
|
case CGO_DRAW_CONNECTORS:
|
|
copy_op_from<cgo::draw::connectors>(pc);
|
|
break;
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
copy_op_from<cgo::draw::buffers_not_indexed>(pc);
|
|
break;
|
|
case CGO_DRAW_SPHERE_BUFFERS:
|
|
copy_op_from<cgo::draw::sphere_buffers>(pc);
|
|
break;
|
|
case CGO_DRAW_CYLINDER_BUFFERS:
|
|
copy_op_from<cgo::draw::cylinder_buffers>(pc);
|
|
break;
|
|
case CGO_DRAW_CUSTOM:
|
|
copy_op_from<cgo::draw::custom>(pc);
|
|
break;
|
|
default:
|
|
int sz = CGO_sz[op];
|
|
std::copy_n(pc - 1, sz + 1, add_to_buffer(sz + 1));
|
|
};
|
|
}
|
|
|
|
void CGO::print_table() const
|
|
{
|
|
display_table_t table;
|
|
table.begin_row()
|
|
.insert_cell("#")
|
|
.insert_cell("OP_CODE")
|
|
.insert_cell("OP_SZ")
|
|
.insert_cell("DATA");
|
|
|
|
unsigned j = 0;
|
|
for (auto it = begin(); !it.is_stop(); ++it) {
|
|
const auto pc = it.data();
|
|
const auto op_code = it.op_code();
|
|
|
|
table.begin_row().insert_cell(++j).insert_cell(op_code).insert_cell(
|
|
CGO_sz[op_code]);
|
|
int sz = CGO_sz[op_code];
|
|
std::stringstream ss;
|
|
for (int i = 0; i < sz; i++) {
|
|
ss << CGO_get_int(pc + i);
|
|
if (i != (sz - 1))
|
|
ss << ", ";
|
|
}
|
|
table.insert_cell(ss.str());
|
|
}
|
|
table.display();
|
|
}
|
|
|
|
CGO* CGOConvertSpheresToPoints(const CGO* I)
|
|
{
|
|
CGO* cgo;
|
|
|
|
int ok = true;
|
|
cgo = CGONew(I->G);
|
|
CHECKOK(ok, cgo);
|
|
CGOBegin(cgo, GL_POINTS);
|
|
|
|
for (auto it = I->begin(); ok && !it.is_stop(); ++it) {
|
|
const auto pc = it.data();
|
|
const auto op = it.op_code();
|
|
|
|
switch (op) {
|
|
case CGO_PICK_COLOR:
|
|
cgo->current_pick_color_index = CGO_get_uint(pc);
|
|
cgo->current_pick_color_bond = CGO_get_int(pc + 1);
|
|
CGOPickColor(
|
|
cgo, cgo->current_pick_color_index, cgo->current_pick_color_bond);
|
|
break;
|
|
case CGO_SHADER_CYLINDER:
|
|
case CGO_SHADER_CYLINDER_WITH_2ND_COLOR:
|
|
case CGO_CYLINDER:
|
|
case CGO_CONE:
|
|
case CGO_SAUSAGE:
|
|
case CGO_CUSTOM_CYLINDER:
|
|
case CGO_CUSTOM_CYLINDER_ALPHA:
|
|
case CGO_END:
|
|
case CGO_VERTEX:
|
|
case CGO_BEGIN:
|
|
case CGO_ELLIPSOID:
|
|
case CGO_QUADRIC:
|
|
case CGO_DRAW_BUFFERS_INDEXED:
|
|
case CGO_DRAW_BUFFERS_NOT_INDEXED:
|
|
case CGO_DRAW_SPHERE_BUFFERS:
|
|
case CGO_DRAW_CYLINDER_BUFFERS:
|
|
case CGO_DRAW_LABELS:
|
|
break;
|
|
case CGO_SPHERE:
|
|
CGOVertexv(cgo, pc);
|
|
break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
default:
|
|
cgo->add_to_cgo(op, pc);
|
|
}
|
|
ok &= !I->G->Interrupt;
|
|
}
|
|
CGOEnd(cgo);
|
|
if (ok) {
|
|
ok &= CGOStop(cgo);
|
|
}
|
|
if (!ok) {
|
|
CGOFree(cgo);
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
// Will create an interleaved VBO with { vertex, otherVertex, uv, and texcoord
|
|
// info } Currently, this function does not support/parse lines inside
|
|
// CGODrawArrays, i.e., a CGO that CGOCombineBeginEnd was used on
|
|
CGO* CGOConvertLinesToTrilines(const CGO* I, bool addshaders)
|
|
{
|
|
static std::set<int> lineops = {CGO_VERTEX, CGO_LINE, CGO_SPLITLINE};
|
|
auto G = I->G;
|
|
const int nLines = CGOCountNumberOfOperationsOfTypeN(I, lineops) + 1;
|
|
|
|
if (nLines == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
int line_counter = 0;
|
|
GLuint glbuff = 0;
|
|
const float* colorv = nullptr;
|
|
unsigned int buff_size = nLines * 6 * (8 * sizeof(float));
|
|
|
|
// VBO memory -- number of lines x 4 vertices per line x (vertex + otherVertex
|
|
// + normal + texCoord + color)
|
|
std::vector<float> buffer_start(buff_size);
|
|
float* buffer = buffer_start.data();
|
|
|
|
std::unique_ptr<CGO> cgo(new CGO(G));
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
const auto op = it.op_code();
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_DRAW_ARRAYS: {
|
|
auto sp = it.cast<cgo::draw::arrays>();
|
|
float* vals =
|
|
cgo->add<cgo::draw::arrays>(sp->mode, sp->arraybits, sp->nverts);
|
|
int nvals = sp->narrays * sp->nverts;
|
|
memcpy(vals, sp->floatdata, nvals);
|
|
} break;
|
|
case CGO_END:
|
|
WARN_UNEXPECTED_OPERATION(G, op);
|
|
return nullptr;
|
|
case CGO_BEGIN: {
|
|
const float *last_vertex = nullptr, *last_color = nullptr,
|
|
*current_color = nullptr, *color = nullptr;
|
|
const int mode = it.cast<cgo::draw::begin>()->mode;
|
|
|
|
for (++it;; ++it) {
|
|
if (it.is_stop()) {
|
|
WARN_UNEXPECTED_OPERATION(G, CGO_STOP);
|
|
return nullptr;
|
|
}
|
|
|
|
const auto op = it.op_code();
|
|
if (op == CGO_END) {
|
|
break;
|
|
}
|
|
|
|
const auto pc = it.data();
|
|
|
|
switch (op) {
|
|
case CGO_VERTEX:
|
|
if (last_vertex) {
|
|
switch (mode) {
|
|
case GL_LINES:
|
|
case GL_LINE_STRIP: {
|
|
float cc[3] = {1, 1, 1};
|
|
float alpha = cgo->alpha;
|
|
CGOTrilines_GetCurrentColor(
|
|
current_color, colorv, last_color, cc);
|
|
trilinesBufferAddVertices(
|
|
buffer, pc, last_vertex, current_color, alpha);
|
|
line_counter++;
|
|
last_vertex = pc;
|
|
}
|
|
if (mode == GL_LINES) {
|
|
last_vertex = nullptr;
|
|
last_color = nullptr;
|
|
}
|
|
}
|
|
} else {
|
|
last_vertex = pc;
|
|
current_color = color;
|
|
}
|
|
break;
|
|
case CGO_LINE: {
|
|
auto line = it.cast<cgo::draw::line>();
|
|
float cc[3] = {1, 1, 1};
|
|
float alpha = cgo->alpha;
|
|
CGOTrilines_GetCurrentColor(current_color, colorv, last_color, cc);
|
|
trilinesBufferAddVertices(
|
|
buffer, line->vertex1, line->vertex2, current_color, alpha);
|
|
line_counter++;
|
|
} break;
|
|
case CGO_SPLITLINE: {
|
|
auto splitline = it.cast<cgo::draw::splitline>();
|
|
float cc[3] = {1, 1, 1};
|
|
float alpha = cgo->alpha;
|
|
float mid[3];
|
|
float color2[] = {CONVERT_COLOR_VALUE(splitline->color2[0]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[1]),
|
|
CONVERT_COLOR_VALUE(splitline->color2[2])};
|
|
add3f(splitline->vertex1, splitline->vertex2, mid);
|
|
mult3f(mid, .5f, mid);
|
|
CGOTrilines_GetCurrentColor(current_color, colorv, last_color, cc);
|
|
trilinesBufferAddVertices(
|
|
buffer, splitline->vertex1, mid, current_color, alpha);
|
|
trilinesBufferAddVertices(
|
|
buffer, mid, splitline->vertex2, color2, alpha);
|
|
line_counter += 2;
|
|
} break;
|
|
case CGO_COLOR:
|
|
last_color = current_color;
|
|
current_color = pc;
|
|
color = pc;
|
|
break;
|
|
}
|
|
}
|
|
} break;
|
|
case CGO_ALPHA:
|
|
cgo->alpha = *pc;
|
|
break;
|
|
case CGO_COLOR:
|
|
colorv = pc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
cgo->use_shader = I->use_shader;
|
|
if (cgo->use_shader) {
|
|
cgo->cgo_shader_ub_color =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_color);
|
|
cgo->cgo_shader_ub_normal =
|
|
SettingGetGlobal_i(cgo->G, cSetting_cgo_shader_ub_normal);
|
|
}
|
|
|
|
{
|
|
/* TODO: Rmove GL calls and use Generic Vertex Buffers */
|
|
glGenBuffers(1, &glbuff);
|
|
glBindBuffer(GL_ARRAY_BUFFER, glbuff);
|
|
glBufferData(GL_ARRAY_BUFFER, line_counter * 6 * 8 * sizeof(float),
|
|
buffer_start.data(), GL_STATIC_DRAW);
|
|
CheckGLErrorOK(G, "ERROR: CGOConvertLinesToTriangleStrips() glBindBuffer "
|
|
"returns err=%d\n");
|
|
if (addshaders)
|
|
cgo->add<cgo::draw::enable>(GL_TRILINES_SHADER);
|
|
cgo->add<cgo::draw::trilines>(line_counter * 6, glbuff);
|
|
cgo->has_draw_buffers = true;
|
|
if (addshaders)
|
|
cgo->add<cgo::draw::disable>(GL_TRILINES_SHADER);
|
|
CGOStop(cgo.get());
|
|
}
|
|
|
|
return cgo.release();
|
|
}
|
|
|
|
/**
|
|
* copies data for a particular attribute operation into the array used to load
|
|
* the VBO. this takes into account whether it is interleaved or not.
|
|
*
|
|
* isInterleaved : whether the VBO is interleaved
|
|
* nvert : which vertex in the VBO
|
|
* attribOp : the attribute op
|
|
* vertexDataSize : total vertex data size in VBO (for interleaved)
|
|
* dataPtrs : all data pointers for attributes (for interleaved, they
|
|
* are all the pointer to the one array) attrOffset : offsets of the
|
|
* attributes (for interleaved) pcarg : pc pointer to CGO operation
|
|
* data pick_data : pointer to pick data for current vertex (writes to if
|
|
* pick data) has_pick_colorBS : keeps track of which pick attributes have been
|
|
* set
|
|
*
|
|
*/
|
|
static void copyAttributeForOp(bool isInterleaved, int& nvert,
|
|
AttribOp* attribOp, int vertexDataSize, std::vector<void*>& dataPtrs,
|
|
std::vector<int>& attrOffset, const float* pcarg, float* pick_data,
|
|
int& has_pick_colorBS, int pstride)
|
|
{
|
|
auto attrDesc = attribOp->desc;
|
|
int ord = attrDesc->order;
|
|
int copyord = -1;
|
|
void* dataPtr = dataPtrs[ord];
|
|
unsigned char* pc = ((unsigned char*) pcarg) + attribOp->offset;
|
|
if (isInterleaved) {
|
|
dataPtr =
|
|
(unsigned char*) dataPtr + nvert * vertexDataSize + attrOffset[ord];
|
|
if (attribOp->copyAttribDesc) {
|
|
copyord = attribOp->copyAttribDesc->order;
|
|
pc = ((unsigned char*) dataPtrs[ord]) + nvert * vertexDataSize +
|
|
attrOffset[copyord];
|
|
}
|
|
} else {
|
|
auto sz = GetSizeOfVertexFormat(attrDesc->m_format);
|
|
dataPtr = (unsigned char*) dataPtr + nvert * sz;
|
|
if (attribOp->copyAttribDesc) {
|
|
copyord = attribOp->copyAttribDesc->order;
|
|
auto copysz = GetSizeOfVertexFormat(attribOp->copyAttribDesc->m_format);
|
|
pc = (unsigned char*) dataPtr + nvert * copysz;
|
|
}
|
|
}
|
|
switch (attribOp->conv_type) {
|
|
case NO_COPY:
|
|
break;
|
|
case FLOAT_TO_FLOAT:
|
|
*((float*) dataPtr) = *((float*) pc);
|
|
break;
|
|
case FLOAT2_TO_FLOAT2:
|
|
*((float*) dataPtr) = *((float*) pc);
|
|
*((float*) dataPtr + 1) = *((float*) pc + 1);
|
|
break;
|
|
case FLOAT3_TO_FLOAT3:
|
|
copy3f((float*) pc, (float*) dataPtr);
|
|
break;
|
|
case FLOAT4_TO_FLOAT4:
|
|
*((float*) dataPtr) = *((float*) pc);
|
|
*((float*) dataPtr + 1) = *((float*) pc + 1);
|
|
*((float*) dataPtr + 2) = *((float*) pc + 2);
|
|
*((float*) dataPtr + 3) = *((float*) pc + 3);
|
|
break;
|
|
case FLOAT3_TO_UB3: {
|
|
auto dataPtrUB = (unsigned char*) dataPtr;
|
|
float* pcf = (float*) pc;
|
|
dataPtrUB[0] = CLIP_COLOR_VALUE(pcf[0]);
|
|
dataPtrUB[1] = CLIP_COLOR_VALUE(pcf[1]);
|
|
dataPtrUB[2] = CLIP_COLOR_VALUE(pcf[2]);
|
|
} break;
|
|
case FLOAT1_TO_UB_4TH: {
|
|
auto dataPtrUB = (unsigned char*) dataPtr;
|
|
float* pcf = (float*) pc;
|
|
dataPtrUB[3] = CLIP_COLOR_VALUE(pcf[0]);
|
|
} break;
|
|
case UB3_TO_UB3: {
|
|
auto dataPtrUB = (unsigned char*) dataPtr;
|
|
auto pcUB = (unsigned char*) pc;
|
|
dataPtrUB[0] = pcUB[0];
|
|
dataPtrUB[1] = pcUB[1];
|
|
dataPtrUB[2] = pcUB[2];
|
|
break;
|
|
}
|
|
case UINT_INT_TO_PICK_DATA: {
|
|
float* pcf = (float*) pc;
|
|
unsigned int index = CGO_get_uint(pcf);
|
|
int bond = CGO_get_int(pcf + 1);
|
|
CGO_put_uint(ord * 2 + pick_data, index);
|
|
CGO_put_int(ord * 2 + pick_data + 1, bond);
|
|
has_pick_colorBS |= (1 << ord);
|
|
} break;
|
|
case FLOAT4_TO_UB4: {
|
|
auto dataPtrUB = (unsigned char*) dataPtr;
|
|
float* pcf = (float*) pc;
|
|
dataPtrUB[0] = CLIP_COLOR_VALUE(pcf[0]);
|
|
dataPtrUB[1] = CLIP_COLOR_VALUE(pcf[1]);
|
|
dataPtrUB[2] = CLIP_COLOR_VALUE(pcf[2]);
|
|
dataPtrUB[3] = CLIP_COLOR_VALUE(pcf[3]);
|
|
} break;
|
|
case CYL_CAP_TO_CAP: {
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
dataPtrUB[0] = *pc;
|
|
} break;
|
|
case CYL_CAPS_ARE_ROUND: {
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
dataPtrUB[0] = cCylShaderBothCapsRound | cCylShaderInterpColor;
|
|
} break;
|
|
case CYL_CAPS_ARE_FLAT: {
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
dataPtrUB[0] = cCylShaderBothCapsFlat | cCylShaderInterpColor;
|
|
} break;
|
|
case CYL_CAPS_ARE_CUSTOM: {
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
float* pcf = (float*) pc;
|
|
const cCylCap cap1 = static_cast<cCylCap>(int(pcf[0]));
|
|
const cCylCap cap2 = static_cast<cCylCap>(int(pcf[1]));
|
|
dataPtrUB[0] =
|
|
cyl_shader_bits_from_caps(cap1, cap2) | cCylShaderInterpColor;
|
|
} break;
|
|
case UB1_TO_INTERP: {
|
|
bool interp = (pc[0] & cgo::draw::splitline::interpolation);
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
dataPtrUB[0] = interp ? 1 : 0;
|
|
} break;
|
|
case UB1_INTERP_TO_CAP: {
|
|
bool interp = (pc[0] & cgo::draw::splitline::interpolation);
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
dataPtrUB[0] =
|
|
(cCylShaderBothCapsRound | (interp ? cCylShaderInterpColor : 0));
|
|
} break;
|
|
case FLOAT1_TO_INTERP: {
|
|
float interp = *((float*) pc);
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
dataPtrUB[0] = (interp > .5f) ? 1 : 0;
|
|
} break;
|
|
case FLOAT1_INTERP_TO_CAP: {
|
|
float interp = *((float*) pc);
|
|
unsigned char* dataPtrUB = (unsigned char*) dataPtr;
|
|
dataPtrUB[0] = (cCylShaderBothCapsRound |
|
|
((interp > .5f) ? cCylShaderInterpColor : 0));
|
|
} break;
|
|
case UB4_TO_UB4: {
|
|
auto dataPtrUB = (unsigned char*) dataPtr;
|
|
auto pcUB = (unsigned char*) pc;
|
|
dataPtrUB[0] = pcUB[0];
|
|
dataPtrUB[1] = pcUB[1];
|
|
dataPtrUB[2] = pcUB[2];
|
|
dataPtrUB[3] = pcUB[3];
|
|
break;
|
|
}
|
|
case PICK_DATA_TO_PICK_DATA: {
|
|
float* pcf;
|
|
if (copyord < 0) {
|
|
pcf = (float*) pc;
|
|
} else {
|
|
pcf = (copyord * 2 + pick_data);
|
|
if (nvert) {
|
|
pcf -= pstride;
|
|
}
|
|
}
|
|
unsigned int index = CGO_get_uint(pcf);
|
|
int bond = CGO_get_int(pcf + 1);
|
|
CGO_put_uint(ord * 2 + pick_data, index);
|
|
CGO_put_int(ord * 2 + pick_data + 1, bond);
|
|
has_pick_colorBS |= (1 << ord);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* copies data for a particular attribute into the array used to load the VBO.
|
|
* this takes into account whether it is interleaved or not, and if an attribute
|
|
* has repeat values
|
|
*
|
|
* isInterleaved : whether the VBO is interleaved
|
|
* nvert : which vertex in the VBO
|
|
* attribDesc : the attribute description
|
|
* vertexDataSize : total vertex data size in VBO (for interleaved)
|
|
* dataPtrs : all data pointers for attributes (for interleaved, they are
|
|
* all the pointer to the one array) attrOffset : offsets of the attributes
|
|
* (for interleaved)
|
|
*
|
|
*/
|
|
static void copyAttributeForVertex(bool isInterleaved, int& nvert,
|
|
AttribDesc& attribDesc, const int vertexDataSize,
|
|
std::vector<void*>& dataPtrs, std::vector<int>& attrOffset)
|
|
{
|
|
int ord = attribDesc.order;
|
|
void* dataPtr = dataPtrs[ord];
|
|
unsigned char* pc = nullptr;
|
|
auto attrSize = GetSizeOfVertexFormat(attribDesc.m_format);
|
|
if (isInterleaved) {
|
|
dataPtr =
|
|
(unsigned char*) dataPtr + nvert * vertexDataSize + attrOffset[ord];
|
|
pc = (unsigned char*) dataPtr - vertexDataSize;
|
|
} else {
|
|
dataPtr = (unsigned char*) dataPtr + nvert * attrSize;
|
|
pc = (unsigned char*) dataPtr - attrSize;
|
|
}
|
|
if (attribDesc.repeat_value && attribDesc.repeat_value_length) {
|
|
int pos = (nvert % attribDesc.repeat_value_length);
|
|
pc = attribDesc.repeat_value + pos * attrSize;
|
|
memcpy(dataPtr, pc, attrSize);
|
|
} else {
|
|
memcpy(dataPtr, pc, attrSize);
|
|
}
|
|
}
|
|
/**
|
|
* check all attributes (pick and non-pick) to see if they are specified in the
|
|
* CGO (I) also checks to see if any picking is specified and sets has_picking
|
|
* argument if any of the attributes are not specified and if a default_value is
|
|
* set (in the AttribDesc) then the associated vertex_attribute CGO operation is
|
|
* inserted into the cgo that is passed in.
|
|
*
|
|
* I: primitive CGO that is processed
|
|
* attrData: definition of attributes
|
|
* pickData: definition of pick attributes
|
|
* cgo: new cgo that could have vertex_attribute CGO operations added
|
|
* has_picking: if there are any operations in the CGO (I) that specifies
|
|
* different values for picking. if has_picking is set, then a VBO for picking
|
|
* is generated in CGOConvertToShader()
|
|
*
|
|
*/
|
|
static void CheckAttributesForUsage(const CGO* I, AttribDataDesc& attrData,
|
|
AttribDataDesc& pickData, CGO* cgo, bool& has_picking)
|
|
{
|
|
size_t attrIdx = 0;
|
|
|
|
// need to check attributes:
|
|
// - remove any that are not needed
|
|
// - add glVertexAttrib for those that are removed
|
|
std::map<int, int>
|
|
opToAttrUsed; // bitmask for each op to which attributes are set
|
|
for (auto& attrDesc : attrData) {
|
|
auto attrOps = &attrDesc.attrOps;
|
|
attrDesc.order = attrIdx++;
|
|
for (auto attrOpIt = attrOps->begin(); attrOpIt != attrOps->end();
|
|
++attrOpIt) {
|
|
auto attrOp = &(*attrOpIt);
|
|
if (opToAttrUsed.find(attrOp->op) == opToAttrUsed.end())
|
|
opToAttrUsed[attrOp->op] = 1 << attrDesc.order;
|
|
else
|
|
opToAttrUsed[attrOp->op] |= 1 << attrDesc.order;
|
|
}
|
|
}
|
|
// add picking ops (1 << attrIdx) i.e., any pick op is the last bit
|
|
int pidx = 0;
|
|
for (auto pickDataIt = pickData.begin(); pickDataIt != pickData.end();
|
|
++pickDataIt) {
|
|
auto pickDesc = &(*pickDataIt);
|
|
auto pickOps = &pickDesc->attrOps;
|
|
pickDesc->order = pidx++;
|
|
for (auto pickOpIt = pickOps->begin(); pickOpIt != pickOps->end();
|
|
++pickOpIt) {
|
|
auto pickOp = &(*pickOpIt);
|
|
pickOp->desc = pickDesc;
|
|
if (opToAttrUsed.find(pickOp->op) == opToAttrUsed.end())
|
|
opToAttrUsed[pickOp->op] = 1 << attrIdx;
|
|
else
|
|
opToAttrUsed[pickOp->op] |= 1 << attrIdx;
|
|
}
|
|
}
|
|
size_t totAttrIdx = (1 << (attrIdx + 1)) - 1;
|
|
size_t allAttrIdxUsed = 0;
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
if (opToAttrUsed.find(op) != opToAttrUsed.end()) {
|
|
int attrUsed = opToAttrUsed[op];
|
|
if (attrUsed & (1 << attrIdx)) { // if picking, need to check values, and
|
|
// take them out if cPickableNoPick
|
|
switch (op) {
|
|
case cgo::draw::shadercylinder2ndcolor::op_code:
|
|
if (reinterpret_cast<const cgo::draw::shadercylinder2ndcolor*>(pc)
|
|
->pick_color_bond == cPickableNoPick)
|
|
attrUsed ^= (1 << attrIdx);
|
|
break;
|
|
case cgo::draw::splitline::op_code:
|
|
if (reinterpret_cast<const cgo::draw::splitline*>(pc)->bond ==
|
|
cPickableNoPick)
|
|
attrUsed ^= (1 << attrIdx);
|
|
}
|
|
}
|
|
allAttrIdxUsed |= attrUsed;
|
|
if (allAttrIdxUsed == totAttrIdx)
|
|
break;
|
|
}
|
|
}
|
|
has_picking =
|
|
allAttrIdxUsed & (1 << attrIdx); // has_picking if the last bit is set
|
|
|
|
if (allAttrIdxUsed != totAttrIdx) {
|
|
// go through any attributes that aren't used:
|
|
// - add associated vertex_attribute type (if default_value is set)
|
|
// - remove attribute from attrData description so that it isn't included
|
|
// in VBO
|
|
AttribDataDesc attrDataNew;
|
|
for (auto idx = 0; idx < attrIdx; ++idx) {
|
|
if (!attrData[idx].repeat_value && !(allAttrIdxUsed & (1 << idx))) {
|
|
// attribute not used, need to create glVertexAttrib
|
|
if (attrData[idx].default_value) {
|
|
// need to add glVertexAttrib CGO OP
|
|
int attr_lookup_idx =
|
|
I->G->ShaderMgr->GetAttributeUID(attrData[idx].attr_name);
|
|
switch (GetVertexFormatBaseType(attrData[idx].m_format)) {
|
|
case VertexFormatBaseType::Float:
|
|
switch (VertexFormatToGLSize(attrData[idx].m_format)) {
|
|
case 1:
|
|
cgo->add<cgo::draw::vertex_attribute_1f>(
|
|
attr_lookup_idx, *(float*) attrData[idx].default_value);
|
|
break;
|
|
case 3:
|
|
cgo->add<cgo::draw::vertex_attribute_3f>(
|
|
attr_lookup_idx, attrData[idx].default_value);
|
|
break;
|
|
default:
|
|
std::cerr << "\tBAD SIZE: attrData[idx].m_format="
|
|
<< static_cast<int>(attrData[idx].m_format)
|
|
<< std::endl;
|
|
}
|
|
break;
|
|
case VertexFormatBaseType::UByte:
|
|
switch (VertexFormatToGLSize(attrData[idx].m_format)) {
|
|
case 1: {
|
|
float val;
|
|
unsigned char valuc = *attrData[idx].default_value;
|
|
if (VertexFormatIsNormalized(attrData[idx].m_format)) {
|
|
val = CLAMPVALUE(valuc / 255.f, 0.f, 1.f);
|
|
} else {
|
|
val = (float) valuc;
|
|
}
|
|
cgo->add<cgo::draw::vertex_attribute_1f>(attr_lookup_idx, val);
|
|
} break;
|
|
case 4:
|
|
cgo->add<cgo::draw::vertex_attribute_4ub>(
|
|
attr_lookup_idx, attrData[idx].default_value);
|
|
break;
|
|
default:
|
|
std::cerr << "\tNOT IMPLEMENTED: attrData[idx].m_format="
|
|
<< static_cast<int>(attrData[idx].m_format)
|
|
<< std::endl;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
attrDataNew.push_back(attrData[idx]);
|
|
}
|
|
}
|
|
attrData.swap(attrDataNew); // only keep attributes that are used
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Populates two structures and sets vertsperpickinfo
|
|
* opToCntPer : CGO op to how many vertices are generated for each op.
|
|
* opToOrderedAttribOps : CGO op to an ordered map of AttribOps that define how
|
|
* we operate on the attribute arrays for each CGO op. attrData :
|
|
* definition of attributes pickData : definition of pick attributes
|
|
* vertsperpickinfo : number of vertices per pick (only used when
|
|
*
|
|
*/
|
|
static void PopulateOpsIntoStructuresForConversion(
|
|
std::map<int, int>& opToCntPer,
|
|
std::map<int, std::map<int, AttribOp*>>& opToOrderedAttribOps,
|
|
AttribDataDesc& attrData, AttribDataDesc& pickData, int& vertsperpickinfo,
|
|
const bool has_picking)
|
|
{
|
|
size_t attrIdx = 0;
|
|
for (auto& attrDesc : attrData) {
|
|
auto attrOps = &attrDesc.attrOps;
|
|
attrDesc.order = attrIdx++;
|
|
for (auto attrOpIt = attrOps->begin(); attrOpIt != attrOps->end();
|
|
++attrOpIt) {
|
|
auto attrOp = &(*attrOpIt);
|
|
attrOp->desc = &attrDesc;
|
|
if (attrOp->copyFromAttr >= 0) {
|
|
attrOp->copyAttribDesc = &attrData[attrOp->copyFromAttr];
|
|
}
|
|
if (attrOp->incr_vertices > 0) {
|
|
if (!vertsperpickinfo) {
|
|
vertsperpickinfo = attrOp->incr_vertices;
|
|
} else {
|
|
if (attrOp->incr_vertices != vertsperpickinfo) {
|
|
std::cerr << "WARNING: attrOp->incr_vertices set to multiple "
|
|
"values, vertsperpickinfo="
|
|
<< vertsperpickinfo
|
|
<< " attrOp->incr_vertices=" << attrOp->incr_vertices
|
|
<< " : picking might get confused" << std::endl;
|
|
}
|
|
}
|
|
if (opToCntPer.find(attrOp->op) == opToCntPer.end())
|
|
opToCntPer[attrOp->op] = attrOp->incr_vertices;
|
|
else
|
|
opToCntPer[attrOp->op] += attrOp->incr_vertices;
|
|
}
|
|
if (opToOrderedAttribOps.find(attrOp->op) == opToOrderedAttribOps.end())
|
|
opToOrderedAttribOps[attrOp->op] = std::map<int, AttribOp*>({});
|
|
opToOrderedAttribOps[attrOp->op][attrOp->order] = attrOp;
|
|
}
|
|
}
|
|
if (has_picking) {
|
|
for (auto pickDataIt = pickData.begin(); pickDataIt != pickData.end();
|
|
++pickDataIt) {
|
|
auto pickDesc = &(*pickDataIt);
|
|
auto pickOps = &pickDesc->attrOps;
|
|
for (auto pickOpIt = pickOps->begin(); pickOpIt != pickOps->end();
|
|
++pickOpIt) {
|
|
auto pickOp = &(*pickOpIt);
|
|
if (pickOp->copyFromAttr >= 0) {
|
|
pickOp->copyAttribDesc = &pickData[pickOp->copyFromAttr];
|
|
}
|
|
if (opToOrderedAttribOps.find(pickOp->op) == opToOrderedAttribOps.end())
|
|
opToOrderedAttribOps[pickOp->op] = std::map<int, AttribOp*>({});
|
|
opToOrderedAttribOps[pickOp->op][pickOp->order] = pickOp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* converts a "primitive" CGO into a CGO that renders a custom operation
|
|
*
|
|
* I: primitive CGO that is processed
|
|
* attrData: definition of attributes that are accumulated and put
|
|
* into the VBO pickData: definition of pick attributes that
|
|
* accumulate pick data and put into the interleaved picking VBO mode: which
|
|
* openGL mode to use for rendering (e.g., GL_POINTS, GL_TRIANGLES,
|
|
* GL_LINE_STRIPS, etc.) layout: SEPARATE, SEQUENTIAL, or
|
|
* INTERLEAVED : how the VBO is layed out in memory (default: INTERLEAVED)
|
|
* check_attr_for_data: if true, this function checks whether all attributes are
|
|
* used, and if any are not specified, and default values are defined in
|
|
* AttribDesc, then glVertexAttrib CGO ops are created on the returned CGO
|
|
* (default: true) idx_array: if specified, this array is used to
|
|
* specify indices for each fragment in an Indexed Buffer and glDrawElements is
|
|
* used to render (instead of glDrawArrays) nvertsperfrag: in conjunction
|
|
* with idx_array, how many vertices for each set of indices (default: 0) both
|
|
* idx_array and nvertsperfrag are used to specify vertices and geometry for
|
|
* each fragment, such as the box for cylinders nfragspergroup: Currently,
|
|
* this represents the number of fragments that are inside of a group. For
|
|
* example, crosses as cylinders (CGOConvertCrossesToCylinderShader) have 36
|
|
* indices per fragment (nvertsperfrag=36) and 3 fragments per group
|
|
* (nfragspergroup=3) (i.e., one for each line). note: idx_array, nvertsperfrag
|
|
* and nfragspergroup should probably be moved to AttrOp in the future to
|
|
* support different types of fragments.
|
|
*
|
|
* returns a CGO that consists of vertex_attrib_* (e.g., glVertexAttrib) and a
|
|
* custom CGO operation that calls either glDrawArrays or glDrawElements. It
|
|
* also supports picking if specified in the pickData.
|
|
*
|
|
*/
|
|
CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData,
|
|
AttribDataDesc& pickData, int mode, const VertexBufferLayout layout,
|
|
bool check_attr_for_data, int* idx_array, int nvertsperfrag,
|
|
int nfragspergroup)
|
|
{
|
|
CGO* cgo;
|
|
int ok = true;
|
|
bool isInterleaved = (layout == VertexBufferLayout::Interleaved);
|
|
bool has_picking = true;
|
|
std::map<std::string, AttribDesc*> attrToDesc;
|
|
|
|
cgo = CGONew(I->G);
|
|
cgo->use_shader = true;
|
|
|
|
if (check_attr_for_data) {
|
|
CheckAttributesForUsage(I, attrData, pickData, cgo, has_picking);
|
|
} else {
|
|
// if attributes aren't checked, still need to set pick order and desc
|
|
// pointer
|
|
int pidx = 0;
|
|
for (auto& pickDesc : pickData) {
|
|
pickDesc.order = pidx++;
|
|
for (auto& pickOp : pickDesc.attrOps) {
|
|
pickOp.desc = &pickDesc;
|
|
}
|
|
}
|
|
}
|
|
std::map<int, int> opToCntPer;
|
|
std::map<int, std::map<int, AttribOp*>> opToOrderedAttribOps;
|
|
|
|
int vertsperpickinfo = 0;
|
|
PopulateOpsIntoStructuresForConversion(opToCntPer, opToOrderedAttribOps,
|
|
attrData, pickData, vertsperpickinfo, has_picking);
|
|
|
|
// Populate these variables used for accumulating and setting VBO data arrays
|
|
int vertexDataSize = 0;
|
|
std::vector<int> attrSizes;
|
|
std::vector<int> attrOffset; // for interleaved
|
|
int curoffset = 0; // for interleaved
|
|
size_t attrIdx = 0;
|
|
for (auto& attrDesc : attrData) {
|
|
attrDesc.order = attrIdx++;
|
|
auto attrSize = GetSizeOfVertexFormat(attrDesc.m_format);
|
|
attrSizes.push_back(attrSize);
|
|
vertexDataSize += attrSize;
|
|
|
|
if (isInterleaved) {
|
|
attrOffset.push_back(curoffset);
|
|
curoffset += attrSize;
|
|
} else {
|
|
attrOffset.push_back(0); // no offset when not interleaved
|
|
}
|
|
attrToDesc[attrDesc.attr_name] = &attrDesc;
|
|
}
|
|
|
|
// populate funcData.attrib
|
|
for (auto& attrDesc : attrData)
|
|
for (auto& attrop : attrDesc.attrOps)
|
|
for (auto& funcData : attrop.funcDataConversions) {
|
|
if (attrToDesc.find(funcData.attribName) != attrToDesc.end()) {
|
|
funcData.attrib = attrToDesc[funcData.attribName];
|
|
}
|
|
}
|
|
|
|
// Since some cards require word-aligned strides (e.g., ATI)
|
|
// we need to make sure it is word-aligned. If it isn't, and you
|
|
// want to save memory, maybe SEQUENTIAL is a better option
|
|
if (vertexDataSize % 4) {
|
|
vertexDataSize += 4 - (vertexDataSize % 4);
|
|
}
|
|
|
|
int ntotalverts = CGOCountNumberOfOperationsOfTypeN(I, opToCntPer);
|
|
|
|
// PYMOL-2668
|
|
if (ntotalverts == 0) {
|
|
CGOStop(cgo);
|
|
return cgo;
|
|
}
|
|
|
|
size_t pickvbohash = 0;
|
|
int pickDataSize = pickData.size();
|
|
int pstride = 2 * pickDataSize;
|
|
// Generate VBOs: both for vertex data (vbo) and picking color data (pickvbo)
|
|
// if necessary
|
|
// - pickvbo is interleaved so that for multiple channels, pick data for each
|
|
// vertex
|
|
// is contiguous
|
|
auto& vbo = CreateVertexBuffer(I->G, layout);
|
|
VertexBuffer* pickvbo = nullptr;
|
|
if (pickDataSize) {
|
|
pickvbo = &CreateVertexBuffer(I->G, VertexBufferLayout::Sequential,
|
|
MemoryUsageProperty::CpuToGpu);
|
|
pickvbohash = pickvbo->get_hash_id();
|
|
}
|
|
|
|
// adding SPECIAL OPERATIONS (for now) before adding custom OP
|
|
// - for now, this is the only operation that needs to be passed to the new
|
|
// CGO
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
switch (op) {
|
|
case CGO_SPECIAL:
|
|
cgo->add_to_cgo(op, pc);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// defines how many passes we have
|
|
// We *could* set npickcolattr=1 if we had access to PickColorConverter here
|
|
// and getTotalBits() == 32
|
|
const int npickcolattr = SHADER_PICKING_PASSES_MAX;
|
|
/* for picking, we need to mask the attributes and bind the buffer in the the
|
|
* picking VBO */
|
|
int pl = 0;
|
|
int npickattr = pickData.size();
|
|
for (auto& pickDesc : pickData) {
|
|
cgo->add<cgo::draw::mask_attribute_if_picking>(
|
|
I->G->ShaderMgr->GetAttributeUID(pickDesc.attr_name),
|
|
vbo.get_hash_id());
|
|
if (has_picking) {
|
|
cgo->add<cgo::draw::bind_vbo_for_picking>(pickvbohash, pl++, npickattr);
|
|
} else {
|
|
/* if no picking, should render black */
|
|
static unsigned char zerocolor[]{0, 0, 0, 0};
|
|
cgo->add<cgo::draw::vertex_attribute_4ub_if_picking>(
|
|
I->G->ShaderMgr->GetAttributeUID(pickDesc.attr_name), zerocolor);
|
|
}
|
|
}
|
|
size_t iboid = 0;
|
|
int num_total_indexes = 0;
|
|
if (nvertsperfrag) {
|
|
int nfrags = nfragspergroup * ntotalverts / vertsperpickinfo;
|
|
int nvertsperindivfrag = vertsperpickinfo / nfragspergroup;
|
|
num_total_indexes = nfrags * nvertsperfrag;
|
|
std::vector<VertexIndex_t> vertexIndices(num_total_indexes);
|
|
int idxpl = 0;
|
|
// using vertsperpickinfo as verts per frag
|
|
for (int cnt = 0, vpl = 0; cnt < nfrags; ++cnt) {
|
|
for (int idx_array_pl = 0; idx_array_pl < nvertsperfrag; ++idx_array_pl) {
|
|
vertexIndices[idxpl] = idx_array[idx_array_pl] + vpl;
|
|
idxpl++;
|
|
}
|
|
vpl += nvertsperindivfrag;
|
|
}
|
|
auto& ibo = CreateIndexBuffer(I->G);
|
|
SetIndexBufferData(I->G, ibo, vertexIndices);
|
|
iboid = ibo.get_hash_id();
|
|
}
|
|
|
|
// pick_data is interleaved if more than one attribute
|
|
float* pick_data = cgo->add<cgo::draw::custom>(mode, ntotalverts,
|
|
vbo.get_hash_id(), pickvbohash, vertsperpickinfo, pickDataSize, iboid,
|
|
num_total_indexes);
|
|
std::vector<unsigned char> allData(ntotalverts * vertexDataSize);
|
|
std::vector<void*> dataPtrs;
|
|
std::vector<int> repeat_attr_idx;
|
|
int allAttrBS = 0;
|
|
|
|
// Initialize first entry in array(s) with default values and populate
|
|
// dataPtrs
|
|
if (isInterleaved) {
|
|
int pl = 0;
|
|
auto attrDataIt = attrData.begin();
|
|
auto attrOffsetIt = attrOffset.begin();
|
|
for (; attrDataIt != attrData.end() && attrOffsetIt != attrOffset.end();
|
|
++attrDataIt, ++attrOffsetIt) {
|
|
auto attrDesc = &(*attrDataIt);
|
|
if (attrDesc->repeat_value) {
|
|
repeat_attr_idx.push_back(pl);
|
|
} else {
|
|
allAttrBS |= (1 << attrDesc->order);
|
|
}
|
|
auto attrOffset = *attrOffsetIt;
|
|
unsigned char* first_value = nullptr;
|
|
first_value = (attrDesc->default_value
|
|
? attrDesc->default_value
|
|
: (attrDesc->repeat_value ? attrDesc->repeat_value
|
|
: nullptr));
|
|
if (first_value) {
|
|
auto attrSize = GetSizeOfVertexFormat(attrDesc->m_format);
|
|
memcpy(allData.data() + attrOffset, first_value, attrSize);
|
|
}
|
|
dataPtrs.push_back(allData.data());
|
|
++pl;
|
|
}
|
|
} else {
|
|
auto* curAllDataPtr = allData.data();
|
|
int pl = 0;
|
|
for (auto& attrDesc : attrData) {
|
|
if (attrDesc.repeat_value) {
|
|
repeat_attr_idx.push_back(pl);
|
|
} else {
|
|
allAttrBS |= (1 << attrDesc.order);
|
|
}
|
|
dataPtrs.push_back(curAllDataPtr);
|
|
unsigned char* first_value = nullptr;
|
|
first_value =
|
|
(attrDesc.default_value
|
|
? attrDesc.default_value
|
|
: (attrDesc.repeat_value ? attrDesc.repeat_value : nullptr));
|
|
if (first_value) {
|
|
memcpy(curAllDataPtr, first_value, attrSizes[pl]);
|
|
}
|
|
curAllDataPtr = curAllDataPtr + ntotalverts * attrSizes[pl];
|
|
++pl;
|
|
}
|
|
}
|
|
|
|
int nvert = 0;
|
|
int attrBS = 0;
|
|
int allPickAttrBS = (1 << pickData.size()) - 1;
|
|
int has_pick_colorBS = allPickAttrBS;
|
|
for (int pi = 0; pi < pickDataSize; ++pi) {
|
|
CGO_put_uint(2 * pi + pick_data, 0);
|
|
CGO_put_int(2 * pi + pick_data + 1, cPickableNoPick);
|
|
}
|
|
bool cont =
|
|
true; // need to break while statement as well if past all vertices
|
|
|
|
// This is the loop that goes through the CGO and accumulates all of the data
|
|
// for both the rendering and picking VBOs.
|
|
// - For each OP, go through the list of Attribute OPs
|
|
// - Each Attribute OP:
|
|
// - copies attribute data from the OP
|
|
// - can generate vertices (incr_vertices)
|
|
// - Attributes are kept track of (attrBS) and when vertices are generated,
|
|
// the attributes that have not been newly written for the current vertex
|
|
// are copied from the previous one.
|
|
for (auto it = I->begin(); cont && !it.is_stop(); ++it) {
|
|
auto pc = it.data();
|
|
int op = it.op_code();
|
|
if (opToOrderedAttribOps.find(op) != opToOrderedAttribOps.end()) {
|
|
std::map<int, AttribOp*>* attribOpsInOrder = &opToOrderedAttribOps[op];
|
|
for (auto attribOpIt : *attribOpsInOrder) {
|
|
AttribOp* attribOp = attribOpIt.second;
|
|
int ord = attribOp->desc->order;
|
|
cont = nvert < ntotalverts;
|
|
if (!cont)
|
|
break;
|
|
copyAttributeForOp(isInterleaved, nvert, attribOp, vertexDataSize,
|
|
dataPtrs, attrOffset, pc, pick_data, has_pick_colorBS, pstride);
|
|
if (ord >= 0) // picking is negative, has_pick_colorBS is used instead
|
|
attrBS |= (1 << ord);
|
|
else
|
|
std::cout << " ord=%d\n" << ord << std::endl;
|
|
if (attribOp->incr_vertices) {
|
|
if (has_pick_colorBS != allPickAttrBS) {
|
|
// copy pick colors that haven't been set from previous vertex
|
|
for (int pi = 0; pi < pickDataSize; ++pi) {
|
|
if (has_pick_colorBS ^ (1 << pi)) {
|
|
CGO_put_uint(pick_data, CGO_get_uint(pick_data - pstride));
|
|
CGO_put_int(
|
|
pick_data + 1, CGO_get_int(pick_data - pstride + 1));
|
|
}
|
|
pick_data += 2;
|
|
}
|
|
} else {
|
|
pick_data += pstride;
|
|
}
|
|
has_pick_colorBS = 0;
|
|
if (!nvert && attrBS != allAttrBS) {
|
|
// for the first vertex, all attributes should be set
|
|
for (auto idx = 0; idx < attrData.size(); ++idx) {
|
|
if (!(attrBS & (1 << idx))) {
|
|
if (!attrData[idx].default_value) {
|
|
std::cerr << "WARNING: attribute #" << idx << " ("
|
|
<< attrData[idx].attr_name
|
|
<< ") not set for first"
|
|
" vertex and does not have default value"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nvert && attrBS != allAttrBS) {
|
|
// for each vertex that hasn't been written for the current vertex,
|
|
// copy it from the previous vertex
|
|
for (auto idx = 0; idx < attrData.size(); ++idx) {
|
|
if (!(attrBS & (1 << idx))) {
|
|
copyAttributeForVertex(isInterleaved, nvert, attrData[idx],
|
|
vertexDataSize, dataPtrs, attrOffset);
|
|
}
|
|
}
|
|
}
|
|
attrBS = 0;
|
|
|
|
// creating new vertices, all attribute data should be copied into new
|
|
// vertex.
|
|
for (int nxt = 0; nxt < attribOp->incr_vertices; ++nxt) {
|
|
/* for now, always copy values from previous */
|
|
++nvert;
|
|
if (nvert < ntotalverts) {
|
|
{
|
|
// last should not need to copy into next, since we call
|
|
// copyAttributeForVertex (above) for all attributes that
|
|
// haven't been set
|
|
// - note: it might be faster (especially for interleaved) to
|
|
// copy into the next
|
|
// vertex, then the above copyAttributeForVertex() would
|
|
// not be needed
|
|
if (isInterleaved) {
|
|
auto* dest = allData.data() + vertexDataSize * nvert;
|
|
memcpy(dest, dest - vertexDataSize, vertexDataSize);
|
|
} else {
|
|
auto dataPtrIt = dataPtrs.begin();
|
|
auto attrDataIt = attrData.begin();
|
|
for (; attrDataIt != attrData.end() &&
|
|
dataPtrIt != dataPtrs.end();
|
|
++attrDataIt, ++dataPtrIt) {
|
|
auto attrDesc = &(*attrDataIt);
|
|
auto dataPtr = *dataPtrIt;
|
|
auto attrSize = GetSizeOfVertexFormat(attrDesc->m_format);
|
|
void* dest = ((unsigned char*) dataPtr) + attrSize * nvert;
|
|
memcpy((unsigned char*) dest,
|
|
((unsigned char*) dest) - attrSize, attrSize);
|
|
}
|
|
}
|
|
}
|
|
// always copy repeat attributes
|
|
if (!repeat_attr_idx.empty()) {
|
|
for (auto ridx = repeat_attr_idx.begin();
|
|
ridx != repeat_attr_idx.end(); ++ridx) {
|
|
copyAttributeForVertex(isInterleaved, nvert, attrData[*ridx],
|
|
vertexDataSize, dataPtrs, attrOffset);
|
|
}
|
|
}
|
|
}
|
|
if (!attribOp->funcDataConversions.empty()) {
|
|
// for all attributes, call the funcDataConversion() if defined
|
|
int nvert_m_1 = nvert - 1;
|
|
for (auto funcData : attribOp->funcDataConversions) {
|
|
auto funcAttrib = funcData.attrib;
|
|
auto order = funcAttrib->order;
|
|
if (isInterleaved) {
|
|
unsigned char* dest = allData.data() +
|
|
vertexDataSize * nvert_m_1 +
|
|
attrOffset[order];
|
|
funcData.funcDataConversion(
|
|
dest, pc, funcData.funcDataGlobalArg, nxt);
|
|
} else {
|
|
auto dataPtr = dataPtrs[order];
|
|
void* dest =
|
|
((unsigned char*) dataPtr) + attrSizes[order] * nvert_m_1;
|
|
funcData.funcDataConversion(
|
|
dest, pc, funcData.funcDataGlobalArg, nxt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Generate Pick Buffers with all pick attributes (if necessary) */
|
|
if (pickvbo) {
|
|
BufferDataDesc pickBufferData;
|
|
for (int i = 0; i < npickcolattr; i++) {
|
|
for (auto& pickDesc : pickData) {
|
|
auto pickSize = GetSizeOfVertexFormat(pickDesc.m_format);
|
|
pickBufferData.descs.push_back(BufferDesc(
|
|
pickDesc.attr_name, pickDesc.m_format, pickSize * nvert));
|
|
}
|
|
}
|
|
SetVertexBufferData(I->G, *pickvbo, std::move(pickBufferData));
|
|
}
|
|
|
|
/* Generate VBO Buffers with all pick attributes based on the VertexBuffer
|
|
* type SEPARATE/SEQUENTIAL/INTERLEAVED*/
|
|
BufferDataDesc bufferData;
|
|
switch (layout) {
|
|
case VertexBufferLayout::Separate:
|
|
case VertexBufferLayout::Sequential: {
|
|
auto attrDataIt = attrData.begin();
|
|
auto dataPtrIt = dataPtrs.begin();
|
|
auto attrSizeIt = attrSizes.begin();
|
|
for (; attrDataIt != attrData.end() && dataPtrIt != dataPtrs.end() &&
|
|
attrSizeIt != attrSizes.end();
|
|
++attrDataIt, ++dataPtrIt, ++attrSizeIt) {
|
|
auto attrDesc = &(*attrDataIt);
|
|
auto dataPtr = *dataPtrIt;
|
|
auto attrSize = *attrSizeIt;
|
|
bufferData.descs.push_back(BufferDesc(
|
|
attrDesc->attr_name, attrDesc->m_format, nvert * attrSize, dataPtr));
|
|
}
|
|
SetVertexBufferData(I->G, vbo, std::move(bufferData));
|
|
break;
|
|
} break;
|
|
case VertexBufferLayout::Interleaved: {
|
|
auto attrDataIt = attrData.begin();
|
|
auto attrOffsetIt = attrOffset.begin();
|
|
for (; attrDataIt != attrData.end() && attrOffsetIt != attrOffset.end();
|
|
++attrDataIt, ++attrOffsetIt) {
|
|
auto attrDesc = &(*attrDataIt);
|
|
auto offset = *attrOffsetIt;
|
|
bufferData.descs.push_back(BufferDesc{attrDesc->attr_name, attrDesc->m_format,
|
|
0, nullptr, (std::uint32_t) offset});
|
|
}
|
|
bufferData.stride = static_cast<std::size_t>(vertexDataSize);
|
|
SetVertexBufferDataSized(I->G, vbo, std::move(bufferData),
|
|
nvert * vertexDataSize, reinterpret_cast<std::byte*>(allData.data()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
CGOStop(cgo);
|
|
return cgo;
|
|
}
|
|
|
|
// CGOCheckSplitLineInterpolationIsSame:
|
|
// - returns true if always the same
|
|
// - returns false if not always the same
|
|
bool CGOCheckSplitLineInterpolationIsSame(const CGO* I, bool& interp_value)
|
|
{
|
|
bool interp_value_first = false;
|
|
bool interp_value_is_set = false;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
switch (it.op_code()) {
|
|
case cgo::draw::splitline::op_code:
|
|
interp_value = (it.cast<cgo::draw::splitline>()->flags &
|
|
cgo::draw::splitline::interpolation);
|
|
break;
|
|
case CGO_INTERPOLATED:
|
|
interp_value = it.cast<float>()[0] > 0.5f;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (!interp_value_is_set) {
|
|
interp_value_first = interp_value;
|
|
interp_value_is_set = true;
|
|
} else if (interp_value != interp_value_first) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// CGOCheckShaderCylinderCapInfoIsSame:
|
|
// - returns true if always the same
|
|
// - returns false if not always the same
|
|
static bool CGOCheckShaderCylinderCapInfoIsSame(
|
|
const CGO* I, unsigned char& cap_value)
|
|
{
|
|
unsigned char cap_value_first = 0;
|
|
bool cap_value_first_is_set = false;
|
|
|
|
for (auto it = I->begin(); !it.is_stop(); ++it) {
|
|
switch (it.op_code()) {
|
|
case cgo::draw::shadercylinder::op_code:
|
|
cap_value = it.cast<cgo::draw::shadercylinder>()->cap;
|
|
break;
|
|
case cgo::draw::shadercylinder2ndcolor::op_code:
|
|
cap_value = it.cast<cgo::draw::shadercylinder2ndcolor>()->cap;
|
|
break;
|
|
case cgo::draw::sausage::op_code:
|
|
cap_value = cCylShaderBothCapsRound | cCylShaderInterpColor;
|
|
break;
|
|
case cgo::draw::cylinder::op_code:
|
|
cap_value = cCylShaderBothCapsFlat | cCylShaderInterpColor;
|
|
break;
|
|
case cgo::draw::custom_cylinder::op_code: {
|
|
auto cc = it.cast<cgo::draw::custom_cylinder>();
|
|
const cCylCap cap1 = cc->get_cap1();
|
|
const cCylCap cap2 = cc->get_cap2();
|
|
cap_value = cyl_shader_bits_from_caps(cap1, cap2) | cCylShaderInterpColor;
|
|
} break;
|
|
case cgo::draw::custom_cylinder_alpha::op_code: {
|
|
auto cc = it.cast<cgo::draw::custom_cylinder_alpha>();
|
|
const cCylCap cap1 = cc->get_cap1();
|
|
const cCylCap cap2 = cc->get_cap2();
|
|
cap_value = cyl_shader_bits_from_caps(cap1, cap2) | cCylShaderInterpColor;
|
|
} break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (!cap_value_first_is_set) {
|
|
cap_value_first = cap_value;
|
|
cap_value_first_is_set = true;
|
|
} else if (cap_value != cap_value_first) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CGO* CGOConvertToTrilinesShader(const CGO* I, CGO* addTo, bool add_color)
|
|
{
|
|
PyMOLGlobals* G = I->G;
|
|
|
|
AttribDataOp vertexOps = {
|
|
{CGO_LINE, 1, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::line, vertex1), 0},
|
|
{CGO_SPLITLINE, 2, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::splitline, vertex1), 0}};
|
|
AttribDataOp vertexOtherOps = {
|
|
{CGO_LINE, 2, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::line, vertex2), 6},
|
|
{CGO_SPLITLINE, 5, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::splitline, vertex2), 6}};
|
|
AttribDataOp colorOps = {{CGO_COLOR, 0, FLOAT3_TO_UB3, 0},
|
|
{CGO_ALPHA, 0, FLOAT1_TO_UB_4TH, 0},
|
|
{CGO_SPLITLINE, 6, UB3_TO_UB3, offsetof(cgo::draw::splitline, color2)}};
|
|
AttribDataOp color2Ops = {{CGO_COLOR, 1, FLOAT3_TO_UB3, 0},
|
|
{CGO_ALPHA, 1, FLOAT1_TO_UB_4TH, 0},
|
|
{CGO_SPLITLINE, 3, UB3_TO_UB3, offsetof(cgo::draw::splitline, color2)}};
|
|
AttribDataOp extraPickColorOps = {
|
|
{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0},
|
|
{CGO_SPLITLINE, 7, UINT_INT_TO_PICK_DATA,
|
|
offsetof(cgo::draw::splitline, index), 0}};
|
|
AttribDataOp extraPickColor2Ops = {
|
|
{CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0},
|
|
{CGO_SPLITLINE, 4, UINT_INT_TO_PICK_DATA,
|
|
offsetof(cgo::draw::splitline, index), 0}};
|
|
AttribDataDesc pickDesc = {
|
|
{"a_Color", VertexFormat::UByte4Norm, extraPickColorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}};
|
|
AttribDataDesc attrDesc = {{"a_Vertex", VertexFormat::Float3, vertexOps},
|
|
{"a_OtherVertex", VertexFormat::Float3, vertexOtherOps},
|
|
{"a_Color", VertexFormat::UByte4Norm, colorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, color2Ops},
|
|
{"a_UV", VertexFormat::UByte}};
|
|
|
|
if (add_color) {
|
|
static unsigned char default_color[] = {
|
|
255, 255, 255, 255}; // to write in alpha if CGO doesn't have it
|
|
attrDesc[2].default_value = default_color;
|
|
attrDesc[3].default_value = default_color;
|
|
}
|
|
AttribDesc* uvdesc = &attrDesc[attrDesc.size() - 1];
|
|
uvdesc->repeat_value_length = 6;
|
|
static unsigned char uv_bits[] = {1, 3, 0, 3, 2, 1};
|
|
uvdesc->repeat_value = uv_bits;
|
|
|
|
bool interp_same, interp_value;
|
|
if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))) {
|
|
addTo->add<cgo::draw::vertex_attribute_1f>(
|
|
G->ShaderMgr->GetAttributeUID("a_interpolate"),
|
|
interp_value ? 1.f : 0.f);
|
|
} else {
|
|
AttribDataOp interpOps = {{CGO_SPLITLINE, 1, UB1_TO_INTERP,
|
|
offsetof(cgo::draw::splitline, flags), 0}};
|
|
// need to add a_interpolate attribute
|
|
attrDesc.push_back({"a_interpolate", VertexFormat::UByte, interpOps});
|
|
}
|
|
if (!add_color) {
|
|
attrDesc.erase(attrDesc.begin() + 2); // a_Color
|
|
attrDesc.erase(attrDesc.begin() + 2); // a_Color2
|
|
}
|
|
|
|
return CGOConvertToShader(
|
|
I, attrDesc, pickDesc, GL_TRIANGLES, VertexBufferLayout::Interleaved);
|
|
}
|
|
|
|
CGO* CGOConvertToLinesShader(const CGO* I, CGO* addTo, bool add_color)
|
|
{
|
|
/* Lines that pass in two vertices per line */
|
|
PyMOLGlobals* G = I->G;
|
|
AttribDataOp vertexOps = {
|
|
{CGO_LINE, 1, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::line, vertex1), 1},
|
|
{CGO_SPLITLINE, 2, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::splitline, vertex1), 1},
|
|
{CGO_LINE, 2, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::line, vertex2), 1},
|
|
{CGO_SPLITLINE, 5, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::splitline, vertex2), 1}};
|
|
AttribDataOp colorOps = {{CGO_COLOR, 0, FLOAT3_TO_UB3, 0},
|
|
{CGO_ALPHA, 0, FLOAT1_TO_UB_4TH, 0},
|
|
{CGO_SPLITLINE, 3, UB3_TO_UB3, offsetof(cgo::draw::splitline, color2)}};
|
|
AttribDataOp extraPickColorOps = {
|
|
{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0},
|
|
{CGO_SPLITLINE, 4, UINT_INT_TO_PICK_DATA,
|
|
offsetof(cgo::draw::splitline, index), 0}};
|
|
|
|
AttribDataDesc pickDesc = {
|
|
{"a_Color", VertexFormat::UByte4Norm, extraPickColorOps}};
|
|
AttribDataDesc attrDesc = {{"a_Vertex", VertexFormat::Float3, vertexOps},
|
|
{"a_Color", VertexFormat::UByte4Norm, colorOps}};
|
|
if (add_color) {
|
|
static unsigned char default_color[] = {
|
|
255, 255, 255, 255}; // to write in alpha if CGO doesn't have it
|
|
attrDesc[1].default_value = default_color;
|
|
}
|
|
bool interp_same, interp_value;
|
|
if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))) {
|
|
addTo->add<cgo::draw::vertex_attribute_1f>(
|
|
G->ShaderMgr->GetAttributeUID("a_interpolate"),
|
|
interp_value ? 1.f : 0.f);
|
|
} else {
|
|
AttribDataOp interpOps = {{CGO_SPLITLINE, 1, UB1_TO_INTERP,
|
|
offsetof(cgo::draw::splitline, flags), 0}};
|
|
// need to add a_interpolate attribute
|
|
attrDesc.push_back({"a_interpolate", VertexFormat::UByte, interpOps});
|
|
}
|
|
#ifndef PURE_OPENGL_ES_2
|
|
{
|
|
attrDesc.push_back({"a_line_position", VertexFormat::UByte});
|
|
AttribDesc* lpdesc = &attrDesc[attrDesc.size() - 1];
|
|
lpdesc->repeat_value_length = 2;
|
|
static unsigned char flip_bits[] = {0, 1};
|
|
lpdesc->repeat_value = flip_bits;
|
|
}
|
|
#endif
|
|
if (!add_color) {
|
|
attrDesc.erase(attrDesc.begin() + 1); // a_Color
|
|
}
|
|
|
|
return CGOConvertToShader(
|
|
I, attrDesc, pickDesc, GL_LINES, VertexBufferLayout::Interleaved);
|
|
}
|
|
|
|
CGO* CGOConvertLinesToCylinderShader(const CGO* I, CGO* addTo, bool add_color)
|
|
{
|
|
/* Lines that pass in two vertices per line */
|
|
PyMOLGlobals* G = I->G;
|
|
|
|
AttribDataOp vertex1Ops = {
|
|
{CGO_LINE, 1, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::line, vertex1), 0},
|
|
{CGO_SPLITLINE, 2, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::splitline, vertex1), 0}};
|
|
AttribDataOp vertex2Ops = {
|
|
{CGO_LINE, 2, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::line, vertex2), 8},
|
|
{CGO_SPLITLINE, 5, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::splitline, vertex2), 8}};
|
|
static AttribDataOp colorOps = {{CGO_COLOR, 0, FLOAT3_TO_UB3, 0},
|
|
{CGO_ALPHA, 0, FLOAT1_TO_UB_4TH, 0},
|
|
{CGO_SPLITLINE, 6, UB3_TO_UB3, offsetof(cgo::draw::splitline, color2)}};
|
|
static AttribDataOp color2Ops = {{CGO_COLOR, 1, FLOAT3_TO_UB3, 0},
|
|
{CGO_ALPHA, 1, FLOAT1_TO_UB_4TH, 0},
|
|
{CGO_SPLITLINE, 3, UB3_TO_UB3, offsetof(cgo::draw::splitline, color2)}};
|
|
|
|
AttribDataDesc attrDesc = {{"attr_vertex1", VertexFormat::Float3, vertex1Ops},
|
|
{"attr_vertex2", VertexFormat::Float3, vertex2Ops},
|
|
{"a_Color", VertexFormat::UByte4Norm, colorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, color2Ops},
|
|
{"attr_radius", VertexFormat::Float}};
|
|
|
|
AttribDesc* fdesc;
|
|
static unsigned char cyl_flags[] = {
|
|
0, 4, 6, 2, 1, 5, 7, 3}; // right(4)/up(2)/out(1)
|
|
|
|
attrDesc.push_back({"attr_flags", VertexFormat::UByte});
|
|
fdesc = &attrDesc[attrDesc.size() - 1];
|
|
fdesc->repeat_value = cyl_flags;
|
|
fdesc->repeat_value_length = 8;
|
|
|
|
if (add_color) {
|
|
static unsigned char default_color[] = {
|
|
255, 255, 255, 255}; // to write in alpha if CGO doesn't have it
|
|
fdesc = &attrDesc[2];
|
|
fdesc->default_value = default_color;
|
|
fdesc = &attrDesc[3];
|
|
fdesc->default_value = default_color;
|
|
}
|
|
|
|
float default_radius = 1.f;
|
|
attrDesc[4].default_value = (unsigned char*) &default_radius;
|
|
|
|
int box_indices[36] = {// box indices
|
|
0, 2, 1, 2, 0, 3, 1, 6, 5, 6, 1, 2, 0, 1, 5, 5, 4, 0, 0, 7, 3, 7, 0, 4, 3,
|
|
6, 2, 6, 3, 7, 4, 5, 6, 6, 7, 4};
|
|
int* box_indices_ptr = nullptr;
|
|
box_indices_ptr = box_indices;
|
|
|
|
bool interp_same, interp_value = false;
|
|
if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))) {
|
|
addTo->add<cgo::draw::vertex_attribute_1f>(
|
|
G->ShaderMgr->GetAttributeUID("a_cap"),
|
|
(cCylShaderBothCapsRound | (interp_value ? cCylShaderInterpColor : 0)));
|
|
} else {
|
|
AttribDataOp interpOps = {{CGO_SPLITLINE, 1, UB1_INTERP_TO_CAP,
|
|
offsetof(cgo::draw::splitline, flags), 0}};
|
|
// need to add a_cap attribute
|
|
attrDesc.push_back({"a_cap", VertexFormat::UByte, interpOps});
|
|
}
|
|
|
|
if (!add_color) {
|
|
attrDesc.erase(attrDesc.begin() + 2); // attr_colors
|
|
attrDesc.erase(attrDesc.begin() + 2); // attr_colors2
|
|
}
|
|
|
|
AttribDataOp extraPickColorOps = {
|
|
{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0},
|
|
{CGO_SPLITLINE, 7, UINT_INT_TO_PICK_DATA,
|
|
offsetof(cgo::draw::splitline, index), 0}};
|
|
AttribDataOp extraPickColor2Ops = {
|
|
{CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0},
|
|
{CGO_SPLITLINE, 4, UINT_INT_TO_PICK_DATA,
|
|
offsetof(cgo::draw::splitline, index), 0}};
|
|
AttribDataDesc pickDesc = {
|
|
{"a_Color", VertexFormat::UByte4Norm, extraPickColorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}};
|
|
return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES,
|
|
VertexBufferLayout::Interleaved, true, box_indices_ptr, 36);
|
|
}
|
|
|
|
struct CrossSizeData {
|
|
float cross_size;
|
|
bool forward;
|
|
CrossSizeData(float _cross_size, bool _forward)
|
|
: cross_size(_cross_size)
|
|
, forward(_forward)
|
|
{
|
|
}
|
|
};
|
|
|
|
static void CrossVertexConversion(
|
|
void* varData, const float* pc, void* crossData, int idx)
|
|
{
|
|
CrossSizeData* csd = (CrossSizeData*) crossData;
|
|
int idxpl = idx / 8; // X Y or Z
|
|
float* varDataF = ((float*) varData);
|
|
varDataF[idxpl] += (csd->forward ? csd->cross_size : -csd->cross_size);
|
|
}
|
|
|
|
CGO* CGOConvertCrossesToCylinderShader(
|
|
const CGO* I, CGO* addTo, float cross_size_arg)
|
|
{
|
|
/* Lines that pass in two vertices per line */
|
|
PyMOLGlobals* G = I->G;
|
|
AttribDataOp vertex1Ops = {{CGO_VERTEX_CROSS, 1, FLOAT3_TO_FLOAT3, 0, 0}};
|
|
AttribDataOp vertex2Ops = {
|
|
{CGO_VERTEX_CROSS, 2, FLOAT3_TO_FLOAT3, 0, 3 * 8, 0}};
|
|
|
|
static AttribDataOp colorOps = {
|
|
{CGO_COLOR, 0, FLOAT3_TO_UB3, 0}, {CGO_ALPHA, 0, FLOAT1_TO_UB_4TH, 0}};
|
|
static AttribDataOp color2Ops = {
|
|
{CGO_COLOR, 1, FLOAT3_TO_UB3, 0}, {CGO_ALPHA, 1, FLOAT1_TO_UB_4TH, 0}};
|
|
|
|
CrossSizeData crossData[] = {{cross_size_arg, false}, {cross_size_arg, true}};
|
|
AttribDataDesc attrDesc = {{"attr_vertex1", VertexFormat::Float3, vertex1Ops},
|
|
{"attr_vertex2", VertexFormat::Float3, vertex2Ops},
|
|
{"a_Color", VertexFormat::UByte4Norm, colorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, color2Ops},
|
|
{"attr_radius", VertexFormat::Float}};
|
|
|
|
attrDesc.reserve(10);
|
|
attrDesc[1].attrOps[0].funcDataConversions.push_back(
|
|
{CrossVertexConversion, &crossData[0], "attr_vertex1"});
|
|
attrDesc[1].attrOps[0].funcDataConversions.push_back(
|
|
{CrossVertexConversion, &crossData[1], "attr_vertex2"});
|
|
|
|
AttribDesc* fdesc;
|
|
static unsigned char cyl_flags[] = {
|
|
0, 4, 6, 2, 1, 5, 7, 3}; // right(4)/up(2)/out(1)
|
|
|
|
attrDesc.push_back({"attr_flags", VertexFormat::UByte});
|
|
fdesc = &attrDesc[attrDesc.size() - 1];
|
|
fdesc->repeat_value = cyl_flags;
|
|
fdesc->repeat_value_length = 8;
|
|
|
|
unsigned char default_color[] = {
|
|
255, 255, 255, 255}; // to write in alpha if CGO doesn't have it
|
|
fdesc = &attrDesc[2];
|
|
fdesc->default_value = default_color;
|
|
fdesc = &attrDesc[3];
|
|
fdesc->default_value = default_color;
|
|
float default_radius = 1.f;
|
|
attrDesc[4].default_value = (unsigned char*) &default_radius;
|
|
|
|
int box_indices[36] = {// box indices
|
|
0, 2, 1, 2, 0, 3, 1, 6, 5, 6, 1, 2, 0, 1, 5, 5, 4, 0, 0, 7, 3, 7, 0, 4, 3,
|
|
6, 2, 6, 3, 7, 4, 5, 6, 6, 7, 4};
|
|
int* box_indices_ptr = nullptr;
|
|
box_indices_ptr = box_indices;
|
|
|
|
addTo->add<cgo::draw::vertex_attribute_1f>(
|
|
G->ShaderMgr->GetAttributeUID("a_cap"), cCylShaderBothCapsRound);
|
|
|
|
AttribDataOp extraPickColorOps = {
|
|
{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0}};
|
|
AttribDataOp extraPickColor2Ops = {
|
|
{CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0}};
|
|
AttribDataDesc pickDesc = {
|
|
{"a_Color", VertexFormat::UByte4Norm, extraPickColorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}};
|
|
return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES,
|
|
VertexBufferLayout::Interleaved, true, box_indices_ptr, 36, 3);
|
|
}
|
|
|
|
struct CrossSizeDataLines {
|
|
float cross_size;
|
|
CrossSizeDataLines(float _cross_size)
|
|
: cross_size(_cross_size)
|
|
{
|
|
}
|
|
};
|
|
|
|
static void CrossVertexConversionLines(
|
|
void* varData, const float* pc, void* crossData, int idx)
|
|
{
|
|
CrossSizeDataLines* csd = (CrossSizeDataLines*) crossData;
|
|
int idxpl = idx / 2; // X Y or Z
|
|
bool forward = idx % 2;
|
|
float* varDataF = ((float*) varData);
|
|
varDataF[idxpl] += (forward ? csd->cross_size : -csd->cross_size);
|
|
}
|
|
|
|
CGO* CGOConvertCrossesToLinesShader(
|
|
const CGO* I, CGO* addTo, float cross_size_arg)
|
|
{
|
|
/* Lines that pass in two vertices per line */
|
|
PyMOLGlobals* G = I->G;
|
|
AttribDataOp vertexOps = {
|
|
{CGO_VERTEX_CROSS, 1, FLOAT3_TO_FLOAT3, 0, 6}}; // 6 vertices for a cross
|
|
AttribDataOp colorOps = {
|
|
{CGO_COLOR, 0, FLOAT3_TO_UB3, 0}, {CGO_ALPHA, 0, FLOAT1_TO_UB_4TH, 0}};
|
|
AttribDataOp extraPickColorOps = {
|
|
{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0}};
|
|
|
|
AttribDataDesc pickDesc = {
|
|
{"a_Color", VertexFormat::UByte4Norm, extraPickColorOps}};
|
|
AttribDataDesc attrDesc = {{"a_Vertex", VertexFormat::Float3, vertexOps},
|
|
{"a_Color", VertexFormat::UByte4Norm, colorOps}};
|
|
unsigned char default_color[] = {
|
|
255, 255, 255, 255}; // to write in alpha if CGO doesn't have it
|
|
attrDesc[1].default_value = default_color;
|
|
|
|
CrossSizeDataLines crossData = {cross_size_arg};
|
|
attrDesc[0].attrOps[0].funcDataConversions.push_back(
|
|
{CrossVertexConversionLines, &crossData, "a_Vertex"});
|
|
|
|
bool interp_same, interp_value = false;
|
|
if ((interp_same = CGOCheckSplitLineInterpolationIsSame(I, interp_value))) {
|
|
addTo->add<cgo::draw::vertex_attribute_1f>(
|
|
G->ShaderMgr->GetAttributeUID("a_interpolate"),
|
|
interp_value ? 1.f : 0.f);
|
|
} else {
|
|
AttribDataOp interpOps = {{CGO_SPLITLINE, 1, UB1_TO_INTERP,
|
|
offsetof(cgo::draw::splitline, flags), 0}};
|
|
// need to add a_interpolate attribute
|
|
attrDesc.push_back({"a_interpolate", VertexFormat::UByte, interpOps});
|
|
}
|
|
#ifndef PURE_OPENGL_ES_2
|
|
{
|
|
attrDesc.push_back({"a_line_position", VertexFormat::UByte});
|
|
AttribDesc* lpdesc = &attrDesc[attrDesc.size() - 1];
|
|
lpdesc->repeat_value_length = 2;
|
|
static unsigned char flip_bits[] = {0, 1};
|
|
lpdesc->repeat_value = flip_bits;
|
|
}
|
|
#endif
|
|
return CGOConvertToShader(
|
|
I, attrDesc, pickDesc, GL_LINES, VertexBufferLayout::Interleaved);
|
|
}
|
|
|
|
static void CrossVertexConversionTrilines(
|
|
void* varData, const float* pc, void* crossData, int idx)
|
|
{
|
|
CrossSizeData* csd = (CrossSizeData*) crossData;
|
|
int idxpl = idx / 6; // X Y or Z
|
|
float* varDataF = ((float*) varData);
|
|
varDataF[idxpl] += (csd->forward ? csd->cross_size : -csd->cross_size);
|
|
}
|
|
|
|
CGO* CGOConvertCrossesToTrilinesShader(
|
|
const CGO* I, CGO* addTo, float cross_size_arg)
|
|
{
|
|
PyMOLGlobals* G = I->G;
|
|
|
|
AttribDataOp vertexOps = {{CGO_VERTEX_CROSS, 1, FLOAT3_TO_FLOAT3, 0, 0}};
|
|
AttribDataOp vertexOtherOps = {
|
|
{CGO_VERTEX_CROSS, 2, FLOAT3_TO_FLOAT3, 0, 6 * 3}};
|
|
AttribDataOp colorOps = {
|
|
{CGO_COLOR, 0, FLOAT3_TO_UB3, 0}, {CGO_ALPHA, 0, FLOAT1_TO_UB_4TH, 0}};
|
|
AttribDataOp color2Ops = {
|
|
{CGO_COLOR, 1, FLOAT3_TO_UB3, 0}, {CGO_ALPHA, 1, FLOAT1_TO_UB_4TH, 0}};
|
|
AttribDataOp extraPickColorOps = {
|
|
{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0}};
|
|
AttribDataOp extraPickColor2Ops = {
|
|
{CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0}};
|
|
AttribDataDesc pickDesc = {
|
|
{"a_Color", VertexFormat::UByte4Norm, extraPickColorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}};
|
|
AttribDataDesc attrDesc = {{"a_Vertex", VertexFormat::Float3, vertexOps},
|
|
{"a_OtherVertex", VertexFormat::Float3, vertexOtherOps},
|
|
{"a_Color", VertexFormat::UByte4Norm, colorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, color2Ops},
|
|
{"a_UV", VertexFormat::UByte}};
|
|
|
|
CrossSizeData crossData[] = {{cross_size_arg, false}, {cross_size_arg, true}};
|
|
|
|
attrDesc[1].attrOps[0].funcDataConversions.push_back(
|
|
{CrossVertexConversionTrilines, &crossData[0], "a_Vertex"});
|
|
attrDesc[1].attrOps[0].funcDataConversions.push_back(
|
|
{CrossVertexConversionTrilines, &crossData[1], "a_OtherVertex"});
|
|
|
|
unsigned char default_color[] = {
|
|
255, 255, 255, 255}; // to write in alpha if CGO doesn't have it
|
|
attrDesc[2].default_value = default_color;
|
|
attrDesc[3].default_value = default_color;
|
|
|
|
AttribDesc* uvdesc = &attrDesc[attrDesc.size() - 1];
|
|
uvdesc->repeat_value_length = 6;
|
|
static unsigned char uv_bits[] = {1, 3, 0, 3, 2, 1};
|
|
uvdesc->repeat_value = uv_bits;
|
|
|
|
addTo->add<cgo::draw::vertex_attribute_1f>(
|
|
G->ShaderMgr->GetAttributeUID("a_interpolate"), 0.f);
|
|
|
|
return CGOConvertToShader(
|
|
I, attrDesc, pickDesc, GL_TRIANGLES, VertexBufferLayout::Interleaved);
|
|
}
|
|
|
|
cgo::draw::shadercylinder2ndcolor::shadercylinder2ndcolor(CGO* I,
|
|
const float* _origin, const float* _axis, const float _tube_size, int _cap,
|
|
const float* _color2, Pickable* pickcolor2, const float _alpha)
|
|
: tube_size(_tube_size)
|
|
, alpha(_alpha)
|
|
{
|
|
copy3f(_origin, origin);
|
|
copy3f(_axis, axis);
|
|
cap = _cap;
|
|
copy3f(_color2, color2);
|
|
if (pickcolor2) {
|
|
I->current_pick_color_index = pick_color_index = pickcolor2->index;
|
|
I->current_pick_color_bond = pick_color_bond = pickcolor2->bond;
|
|
} else {
|
|
pick_color_index = I->current_pick_color_index;
|
|
pick_color_bond = I->current_pick_color_bond;
|
|
}
|
|
};
|
|
|
|
static void SetVertexFromOriginAxisForCylinder(
|
|
void* varData, const float* pc, void* np, int idx)
|
|
{
|
|
float* varDataF = ((float*) varData);
|
|
add3f(pc, pc + 3, varDataF); // adding origin and axis for both shadercylinder
|
|
// and shadercylinder2ndcolor
|
|
}
|
|
|
|
/**
|
|
* converts all cylinders in the input CGO to a CGO custom operation, which
|
|
* includes picking information (if it exists)
|
|
*
|
|
* I - input CGO (includes cylinders)
|
|
* addTo - CGO that vertex_attribute operations are added to (if needed), in
|
|
* this case for caps if values for all cylinders are the same
|
|
*
|
|
*/
|
|
CGO* CGOConvertShaderCylindersToCylinderShader(const CGO* I, CGO* addTo)
|
|
{
|
|
/* Lines that pass in two vertices per line */
|
|
PyMOLGlobals* G = I->G;
|
|
|
|
// TODO: NEED TO ADD: CGO_CUSTOM_CYLINDER and CGO_CYLINDER
|
|
AttribDataOp vertex1Ops = {
|
|
{CGO_SHADER_CYLINDER, 1, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::shadercylinder, origin), 0},
|
|
{CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 1, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::shadercylinder2ndcolor, origin), 0},
|
|
{CGO_SAUSAGE, 1, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::sausage, vertex1),
|
|
0},
|
|
{CGO_CYLINDER, 1, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::cylinder, vertex1), 0},
|
|
{CGO_CUSTOM_CYLINDER, 1, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::custom_cylinder, vertex1), 0},
|
|
{CGO_CUSTOM_CYLINDER_ALPHA, 1, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::custom_cylinder_alpha, vertex1), 0}};
|
|
AttribDataOp vertex2Ops = {{CGO_SHADER_CYLINDER, 5, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::shadercylinder, axis), 8},
|
|
{CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 6, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::shadercylinder2ndcolor, axis), 8},
|
|
{CGO_SAUSAGE, 6, FLOAT3_TO_FLOAT3, offsetof(cgo::draw::sausage, vertex2),
|
|
8},
|
|
{CGO_CYLINDER, 6, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::cylinder, vertex2), 8},
|
|
{CGO_CUSTOM_CYLINDER, 6, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::custom_cylinder, vertex2), 8},
|
|
{CGO_CUSTOM_CYLINDER_ALPHA, 6, FLOAT3_TO_FLOAT3,
|
|
offsetof(cgo::draw::custom_cylinder_alpha, vertex2), 8}};
|
|
static AttribDataOp colorOps = {{CGO_COLOR, 0, FLOAT3_TO_UB3, 0},
|
|
{CGO_ALPHA, 0, FLOAT1_TO_UB_4TH, 0},
|
|
{CGO_SAUSAGE, 4, FLOAT3_TO_UB3, offsetof(cgo::draw::sausage, color1)},
|
|
{CGO_CYLINDER, 4, FLOAT3_TO_UB3, offsetof(cgo::draw::cylinder, color1)},
|
|
{CGO_CUSTOM_CYLINDER, 4, FLOAT3_TO_UB3,
|
|
offsetof(cgo::draw::custom_cylinder, color1)},
|
|
{CGO_CUSTOM_CYLINDER_ALPHA, 4, FLOAT4_TO_UB4,
|
|
offsetof(cgo::draw::custom_cylinder_alpha, color1)}};
|
|
static AttribDataOp color2Ops = {{CGO_COLOR, 1, FLOAT3_TO_UB3, 0},
|
|
{CGO_ALPHA, 1, FLOAT1_TO_UB_4TH, 0},
|
|
{CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 2, FLOAT3_TO_UB3,
|
|
offsetof(cgo::draw::shadercylinder2ndcolor, color2)},
|
|
{CGO_SAUSAGE, 5, FLOAT3_TO_UB3, offsetof(cgo::draw::sausage, color2)},
|
|
{CGO_CYLINDER, 5, FLOAT3_TO_UB3, offsetof(cgo::draw::cylinder, color2)},
|
|
{CGO_CUSTOM_CYLINDER, 5, FLOAT3_TO_UB3,
|
|
offsetof(cgo::draw::custom_cylinder, color2)},
|
|
{CGO_CUSTOM_CYLINDER_ALPHA, 5, FLOAT4_TO_UB4,
|
|
offsetof(cgo::draw::custom_cylinder_alpha, color2)}};
|
|
AttribDataOp radiusOps = {
|
|
{CGO_SHADER_CYLINDER, 2, FLOAT_TO_FLOAT,
|
|
offsetof(cgo::draw::shadercylinder, tube_size), 0},
|
|
{CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 3, FLOAT_TO_FLOAT,
|
|
offsetof(cgo::draw::shadercylinder2ndcolor, tube_size), 0},
|
|
{CGO_SAUSAGE, 3, FLOAT_TO_FLOAT, offsetof(cgo::draw::sausage, radius), 0},
|
|
{CGO_CYLINDER, 3, FLOAT_TO_FLOAT, offsetof(cgo::draw::cylinder, radius),
|
|
0},
|
|
{CGO_CUSTOM_CYLINDER, 3, FLOAT_TO_FLOAT,
|
|
offsetof(cgo::draw::custom_cylinder, radius), 0},
|
|
{CGO_CUSTOM_CYLINDER_ALPHA, 3, FLOAT_TO_FLOAT,
|
|
offsetof(cgo::draw::custom_cylinder_alpha, radius), 0}};
|
|
|
|
AttribDataDesc attrDesc = {{"attr_vertex1", VertexFormat::Float3, vertex1Ops},
|
|
{"attr_vertex2", VertexFormat::Float3, vertex2Ops},
|
|
{"a_Color", VertexFormat::UByte4Norm, colorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, color2Ops},
|
|
{"attr_radius", VertexFormat::Float, radiusOps}};
|
|
|
|
AttribDesc* fdesc;
|
|
static unsigned char cyl_flags[] = {
|
|
0, 4, 6, 2, 1, 5, 7, 3}; // right(4)/up(2)/out(1)
|
|
|
|
attrDesc[1].attrOps[0].funcDataConversions.push_back(
|
|
{SetVertexFromOriginAxisForCylinder, nullptr, "attr_vertex2"});
|
|
attrDesc[1].attrOps[1].funcDataConversions.push_back(
|
|
{SetVertexFromOriginAxisForCylinder, nullptr, "attr_vertex2"});
|
|
|
|
attrDesc.push_back({"attr_flags", VertexFormat::UByte});
|
|
fdesc = &attrDesc[attrDesc.size() - 1];
|
|
fdesc->repeat_value = cyl_flags;
|
|
fdesc->repeat_value_length = 8;
|
|
|
|
unsigned char default_color[] = {
|
|
255, 255, 255, 255}; // to write in alpha if CGO doesn't have it
|
|
fdesc = &attrDesc[2];
|
|
fdesc->default_value = default_color;
|
|
fdesc = &attrDesc[3];
|
|
fdesc->default_value = default_color;
|
|
float default_radius = 1.f;
|
|
attrDesc[4].default_value = (unsigned char*) &default_radius;
|
|
|
|
int box_indices[36] = {// box indices
|
|
0, 2, 1, 2, 0, 3, 1, 6, 5, 6, 1, 2, 0, 1, 5, 5, 4, 0, 0, 7, 3, 7, 0, 4, 3,
|
|
6, 2, 6, 3, 7, 4, 5, 6, 6, 7, 4};
|
|
int* box_indices_ptr = nullptr;
|
|
box_indices_ptr = box_indices;
|
|
|
|
bool interp_same;
|
|
unsigned char cap_value = 0;
|
|
if ((interp_same = CGOCheckShaderCylinderCapInfoIsSame(I, cap_value))) {
|
|
addTo->add<cgo::draw::vertex_attribute_1f>(
|
|
G->ShaderMgr->GetAttributeUID("a_cap"), cap_value);
|
|
} else {
|
|
AttribDataOp interpOps = {
|
|
{CGO_SHADER_CYLINDER, 3, CYL_CAP_TO_CAP,
|
|
offsetof(cgo::draw::shadercylinder, cap), 0},
|
|
{CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 4, CYL_CAP_TO_CAP,
|
|
offsetof(cgo::draw::shadercylinder2ndcolor, cap), 0},
|
|
{CGO_SAUSAGE, 2, CYL_CAPS_ARE_ROUND, 0, 0},
|
|
{CGO_CYLINDER, 2, CYL_CAPS_ARE_FLAT, 0, 0},
|
|
{CGO_CUSTOM_CYLINDER, 2, CYL_CAPS_ARE_CUSTOM,
|
|
offsetof(cgo::draw::custom_cylinder, cap1), 0},
|
|
{CGO_CUSTOM_CYLINDER_ALPHA, 2, CYL_CAPS_ARE_CUSTOM,
|
|
offsetof(cgo::draw::custom_cylinder_alpha, cap1), 0},
|
|
};
|
|
attrDesc.push_back({"a_cap", VertexFormat::UByte, interpOps});
|
|
}
|
|
|
|
AttribDataOp extraPickColorOps = {
|
|
{CGO_PICK_COLOR, 1, UINT_INT_TO_PICK_DATA, 0, 0},
|
|
{CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 8, UINT_INT_TO_PICK_DATA,
|
|
offsetof(cgo::draw::shadercylinder2ndcolor, pick_color_index), 0}};
|
|
AttribDataOp extraPickColor2Ops = {
|
|
{CGO_PICK_COLOR, 2, UINT_INT_TO_PICK_DATA, 0, 0},
|
|
{CGO_SHADER_CYLINDER_WITH_2ND_COLOR, 5, UINT_INT_TO_PICK_DATA,
|
|
offsetof(cgo::draw::shadercylinder2ndcolor, pick_color_index), 0}};
|
|
AttribDataDesc pickDesc = {
|
|
{"a_Color", VertexFormat::UByte4Norm, extraPickColorOps},
|
|
{"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}};
|
|
return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES,
|
|
VertexBufferLayout::Interleaved, true, box_indices_ptr, 36);
|
|
}
|
|
|
|
/**
|
|
* CGO iterator increment
|
|
*/
|
|
CGO::const_iterator& CGO::const_iterator::operator++()
|
|
{
|
|
const unsigned op = op_code();
|
|
|
|
// Corrupted OP codes should never make it into a CGO, so we don't want to
|
|
// handle this gracefully. Only import of CGOs (from PSEs, from Python, etc.)
|
|
// should handle invalid codes gracefully.
|
|
assert(op < CGO_sz_size());
|
|
|
|
m_pc += CGO_sz[op] + 1;
|
|
return *this;
|
|
}
|
|
|
|
void CGORender(CGO* I, const float* color, CSetting* set1, CSetting* set2,
|
|
RenderInfo* info, Rep* rep)
|
|
{
|
|
CGORenderGL(I, color, set1, set2, info, rep);
|
|
}
|
|
|
|
void CGORenderPicking(CGO* I, RenderInfo* info, PickContext* context,
|
|
CSetting* set1, CSetting* set2, Rep* rep)
|
|
{
|
|
CGORenderGLPicking(I, info, context, set1, set2, rep);
|
|
}
|
|
|
|
void CGORenderAlpha(CGO* I, RenderInfo* info, bool calcDepth)
|
|
{
|
|
CGORenderGLAlpha(I, info, calcDepth);
|
|
}
|