Packages

VElixir is a simple to use primitive 3D graphics library. It offers great performance, and ease of use. VElixir was inspired by VPython, which makes basic 3D graphics in python trivial. Now it's just as easy to make 3D graphical visualizations in Elixir.

Current section

Files

Jump to
velixir c_src cnano.cpp
Raw

c_src/cnano.cpp

#include <nanomsg/nn.h>
#include <nanomsg/pair.h>
#include <msgpack.hpp>
#include <stdlib.h>
#include <stdio.h>
// #define GLFW_INCLUDE_GLU
// #include <OpenGL/gl.h>
// #include <OpenGL/glu.h>
// #include <OpenGL/glext.h>
// #include <OpenGL/gl3.h>
// #include <GLUT/glut.h>
// #include <GL/gl3w.h>
// #include <GL/glew.h>
#define GLFW_INCLUDE_GLCOREARB
#include <GLFW/glfw3.h>
typedef struct _Mesh {
unsigned int vao, triangles, triangleCount, strips, stripCount, fans, fanCount;
} Mesh;
typedef std::map<unsigned int, Mesh> meshMap;
static void error_callback(int error, const char* description)
{
fputs(description, stderr);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
else
printf("Key press: %i\n", key);
}
void _update_fps_counter (GLFWwindow* window) {
static double previous_seconds = glfwGetTime ();
static int frame_count;
double current_seconds = glfwGetTime ();
double elapsed_seconds = current_seconds - previous_seconds;
if (elapsed_seconds > 0.25) {
previous_seconds = current_seconds;
double fps = (double)frame_count / elapsed_seconds;
char tmp[128];
sprintf (tmp, "VElixir @ fps: %.2f", fps);
glfwSetWindowTitle (window, tmp);
frame_count = 0;
}
frame_count++;
}
/* A simple function that will read a file into an allocated char pointer buffer */
char* filetobuf(char *file)
{
FILE *fptr;
long length;
char *buf;
fptr = fopen(file, "rb"); /* Open file for reading */
if (!fptr) /* Return NULL on failure */
return NULL;
fseek(fptr, 0, SEEK_END); /* Seek to the end of the file */
length = ftell(fptr); /* Find out how many bytes into the file we are */
buf = (char*)malloc(length+1); /* Allocate a buffer for the entire length of the file and a null terminator */
fseek(fptr, 0, SEEK_SET); /* Go back to the beginning of the file */
fread(buf, length, 1, fptr); /* Read the contents of the file in to the buffer */
fclose(fptr); /* Close the file */
buf[length] = 0; /* Null terminator */
return buf; /* Return the buffer */
}
// void _print_shader_info_log (GLuint shader_index) {
// int max_length = 2048;
// int actual_length = 0;
// char log[2048];
// glGetShaderInfoLog (shader_index, max_length, &actual_length, log);
// fprintf(stderr, "shader info log for GL index %u:\n%s\n", shader_index, log);
// }
//
// void _print_programme_info_log (GLuint program) {
// int max_length = 2048;
// int actual_length = 0;
// char log[2048];
// glGetProgramInfoLog (program, max_length, &actual_length, log);
// fprintf(stderr, "program info log for GL index %u:\n%s\n", program, log);
// }
void _print_shader_info_log(GLuint obj)
{
int infologLength = 0;
int charsWritten = 0;
char *infoLog;
glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &infologLength);
if (infologLength > 0)
{
infoLog = (char *)malloc(infologLength);
glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
fprintf(stderr, "%s\n",infoLog);
free(infoLog);
} else {
fprintf(stderr, "No log.\n");
}
}
void _print_programme_info_log(GLuint obj)
{
int infologLength = 0;
int charsWritten = 0;
char *infoLog;
glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &infologLength);
if (infologLength > 0)
{
infoLog = (char *)malloc(infologLength);
glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
fprintf(stderr, "%s\n",infoLog);
free(infoLog);
} else {
fprintf(stderr, "No log.\n");
}
}
void BuildPerspProjMat(float *m, float fov, float aspect, float near, float far) {
float angle = (fov / 180.0f) * M_PI;
float f = 1.0f / tan(angle * 0.5f);
m[0] = f / aspect;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[4] = 0;
m[5] = f;
m[6] = 0;
m[7] = 0;
m[8] = 0;
m[9] = 0;
m[10] = (far + near)/(near - far);
m[11] = -1;
m[12] = 0;
m[13] = 0;
m[14] = 2*far*near/(near - far);
m[15] = 0;
}
static GLFWwindow* window;
static int s;
static FILE *out;
meshMap meshes;
GLchar *vertexsource, *fragmentsource;
GLuint vertexshader, fragmentshader, shaderprogram;
void cleanup()
{
glUseProgram(0);
glDetachShader(shaderprogram, vertexshader);
glDetachShader(shaderprogram, fragmentshader);
glDeleteProgram(shaderprogram);
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
free(vertexsource);
free(fragmentsource);
glfwDestroyWindow(window);
glfwTerminate();
nn_close(s);
fflush(out);
fclose(out);
}
int main()
{
out = fopen("/tmp/velixir_debug","w");
if (out == NULL) {
perror("tty");
exit(2);
}
//GLFW init
glfwSetErrorCallback(error_callback);
if (!glfwInit())
exit(EXIT_FAILURE);
glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint (GLFW_SAMPLES, 4); //anti aliasing sample #, set to 16 for screenshots ;)
window = glfwCreateWindow(640, 480, "VElixir @ fps: 0", NULL, NULL);
if (!window)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
glfwSetKeyCallback(window, key_callback);
int IsCompiled_VS, IsCompiled_FS, IsLinked;
vertexsource = filetobuf((char*)"c_src/vertex_shader.vert");
fragmentsource = filetobuf((char*)"c_src/fragment_shader.frag");
vertexshader = glCreateShader(GL_VERTEX_SHADER);
/* Send the vertex shader source code to GL */
/* Note that the source code is NULL character terminated. */
/* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
glShaderSource(vertexshader, 1, (const GLchar**)&vertexsource, 0);
glCompileShader(vertexshader);
glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &IsCompiled_VS);
if (GL_TRUE != IsCompiled_VS) {
fprintf (stderr, "ERROR: GL shader index %i did not compile\n", vertexshader);
_print_shader_info_log (vertexshader);
exit(2);
}
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
/* Send the fragment shader source code to GL */
/* Note that the source code is NULL character terminated. */
/* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
glShaderSource(fragmentshader, 1, (const GLchar**)&fragmentsource, 0);
glCompileShader(fragmentshader);
glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &IsCompiled_FS);
if (GL_TRUE != IsCompiled_FS) {
fprintf (stderr, "ERROR: GL shader index %i did not compile\n", fragmentshader);
_print_shader_info_log (fragmentshader);
exit(2);
}
/* If we reached this point it means the vertex and fragment shaders compiled and are syntax error free. */
/* We must link them together to make a GL shader program */
/* GL shader programs are monolithic. It is a single piece made of 1 vertex shader and 1 fragment shader. */
/* Assign our program handle a "name" */
shaderprogram = glCreateProgram();
glAttachShader(shaderprogram, vertexshader);
glAttachShader(shaderprogram, fragmentshader);
/* Attribute locations must be setup before calling glLinkProgram. */
glBindAttribLocation(shaderprogram, 0, "vertex_position");
glBindAttribLocation(shaderprogram, 1, "vertex_normal");
glLinkProgram(shaderprogram);
/* Again, we must check and make sure that it linked. If it fails, it would mean either there is a mismatch between the vertex */
/* and fragment shaders. It might be that you have surpassed your GPU's abilities. Perhaps too many ALU operations or */
/* too many texel fetch instructions or too many interpolators or dynamic loops. */
// check if link was successful
int params = -1;
glGetProgramiv (shaderprogram, GL_LINK_STATUS, (int *)&IsLinked);
if (GL_TRUE != IsLinked) {
fprintf (
stderr,
"ERROR: could not link shader programme GL index %u\n",
shaderprogram
);
_print_programme_info_log (shaderprogram);
return false;
}
/* Load the shader into the rendering pipeline */
glUseProgram(shaderprogram);
/* Use depth buffering for hidden surface elimination. */
glEnable(GL_DEPTH_TEST);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(INT_MAX);
/* Enable a single OpenGL light. */
// glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
// glLightfv(GL_LIGHT0, GL_POSITION, light_position);
// glEnable(GL_LIGHT0);
// glEnable(GL_LIGHTING);
// glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
// glShadeModel(GL_SMOOTH);
float infPointLightPos[4] = {1, 1, 0, 1.0};
float infPointLightIntensity = 0.97;
float ambientMin = 0.1;
int infPointLightPos_Loc = glGetUniformLocation(shaderprogram, "infPointLightPos");
int infPointLightIntensity_Loc = glGetUniformLocation(shaderprogram, "infPointLightIntensity");
int ambientMin_Loc = glGetUniformLocation(shaderprogram, "ambientMin");
glUniform4fv(infPointLightPos_Loc, 1, &infPointLightPos[0]);
glUniform1f(infPointLightIntensity_Loc, infPointLightIntensity);
glUniform1f(ambientMin_Loc, ambientMin);
float translation_matrix[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, -3,
0, 0, 0, 1
};
int translation_matrix_Loc = glGetUniformLocation(shaderprogram, "translation_matrix");
glUniformMatrix4fv(translation_matrix_Loc, 1, true, translation_matrix);
int obj_transform_Loc = glGetUniformLocation(shaderprogram, "obj_transform");
int obj_color_Loc = glGetUniformLocation(shaderprogram, "obj_color");
int proj_matrix_Loc = glGetUniformLocation(shaderprogram, "proj_matrix");
float proj_matrix[16];
//nanomsg init
s = nn_socket(AF_SP, NN_PAIR);
nn_bind(s, "ipc:///tmp/velixir.ipc");
nn_send(s, "start", strlen("start"), 0);
const int nn_buf_len = 512*1024; //512k
char buf[nn_buf_len];
std::memset(buf, 0, sizeof(char) * nn_buf_len);
int count;
while(!glfwWindowShouldClose(window) && (count = nn_recv(s, buf, nn_buf_len, 0)) != -1) {
_update_fps_counter (window);
//nanomsg update
int type = 0;
std::memcpy(&type, buf, sizeof(uint8_t));
if(type == 0) break;
// std::cerr << "recieved " << (buf+1) << "\n";
msgpack::object_handle oh = msgpack::unpack(buf+1, count-1);
msgpack::object deserialized = oh.get();
// std::cerr << "deserialized " << deserialized << "\n";
// std::cerr << "Got instruction type " << type << "\n";
if (type == 1) { //declare_mesh
msgpack::object mesh_dec[6];
deserialized.convert(mesh_dec);
//[id, vecs, normals, triangles, strips, fans]
unsigned int id = mesh_dec[0].convert();
std::list<std::vector<float> > vertex_list[2];
mesh_dec[1].convert(vertex_list[0]);
mesh_dec[2].convert(vertex_list[1]);
unsigned int vec_size = vertex_list[0].size();
unsigned int norm_size = vertex_list[1].size();
if (vec_size != norm_size) {
fprintf(stderr, "vec_list length not equal to normal_list length!\n");
exit(2);
}
unsigned int buf_len = vec_size * 3;
float vertex_ary[2][buf_len];
for(int v = 0; v < 2; v++) {
int i = 0;
for(std::list<std::vector<float> >::iterator iter = vertex_list[v].begin(); iter != vertex_list[v].end(); iter++) {
for(int xyz = 0; xyz < 3; xyz++) {
vertex_ary[v][i] = (*iter)[xyz];
i++;
}
}
} //convert into linear buffer
unsigned int vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
unsigned int vertex_vbo[2] = {0, 0};
for (int v = 0; v < 2; v++) {
glGenBuffers (1, &vertex_vbo[v]);
glBindBuffer (GL_ARRAY_BUFFER, vertex_vbo[v]);
glBufferData (GL_ARRAY_BUFFER, buf_len * sizeof (float), vertex_ary[v], GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo[1]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//vao is now filled with vertex data. Now it needs the faces.
std::list<std::list<unsigned int> > lists[3];
int buf_lens[3];
for(int i = 0; i < 3; i++) {
mesh_dec[3 + i].convert(lists[i]);
buf_len = lists[i].size(); //go ahead and add the number of primitive restarts to be inserted.
for(std::list<std::list<unsigned int> >::iterator iter = lists[i].begin(); iter != lists[i].end(); iter++) {
buf_len += iter->size();
}
buf_lens[i] = buf_len;
}
unsigned int vbos[3];
for(int i = 0; i < 3; i++) {
int buf_len = buf_lens[i];
unsigned int buffer[buf_len];
int buf_i = 0;
for(std::list<std::list<unsigned int> >::iterator iter = lists[i].begin(); iter != lists[i].end(); iter++) {
for(std::list<unsigned int>::iterator subiter = iter->begin(); subiter != iter->end(); subiter++) {
buffer[buf_i] = (*subiter);
buf_i++;
}
buffer[buf_i] = INT_MAX;
buf_i++;
}
if (buf_i != buf_len) {
fprintf(stderr, "buf_i: %d, and buf_len: %d are not equal!", buf_i, buf_len);
exit(2);
}
glGenBuffers(1, &vbos[i]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, buf_len * sizeof(unsigned int), buffer, GL_STATIC_DRAW);
}
Mesh mesh = {vao, vbos[0], buf_lens[0], vbos[1], buf_lens[1], vbos[2], buf_lens[2]};
meshes[id] = mesh;
} else if (type == 2) { //transforms
nn_send(s, "t", strlen("t"), 0);
std::list<std::vector<msgpack::object> > renderData;
deserialized.convert(renderData);
//GLFW update
int width, height;
glfwGetFramebufferSize(window, &width, &height);
float ratio = width / (float) height;
glViewport(0, 0, width, height);
// glViewport(-width/2, -height/2, width, height);
BuildPerspProjMat(&proj_matrix[0], 60.0, ratio, 0.001, 100.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(proj_matrix_Loc, 1, false, proj_matrix);
for(std::list<std::vector<msgpack::object> >::iterator iter = renderData.begin(); iter != renderData.end(); iter++) {
unsigned int mesh_id = (*iter)[0].convert();
float matrix[16];
(*iter)[1].convert(matrix);
float color[4];
(*iter)[2].convert(color);
Mesh mesh = meshes[mesh_id];
glBindVertexArray(mesh.vao);
glUniformMatrix4fv(obj_transform_Loc, 1, true, &matrix[0]);
glUniform4fv(obj_color_Loc, 1, &color[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.triangles);
glDrawElements(GL_TRIANGLES, mesh.triangleCount, GL_UNSIGNED_INT, (void*)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.strips);
glDrawElements(GL_TRIANGLE_STRIP, mesh.stripCount, GL_UNSIGNED_INT, (void*)0);
// fprintf(stderr, "Drawing %d strip vertices.\n", obj.stripCount);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.fans);
glDrawElements(GL_TRIANGLE_FAN, mesh.fanCount, GL_UNSIGNED_INT, (void*)0);
// fprintf(stderr, "Drawing %d fan vertices.\n", obj.fanCount);
}
glfwSwapBuffers(window);
glfwPollEvents();
}
}
cleanup();
return 0;
}