SimpleFastOpenAtomicVisualiser
Loading...
Searching...
No Matches
bondRenderer.h
Go to the documentation of this file.
1#ifndef BONDRENDERER_H
2#define BONDRENDERER_H
3
4#include <map>
5#include <vector>
6#include <cstdint>
7
8#include <jGL/OpenGL/gl.h>
9
10#include <glUtils.h>
11#include <atom.h>
12
18{
19public:
20
32 (
33 const std::map<uint64_t, std::set<uint64_t>> & bonds,
34 const std::vector<Atom> & atoms,
35 uint64_t maxBonds,
36 uint64_t bondPad = 1024,
37 float clipCorrection = 5.0f
38 )
39 : nBonds(0), bondPadding(bondPad)
40 {
41 maximumBonds = bondPadding;
42 for (const auto & ibonds : bonds)
43 {
44 maximumBonds += ibonds.second.size();
45 }
46
47 shader = std::make_unique<jGL::GL::glShader>(vertexShader, fragmentShader);
48 shader->use();
49 shader->setUniform<float>("clipCorrection", clipCorrection);
50 shader->setUniform<glm::vec4>("lightColour", glm::vec4(1.0f,1.0f,1.0f,1.0f));
51 shader->setUniform<float>("ambientLight", 0.1f);
52 setBondScale(1.0f);
53 setGlobalAlpha(globalAlpha);
54 init();
55 for (const auto & ibonds : bonds)
56 {
57 for (const auto & j : ibonds.second)
58 {
59 insert({ibonds.first, j}, atoms);
60 }
61 }
62
63 updateVertexArray();
64 }
65
67 {
68 deallocate();
69 }
70
81 void setClipCorrection(float correction)
82 {
83 shader->use();
84 shader->setUniform<float>("clipCorrection", correction);
85 }
86
92 void setView(glm::mat4 v) { view = v; setProjectionView(); }
93
99 void setProjection(glm::mat4 p) { projection = p; setProjectionView(); }
100
109 (
110 glm::vec3 position,
111 glm::vec3 colour = {1.0f, 1.0f, 1.0f},
112 float ambient = 0.1f
113 )
114 {
115 cameraPosition = position;
116 shader->use();
117 shader->setUniform<glm::vec4>("lightPos", glm::vec4(position, 1.0f));
118 shader->setUniform<glm::vec4>("lightColour", glm::vec4(colour, 1.0f));
119 shader->setUniform<float>("ambientLight", ambient);
120 }
121
128 void updateCamera(const Camera & camera)
129 {
130 cameraPosition = camera.position();
131 shader->use();
132 shader->setUniform<glm::vec4>("lightPos", glm::vec4(cameraPosition, 1.0f));
133 view = camera.getView();
134 projection = camera.getProjection();
135 setProjectionView();
136 if (transparencySortingEnabled) { updateVertexArray(); };
137 }
138
145 {
146 transparencySortingEnabled = sort;
147 if (transparencySortingEnabled) { updateVertexArray(); };
148 }
149
155 void setBondScale(float scale)
156 {
157 this->scale = scale;
158 shader->use();
159 shader->setUniform<float>("bondScale", scale);
160 }
161
167 uint64_t triangles() const
168 {
169 return nBonds*2;
170 }
171
181 (
182 const std::map<uint64_t, std::set<uint64_t>> & bonds,
183 const std::vector<Atom> & atoms
184 )
185 {
186 uint64_t bCount = 0;
187 for (const auto & ibonds : bonds)
188 {
189 bCount += ibonds.second.size();
190 }
191 if (bCount > maximumBonds)
192 {
193 std::cout << "Reallocating bonds:\n"
194 << " New bonds size: " << bCount
195 << "\n Storage: " << maximumBonds
196 << "\n New storage: " << bCount+bondPadding
197 << "\n";
198 deallocate();
199 maximumBonds = bCount+bondPadding;
200 init();
201 }
202 flip();
203 for (const auto & ibonds : bonds)
204 {
205 for (const auto & j : ibonds.second)
206 {
207 insert({ibonds.first, j}, atoms);
208 }
209 }
210 updateVertexArray();
211 }
212
218 void draw(uint64_t count)
219 {
220 count = std::min(count, nBonds);
221 if (count == 0) { return; }
222
223 shader->use();
224
225 glEnable(GL_BLEND);
226 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
227 glEnable(GL_DEPTH_TEST);
228 glEnable(GL_CULL_FACE);
229 glCullFace(GL_BACK);
230
231 glFrontFace(GL_CW);
232 glBindVertexArray(vao);
233
234 glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count);
235
236 glBindVertexArray(0);
237 glFrontFace(GL_CCW);
238 }
239
244 void draw() { draw(nBonds); }
245
252 void setGlobalAlpha(float alpha)
253 {
254 globalAlpha = std::max(0.0f, std::min(alpha, 1.0f));
255 shader->use();
256 shader->setUniform<float>("globalAlpha", globalAlpha);
257 }
258
259
260private:
261
262 uint64_t nBonds;
263 uint64_t bondPadding;
264 uint64_t maximumBonds;
265 std::unique_ptr<jGL::GL::glShader> shader;
266 glm::vec3 cameraPosition = glm::vec3(0.0f);
267 float scale = 1.0f;
268
269 GLuint vao, a_vertices, b_vertices, a_colours, b_colours, a_quad;
270 std::vector<float> positionsAAndScale;
271 std::vector<float> positionsBAndScale;
272 std::vector<float> coloursA;
273 std::vector<float> coloursB;
274
275 glm::mat4 view, projection;
276
277 uint64_t index = 0;
278 bool transparencySortingEnabled = true;
279
280 float globalAlpha;
281
282 const std::array<float, 8> quad =
283 {
284 -1.0,-1.0,
285 -1.0,1.0,
286 1.0,-1.0,
287 1.0,1.0
288 };
289
290 const char * vertexShader =
291 "#version " GLSL_VERSION "\n"
292 "precision lowp float; precision lowp int;\n"
293 "layout(location=0) in vec2 a_vertices;\n"
294 "layout(location=1) in vec4 a_positionsAAndScales;\n"
295 "layout(location=2) in vec4 a_positionsBAndScales;\n"
296 "layout(location=3) in vec4 a_colours;\n"
297 "layout(location=4) in vec4 b_colours;\n"
298 "out vec2 billboard;\n"
299 "uniform mat4 view;\n"
300 "uniform mat4 proj;\n"
301 "uniform float clipCorrection;\n"
302 "uniform float bondScale;\n"
303 "out vec4 aPosScale;\n"
304 "out vec3 aViewPos;\n"
305 "out vec4 bPosScale;\n"
306 "out vec3 bViewPos;\n"
307 "out vec4 a_colour;\n"
308 "out vec4 b_colour;\n"
309 "out vec3 comViewPos;\n"
310 "void main()\n"
311 "{\n"
312 " billboard = a_vertices * clipCorrection;\n"
313 " aViewPos = (view * vec4(a_positionsAAndScales.xyz, 1.0)).xyz;"
314 " bViewPos = (view * vec4(a_positionsBAndScales.xyz, 1.0)).xyz;"
315 " comViewPos = (aViewPos+bViewPos)*0.5;\n"
316 " gl_Position = proj * (vec4(comViewPos, 1.0)+vec4(bondScale * a_vertices * clipCorrection, 0.0, 1.0));"
317 " aPosScale = a_positionsAAndScales;\n"
318 " bPosScale = a_positionsBAndScales;\n"
319 " a_colour = a_colours;\n"
320 " b_colour = b_colours;\n"
321 "}";
322
323 const char * fragmentShader =
324 "#version " GLSL_VERSION "\n"
325 "precision lowp float; precision lowp int;\n"
326 "in vec2 billboard;\n"
327 "in vec3 aViewPos;\n"
328 "in vec3 bViewPos;\n"
329 "in vec4 aPosScale;\n"
330 "in vec4 bPosScale;\n"
331 "in vec4 a_colour;\n"
332 "in vec4 b_colour;\n"
333 "in vec3 comViewPos;\n"
334 "out vec4 colour;\n"
335 "uniform mat4 view;\n"
336 "uniform mat4 proj;\n"
337 "uniform vec4 lightPos;\n"
338 "uniform vec4 lightColour;\n"
339 "uniform float ambientLight;\n"
340 "uniform float bondScale;\n"
341 "uniform float globalAlpha;\n"
342 "bool sphereHit(vec3 rayDirection, vec3 centre, float radius, out vec3 pos, out vec3 normal)\n"
343 "{\n"
344 " float b = 2.0 * dot(rayDirection, -centre);\n"
345 " float r2 = radius*radius;\n"
346 " float determinant = b * b - (4.0 * (dot(centre, centre) - r2));\n"
347 " if(determinant < 0.0) { return false; }\n"
348 " determinant = sqrt(determinant);\n"
349 " pos = rayDirection * min((-b+determinant)*0.5, (-b-determinant)*0.5);\n"
350 " normal = normalize(pos - centre);\n"
351 " return true;\n"
352 "}\n"
353 /*
354 * Creates a capsule (sphere integrated along a line).
355 * .-.B
356 * / / \
357 * / / /
358 * / / /
359 * / / /
360 * / / /
361 * \ / /
362 * A--
363 *
364 * To derive, consider in view space a ray r(t) = t*v, and a capsule with midline c(s) = a+s*n.
365 * Then solve the quadratic ||a-r(t)-[(a-r(t).n)]n||^2-R^2 = 0 (=> r(t)->midline distance = R);
366 * Also then project the hit point to the midline for normals.
367 */
368 "bool capsuleHit"
369 "("
370 " vec3 rayDirection,"
371 " vec3 capsuleDirection,"
372 " vec3 capsuleA,"
373 " vec3 capsuleB,"
374 " float radius,"
375 " float midLength,"
376 " out vec3 viewPos,"
377 " out vec3 viewNormal,"
378 " out vec3 projectedHitPoint,"
379 " out float s"
380 ")\n"
381 "{\n"
382 " float a = dot(capsuleA, capsuleDirection);"
383 " float b = dot(rayDirection, capsuleDirection);\n"
384 " float c = dot(capsuleA, rayDirection);\n"
385 " float l = dot(capsuleA, capsuleA);\n"
386 " float r2 = radius*radius;\n"
387 " float determinant = (a*b-c)*(a*b-c)-(1.0-b*b)*(l-a*a-r2);\n"
388 " if (determinant < 0)\n"
389 " {\n"
390 " if (sphereHit(rayDirection, capsuleA, radius, viewPos, viewNormal)){ projectedHitPoint = capsuleA; s = 0.0; return true; }\n"
391 " if (sphereHit(rayDirection, capsuleB, radius, viewPos, viewNormal)){ projectedHitPoint = capsuleB; s = midLength; return true; }\n"
392 " return false;"
393 " \n}"
394 " float d = sqrt(determinant);\n"
395 // Ray hit point.
396 " float t = min( (-(a*b-c)+d)/(1.0-b*b), (-(a*b-c)-d)/(1.0-b*b) );"
397 " viewPos = rayDirection * t;\n"
398 " s = -dot(capsuleA-viewPos, capsuleDirection);\n"
399 " if (s < 0){\n"
400 " if (sphereHit(rayDirection, capsuleA, radius, viewPos, viewNormal)){ projectedHitPoint = capsuleA; s = 0.0; return true; }\n"
401 " return false;"
402 " \n}"
403 " if (s > midLength){\n"
404 " if (sphereHit(rayDirection, capsuleB, radius, viewPos, viewNormal)){ projectedHitPoint = capsuleB; s = midLength; return true; }\n"
405 " return false;"
406 " }"
407 " vec3 u = s*capsuleDirection;\n"
408 " projectedHitPoint = capsuleA+u;\n"
409 " viewNormal = normalize(viewPos-projectedHitPoint);\n"
410 " return true;\n"
411 "}"
412 "void main()\n"
413 "{\n"
414 " vec3 lightViewPos = (view*lightPos).xyz;\n"
415 " vec3 rayDirection = normalize(vec3(billboard * bondScale, 0.0) + comViewPos);"
416 " float midLength = length(bViewPos-aViewPos);\n"
417 " vec3 capsuleDirection = normalize(bViewPos-aViewPos);\n"
418 " vec3 viewPos; vec3 viewNormal; vec3 projectedHitPoint; float s;\n"
419 " bool hit = capsuleHit(rayDirection, capsuleDirection, aViewPos, bViewPos, bondScale, midLength, viewPos, viewNormal, projectedHitPoint, s);\n"
420 " if (!hit) { discard; }\n"
421 " else\n"
422 " {\n"
423 " vec4 clipPos = proj * vec4(viewPos, 1.0);\n"
424 " float ndcDepth = clipPos.z / clipPos.w;\n"
425 " gl_FragDepth = ((gl_DepthRange.diff * ndcDepth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;\n"
426 " float diff = max(dot(viewNormal, normalize(lightViewPos-projectedHitPoint)), 0.0);\n"
427 " vec4 col = a_colour;\n"
428 " if (s > 0.5*midLength) { col = b_colour; }\n"
429 " colour = vec4((ambientLight + diff)*lightColour.rgb * col.rgb, col.a*globalAlpha);\n"
430 " }\n"
431 "}";
432
433 void deallocate()
434 {
435 glDeleteVertexArrays(1, &vao);
436 glDeleteBuffers(1, &a_vertices);
437 glDeleteBuffers(1, &b_vertices);
438 glDeleteBuffers(1, &a_colours);
439 glDeleteBuffers(1, &b_colours);
440 glDeleteBuffers(1, &a_quad);
441 }
442
443 void setProjectionView()
444 {
445 shader->setUniform<glm::mat4>("view", view);
446 shader->setUniform<glm::mat4>("proj", projection);
447 }
448
449 void init()
450 {
451 glGenVertexArrays(1, &vao);
452 glGenBuffers(1, &a_vertices);
453 glGenBuffers(1, &b_vertices);
454 glGenBuffers(1, &a_colours);
455 glGenBuffers(1, &b_colours);
456 glGenBuffers(1, &a_quad);
457
458 positionsAAndScale.resize(4*maximumBonds);
459 positionsBAndScale.resize(4*maximumBonds);
460 coloursA.resize(4*maximumBonds);
461 coloursB.resize(4*maximumBonds);
462
463 glBindVertexArray(vao);
464
466 (
467 a_quad,
468 quad.data(),
469 quad.size(),
470 GL_STATIC_DRAW,
471 0,
472 2,
473 0
474 );
475
477 (
478 a_vertices,
479 positionsAAndScale.data(),
480 positionsAAndScale.size(),
481 GL_DYNAMIC_DRAW,
482 1,
483 4,
484 1
485 );
486
488 (
489 b_vertices,
490 positionsBAndScale.data(),
491 positionsBAndScale.size(),
492 GL_DYNAMIC_DRAW,
493 2,
494 4,
495 1
496 );
497
499 (
500 a_colours,
501 coloursA.data(),
502 coloursA.size(),
503 GL_DYNAMIC_DRAW,
504 3,
505 4,
506 1
507 );
508
510 (
511 b_colours,
512 coloursB.data(),
513 coloursB.size(),
514 GL_DYNAMIC_DRAW,
515 4,
516 4,
517 1
518 );
519 glBindVertexArray(0);
520 }
521
526 void flip() { index = 0; nBonds = 0; }
527
534 void insert
535 (
536 const std::pair<uint64_t, uint64_t> & bond,
537 const std::vector<Atom> & atoms
538 )
539 {
540 const Atom & a = atoms[bond.first];
541 const Atom & b = atoms[bond.second];
542 if (a.colour.a == 0.0 && b.colour.a == 0.0) { return; }
543
544 positionsAAndScale[index] = a.position.x;
545 positionsAAndScale[index+1] = a.position.y;
546 positionsAAndScale[index+2] = a.position.z;
547 positionsAAndScale[index+3] = a.scale;
548
549 positionsBAndScale[index] = b.position.x;
550 positionsBAndScale[index+1] = b.position.y;
551 positionsBAndScale[index+2] = b.position.z;
552 positionsBAndScale[index+3] = b.scale;
553
554 coloursA[index] = a.colour.r;
555 coloursA[index+1] = a.colour.g;
556 coloursA[index+2] = a.colour.b;
557 coloursA[index+3] = a.colour.a;
558
559 coloursB[index] = b.colour.r;
560 coloursB[index+1] = b.colour.g;
561 coloursB[index+2] = b.colour.b;
562 coloursB[index+3] = b.colour.a;
563
564 index += 4;
565 nBonds++;
566 }
567
572 void updateVertexArray()
573 {
574 depthSort();
575 glBindVertexArray(vao);
576
577 subFullBuffer(a_vertices, positionsAAndScale.data(), positionsAAndScale.size());
578 subFullBuffer(b_vertices, positionsBAndScale.data(), positionsBAndScale.size());
579 subFullBuffer(a_colours, coloursA.data(), coloursA.size());
580 subFullBuffer(b_colours, coloursB.data(), coloursB.size());
581
582 glBindVertexArray(0);
583 }
584
585 void depthSort()
586 {
587 // CPU expensive.
588 if (!transparencySortingEnabled || nBonds == 0) { return; }
589 // NB pushing to a vector and std::sort'ing it is an order
590 // of magnitude faster than pushing to an std::map.
591 // Checked with 1,000,000 atoms (10 fps vs. 1-2).
592 std::vector<std::pair<float, uint64_t>> order;
593 order.reserve(nBonds);
594 for (uint64_t i = 0; i < nBonds; i++)
595 {
596
597 float rx = 0.5f*(positionsAAndScale[i*4]+positionsBAndScale[i*4])-cameraPosition.x;
598 float ry = 0.5f*(positionsAAndScale[i*4+1]+positionsBAndScale[i*4+1])-cameraPosition.y;
599 float rz = 0.5f*(positionsAAndScale[i*4+2]+positionsBAndScale[i*4+2])-cameraPosition.z;
600 float d2 = rx*rx+ry*ry+rz*rz+scale*scale;
601 order.push_back({d2, i});
602 }
603 std::sort
604 (
605 order.begin(),
606 order.end(),
607 []
608 (
609 const std::pair<float, uint64_t> & a,
610 const std::pair<float, uint64_t> & b
611 )
612 {
613 return a.first > b.first;
614 }
615 );
616 // Apply the re-ordering of the data.
617 index = 0;
618 std::vector<float> positionsAAndScale_tmp = positionsAAndScale;
619 std::vector<float> positionsBAndScale_tmp = positionsBAndScale;
620 std::vector<float> coloursA_tmp = coloursA;
621 std::vector<float> coloursB_tmp = coloursB;
622 for (auto iter = order.begin(); iter != order.end(); iter++)
623 {
624 positionsAAndScale[index] = positionsAAndScale_tmp[iter->second*4];
625 positionsAAndScale[index+1] = positionsAAndScale_tmp[iter->second*4+1];
626 positionsAAndScale[index+2] = positionsAAndScale_tmp[iter->second*4+2];
627 positionsAAndScale[index+3] = positionsAAndScale_tmp[iter->second*4+3];
628
629 coloursA[index] = coloursA_tmp[iter->second*4];
630 coloursA[index+1] = coloursA_tmp[iter->second*4+1];
631 coloursA[index+2] = coloursA_tmp[iter->second*4+2];
632 coloursA[index+3] = coloursA_tmp[iter->second*4+3];
633
634 positionsBAndScale[index] = positionsBAndScale_tmp[iter->second*4];
635 positionsBAndScale[index+1] = positionsBAndScale_tmp[iter->second*4+1];
636 positionsBAndScale[index+2] = positionsBAndScale_tmp[iter->second*4+2];
637 positionsBAndScale[index+3] = positionsBAndScale_tmp[iter->second*4+3];
638
639 coloursB[index] = coloursB_tmp[iter->second*4];
640 coloursB[index+1] = coloursB_tmp[iter->second*4+1];
641 coloursB[index+2] = coloursB_tmp[iter->second*4+2];
642 coloursB[index+3] = coloursB_tmp[iter->second*4+3];
643 index += 4;
644 }
645 }
646};
647#endif /* BONDRENDERER_H */
An atom structure.
Definition atom.h:20
glm::vec3 position
Definition atom.h:78
float scale
Definition atom.h:79
glm::vec4 colour
Definition atom.h:80
Render Bonds as ray-traced cylinders.
Definition bondRenderer.h:18
void draw()
Draw all the bonds.
Definition bondRenderer.h:244
void setLighting(glm::vec3 position, glm::vec3 colour={1.0f, 1.0f, 1.0f}, float ambient=0.1f)
Set the lighting of the scene.
Definition bondRenderer.h:109
BondRenderer(const std::map< uint64_t, std::set< uint64_t > > &bonds, const std::vector< Atom > &atoms, uint64_t maxBonds, uint64_t bondPad=1024, float clipCorrection=5.0f)
Construct a new BondRenderer from set bonds and atoms.
Definition bondRenderer.h:32
void setProjection(glm::mat4 p)
Set the Projection matrix.
Definition bondRenderer.h:99
void updateCamera(const Camera &camera)
Update shaders from a Camera.
Definition bondRenderer.h:128
void setClipCorrection(float correction)
Set the clip correction.
Definition bondRenderer.h:81
~BondRenderer()
Definition bondRenderer.h:66
void setBondScale(float scale)
Set the uniform radii of bonds.
Definition bondRenderer.h:155
void draw(uint64_t count)
Draw the bonds.
Definition bondRenderer.h:218
void setTransparencySorting(bool sort)
Set whether transparency sorting is performed.
Definition bondRenderer.h:144
uint64_t triangles() const
The number of triangles drawn.
Definition bondRenderer.h:167
void update(const std::map< uint64_t, std::set< uint64_t > > &bonds, const std::vector< Atom > &atoms)
Update the bonds rendered on the GPU.
Definition bondRenderer.h:181
void setGlobalAlpha(float alpha)
Set the global alpha multiplier.
Definition bondRenderer.h:252
void setView(glm::mat4 v)
Set the view matrix.
Definition bondRenderer.h:92
A 3D projective camera centered on a focus moving on a sphere.
Definition camera.h:30
glm::mat4 getProjection() const
Get the Projection matrix.
Definition camera.h:188
glm::mat4 getView() const
Get the View matrix.
Definition camera.h:202
glm::vec3 position(bool spherical=false) const
Return the cartesian position vector.
Definition camera.h:163
void subFullBuffer(GLuint &buffer, float *data, GLuint size)
Substitute a GL_ARRAY_BUFFER fully.
Definition glUtils.h:15
void createBuffer(GLuint &buffer, const float *data, GLuint dataSize, int drawType, GLuint attribute, GLuint size, GLuint divisor)
Create a GL_ARRAY_BUFFER from data.
Definition glUtils.h:71