Skip to content
Snippets Groups Projects
bonds_GL.cpp 13.01 KiB
/*
draw only active links
*/
//
// bonds_GL.cpp
//    input bonds, display GL
//    (c) MIT CBA Neil Gershenfeld 6/25/20
//    reads from stdin 
//    bonds_source | bonds_GL size
//
// includes
//
#include <iostream>
#include <cmath>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
using namespace std;
//
// defines
//
#define CPP (12*2*6) // render coordinates per particle
   // 12 lines * 2 verts * (3 coords + 3 colors)
#define VPP (12*2) // render vertices per particle
   // 12 lines * 2 verts
//
// global variables
//
class vars {
   public:
      float rx = 0;//-0.25; // view rotation
      float ry = 0;//0.67;
      float rz = 0.0;
      float dr = 0.01;
      float tx = 0; // view translation
      float ty = 0;
      float tz = 0;
      float dt = 0.01;
      float sxyz = 0.9; // view scale
      int mx,my; // mouse position
      int mb = -1; // mouse state
      int np; // number of particles
      float xmin,xmax,ymin,ymax,zmin,zmax; // data limits
      float *px,*py,*pz; // particle positions
      float d; // particle pitch
      float size = 1; // particle size
      int lpp; // links per particle
      int *nlinks,*links; // links
      float* data; // GL drawing buffer
      GLuint program; // GL program
      int width = 1000; // GL window width
      int height = 1000; // GL window height
   } v;
//
// GL class
//
class GL {
   public:
      //
      // variables
      //
      GLFWwindow* window;
      GLuint vertex;
      GLuint fragment;
      GLuint buffer;
      GLint location;
      //
      // keyboard callback (escape to quit)
      //
      static void keyboard(GLFWwindow* window,int key,int scancode,int action,int mods) {
         GLint location;
         if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
            glfwSetWindowShouldClose(window,GLFW_TRUE);
         else if (key == 'Q' && action == GLFW_PRESS)
            glfwSetWindowShouldClose(window,GLFW_TRUE);
         else if (key == '-' && action == GLFW_PRESS) {
            v.sxyz /= 1.1;
            location = glGetUniformLocation(v.program,"sxyz");
            glUniform1f(location,v.sxyz);
            }
         else if (key == '=' && action == GLFW_PRESS) {
            v.sxyz *= 1.1;
            location = glGetUniformLocation(v.program,"sxyz");
            glUniform1f(location,v.sxyz);
            }
         }
      //
      // scroll callback
      //
      static void scroll(GLFWwindow* window,double x,double y) {
         GLint location;
         if (y > 0) {
            v.sxyz /= 1.1;
            location = glGetUniformLocation(v.program,"sxyz");
            glUniform1f(location,v.sxyz);
            }
         else {
            v.sxyz *= 1.1;
            location = glGetUniformLocation(v.program,"sxyz");
            glUniform1f(location,v.sxyz);
            }
         }
      //
      // mouse cursor callback (left down to rotate)
      //
      static void cursor(GLFWwindow* window,double x,double y) {
         GLint location;
         if (v.mb == GLFW_MOUSE_BUTTON_LEFT) {
            if (v.mx < x)
               v.ry -= v.dr;
            else if (v.mx > x)
               v.ry += v.dr;
            v.mx = x;
            if (v.my < y)
               v.rx -= v.dr;
            else if (v.my > y)
               v.rx += v.dr;
            v.my = y;
            cerr << "   rotate " << 180*v.rx/M_PI << ' ' << 180*v.ry/M_PI << endl;
            location = glGetUniformLocation(v.program,"rx");
            glUniform1f(location,v.rx);
            location = glGetUniformLocation(v.program,"ry");
            glUniform1f(location,v.ry);
            }
         }
      //
      // mouse button callback
      //
      static void button(GLFWwindow* window,int button,int action,int mods) {
         double x,y;
         if ((button == GLFW_MOUSE_BUTTON_LEFT)
               && (action == GLFW_PRESS)) {
            glfwGetCursorPos(window,&x,&y);
            v.mb = GLFW_MOUSE_BUTTON_LEFT;
            v.mx = x;
            v.my = y;
            }
         else if ((button == GLFW_MOUSE_BUTTON_LEFT)
               && (action == GLFW_RELEASE))
            v.mb = -1;
         }
      //
      // drawing loop
      //
      void draw(void (*update)()) {
         update();
         glBufferSubData(GL_ARRAY_BUFFER,0,CPP*v.np*sizeof(float),v.data);
         while (!glfwWindowShouldClose(window)) {
            glfwPollEvents();
            glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);  
            glDrawArrays(GL_LINES,0,VPP*v.np);
            glfwSwapBuffers(window);
            }
         glfwDestroyWindow(window);
         glfwTerminate();
         }
      //
      // constructor
      //
      GL(int width,int height,char *title) {
         //
         // init GLFW
         //
         if (!glfwInit())
            cerr << "   could not start GLFW" << endl;
         window = glfwCreateWindow(width,height,title,NULL,NULL);
         if (!window)
            cerr << "   could not open window" << endl;
         glfwMakeContextCurrent(window);
         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
         glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
         //
         // init GLEW
         //
         glewExperimental = GL_TRUE;
         glewInit();
         const GLubyte* renderer = glGetString(GL_RENDERER);
         const GLubyte* version = glGetString(GL_VERSION);
         cerr << "   renderer: " << renderer << endl;
         cerr << "   GL version " << version << endl;
         //
         // vertex shader
         //
         const char* vertex_code = R"(
            attribute vec3 position;
            attribute vec3 color;
            varying vec3 fcolor;
            uniform float rx,ry,rz,tx,ty,tz,sxyz;
            mat4 rotatex(float angle) {
               return mat4(1.0,0.0,0.0,0.0,
               0.0,cos(angle),-sin(angle),0.0,
               0.0,sin(angle),cos(angle),0.0,
               0.0,0.0,0.0,1.0);
               }
            mat4 rotatey(float angle) {
               return mat4(cos(angle),0.0,sin(angle),0.0,
               0.0,1.0,0.0,0.0,
               -sin(angle),0.0,cos(angle),0.0,
               0.0,0.0,0.0,1.0);
               }
            mat4 rotatez(float angle) {
               return mat4(cos(angle),-sin(angle),0.0,0.0,
               sin(angle),cos(angle),0.0,0.0,
               0.0,0.0,1.0,0.0,
               0.0,0.0,0.0,1.0);
               }
            mat4 scale(float s) {
               return mat4(s,0,0.0,0.0,
               0,s,0.0,0.0,
               0.0,0.0,s,0.0,
               0.0,0.0,0.0,1.0);
               }
            mat4 translate(float dx,float dy,float dz) {
               return mat4(1.0,0.0,0.0,dx,
               0.0,1.0,0.0,dy,
               0.0,0.0,1.0,dz,
               0.0,0.0,0.0,1.0);
               }
            void main() {
               fcolor = color;
               vec4 vertex = vec4(position,1.0);
               gl_Position = vertex*rotatex(rx)
                  *rotatey(ry)*rotatez(rz)
                  *translate(tx,ty,tz)*scale(sxyz);
               }
            )";
         //
         // fragment shader
         //
         const char* fragment_code = R"(
            varying vec3 fcolor;
            float depth;
            void main() {
               depth = 1.0-1.0*gl_FragCoord.z;
               gl_FragColor = vec4(depth*fcolor[0],depth*fcolor[1],depth*fcolor[2],1.0);
               }
            )";
         //
         // compile and link shaders
         //
         v.program = glCreateProgram();
         GLuint vertex = glCreateShader(GL_VERTEX_SHADER);
         GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER);
         glShaderSource(vertex,1,&vertex_code,NULL);
         glShaderSource(fragment,1,&fragment_code,NULL);
         glCompileShader(vertex);
         glCompileShader(fragment);
         glAttachShader(v.program,vertex);
         glAttachShader(v.program,fragment);
         glLinkProgram(v.program);
         glDetachShader(v.program,vertex);
         glDetachShader(v.program,fragment);
         glUseProgram(v.program);
         //
         // set up buffers
         //
         glGenBuffers(1,&buffer);
         glBindBuffer(GL_ARRAY_BUFFER,buffer);
         glBufferData(GL_ARRAY_BUFFER,CPP*v.np*sizeof(float),
            v.data,GL_DYNAMIC_DRAW);
         location = glGetAttribLocation(v.program,"position");
         glEnableVertexAttribArray(location);
         glVertexAttribPointer(location,3,GL_FLOAT,GL_FALSE,
            6*sizeof(float),0);
         location = glGetAttribLocation(v.program,"color");
         glEnableVertexAttribArray(location);
         glVertexAttribPointer(location,3,GL_FLOAT,GL_FALSE,
            6*sizeof(float),(GLvoid*)(3*sizeof(float)));
         //
         // set up drawing
         //
         glEnable(GL_DEPTH_TEST);
         glDepthMask(GL_TRUE);
         glDepthFunc(GL_LESS);
         glDepthRange(0.0,1.0);
         glEnable(GL_BLEND);
         glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
         //
         // set up view
         //
         location = glGetUniformLocation(v.program,"rx");
         glUniform1f(location,v.rx);
         location = glGetUniformLocation(v.program,"ry");
         glUniform1f(location,v.ry);
         location = glGetUniformLocation(v.program,"rz");
         glUniform1f(location,v.rz);
         location = glGetUniformLocation(v.program,"tx");
         glUniform1f(location,v.tx);
         location = glGetUniformLocation(v.program,"ty");
         glUniform1f(location,v.ty);
         location = glGetUniformLocation(v.program,"tz");
         glUniform1f(location,v.tz);
         location = glGetUniformLocation(v.program,"sxyz");
         glUniform1f(location,v.sxyz);
         //
         // set up callbacks
         //
         glfwSetKeyCallback(window,keyboard);
         glfwSetScrollCallback(window,scroll);
         glfwSetMouseButtonCallback(window,button);
         glfwSetCursorPosCallback(window,cursor);
         }
   };
void update() {
   int index = 0;
   float scale = 2/(v.xmax-v.xmin);
   float d = v.size*v.d*scale;
   float xmid = (v.xmax+v.xmin)/2.0;
   float ymid = (v.ymax+v.ymin)/2.0;
   float zmid = (v.zmax+v.zmin)/2.0;
   float r = 0.6;
   float g = 1.0;
   float b = 0.6;
   for (int i = 0; i < v.np; ++i) {
      float x0 = (v.px[i]-xmid)*scale;
      float y0 = (v.py[i]-ymid)*scale;
      float z0 = (zmid-v.pz[i])*scale; // ?
      for (int j = 0; j < v.nlinks[i]; ++j) {
         float x1 = (v.px[v.links[12*i+j]]-xmid)*scale;
         float y1 = (v.py[v.links[12*i+j]]-ymid)*scale;
         float z1 = (zmid-v.pz[v.links[12*i+j]])*scale; // ?
         v.data[index++] = x0;
         v.data[index++] = y0;
         v.data[index++] = z0;
         v.data[index++] = r;
         v.data[index++] = g;
         v.data[index++] = b;
         v.data[index++] = x0+(x1-x0)/2.5;
         v.data[index++] = y0+(y1-y0)/2.5;
         v.data[index++] = z0+(z1-z0)/2.5;
         v.data[index++] = r;
         v.data[index++] = g;
         v.data[index++] = b;
         }
      }
   }
//
// main
//
int main(int argc, char** argv) {
   if (argc != 1) {
      cerr << "command line: bonds_source | bonds_GL" << endl;
      return 1;
      }
   //
   // read and parse inputs
   //
   string s;
   while (1) {
      getline(cin,s);
      if (s.compare("number:") == 0) {
         getline(cin,s);
         v.np = stoi(s);
         v.px = new float[v.np];
         v.py = new float[v.np];
         v.pz = new float[v.np];
         v.data = new float[CPP*v.np];
         }
      else if (s.compare("pitch:") == 0) {
         getline(cin,s);
         v.d = stof(s);
         }
      else if (s.compare("xmin:") == 0) {
         getline(cin,s);
         v.xmin = stof(s);
         }
      else if (s.compare("xmax:") == 0) {
         getline(cin,s);
         v.xmax = stof(s);
         }
      else if (s.compare("ymin:") == 0) {
         getline(cin,s);
         v.ymin = stof(s);
         }
      else if (s.compare("ymax:") == 0) {
         getline(cin,s);
         v.ymax = stof(s);
         }
      else if (s.compare("zmin:") == 0) {
         getline(cin,s);
         v.zmin = stof(s);
         }
      else if (s.compare("zmax:") == 0) {
         getline(cin,s);
         v.zmax = stof(s);
         }
      else if (s.compare("particles:") == 0) {
         for (int i = 0; i < v.np ; ++i) {
            cin.read((char *)&v.px[i],4);
            cin.read((char *)&v.py[i],4);
            cin.read((char *)&v.pz[i],4);
            }
         }
      else if (s.compare("links per particle:") == 0) {
         getline(cin,s);
         v.lpp = stoi(s);
         }
      else if (s.compare("nlinks:") == 0) {
         v.nlinks = new int[v.np];
         for (int i = 0; i < v.np; ++i)
            cin.read((char *)&v.nlinks[i],4);
         }
      else if (s.compare("links:") == 0) {
         v.links = new int[v.lpp*v.np];
         for (int i = 0; i < v.lpp*v.np; ++i)
            cin.read((char *)&v.links[i],4);
         }
      else if (s.compare("end:") == 0)
         break;
      }
   cerr << "bonds_GL:" << endl;
   cerr << "   input " << v.np << " particles" << endl;
   //
   // initialize GL
   //
   GL g(v.width,v.height,(char *)"particles_GL");
   //
   // start drawing loop
   //
   g.draw(update);
   }