diff options
author | Lukas Matena <lukasmatena@seznam.cz> | 2018-10-26 16:45:52 +0300 |
---|---|---|
committer | Lukas Matena <lukasmatena@seznam.cz> | 2018-10-26 16:45:52 +0300 |
commit | 7681d00ee50c58a06b0ab61f776a08a693a6e510 (patch) | |
tree | f9bb0a57375af93113e52e31d05bc4f86f1a3907 /src/igl/opengl | |
parent | c1e6eda554813763378f959435df766a600b896c (diff) | |
parent | f65118210124d460d9cb6478a84976bb98160595 (diff) |
Merged branch 'dev_native' into lm_sla_supports_auto
Added igl library files
Diffstat (limited to 'src/igl/opengl')
45 files changed, 5573 insertions, 0 deletions
diff --git a/src/igl/opengl/MeshGL.cpp b/src/igl/opengl/MeshGL.cpp new file mode 100644 index 000000000..f49924324 --- /dev/null +++ b/src/igl/opengl/MeshGL.cpp @@ -0,0 +1,313 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "MeshGL.h" +#include "bind_vertex_attrib_array.h" +#include "create_shader_program.h" +#include "destroy_shader_program.h" +#include <iostream> + +IGL_INLINE void igl::opengl::MeshGL::init_buffers() +{ + // Mesh: Vertex Array Object & Buffer objects + glGenVertexArrays(1, &vao_mesh); + glBindVertexArray(vao_mesh); + glGenBuffers(1, &vbo_V); + glGenBuffers(1, &vbo_V_normals); + glGenBuffers(1, &vbo_V_ambient); + glGenBuffers(1, &vbo_V_diffuse); + glGenBuffers(1, &vbo_V_specular); + glGenBuffers(1, &vbo_V_uv); + glGenBuffers(1, &vbo_F); + glGenTextures(1, &vbo_tex); + + // Line overlay + glGenVertexArrays(1, &vao_overlay_lines); + glBindVertexArray(vao_overlay_lines); + glGenBuffers(1, &vbo_lines_F); + glGenBuffers(1, &vbo_lines_V); + glGenBuffers(1, &vbo_lines_V_colors); + + // Point overlay + glGenVertexArrays(1, &vao_overlay_points); + glBindVertexArray(vao_overlay_points); + glGenBuffers(1, &vbo_points_F); + glGenBuffers(1, &vbo_points_V); + glGenBuffers(1, &vbo_points_V_colors); + + dirty = MeshGL::DIRTY_ALL; +} + +IGL_INLINE void igl::opengl::MeshGL::free_buffers() +{ + if (is_initialized) + { + glDeleteVertexArrays(1, &vao_mesh); + glDeleteVertexArrays(1, &vao_overlay_lines); + glDeleteVertexArrays(1, &vao_overlay_points); + + glDeleteBuffers(1, &vbo_V); + glDeleteBuffers(1, &vbo_V_normals); + glDeleteBuffers(1, &vbo_V_ambient); + glDeleteBuffers(1, &vbo_V_diffuse); + glDeleteBuffers(1, &vbo_V_specular); + glDeleteBuffers(1, &vbo_V_uv); + glDeleteBuffers(1, &vbo_F); + glDeleteBuffers(1, &vbo_lines_F); + glDeleteBuffers(1, &vbo_lines_V); + glDeleteBuffers(1, &vbo_lines_V_colors); + glDeleteBuffers(1, &vbo_points_F); + glDeleteBuffers(1, &vbo_points_V); + glDeleteBuffers(1, &vbo_points_V_colors); + + glDeleteTextures(1, &vbo_tex); + } +} + +IGL_INLINE void igl::opengl::MeshGL::bind_mesh() +{ + glBindVertexArray(vao_mesh); + glUseProgram(shader_mesh); + bind_vertex_attrib_array(shader_mesh,"position", vbo_V, V_vbo, dirty & MeshGL::DIRTY_POSITION); + bind_vertex_attrib_array(shader_mesh,"normal", vbo_V_normals, V_normals_vbo, dirty & MeshGL::DIRTY_NORMAL); + bind_vertex_attrib_array(shader_mesh,"Ka", vbo_V_ambient, V_ambient_vbo, dirty & MeshGL::DIRTY_AMBIENT); + bind_vertex_attrib_array(shader_mesh,"Kd", vbo_V_diffuse, V_diffuse_vbo, dirty & MeshGL::DIRTY_DIFFUSE); + bind_vertex_attrib_array(shader_mesh,"Ks", vbo_V_specular, V_specular_vbo, dirty & MeshGL::DIRTY_SPECULAR); + bind_vertex_attrib_array(shader_mesh,"texcoord", vbo_V_uv, V_uv_vbo, dirty & MeshGL::DIRTY_UV); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_F); + if (dirty & MeshGL::DIRTY_FACE) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*F_vbo.size(), F_vbo.data(), GL_DYNAMIC_DRAW); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, vbo_tex); + if (dirty & MeshGL::DIRTY_TEXTURE) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_u, tex_v, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.data()); + } + glUniform1i(glGetUniformLocation(shader_mesh,"tex"), 0); + dirty &= ~MeshGL::DIRTY_MESH; +} + +IGL_INLINE void igl::opengl::MeshGL::bind_overlay_lines() +{ + bool is_dirty = dirty & MeshGL::DIRTY_OVERLAY_LINES; + + glBindVertexArray(vao_overlay_lines); + glUseProgram(shader_overlay_lines); + bind_vertex_attrib_array(shader_overlay_lines,"position", vbo_lines_V, lines_V_vbo, is_dirty); + bind_vertex_attrib_array(shader_overlay_lines,"color", vbo_lines_V_colors, lines_V_colors_vbo, is_dirty); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_lines_F); + if (is_dirty) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*lines_F_vbo.size(), lines_F_vbo.data(), GL_DYNAMIC_DRAW); + + dirty &= ~MeshGL::DIRTY_OVERLAY_LINES; +} + +IGL_INLINE void igl::opengl::MeshGL::bind_overlay_points() +{ + bool is_dirty = dirty & MeshGL::DIRTY_OVERLAY_POINTS; + + glBindVertexArray(vao_overlay_points); + glUseProgram(shader_overlay_points); + bind_vertex_attrib_array(shader_overlay_points,"position", vbo_points_V, points_V_vbo, is_dirty); + bind_vertex_attrib_array(shader_overlay_points,"color", vbo_points_V_colors, points_V_colors_vbo, is_dirty); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_points_F); + if (is_dirty) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*points_F_vbo.size(), points_F_vbo.data(), GL_DYNAMIC_DRAW); + + dirty &= ~MeshGL::DIRTY_OVERLAY_POINTS; +} + +IGL_INLINE void igl::opengl::MeshGL::draw_mesh(bool solid) +{ + glPolygonMode(GL_FRONT_AND_BACK, solid ? GL_FILL : GL_LINE); + + /* Avoid Z-buffer fighting between filled triangles & wireframe lines */ + if (solid) + { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0, 1.0); + } + glDrawElements(GL_TRIANGLES, 3*F_vbo.rows(), GL_UNSIGNED_INT, 0); + + glDisable(GL_POLYGON_OFFSET_FILL); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +IGL_INLINE void igl::opengl::MeshGL::draw_overlay_lines() +{ + glDrawElements(GL_LINES, lines_F_vbo.rows(), GL_UNSIGNED_INT, 0); +} + +IGL_INLINE void igl::opengl::MeshGL::draw_overlay_points() +{ + glDrawElements(GL_POINTS, points_F_vbo.rows(), GL_UNSIGNED_INT, 0); +} + +IGL_INLINE void igl::opengl::MeshGL::init() +{ + if(is_initialized) + { + return; + } + is_initialized = true; + std::string mesh_vertex_shader_string = +R"(#version 150 + uniform mat4 view; + uniform mat4 proj; + uniform mat4 normal_matrix; + in vec3 position; + in vec3 normal; + out vec3 position_eye; + out vec3 normal_eye; + in vec4 Ka; + in vec4 Kd; + in vec4 Ks; + in vec2 texcoord; + out vec2 texcoordi; + out vec4 Kai; + out vec4 Kdi; + out vec4 Ksi; + + void main() + { + position_eye = vec3 (view * vec4 (position, 1.0)); + normal_eye = vec3 (normal_matrix * vec4 (normal, 0.0)); + normal_eye = normalize(normal_eye); + gl_Position = proj * vec4 (position_eye, 1.0); //proj * view * vec4(position, 1.0);" + Kai = Ka; + Kdi = Kd; + Ksi = Ks; + texcoordi = texcoord; + } +)"; + + std::string mesh_fragment_shader_string = +R"(#version 150 + uniform mat4 view; + uniform mat4 proj; + uniform vec4 fixed_color; + in vec3 position_eye; + in vec3 normal_eye; + uniform vec3 light_position_eye; + vec3 Ls = vec3 (1, 1, 1); + vec3 Ld = vec3 (1, 1, 1); + vec3 La = vec3 (1, 1, 1); + in vec4 Ksi; + in vec4 Kdi; + in vec4 Kai; + in vec2 texcoordi; + uniform sampler2D tex; + uniform float specular_exponent; + uniform float lighting_factor; + uniform float texture_factor; + out vec4 outColor; + void main() + { + vec3 Ia = La * vec3(Kai); // ambient intensity + + vec3 vector_to_light_eye = light_position_eye - position_eye; + vec3 direction_to_light_eye = normalize (vector_to_light_eye); + float dot_prod = dot (direction_to_light_eye, normalize(normal_eye)); + float clamped_dot_prod = max (dot_prod, 0.0); + vec3 Id = Ld * vec3(Kdi) * clamped_dot_prod; // Diffuse intensity + + vec3 reflection_eye = reflect (-direction_to_light_eye, normalize(normal_eye)); + vec3 surface_to_viewer_eye = normalize (-position_eye); + float dot_prod_specular = dot (reflection_eye, surface_to_viewer_eye); + dot_prod_specular = float(abs(dot_prod)==dot_prod) * max (dot_prod_specular, 0.0); + float specular_factor = pow (dot_prod_specular, specular_exponent); + vec3 Is = Ls * vec3(Ksi) * specular_factor; // specular intensity + vec4 color = vec4(lighting_factor * (Is + Id) + Ia + (1.0-lighting_factor) * vec3(Kdi),(Kai.a+Ksi.a+Kdi.a)/3); + outColor = mix(vec4(1,1,1,1), texture(tex, texcoordi), texture_factor) * color; + if (fixed_color != vec4(0.0)) outColor = fixed_color; + } +)"; + + std::string overlay_vertex_shader_string = +R"(#version 150 + uniform mat4 view; + uniform mat4 proj; + in vec3 position; + in vec3 color; + out vec3 color_frag; + + void main() + { + gl_Position = proj * view * vec4 (position, 1.0); + color_frag = color; + } +)"; + + std::string overlay_fragment_shader_string = +R"(#version 150 + in vec3 color_frag; + out vec4 outColor; + void main() + { + outColor = vec4(color_frag, 1.0); + } +)"; + + std::string overlay_point_fragment_shader_string = +R"(#version 150 + in vec3 color_frag; + out vec4 outColor; + void main() + { + if (length(gl_PointCoord - vec2(0.5)) > 0.5) + discard; + outColor = vec4(color_frag, 1.0); + } +)"; + + init_buffers(); + create_shader_program( + mesh_vertex_shader_string, + mesh_fragment_shader_string, + {}, + shader_mesh); + create_shader_program( + overlay_vertex_shader_string, + overlay_fragment_shader_string, + {}, + shader_overlay_lines); + create_shader_program( + overlay_vertex_shader_string, + overlay_point_fragment_shader_string, + {}, + shader_overlay_points); +} + +IGL_INLINE void igl::opengl::MeshGL::free() +{ + const auto free = [](GLuint & id) + { + if(id) + { + destroy_shader_program(id); + id = 0; + } + }; + + if (is_initialized) + { + free(shader_mesh); + free(shader_overlay_lines); + free(shader_overlay_points); + free_buffers(); + } +} diff --git a/src/igl/opengl/MeshGL.h b/src/igl/opengl/MeshGL.h new file mode 100644 index 000000000..74c358903 --- /dev/null +++ b/src/igl/opengl/MeshGL.h @@ -0,0 +1,133 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_MESHGL_H +#define IGL_OPENGL_MESHGL_H + +// Coverts mesh data inside a igl::ViewerData class in an OpenGL +// compatible format The class includes a shader and the opengl calls to plot +// the data + +#include <igl/igl_inline.h> +#include <Eigen/Core> + +namespace igl +{ +namespace opengl +{ + +class MeshGL +{ +public: + typedef unsigned int GLuint; + + enum DirtyFlags + { + DIRTY_NONE = 0x0000, + DIRTY_POSITION = 0x0001, + DIRTY_UV = 0x0002, + DIRTY_NORMAL = 0x0004, + DIRTY_AMBIENT = 0x0008, + DIRTY_DIFFUSE = 0x0010, + DIRTY_SPECULAR = 0x0020, + DIRTY_TEXTURE = 0x0040, + DIRTY_FACE = 0x0080, + DIRTY_MESH = 0x00FF, + DIRTY_OVERLAY_LINES = 0x0100, + DIRTY_OVERLAY_POINTS = 0x0200, + DIRTY_ALL = 0x03FF + }; + + bool is_initialized = false; + GLuint vao_mesh; + GLuint vao_overlay_lines; + GLuint vao_overlay_points; + GLuint shader_mesh; + GLuint shader_overlay_lines; + GLuint shader_overlay_points; + + GLuint vbo_V; // Vertices of the current mesh (#V x 3) + GLuint vbo_V_uv; // UV coordinates for the current mesh (#V x 2) + GLuint vbo_V_normals; // Vertices of the current mesh (#V x 3) + GLuint vbo_V_ambient; // Ambient material (#V x 3) + GLuint vbo_V_diffuse; // Diffuse material (#V x 3) + GLuint vbo_V_specular; // Specular material (#V x 3) + + GLuint vbo_F; // Faces of the mesh (#F x 3) + GLuint vbo_tex; // Texture + + GLuint vbo_lines_F; // Indices of the line overlay + GLuint vbo_lines_V; // Vertices of the line overlay + GLuint vbo_lines_V_colors; // Color values of the line overlay + GLuint vbo_points_F; // Indices of the point overlay + GLuint vbo_points_V; // Vertices of the point overlay + GLuint vbo_points_V_colors; // Color values of the point overlay + + // Temporary copy of the content of each VBO + typedef Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> RowMatrixXf; + RowMatrixXf V_vbo; + RowMatrixXf V_normals_vbo; + RowMatrixXf V_ambient_vbo; + RowMatrixXf V_diffuse_vbo; + RowMatrixXf V_specular_vbo; + RowMatrixXf V_uv_vbo; + RowMatrixXf lines_V_vbo; + RowMatrixXf lines_V_colors_vbo; + RowMatrixXf points_V_vbo; + RowMatrixXf points_V_colors_vbo; + + int tex_u; + int tex_v; + Eigen::Matrix<char,Eigen::Dynamic,1> tex; + + Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> F_vbo; + Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> lines_F_vbo; + Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> points_F_vbo; + + // Marks dirty buffers that need to be uploaded to OpenGL + uint32_t dirty; + + // Initialize shaders and buffers + IGL_INLINE void init(); + + // Release all resources + IGL_INLINE void free(); + + // Create a new set of OpenGL buffer objects + IGL_INLINE void init_buffers(); + + // Bind the underlying OpenGL buffer objects for subsequent mesh draw calls + IGL_INLINE void bind_mesh(); + + /// Draw the currently buffered mesh (either solid or wireframe) + IGL_INLINE void draw_mesh(bool solid); + + // Bind the underlying OpenGL buffer objects for subsequent line overlay draw calls + IGL_INLINE void bind_overlay_lines(); + + /// Draw the currently buffered line overlay + IGL_INLINE void draw_overlay_lines(); + + // Bind the underlying OpenGL buffer objects for subsequent point overlay draw calls + IGL_INLINE void bind_overlay_points(); + + /// Draw the currently buffered point overlay + IGL_INLINE void draw_overlay_points(); + + // Release the OpenGL buffer objects + IGL_INLINE void free_buffers(); + +}; + +} +} + +#ifndef IGL_STATIC_LIBRARY +# include "MeshGL.cpp" +#endif + +#endif diff --git a/src/igl/opengl/ViewerCore.cpp b/src/igl/opengl/ViewerCore.cpp new file mode 100644 index 000000000..fc5f4664b --- /dev/null +++ b/src/igl/opengl/ViewerCore.cpp @@ -0,0 +1,391 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "ViewerCore.h" +#include "gl.h" +#include "../quat_to_mat.h" +#include "../snap_to_fixed_up.h" +#include "../look_at.h" +#include "../frustum.h" +#include "../ortho.h" +#include "../massmatrix.h" +#include "../barycenter.h" +#include "../PI.h" +#include <Eigen/Geometry> +#include <iostream> + +IGL_INLINE void igl::opengl::ViewerCore::align_camera_center( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F) +{ + if(V.rows() == 0) + return; + + get_scale_and_shift_to_fit_mesh(V,F,camera_base_zoom,camera_base_translation); + // Rather than crash on empty mesh... + if(V.size() > 0) + { + object_scale = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm(); + } +} + +IGL_INLINE void igl::opengl::ViewerCore::get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + float& zoom, + Eigen::Vector3f& shift) +{ + if (V.rows() == 0) + return; + + Eigen::MatrixXd BC; + if (F.rows() <= 1) + { + BC = V; + } else + { + igl::barycenter(V,F,BC); + } + return get_scale_and_shift_to_fit_mesh(BC,zoom,shift); +} + +IGL_INLINE void igl::opengl::ViewerCore::align_camera_center( + const Eigen::MatrixXd& V) +{ + if(V.rows() == 0) + return; + + get_scale_and_shift_to_fit_mesh(V,camera_base_zoom,camera_base_translation); + // Rather than crash on empty mesh... + if(V.size() > 0) + { + object_scale = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm(); + } +} + +IGL_INLINE void igl::opengl::ViewerCore::get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + float& zoom, + Eigen::Vector3f& shift) +{ + if (V.rows() == 0) + return; + + auto min_point = V.colwise().minCoeff(); + auto max_point = V.colwise().maxCoeff(); + auto centroid = (0.5*(min_point + max_point)).eval(); + shift.setConstant(0); + shift.head(centroid.size()) = -centroid.cast<float>(); + zoom = 2.0 / (max_point-min_point).array().abs().maxCoeff(); +} + + +IGL_INLINE void igl::opengl::ViewerCore::clear_framebuffers() +{ + glClearColor(background_color[0], + background_color[1], + background_color[2], + background_color[3]); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +IGL_INLINE void igl::opengl::ViewerCore::draw( + ViewerData& data, + bool update_matrices) +{ + using namespace std; + using namespace Eigen; + + if (depth_test) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* Bind and potentially refresh mesh/line/point data */ + if (data.dirty) + { + data.updateGL(data, data.invert_normals,data.meshgl); + data.dirty = MeshGL::DIRTY_NONE; + } + data.meshgl.bind_mesh(); + + // Initialize uniform + glViewport(viewport(0), viewport(1), viewport(2), viewport(3)); + + if(update_matrices) + { + view = Eigen::Matrix4f::Identity(); + proj = Eigen::Matrix4f::Identity(); + norm = Eigen::Matrix4f::Identity(); + + float width = viewport(2); + float height = viewport(3); + + // Set view + look_at( camera_eye, camera_center, camera_up, view); + view = view + * (trackball_angle * Eigen::Scaling(camera_zoom * camera_base_zoom) + * Eigen::Translation3f(camera_translation + camera_base_translation)).matrix(); + + norm = view.inverse().transpose(); + + // Set projection + if (orthographic) + { + float length = (camera_eye - camera_center).norm(); + float h = tan(camera_view_angle/360.0 * igl::PI) * (length); + ortho(-h*width/height, h*width/height, -h, h, camera_dnear, camera_dfar,proj); + } + else + { + float fH = tan(camera_view_angle / 360.0 * igl::PI) * camera_dnear; + float fW = fH * (double)width/(double)height; + frustum(-fW, fW, -fH, fH, camera_dnear, camera_dfar,proj); + } + } + + // Send transformations to the GPU + GLint viewi = glGetUniformLocation(data.meshgl.shader_mesh,"view"); + GLint proji = glGetUniformLocation(data.meshgl.shader_mesh,"proj"); + GLint normi = glGetUniformLocation(data.meshgl.shader_mesh,"normal_matrix"); + glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data()); + glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data()); + glUniformMatrix4fv(normi, 1, GL_FALSE, norm.data()); + + // Light parameters + GLint specular_exponenti = glGetUniformLocation(data.meshgl.shader_mesh,"specular_exponent"); + GLint light_position_eyei = glGetUniformLocation(data.meshgl.shader_mesh,"light_position_eye"); + GLint lighting_factori = glGetUniformLocation(data.meshgl.shader_mesh,"lighting_factor"); + GLint fixed_colori = glGetUniformLocation(data.meshgl.shader_mesh,"fixed_color"); + GLint texture_factori = glGetUniformLocation(data.meshgl.shader_mesh,"texture_factor"); + + glUniform1f(specular_exponenti, data.shininess); + glUniform3fv(light_position_eyei, 1, light_position.data()); + glUniform1f(lighting_factori, lighting_factor); // enables lighting + glUniform4f(fixed_colori, 0.0, 0.0, 0.0, 0.0); + + if (data.V.rows()>0) + { + // Render fill + if (data.show_faces) + { + // Texture + glUniform1f(texture_factori, data.show_texture ? 1.0f : 0.0f); + data.meshgl.draw_mesh(true); + glUniform1f(texture_factori, 0.0f); + } + + // Render wireframe + if (data.show_lines) + { + glLineWidth(data.line_width); + glUniform4f(fixed_colori, + data.line_color[0], + data.line_color[1], + data.line_color[2], 1.0f); + data.meshgl.draw_mesh(false); + glUniform4f(fixed_colori, 0.0f, 0.0f, 0.0f, 0.0f); + } + } + + if (data.show_overlay) + { + if (data.show_overlay_depth) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + if (data.lines.rows() > 0) + { + data.meshgl.bind_overlay_lines(); + viewi = glGetUniformLocation(data.meshgl.shader_overlay_lines,"view"); + proji = glGetUniformLocation(data.meshgl.shader_overlay_lines,"proj"); + + glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data()); + glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data()); + // This must be enabled, otherwise glLineWidth has no effect + glEnable(GL_LINE_SMOOTH); + glLineWidth(data.line_width); + + data.meshgl.draw_overlay_lines(); + } + + if (data.points.rows() > 0) + { + data.meshgl.bind_overlay_points(); + viewi = glGetUniformLocation(data.meshgl.shader_overlay_points,"view"); + proji = glGetUniformLocation(data.meshgl.shader_overlay_points,"proj"); + + glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data()); + glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data()); + glPointSize(data.point_size); + + data.meshgl.draw_overlay_points(); + } + + glEnable(GL_DEPTH_TEST); + } + +} + +IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data, + bool update_matrices, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A) +{ + assert(R.rows() == G.rows() && G.rows() == B.rows() && B.rows() == A.rows()); + assert(R.cols() == G.cols() && G.cols() == B.cols() && B.cols() == A.cols()); + + unsigned width = R.rows(); + unsigned height = R.cols(); + + // https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing + unsigned int framebuffer; + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + // create a multisampled color attachment texture + unsigned int textureColorBufferMultiSampled; + glGenTextures(1, &textureColorBufferMultiSampled); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA, width, height, GL_TRUE); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0); + // create a (also multisampled) renderbuffer object for depth and stencil attachments + unsigned int rbo; + glGenRenderbuffers(1, &rbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // configure second post-processing framebuffer + unsigned int intermediateFBO; + glGenFramebuffers(1, &intermediateFBO); + glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO); + // create a color attachment texture + unsigned int screenTexture; + glGenTextures(1, &screenTexture); + glBindTexture(GL_TEXTURE_2D, screenTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0); // we only need a color buffer + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + // Clear the buffer + glClearColor(background_color(0), background_color(1), background_color(2), 0.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // Save old viewport + Eigen::Vector4f viewport_ori = viewport; + viewport << 0,0,width,height; + // Draw + draw(data,update_matrices); + // Restore viewport + viewport = viewport_ori; + + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO); + // Copy back in the given Eigen matrices + GLubyte* pixels = (GLubyte*)calloc(width*height*4,sizeof(GLubyte)); + glReadPixels(0, 0,width, height,GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Clean up + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, &screenTexture); + glDeleteTextures(1, &textureColorBufferMultiSampled); + glDeleteFramebuffers(1, &framebuffer); + glDeleteFramebuffers(1, &intermediateFBO); + glDeleteRenderbuffers(1, &rbo); + + int count = 0; + for (unsigned j=0; j<height; ++j) + { + for (unsigned i=0; i<width; ++i) + { + R(i,j) = pixels[count*4+0]; + G(i,j) = pixels[count*4+1]; + B(i,j) = pixels[count*4+2]; + A(i,j) = pixels[count*4+3]; + ++count; + } + } + // Clean up + free(pixels); +} + +IGL_INLINE void igl::opengl::ViewerCore::set_rotation_type( + const igl::opengl::ViewerCore::RotationType & value) +{ + using namespace Eigen; + using namespace std; + const RotationType old_rotation_type = rotation_type; + rotation_type = value; + if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP && + old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP) + { + snap_to_fixed_up(Quaternionf(trackball_angle),trackball_angle); + } +} + + +IGL_INLINE igl::opengl::ViewerCore::ViewerCore() +{ + // Default colors + background_color << 0.3f, 0.3f, 0.5f, 1.0f; + + // Default lights settings + light_position << 0.0f, 0.3f, 0.0f; + lighting_factor = 1.0f; //on + + // Default trackball + trackball_angle = Eigen::Quaternionf::Identity(); + set_rotation_type(ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP); + + // Camera parameters + camera_base_zoom = 1.0f; + camera_zoom = 1.0f; + orthographic = false; + camera_view_angle = 45.0; + camera_dnear = 1.0; + camera_dfar = 100.0; + camera_base_translation << 0, 0, 0; + camera_translation << 0, 0, 0; + camera_eye << 0, 0, 5; + camera_center << 0, 0, 0; + camera_up << 0, 1, 0; + + depth_test = true; + + is_animating = false; + animation_max_fps = 30.; + + viewport.setZero(); +} + +IGL_INLINE void igl::opengl::ViewerCore::init() +{ +} + +IGL_INLINE void igl::opengl::ViewerCore::shut() +{ +} diff --git a/src/igl/opengl/ViewerCore.h b/src/igl/opengl/ViewerCore.h new file mode 100644 index 000000000..94d3367e0 --- /dev/null +++ b/src/igl/opengl/ViewerCore.h @@ -0,0 +1,199 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_VIEWERCORE_H +#define IGL_OPENGL_VIEWERCORE_H + +#include <igl/opengl/MeshGL.h> +#include <igl/opengl/ViewerData.h> + +#include <igl/igl_inline.h> +#include <Eigen/Geometry> +#include <Eigen/Core> + +namespace igl +{ +namespace opengl +{ + +// Basic class of the 3D mesh viewer +// TODO: write documentation + +class ViewerCore +{ +public: + IGL_INLINE ViewerCore(); + + // Initialization + IGL_INLINE void init(); + + // Shutdown + IGL_INLINE void shut(); + + // Serialization code + IGL_INLINE void InitSerialization(); + + + // ------------------- Camera control functions + + // Adjust the view to see the entire model + IGL_INLINE void align_camera_center( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F); + + // Determines how much to zoom and shift such that the mesh fills the unit + // box (centered at the origin) + IGL_INLINE void get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + float & zoom, + Eigen::Vector3f& shift); + + // Adjust the view to see the entire model + IGL_INLINE void align_camera_center( + const Eigen::MatrixXd& V); + + // Determines how much to zoom and shift such that the mesh fills the unit + // box (centered at the origin) + IGL_INLINE void get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + float & zoom, + Eigen::Vector3f& shift); + + // ------------------- Drawing functions + + // Clear the frame buffers + IGL_INLINE void clear_framebuffers(); + + // Draw everything + // + // data cannot be const because it is being set to "clean" + IGL_INLINE void draw(ViewerData& data, bool update_matrices = true); + IGL_INLINE void draw_buffer( + ViewerData& data, + bool update_matrices, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B, + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A); + + // Trackball angle (quaternion) + enum RotationType + { + ROTATION_TYPE_TRACKBALL = 0, + ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1, + ROTATION_TYPE_NO_ROTATION = 2, + NUM_ROTATION_TYPES = 3 + }; + IGL_INLINE void set_rotation_type(const RotationType & value); + + // ------------------- Properties + + // Colors + Eigen::Vector4f background_color; + + // Lighting + Eigen::Vector3f light_position; + float lighting_factor; + + RotationType rotation_type; + Eigen::Quaternionf trackball_angle; + + // Camera parameters + float camera_base_zoom; + float camera_zoom; + bool orthographic; + Eigen::Vector3f camera_base_translation; + Eigen::Vector3f camera_translation; + Eigen::Vector3f camera_eye; + Eigen::Vector3f camera_up; + Eigen::Vector3f camera_center; + float camera_view_angle; + float camera_dnear; + float camera_dfar; + + bool depth_test; + + // Animation + bool is_animating; + double animation_max_fps; + + // Caches the two-norm between the min/max point of the bounding box + float object_scale; + + // Viewport size + Eigen::Vector4f viewport; + + // Save the OpenGL transformation matrices used for the previous rendering pass + Eigen::Matrix4f view; + Eigen::Matrix4f proj; + Eigen::Matrix4f norm; + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + +} +} + +#include <igl/serialize.h> +namespace igl { + namespace serialization { + + inline void serialization(bool s, igl::opengl::ViewerCore& obj, std::vector<char>& buffer) + { + + SERIALIZE_MEMBER(background_color); + + SERIALIZE_MEMBER(light_position); + SERIALIZE_MEMBER(lighting_factor); + + SERIALIZE_MEMBER(trackball_angle); + SERIALIZE_MEMBER(rotation_type); + + SERIALIZE_MEMBER(camera_base_zoom); + SERIALIZE_MEMBER(camera_zoom); + SERIALIZE_MEMBER(orthographic); + SERIALIZE_MEMBER(camera_base_translation); + SERIALIZE_MEMBER(camera_translation); + SERIALIZE_MEMBER(camera_view_angle); + SERIALIZE_MEMBER(camera_dnear); + SERIALIZE_MEMBER(camera_dfar); + SERIALIZE_MEMBER(camera_eye); + SERIALIZE_MEMBER(camera_center); + SERIALIZE_MEMBER(camera_up); + + SERIALIZE_MEMBER(depth_test); + SERIALIZE_MEMBER(is_animating); + SERIALIZE_MEMBER(animation_max_fps); + + SERIALIZE_MEMBER(object_scale); + + SERIALIZE_MEMBER(viewport); + SERIALIZE_MEMBER(view); + SERIALIZE_MEMBER(proj); + SERIALIZE_MEMBER(norm); + } + + template<> + inline void serialize(const igl::opengl::ViewerCore& obj, std::vector<char>& buffer) + { + serialization(true, const_cast<igl::opengl::ViewerCore&>(obj), buffer); + } + + template<> + inline void deserialize(igl::opengl::ViewerCore& obj, const std::vector<char>& buffer) + { + serialization(false, obj, const_cast<std::vector<char>&>(buffer)); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "ViewerCore.cpp" +#endif + +#endif diff --git a/src/igl/opengl/ViewerData.cpp b/src/igl/opengl/ViewerData.cpp new file mode 100644 index 000000000..0a7f2c44b --- /dev/null +++ b/src/igl/opengl/ViewerData.cpp @@ -0,0 +1,691 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "ViewerData.h" + +#include "../per_face_normals.h" +#include "../material_colors.h" +#include "../parula.h" +#include "../per_vertex_normals.h" + +#include <iostream> + + +IGL_INLINE igl::opengl::ViewerData::ViewerData() +: dirty(MeshGL::DIRTY_ALL), + show_faces(true), + show_lines(true), + invert_normals(false), + show_overlay(true), + show_overlay_depth(true), + show_vertid(false), + show_faceid(false), + show_texture(false), + point_size(30), + line_width(0.5f), + line_color(0,0,0,1), + shininess(35.0f), + id(-1) +{ + clear(); +}; + +IGL_INLINE void igl::opengl::ViewerData::set_face_based(bool newvalue) +{ + if (face_based != newvalue) + { + face_based = newvalue; + dirty = MeshGL::DIRTY_ALL; + } +} + +// Helpers that draws the most common meshes +IGL_INLINE void igl::opengl::ViewerData::set_mesh( + const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F) +{ + using namespace std; + + Eigen::MatrixXd V_temp; + + // If V only has two columns, pad with a column of zeros + if (_V.cols() == 2) + { + V_temp = Eigen::MatrixXd::Zero(_V.rows(),3); + V_temp.block(0,0,_V.rows(),2) = _V; + } + else + V_temp = _V; + + if (V.rows() == 0 && F.rows() == 0) + { + V = V_temp; + F = _F; + + compute_normals(); + uniform_colors( + Eigen::Vector3d(GOLD_AMBIENT[0], GOLD_AMBIENT[1], GOLD_AMBIENT[2]), + Eigen::Vector3d(GOLD_DIFFUSE[0], GOLD_DIFFUSE[1], GOLD_DIFFUSE[2]), + Eigen::Vector3d(GOLD_SPECULAR[0], GOLD_SPECULAR[1], GOLD_SPECULAR[2])); + + grid_texture(); + } + else + { + if (_V.rows() == V.rows() && _F.rows() == F.rows()) + { + V = V_temp; + F = _F; + } + else + cerr << "ERROR (set_mesh): The new mesh has a different number of vertices/faces. Please clear the mesh before plotting."<<endl; + } + dirty |= MeshGL::DIRTY_FACE | MeshGL::DIRTY_POSITION; +} + +IGL_INLINE void igl::opengl::ViewerData::set_vertices(const Eigen::MatrixXd& _V) +{ + V = _V; + assert(F.size() == 0 || F.maxCoeff() < V.rows()); + dirty |= MeshGL::DIRTY_POSITION; +} + +IGL_INLINE void igl::opengl::ViewerData::set_normals(const Eigen::MatrixXd& N) +{ + using namespace std; + if (N.rows() == V.rows()) + { + set_face_based(false); + V_normals = N; + } + else if (N.rows() == F.rows() || N.rows() == F.rows()*3) + { + set_face_based(true); + F_normals = N; + } + else + cerr << "ERROR (set_normals): Please provide a normal per face, per corner or per vertex."<<endl; + dirty |= MeshGL::DIRTY_NORMAL; +} + +IGL_INLINE void igl::opengl::ViewerData::set_colors(const Eigen::MatrixXd &C) +{ + using namespace std; + using namespace Eigen; + if(C.rows()>0 && C.cols() == 1) + { + Eigen::MatrixXd C3; + igl::parula(C,true,C3); + return set_colors(C3); + } + // Ambient color should be darker color + const auto ambient = [](const MatrixXd & C)->MatrixXd + { + MatrixXd T = 0.1*C; + T.col(3) = C.col(3); + return T; + }; + // Specular color should be a less saturated and darker color: dampened + // highlights + const auto specular = [](const MatrixXd & C)->MatrixXd + { + const double grey = 0.3; + MatrixXd T = grey+0.1*(C.array()-grey); + T.col(3) = C.col(3); + return T; + }; + if (C.rows() == 1) + { + for (unsigned i=0;i<V_material_diffuse.rows();++i) + { + if (C.cols() == 3) + V_material_diffuse.row(i) << C.row(0),1; + else if (C.cols() == 4) + V_material_diffuse.row(i) << C.row(0); + } + V_material_ambient = ambient(V_material_diffuse); + V_material_specular = specular(V_material_diffuse); + + for (unsigned i=0;i<F_material_diffuse.rows();++i) + { + if (C.cols() == 3) + F_material_diffuse.row(i) << C.row(0),1; + else if (C.cols() == 4) + F_material_diffuse.row(i) << C.row(0); + } + F_material_ambient = ambient(F_material_diffuse); + F_material_specular = specular(F_material_diffuse); + } + else if (C.rows() == V.rows()) + { + set_face_based(false); + for (unsigned i=0;i<V_material_diffuse.rows();++i) + { + if (C.cols() == 3) + V_material_diffuse.row(i) << C.row(i), 1; + else if (C.cols() == 4) + V_material_diffuse.row(i) << C.row(i); + } + V_material_ambient = ambient(V_material_diffuse); + V_material_specular = specular(V_material_diffuse); + } + else if (C.rows() == F.rows()) + { + set_face_based(true); + for (unsigned i=0;i<F_material_diffuse.rows();++i) + { + if (C.cols() == 3) + F_material_diffuse.row(i) << C.row(i), 1; + else if (C.cols() == 4) + F_material_diffuse.row(i) << C.row(i); + } + F_material_ambient = ambient(F_material_diffuse); + F_material_specular = specular(F_material_diffuse); + } + else + cerr << "ERROR (set_colors): Please provide a single color, or a color per face or per vertex."<<endl; + dirty |= MeshGL::DIRTY_DIFFUSE; + +} + +IGL_INLINE void igl::opengl::ViewerData::set_uv(const Eigen::MatrixXd& UV) +{ + using namespace std; + if (UV.rows() == V.rows()) + { + set_face_based(false); + V_uv = UV; + } + else + cerr << "ERROR (set_UV): Please provide uv per vertex."<<endl;; + dirty |= MeshGL::DIRTY_UV; +} + +IGL_INLINE void igl::opengl::ViewerData::set_uv(const Eigen::MatrixXd& UV_V, const Eigen::MatrixXi& UV_F) +{ + set_face_based(true); + V_uv = UV_V.block(0,0,UV_V.rows(),2); + F_uv = UV_F; + dirty |= MeshGL::DIRTY_UV; +} + + +IGL_INLINE void igl::opengl::ViewerData::set_texture( + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B) +{ + texture_R = R; + texture_G = G; + texture_B = B; + texture_A = Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>::Constant(R.rows(),R.cols(),255); + dirty |= MeshGL::DIRTY_TEXTURE; +} + +IGL_INLINE void igl::opengl::ViewerData::set_texture( + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A) +{ + texture_R = R; + texture_G = G; + texture_B = B; + texture_A = A; + dirty |= MeshGL::DIRTY_TEXTURE; +} + +IGL_INLINE void igl::opengl::ViewerData::set_points( + const Eigen::MatrixXd& P, + const Eigen::MatrixXd& C) +{ + // clear existing points + points.resize(0,0); + add_points(P,C); +} + +IGL_INLINE void igl::opengl::ViewerData::add_points(const Eigen::MatrixXd& P, const Eigen::MatrixXd& C) +{ + Eigen::MatrixXd P_temp; + + // If P only has two columns, pad with a column of zeros + if (P.cols() == 2) + { + P_temp = Eigen::MatrixXd::Zero(P.rows(),3); + P_temp.block(0,0,P.rows(),2) = P; + } + else + P_temp = P; + + int lastid = points.rows(); + points.conservativeResize(points.rows() + P_temp.rows(),6); + for (unsigned i=0; i<P_temp.rows(); ++i) + points.row(lastid+i) << P_temp.row(i), i<C.rows() ? C.row(i) : C.row(C.rows()-1); + + dirty |= MeshGL::DIRTY_OVERLAY_POINTS; +} + +IGL_INLINE void igl::opengl::ViewerData::set_edges( + const Eigen::MatrixXd& P, + const Eigen::MatrixXi& E, + const Eigen::MatrixXd& C) +{ + using namespace Eigen; + lines.resize(E.rows(),9); + assert(C.cols() == 3); + for(int e = 0;e<E.rows();e++) + { + RowVector3d color; + if(C.size() == 3) + { + color<<C; + }else if(C.rows() == E.rows()) + { + color<<C.row(e); + } + lines.row(e)<< P.row(E(e,0)), P.row(E(e,1)), color; + } + dirty |= MeshGL::DIRTY_OVERLAY_LINES; +} + +IGL_INLINE void igl::opengl::ViewerData::add_edges(const Eigen::MatrixXd& P1, const Eigen::MatrixXd& P2, const Eigen::MatrixXd& C) +{ + Eigen::MatrixXd P1_temp,P2_temp; + + // If P1 only has two columns, pad with a column of zeros + if (P1.cols() == 2) + { + P1_temp = Eigen::MatrixXd::Zero(P1.rows(),3); + P1_temp.block(0,0,P1.rows(),2) = P1; + P2_temp = Eigen::MatrixXd::Zero(P2.rows(),3); + P2_temp.block(0,0,P2.rows(),2) = P2; + } + else + { + P1_temp = P1; + P2_temp = P2; + } + + int lastid = lines.rows(); + lines.conservativeResize(lines.rows() + P1_temp.rows(),9); + for (unsigned i=0; i<P1_temp.rows(); ++i) + lines.row(lastid+i) << P1_temp.row(i), P2_temp.row(i), i<C.rows() ? C.row(i) : C.row(C.rows()-1); + + dirty |= MeshGL::DIRTY_OVERLAY_LINES; +} + +IGL_INLINE void igl::opengl::ViewerData::add_label(const Eigen::VectorXd& P, const std::string& str) +{ + Eigen::RowVectorXd P_temp; + + // If P only has two columns, pad with a column of zeros + if (P.size() == 2) + { + P_temp = Eigen::RowVectorXd::Zero(3); + P_temp << P.transpose(), 0; + } + else + P_temp = P; + + int lastid = labels_positions.rows(); + labels_positions.conservativeResize(lastid+1, 3); + labels_positions.row(lastid) = P_temp; + labels_strings.push_back(str); +} + +IGL_INLINE void igl::opengl::ViewerData::clear() +{ + V = Eigen::MatrixXd (0,3); + F = Eigen::MatrixXi (0,3); + + F_material_ambient = Eigen::MatrixXd (0,4); + F_material_diffuse = Eigen::MatrixXd (0,4); + F_material_specular = Eigen::MatrixXd (0,4); + + V_material_ambient = Eigen::MatrixXd (0,4); + V_material_diffuse = Eigen::MatrixXd (0,4); + V_material_specular = Eigen::MatrixXd (0,4); + + F_normals = Eigen::MatrixXd (0,3); + V_normals = Eigen::MatrixXd (0,3); + + V_uv = Eigen::MatrixXd (0,2); + F_uv = Eigen::MatrixXi (0,3); + + lines = Eigen::MatrixXd (0,9); + points = Eigen::MatrixXd (0,6); + labels_positions = Eigen::MatrixXd (0,3); + labels_strings.clear(); + + face_based = false; +} + +IGL_INLINE void igl::opengl::ViewerData::compute_normals() +{ + igl::per_face_normals(V, F, F_normals); + igl::per_vertex_normals(V, F, F_normals, V_normals); + dirty |= MeshGL::DIRTY_NORMAL; +} + +IGL_INLINE void igl::opengl::ViewerData::uniform_colors( + const Eigen::Vector3d& ambient, + const Eigen::Vector3d& diffuse, + const Eigen::Vector3d& specular) +{ + Eigen::Vector4d ambient4; + Eigen::Vector4d diffuse4; + Eigen::Vector4d specular4; + + ambient4 << ambient, 1; + diffuse4 << diffuse, 1; + specular4 << specular, 1; + + uniform_colors(ambient4,diffuse4,specular4); +} + +IGL_INLINE void igl::opengl::ViewerData::uniform_colors( + const Eigen::Vector4d& ambient, + const Eigen::Vector4d& diffuse, + const Eigen::Vector4d& specular) +{ + V_material_ambient.resize(V.rows(),4); + V_material_diffuse.resize(V.rows(),4); + V_material_specular.resize(V.rows(),4); + + for (unsigned i=0; i<V.rows();++i) + { + V_material_ambient.row(i) = ambient; + V_material_diffuse.row(i) = diffuse; + V_material_specular.row(i) = specular; + } + + F_material_ambient.resize(F.rows(),4); + F_material_diffuse.resize(F.rows(),4); + F_material_specular.resize(F.rows(),4); + + for (unsigned i=0; i<F.rows();++i) + { + F_material_ambient.row(i) = ambient; + F_material_diffuse.row(i) = diffuse; + F_material_specular.row(i) = specular; + } + dirty |= MeshGL::DIRTY_SPECULAR | MeshGL::DIRTY_DIFFUSE | MeshGL::DIRTY_AMBIENT; +} + +IGL_INLINE void igl::opengl::ViewerData::grid_texture() +{ + // Don't do anything for an empty mesh + if(V.rows() == 0) + { + V_uv.resize(V.rows(),2); + return; + } + if (V_uv.rows() == 0) + { + V_uv = V.block(0, 0, V.rows(), 2); + V_uv.col(0) = V_uv.col(0).array() - V_uv.col(0).minCoeff(); + V_uv.col(0) = V_uv.col(0).array() / V_uv.col(0).maxCoeff(); + V_uv.col(1) = V_uv.col(1).array() - V_uv.col(1).minCoeff(); + V_uv.col(1) = V_uv.col(1).array() / V_uv.col(1).maxCoeff(); + V_uv = V_uv.array() * 10; + dirty |= MeshGL::DIRTY_TEXTURE; + } + + unsigned size = 128; + unsigned size2 = size/2; + texture_R.resize(size, size); + for (unsigned i=0; i<size; ++i) + { + for (unsigned j=0; j<size; ++j) + { + texture_R(i,j) = 0; + if ((i<size2 && j<size2) || (i>=size2 && j>=size2)) + texture_R(i,j) = 255; + } + } + + texture_G = texture_R; + texture_B = texture_R; + texture_A = Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>::Constant(texture_R.rows(),texture_R.cols(),255); + dirty |= MeshGL::DIRTY_TEXTURE; +} + +IGL_INLINE void igl::opengl::ViewerData::updateGL( + const igl::opengl::ViewerData& data, + const bool invert_normals, + igl::opengl::MeshGL& meshgl + ) +{ + if (!meshgl.is_initialized) + { + meshgl.init(); + } + + bool per_corner_uv = (data.F_uv.rows() == data.F.rows()); + bool per_corner_normals = (data.F_normals.rows() == 3 * data.F.rows()); + + meshgl.dirty |= data.dirty; + + // Input: + // X #F by dim quantity + // Output: + // X_vbo #F*3 by dim scattering per corner + const auto per_face = [&data]( + const Eigen::MatrixXd & X, + MeshGL::RowMatrixXf & X_vbo) + { + assert(X.cols() == 4); + X_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + X_vbo.row(i*3+j) = X.row(i).cast<float>(); + }; + + // Input: + // X #V by dim quantity + // Output: + // X_vbo #F*3 by dim scattering per corner + const auto per_corner = [&data]( + const Eigen::MatrixXd & X, + MeshGL::RowMatrixXf & X_vbo) + { + X_vbo.resize(data.F.rows()*3,X.cols()); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + X_vbo.row(i*3+j) = X.row(data.F(i,j)).cast<float>(); + }; + + if (!data.face_based) + { + if (!(per_corner_uv || per_corner_normals)) + { + // Vertex positions + if (meshgl.dirty & MeshGL::DIRTY_POSITION) + meshgl.V_vbo = data.V.cast<float>(); + + // Vertex normals + if (meshgl.dirty & MeshGL::DIRTY_NORMAL) + { + meshgl.V_normals_vbo = data.V_normals.cast<float>(); + if (invert_normals) + meshgl.V_normals_vbo = -meshgl.V_normals_vbo; + } + + // Per-vertex material settings + if (meshgl.dirty & MeshGL::DIRTY_AMBIENT) + meshgl.V_ambient_vbo = data.V_material_ambient.cast<float>(); + if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE) + meshgl.V_diffuse_vbo = data.V_material_diffuse.cast<float>(); + if (meshgl.dirty & MeshGL::DIRTY_SPECULAR) + meshgl.V_specular_vbo = data.V_material_specular.cast<float>(); + + // Face indices + if (meshgl.dirty & MeshGL::DIRTY_FACE) + meshgl.F_vbo = data.F.cast<unsigned>(); + + // Texture coordinates + if (meshgl.dirty & MeshGL::DIRTY_UV) + { + meshgl.V_uv_vbo = data.V_uv.cast<float>(); + } + } + else + { + + // Per vertex properties with per corner UVs + if (meshgl.dirty & MeshGL::DIRTY_POSITION) + { + per_corner(data.V,meshgl.V_vbo); + } + + if (meshgl.dirty & MeshGL::DIRTY_AMBIENT) + { + meshgl.V_ambient_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + meshgl.V_ambient_vbo.row(i*3+j) = data.V_material_ambient.row(data.F(i,j)).cast<float>(); + } + if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE) + { + meshgl.V_diffuse_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + meshgl.V_diffuse_vbo.row(i*3+j) = data.V_material_diffuse.row(data.F(i,j)).cast<float>(); + } + if (meshgl.dirty & MeshGL::DIRTY_SPECULAR) + { + meshgl.V_specular_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + meshgl.V_specular_vbo.row(i*3+j) = data.V_material_specular.row(data.F(i,j)).cast<float>(); + } + + if (meshgl.dirty & MeshGL::DIRTY_NORMAL) + { + meshgl.V_normals_vbo.resize(data.F.rows()*3,3); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + meshgl.V_normals_vbo.row(i*3+j) = + per_corner_normals ? + data.F_normals.row(i*3+j).cast<float>() : + data.V_normals.row(data.F(i,j)).cast<float>(); + + + if (invert_normals) + meshgl.V_normals_vbo = -meshgl.V_normals_vbo; + } + + if (meshgl.dirty & MeshGL::DIRTY_FACE) + { + meshgl.F_vbo.resize(data.F.rows(),3); + for (unsigned i=0; i<data.F.rows();++i) + meshgl.F_vbo.row(i) << i*3+0, i*3+1, i*3+2; + } + + if (meshgl.dirty & MeshGL::DIRTY_UV) + { + meshgl.V_uv_vbo.resize(data.F.rows()*3,2); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + meshgl.V_uv_vbo.row(i*3+j) = + data.V_uv.row(per_corner_uv ? + data.F_uv(i,j) : data.F(i,j)).cast<float>(); + } + } + } + else + { + if (meshgl.dirty & MeshGL::DIRTY_POSITION) + { + per_corner(data.V,meshgl.V_vbo); + } + if (meshgl.dirty & MeshGL::DIRTY_AMBIENT) + { + per_face(data.F_material_ambient,meshgl.V_ambient_vbo); + } + if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE) + { + per_face(data.F_material_diffuse,meshgl.V_diffuse_vbo); + } + if (meshgl.dirty & MeshGL::DIRTY_SPECULAR) + { + per_face(data.F_material_specular,meshgl.V_specular_vbo); + } + + if (meshgl.dirty & MeshGL::DIRTY_NORMAL) + { + meshgl.V_normals_vbo.resize(data.F.rows()*3,3); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + meshgl.V_normals_vbo.row(i*3+j) = + per_corner_normals ? + data.F_normals.row(i*3+j).cast<float>() : + data.F_normals.row(i).cast<float>(); + + if (invert_normals) + meshgl.V_normals_vbo = -meshgl.V_normals_vbo; + } + + if (meshgl.dirty & MeshGL::DIRTY_FACE) + { + meshgl.F_vbo.resize(data.F.rows(),3); + for (unsigned i=0; i<data.F.rows();++i) + meshgl.F_vbo.row(i) << i*3+0, i*3+1, i*3+2; + } + + if (meshgl.dirty & MeshGL::DIRTY_UV) + { + meshgl.V_uv_vbo.resize(data.F.rows()*3,2); + for (unsigned i=0; i<data.F.rows();++i) + for (unsigned j=0;j<3;++j) + meshgl.V_uv_vbo.row(i*3+j) = data.V_uv.row(per_corner_uv ? data.F_uv(i,j) : data.F(i,j)).cast<float>(); + } + } + + if (meshgl.dirty & MeshGL::DIRTY_TEXTURE) + { + meshgl.tex_u = data.texture_R.rows(); + meshgl.tex_v = data.texture_R.cols(); + meshgl.tex.resize(data.texture_R.size()*4); + for (unsigned i=0;i<data.texture_R.size();++i) + { + meshgl.tex(i*4+0) = data.texture_R(i); + meshgl.tex(i*4+1) = data.texture_G(i); + meshgl.tex(i*4+2) = data.texture_B(i); + meshgl.tex(i*4+3) = data.texture_A(i); + } + } + + if (meshgl.dirty & MeshGL::DIRTY_OVERLAY_LINES) + { + meshgl.lines_V_vbo.resize(data.lines.rows()*2,3); + meshgl.lines_V_colors_vbo.resize(data.lines.rows()*2,3); + meshgl.lines_F_vbo.resize(data.lines.rows()*2,1); + for (unsigned i=0; i<data.lines.rows();++i) + { + meshgl.lines_V_vbo.row(2*i+0) = data.lines.block<1, 3>(i, 0).cast<float>(); + meshgl.lines_V_vbo.row(2*i+1) = data.lines.block<1, 3>(i, 3).cast<float>(); + meshgl.lines_V_colors_vbo.row(2*i+0) = data.lines.block<1, 3>(i, 6).cast<float>(); + meshgl.lines_V_colors_vbo.row(2*i+1) = data.lines.block<1, 3>(i, 6).cast<float>(); + meshgl.lines_F_vbo(2*i+0) = 2*i+0; + meshgl.lines_F_vbo(2*i+1) = 2*i+1; + } + } + + if (meshgl.dirty & MeshGL::DIRTY_OVERLAY_POINTS) + { + meshgl.points_V_vbo.resize(data.points.rows(),3); + meshgl.points_V_colors_vbo.resize(data.points.rows(),3); + meshgl.points_F_vbo.resize(data.points.rows(),1); + for (unsigned i=0; i<data.points.rows();++i) + { + meshgl.points_V_vbo.row(i) = data.points.block<1, 3>(i, 0).cast<float>(); + meshgl.points_V_colors_vbo.row(i) = data.points.block<1, 3>(i, 3).cast<float>(); + meshgl.points_F_vbo(i) = i; + } + } +} diff --git a/src/igl/opengl/ViewerData.h b/src/igl/opengl/ViewerData.h new file mode 100644 index 000000000..b3ec9178c --- /dev/null +++ b/src/igl/opengl/ViewerData.h @@ -0,0 +1,276 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VIEWERDATA_H +#define IGL_VIEWERDATA_H + +#include "../igl_inline.h" +#include "MeshGL.h" +#include <cassert> +#include <cstdint> +#include <Eigen/Core> +#include <memory> +#include <vector> + +// Alec: This is a mesh class containing a variety of data types (normals, +// overlays, material colors, etc.) +// +namespace igl +{ + +// TODO: write documentation +namespace opengl +{ + +class ViewerData +{ +public: + ViewerData(); + + // Empty all fields + IGL_INLINE void clear(); + + // Change the visualization mode, invalidating the cache if necessary + IGL_INLINE void set_face_based(bool newvalue); + + // Helpers that can draw the most common meshes + IGL_INLINE void set_mesh(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F); + IGL_INLINE void set_vertices(const Eigen::MatrixXd& V); + IGL_INLINE void set_normals(const Eigen::MatrixXd& N); + + // Set the color of the mesh + // + // Inputs: + // C #V|#F|1 by 3 list of colors + IGL_INLINE void set_colors(const Eigen::MatrixXd &C); + // Set per-vertex UV coordinates + // + // Inputs: + // UV #V by 2 list of UV coordinates (indexed by F) + IGL_INLINE void set_uv(const Eigen::MatrixXd& UV); + // Set per-corner UV coordinates + // + // Inputs: + // UV_V #UV by 2 list of UV coordinates + // UV_F #F by 3 list of UV indices into UV_V + IGL_INLINE void set_uv(const Eigen::MatrixXd& UV_V, const Eigen::MatrixXi& UV_F); + // Set the texture associated with the mesh. + // + // Inputs: + // R width by height image matrix of red channel + // G width by height image matrix of green channel + // B width by height image matrix of blue channel + // + IGL_INLINE void set_texture( + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B); + + // Set the texture associated with the mesh. + // + // Inputs: + // R width by height image matrix of red channel + // G width by height image matrix of green channel + // B width by height image matrix of blue channel + // A width by height image matrix of alpha channel + // + IGL_INLINE void set_texture( + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B, + const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A); + + // Sets points given a list of point vertices. In constrast to `set_points` + // this will (purposefully) clober existing points. + // + // Inputs: + // P #P by 3 list of vertex positions + // C #P|1 by 3 color(s) + IGL_INLINE void set_points( + const Eigen::MatrixXd& P, + const Eigen::MatrixXd& C); + IGL_INLINE void add_points(const Eigen::MatrixXd& P, const Eigen::MatrixXd& C); + // Sets edges given a list of edge vertices and edge indices. In constrast + // to `add_edges` this will (purposefully) clober existing edges. + // + // Inputs: + // P #P by 3 list of vertex positions + // E #E by 2 list of edge indices into P + // C #E|1 by 3 color(s) + IGL_INLINE void set_edges (const Eigen::MatrixXd& P, const Eigen::MatrixXi& E, const Eigen::MatrixXd& C); + // Alec: This is very confusing. Why does add_edges have a different API from + // set_edges? + IGL_INLINE void add_edges (const Eigen::MatrixXd& P1, const Eigen::MatrixXd& P2, const Eigen::MatrixXd& C); + IGL_INLINE void add_label (const Eigen::VectorXd& P, const std::string& str); + + // Computes the normals of the mesh + IGL_INLINE void compute_normals(); + + // Assigns uniform colors to all faces/vertices + IGL_INLINE void uniform_colors( + const Eigen::Vector3d& diffuse, + const Eigen::Vector3d& ambient, + const Eigen::Vector3d& specular); + + // Assigns uniform colors to all faces/vertices + IGL_INLINE void uniform_colors( + const Eigen::Vector4d& ambient, + const Eigen::Vector4d& diffuse, + const Eigen::Vector4d& specular); + + // Generates a default grid texture + IGL_INLINE void grid_texture(); + + Eigen::MatrixXd V; // Vertices of the current mesh (#V x 3) + Eigen::MatrixXi F; // Faces of the mesh (#F x 3) + + // Per face attributes + Eigen::MatrixXd F_normals; // One normal per face + + Eigen::MatrixXd F_material_ambient; // Per face ambient color + Eigen::MatrixXd F_material_diffuse; // Per face diffuse color + Eigen::MatrixXd F_material_specular; // Per face specular color + + // Per vertex attributes + Eigen::MatrixXd V_normals; // One normal per vertex + + Eigen::MatrixXd V_material_ambient; // Per vertex ambient color + Eigen::MatrixXd V_material_diffuse; // Per vertex diffuse color + Eigen::MatrixXd V_material_specular; // Per vertex specular color + + // UV parametrization + Eigen::MatrixXd V_uv; // UV vertices + Eigen::MatrixXi F_uv; // optional faces for UVs + + // Texture + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_R; + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_G; + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_B; + Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_A; + + // Overlays + + // Lines plotted over the scene + // (Every row contains 9 doubles in the following format S_x, S_y, S_z, T_x, T_y, T_z, C_r, C_g, C_b), + // with S and T the coordinates of the two vertices of the line in global coordinates, and C the color in floating point rgb format + Eigen::MatrixXd lines; + + // Points plotted over the scene + // (Every row contains 6 doubles in the following format P_x, P_y, P_z, C_r, C_g, C_b), + // with P the position in global coordinates of the center of the point, and C the color in floating point rgb format + Eigen::MatrixXd points; + + // Text labels plotted over the scene + // Textp contains, in the i-th row, the position in global coordinates where the i-th label should be anchored + // Texts contains in the i-th position the text of the i-th label + Eigen::MatrixXd labels_positions; + std::vector<std::string> labels_strings; + + // Marks dirty buffers that need to be uploaded to OpenGL + uint32_t dirty; + + // Enable per-face or per-vertex properties + bool face_based; + + // Visualization options + bool show_overlay; + bool show_overlay_depth; + bool show_texture; + bool show_faces; + bool show_lines; + bool show_vertid; + bool show_faceid; + bool invert_normals; + + // Point size / line width + float point_size; + float line_width; + Eigen::Vector4f line_color; + + // Shape material + float shininess; + + // Unique identifier + int id; + + // OpenGL representation of the mesh + igl::opengl::MeshGL meshgl; + + // Update contents from a 'Data' instance + IGL_INLINE void updateGL( + const igl::opengl::ViewerData& data, + const bool invert_normals, + igl::opengl::MeshGL& meshgl); +}; + +} // namespace opengl +} // namespace igl + +//////////////////////////////////////////////////////////////////////////////// + +#include <igl/serialize.h> +namespace igl +{ + namespace serialization + { + inline void serialization(bool s, igl::opengl::ViewerData& obj, std::vector<char>& buffer) + { + SERIALIZE_MEMBER(V); + SERIALIZE_MEMBER(F); + SERIALIZE_MEMBER(F_normals); + SERIALIZE_MEMBER(F_material_ambient); + SERIALIZE_MEMBER(F_material_diffuse); + SERIALIZE_MEMBER(F_material_specular); + SERIALIZE_MEMBER(V_normals); + SERIALIZE_MEMBER(V_material_ambient); + SERIALIZE_MEMBER(V_material_diffuse); + SERIALIZE_MEMBER(V_material_specular); + SERIALIZE_MEMBER(V_uv); + SERIALIZE_MEMBER(F_uv); + SERIALIZE_MEMBER(texture_R); + SERIALIZE_MEMBER(texture_G); + SERIALIZE_MEMBER(texture_B); + SERIALIZE_MEMBER(texture_A); + SERIALIZE_MEMBER(lines); + SERIALIZE_MEMBER(points); + SERIALIZE_MEMBER(labels_positions); + SERIALIZE_MEMBER(labels_strings); + SERIALIZE_MEMBER(dirty); + SERIALIZE_MEMBER(face_based); + SERIALIZE_MEMBER(show_faces); + SERIALIZE_MEMBER(show_lines); + SERIALIZE_MEMBER(invert_normals); + SERIALIZE_MEMBER(show_overlay); + SERIALIZE_MEMBER(show_overlay_depth); + SERIALIZE_MEMBER(show_vertid); + SERIALIZE_MEMBER(show_faceid); + SERIALIZE_MEMBER(show_texture); + SERIALIZE_MEMBER(point_size); + SERIALIZE_MEMBER(line_width); + SERIALIZE_MEMBER(line_color); + SERIALIZE_MEMBER(shininess); + SERIALIZE_MEMBER(id); + } + template<> + inline void serialize(const igl::opengl::ViewerData& obj, std::vector<char>& buffer) + { + serialization(true, const_cast<igl::opengl::ViewerData&>(obj), buffer); + } + template<> + inline void deserialize(igl::opengl::ViewerData& obj, const std::vector<char>& buffer) + { + serialization(false, obj, const_cast<std::vector<char>&>(buffer)); + obj.dirty = igl::opengl::MeshGL::DIRTY_ALL; + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "ViewerData.cpp" +#endif + +#endif diff --git a/src/igl/opengl/bind_vertex_attrib_array.cpp b/src/igl/opengl/bind_vertex_attrib_array.cpp new file mode 100644 index 000000000..4aa807c18 --- /dev/null +++ b/src/igl/opengl/bind_vertex_attrib_array.cpp @@ -0,0 +1,24 @@ +#include "bind_vertex_attrib_array.h" + +IGL_INLINE GLint igl::opengl::bind_vertex_attrib_array( + const GLuint program_shader, + const std::string &name, + GLuint bufferID, + const Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> &M, + bool refresh) +{ + GLint id = glGetAttribLocation(program_shader, name.c_str()); + if (id < 0) + return id; + if (M.size() == 0) + { + glDisableVertexAttribArray(id); + return id; + } + glBindBuffer(GL_ARRAY_BUFFER, bufferID); + if (refresh) + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*M.size(), M.data(), GL_DYNAMIC_DRAW); + glVertexAttribPointer(id, M.cols(), GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(id); + return id; +} diff --git a/src/igl/opengl/bind_vertex_attrib_array.h b/src/igl/opengl/bind_vertex_attrib_array.h new file mode 100644 index 000000000..4e9443136 --- /dev/null +++ b/src/igl/opengl/bind_vertex_attrib_array.h @@ -0,0 +1,32 @@ +#ifndef IGL_OPENGL_BIND_VERTEX_ATTRIB_ARRAY_H +#define IGL_OPENGL_BIND_VERTEX_ATTRIB_ARRAY_H +#include "gl.h" +#include "../igl_inline.h" +#include <Eigen/Core> +#include <string> +namespace igl +{ + namespace opengl + { + // Bind a per-vertex array attribute and refresh its contents from an Eigen + // matrix + // + // Inputs: + // program_shader id of shader program + // name name of attribute in vertex shader + // bufferID id of buffer to bind to + // M #V by dim matrix of per-vertex data + // refresh whether to actually call glBufferData or just bind the buffer + // Returns id of named attribute in shader + IGL_INLINE GLint bind_vertex_attrib_array( + const GLuint program_shader, + const std::string &name, + GLuint bufferID, + const Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> &M, + bool refresh); + } +} +#ifndef IGL_STATIC_LIBRARY +#include "bind_vertex_attrib_array.cpp" +#endif +#endif diff --git a/src/igl/opengl/create_index_vbo.cpp b/src/igl/opengl/create_index_vbo.cpp new file mode 100644 index 000000000..76f6248c0 --- /dev/null +++ b/src/igl/opengl/create_index_vbo.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_index_vbo.h" + +// http://www.songho.ca/opengl/gl_vbo.html#create +IGL_INLINE void igl::opengl::create_index_vbo( + const Eigen::MatrixXi & F, + GLuint & F_vbo_id) +{ + // Generate Buffers + glGenBuffers(1,&F_vbo_id); + // Bind Buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,F_vbo_id); + // Copy data to buffers + // We expect a matrix with each vertex position on a row, we then want to + // pass this data to OpenGL reading across rows (row-major) + if(F.Options & Eigen::RowMajor) + { + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(int)*F.size(), + F.data(), + GL_STATIC_DRAW); + }else + { + // Create temporary copy of transpose + Eigen::MatrixXi FT = F.transpose(); + // If its column major then we need to temporarily store a transpose + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(int)*F.size(), + FT.data(), + GL_STATIC_DRAW); + } + // bind with 0, so, switch back to normal pointer operation + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/opengl/create_index_vbo.h b/src/igl/opengl/create_index_vbo.h new file mode 100644 index 000000000..e48f3a489 --- /dev/null +++ b/src/igl/opengl/create_index_vbo.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_INDEX_VBO_H +#define IGL_OPENGL_CREATE_INDEX_VBO_H +#include "../igl_inline.h" +#include "gl.h" +#include <Eigen/Core> + +// Create a VBO (Vertex Buffer Object) for a list of indices: +// GL_ELEMENT_ARRAY_BUFFER_ARB for the triangle indices (F) +namespace igl +{ + namespace opengl + { + // Inputs: + // F #F by 3 eigen Matrix of face (triangle) indices + // Outputs: + // F_vbo_id buffer id for face indices + // + IGL_INLINE void create_index_vbo( + const Eigen::MatrixXi & F, + GLuint & F_vbo_id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_index_vbo.cpp" +#endif + +#endif diff --git a/src/igl/opengl/create_mesh_vbo.cpp b/src/igl/opengl/create_mesh_vbo.cpp new file mode 100644 index 000000000..3876e1dfa --- /dev/null +++ b/src/igl/opengl/create_mesh_vbo.cpp @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_mesh_vbo.h" + +#include "create_vector_vbo.h" +#include "create_index_vbo.h" + +// http://www.songho.ca/opengl/gl_vbo.html#create +IGL_INLINE void igl::opengl::create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + GLuint & V_vbo_id, + GLuint & F_vbo_id) +{ + // Create VBO for vertex position vectors + create_vector_vbo(V,V_vbo_id); + // Create VBO for face index lists + create_index_vbo(F,F_vbo_id); +} + +// http://www.songho.ca/opengl/gl_vbo.html#create +IGL_INLINE void igl::opengl::create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + GLuint & V_vbo_id, + GLuint & F_vbo_id, + GLuint & N_vbo_id) +{ + // Create VBOs for faces and vertices + create_mesh_vbo(V,F,V_vbo_id,F_vbo_id); + // Create VBO for normal vectors + create_vector_vbo(N,N_vbo_id); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/opengl/create_mesh_vbo.h b/src/igl/opengl/create_mesh_vbo.h new file mode 100644 index 000000000..ecb303b09 --- /dev/null +++ b/src/igl/opengl/create_mesh_vbo.h @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_MESH_VBO_H +#define IGL_OPENGL_CREATE_MESH_VBO_H +#include "../igl_inline.h" +#include "gl.h" +#include <Eigen/Core> + +// Create a VBO (Vertex Buffer Object) for a mesh. Actually two VBOs: one +// GL_ARRAY_BUFFER for the vertex positions (V) and one +// GL_ELEMENT_ARRAY_BUFFER for the triangle indices (F) +namespace igl +{ + namespace opengl + { + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // Outputs: + // V_vbo_id buffer id for vertex positions + // F_vbo_id buffer id for face indices + // + // NOTE: when using glDrawElements VBOs for V and F using MatrixXd and + // MatrixXi will have types GL_DOUBLE and GL_UNSIGNED_INT respectively + // + IGL_INLINE void create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + GLuint & V_vbo_id, + GLuint & F_vbo_id); + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // N #V by 3 eigen Matrix of mesh vertex 3D normals + // Outputs: + // V_vbo_id buffer id for vertex positions + // F_vbo_id buffer id for face indices + // N_vbo_id buffer id for vertex positions + IGL_INLINE void create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + GLuint & V_vbo_id, + GLuint & F_vbo_id, + GLuint & N_vbo_id); + } + +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_mesh_vbo.cpp" +#endif + +#endif diff --git a/src/igl/opengl/create_shader_program.cpp b/src/igl/opengl/create_shader_program.cpp new file mode 100644 index 000000000..6c4b31a2f --- /dev/null +++ b/src/igl/opengl/create_shader_program.cpp @@ -0,0 +1,141 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_shader_program.h" + +#include "load_shader.h" +#include "print_program_info_log.h" +#include <iostream> +#include <cstdio> + +IGL_INLINE bool igl::opengl::create_shader_program( + const std::string & geom_source, + const std::string & vert_source, + const std::string & frag_source, + const std::map<std::string,GLuint> & attrib, + GLuint & id) +{ + using namespace std; + if(vert_source == "" && frag_source == "") + { + cerr<< + "create_shader_program() could not create shader program," + " both .vert and .frag source given were empty"<<endl; + return false; + } + + // create program + id = glCreateProgram(); + if(id == 0) + { + cerr<<"create_shader_program() could not create shader program."<<endl; + return false; + } + GLuint g = 0,f = 0,v = 0; + + if(geom_source != "") + { + // load vertex shader + g = igl::opengl::load_shader(geom_source.c_str(),GL_GEOMETRY_SHADER); + if(g == 0) + { + cerr<<"geometry shader failed to compile."<<endl; + return false; + } + glAttachShader(id,g); + } + + if(vert_source != "") + { + // load vertex shader + v = igl::opengl::load_shader(vert_source.c_str(),GL_VERTEX_SHADER); + if(v == 0) + { + cerr<<"vertex shader failed to compile."<<endl; + return false; + } + glAttachShader(id,v); + } + + if(frag_source != "") + { + // load fragment shader + f = igl::opengl::load_shader(frag_source.c_str(),GL_FRAGMENT_SHADER); + if(f == 0) + { + cerr<<"fragment shader failed to compile."<<endl; + return false; + } + glAttachShader(id,f); + } + + // loop over attributes + for( + std::map<std::string,GLuint>::const_iterator ait = attrib.begin(); + ait != attrib.end(); + ait++) + { + glBindAttribLocation( + id, + (*ait).second, + (*ait).first.c_str()); + } + // Link program + glLinkProgram(id); + const auto & detach = [&id](const GLuint shader) + { + if(shader) + { + glDetachShader(id,shader); + glDeleteShader(shader); + } + }; + detach(g); + detach(f); + detach(v); + + // print log if any + igl::opengl::print_program_info_log(id); + + return true; +} + +IGL_INLINE bool igl::opengl::create_shader_program( + const std::string & vert_source, + const std::string & frag_source, + const std::map<std::string,GLuint> & attrib, + GLuint & prog_id) +{ + return create_shader_program("",vert_source,frag_source,attrib,prog_id); +} + + +IGL_INLINE GLuint igl::opengl::create_shader_program( + const std::string & geom_source, + const std::string & vert_source, + const std::string & frag_source, + const std::map<std::string,GLuint> & attrib) +{ + GLuint prog_id = 0; + create_shader_program(geom_source,vert_source,frag_source,attrib,prog_id); + return prog_id; +} + +IGL_INLINE GLuint igl::opengl::create_shader_program( + const std::string & vert_source, + const std::string & frag_source, + const std::map<std::string,GLuint> & attrib) +{ + GLuint prog_id = 0; + create_shader_program(vert_source,frag_source,attrib,prog_id); + return prog_id; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif + diff --git a/src/igl/opengl/create_shader_program.h b/src/igl/opengl/create_shader_program.h new file mode 100644 index 000000000..5dfdc701d --- /dev/null +++ b/src/igl/opengl/create_shader_program.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_SHADER_PROGRAM_H +#define IGL_OPENGL_CREATE_SHADER_PROGRAM_H +#include "../igl_inline.h" +#include "gl.h" +#include <string> +#include <map> + +namespace igl +{ + namespace opengl + { + // Create a shader program with a vertex and fragments shader loading from + // source strings and vertex attributes assigned from a map before linking the + // shaders to the program, making it ready to use with glUseProgram(id) + // Inputs: + // geom_source string containing source code of geometry shader (can be + // "" to mean use default pass-through) + // vert_source string containing source code of vertex shader + // frag_source string containing source code of fragment shader + // attrib map containing table of vertex attribute strings add their + // correspondingly ids (generated previously using glBindAttribLocation) + // Outputs: + // id index id of created shader, set to 0 on error + // Returns true on success, false on error + // + // Note: Caller is responsible for making sure that current value of id is not + // leaking a shader (since it will be overwritten) + // + // See also: destroy_shader_program + IGL_INLINE bool create_shader_program( + const std::string &geom_source, + const std::string &vert_source, + const std::string &frag_source, + const std::map<std::string,GLuint> &attrib, + GLuint & id); + IGL_INLINE bool create_shader_program( + const std::string &vert_source, + const std::string &frag_source, + const std::map<std::string,GLuint> &attrib, + GLuint & id); + IGL_INLINE GLuint create_shader_program( + const std::string & geom_source, + const std::string & vert_source, + const std::string & frag_source, + const std::map<std::string,GLuint> &attrib); + IGL_INLINE GLuint create_shader_program( + const std::string & vert_source, + const std::string & frag_source, + const std::map<std::string,GLuint> &attrib); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_shader_program.cpp" +#endif + +#endif diff --git a/src/igl/opengl/create_vector_vbo.cpp b/src/igl/opengl/create_vector_vbo.cpp new file mode 100644 index 000000000..8690359b7 --- /dev/null +++ b/src/igl/opengl/create_vector_vbo.cpp @@ -0,0 +1,57 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_vector_vbo.h" + +#include <cassert> + +// http://www.songho.ca/opengl/gl_vbo.html#create +template <typename T> +IGL_INLINE void igl::opengl::create_vector_vbo( + const Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & V, + GLuint & V_vbo_id) +{ + //// Expects that input is list of 3D vectors along rows + //assert(V.cols() == 3); + + // Generate Buffers + glGenBuffers(1,&V_vbo_id); + // Bind Buffers + glBindBuffer(GL_ARRAY_BUFFER,V_vbo_id); + // Copy data to buffers + // We expect a matrix with each vertex position on a row, we then want to + // pass this data to OpenGL reading across rows (row-major) + if(V.Options & Eigen::RowMajor) + { + glBufferData( + GL_ARRAY_BUFFER, + sizeof(T)*V.size(), + V.data(), + GL_STATIC_DRAW); + }else + { + // Create temporary copy of transpose + Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> VT = V.transpose(); + // If its column major then we need to temporarily store a transpose + glBufferData( + GL_ARRAY_BUFFER, + sizeof(T)*V.size(), + VT.data(), + GL_STATIC_DRAW); + } + // bind with 0, so, switch back to normal pointer operation + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::opengl::create_vector_vbo<int>(Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, unsigned int&); +// generated by autoexplicit.sh +template void igl::opengl::create_vector_vbo<double>(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, unsigned int&); +#endif + diff --git a/src/igl/opengl/create_vector_vbo.h b/src/igl/opengl/create_vector_vbo.h new file mode 100644 index 000000000..757c1a199 --- /dev/null +++ b/src/igl/opengl/create_vector_vbo.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_VECTOR_VBO_H +#define IGL_OPENGL_CREATE_VECTOR_VBO_H +#include "../igl_inline.h" +#include "gl.h" +#include <Eigen/Core> + +// Create a VBO (Vertex Buffer Object) for a list of vectors: +// GL_ARRAY_BUFFER for the vectors (V) +namespace igl +{ + namespace opengl + { + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // V m by n eigen Matrix of type T values + // Outputs: + // V_vbo_id buffer id for vectors + // + template <typename T> + IGL_INLINE void create_vector_vbo( + const Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & V, + GLuint & V_vbo_id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_vector_vbo.cpp" +#endif + +#endif diff --git a/src/igl/opengl/destroy_shader_program.cpp b/src/igl/opengl/destroy_shader_program.cpp new file mode 100644 index 000000000..2ceace7c2 --- /dev/null +++ b/src/igl/opengl/destroy_shader_program.cpp @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "destroy_shader_program.h" +#include "report_gl_error.h" +#include <cstdio> + +IGL_INLINE bool igl::opengl::destroy_shader_program(const GLuint id) +{ + // Don't try to destroy id == 0 (no shader program) + if(id == 0) + { + fprintf(stderr,"Error: destroy_shader_program() id = %d" + " but must should be positive\n",id); + return false; + } + // Get each attached shader one by one and detach and delete it + GLsizei count; + // shader id + GLuint s; + do + { + // Try to get at most *1* attached shader + glGetAttachedShaders(id,1,&count,&s); + GLenum err = igl::opengl::report_gl_error(); + if (GL_NO_ERROR != err) + { + return false; + } + // Check that we actually got *1* + if(count == 1) + { + // Detach and delete this shader + glDetachShader(id,s); + glDeleteShader(s); + } + }while(count > 0); + // Now that all of the shaders are gone we can just delete the program + glDeleteProgram(id); + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif + diff --git a/src/igl/opengl/destroy_shader_program.h b/src/igl/opengl/destroy_shader_program.h new file mode 100644 index 000000000..5d53d8c3e --- /dev/null +++ b/src/igl/opengl/destroy_shader_program.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_DESTROY_SHADER_PROGRAM_H +#define IGL_OPENGL_DESTROY_SHADER_PROGRAM_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Properly destroy a shader program. Detach and delete each of its shaders + // and delete it + // Inputs: + // id index id of created shader, set to 0 on error + // Returns true on success, false on error + // + // Note: caller is responsible for making sure he doesn't foolishly continue + // to use id as if it still contains a program + // + // See also: create_shader_program + IGL_INLINE bool destroy_shader_program(const GLuint id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "destroy_shader_program.cpp" +#endif + +#endif diff --git a/src/igl/opengl/gl.h b/src/igl/opengl/gl.h new file mode 100644 index 000000000..fc037c198 --- /dev/null +++ b/src/igl/opengl/gl.h @@ -0,0 +1,25 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013, 2017 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GL_H +#define IGL_OPENGL_GL_H + +#ifdef IGL_OPENGL2_GL_H +# error "igl/opengl2/gl.h already included" +#endif + +// Always use this: +// #include "gl.h" +// Instead of: +// #include <OpenGL/gl3.h> +// or +// #include <GL/gl.h> +// + +#include <glad/glad.h> + +#endif diff --git a/src/igl/opengl/gl_type_size.cpp b/src/igl/opengl/gl_type_size.cpp new file mode 100644 index 000000000..0fd02bf05 --- /dev/null +++ b/src/igl/opengl/gl_type_size.cpp @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "gl_type_size.h" +#include <cassert> + +IGL_INLINE int igl::opengl::gl_type_size(const GLenum type) +{ + switch(type) + { + case GL_DOUBLE: + return 8; + break; + case GL_FLOAT: + return 4; + break; + case GL_INT: + return 4; + break; + default: + // should handle all other GL_[types] + assert(false && "Implementation incomplete."); + break; + } + return -1; +} diff --git a/src/igl/opengl/gl_type_size.h b/src/igl/opengl/gl_type_size.h new file mode 100644 index 000000000..a74119549 --- /dev/null +++ b/src/igl/opengl/gl_type_size.h @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GL_TYPE_SIZE_H +#define IGL_OPENGL_GL_TYPE_SIZE_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Return the number of bytes for a given OpenGL type // Inputs: + // type enum value of opengl type + // Returns size in bytes of type + IGL_INLINE int gl_type_size(const GLenum type); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "gl_type_size.cpp" +#endif + +#endif diff --git a/src/igl/opengl/glfw/Viewer.cpp b/src/igl/opengl/glfw/Viewer.cpp new file mode 100644 index 000000000..76d1604a0 --- /dev/null +++ b/src/igl/opengl/glfw/Viewer.cpp @@ -0,0 +1,950 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "Viewer.h" + +#include <chrono> +#include <thread> + +#include <Eigen/LU> + +#include "../gl.h" +#include <GLFW/glfw3.h> + +#include <cmath> +#include <cstdio> +#include <sstream> +#include <iomanip> +#include <iostream> +#include <fstream> +#include <algorithm> +#include <limits> +#include <cassert> + +#include <igl/project.h> +#include <igl/get_seconds.h> +#include <igl/readOBJ.h> +#include <igl/readOFF.h> +#include <igl/adjacency_list.h> +#include <igl/writeOBJ.h> +#include <igl/writeOFF.h> +#include <igl/massmatrix.h> +#include <igl/file_dialog_open.h> +#include <igl/file_dialog_save.h> +#include <igl/quat_mult.h> +#include <igl/axis_angle_to_quat.h> +#include <igl/trackball.h> +#include <igl/two_axis_valuator_fixed_up.h> +#include <igl/snap_to_canonical_view_quat.h> +#include <igl/unproject.h> +#include <igl/serialize.h> + +// Internal global variables used for glfw event handling +static igl::opengl::glfw::Viewer * __viewer; +static double highdpi = 1; +static double scroll_x = 0; +static double scroll_y = 0; + +static void glfw_mouse_press(GLFWwindow* window, int button, int action, int modifier) +{ + + igl::opengl::glfw::Viewer::MouseButton mb; + + if (button == GLFW_MOUSE_BUTTON_1) + mb = igl::opengl::glfw::Viewer::MouseButton::Left; + else if (button == GLFW_MOUSE_BUTTON_2) + mb = igl::opengl::glfw::Viewer::MouseButton::Right; + else //if (button == GLFW_MOUSE_BUTTON_3) + mb = igl::opengl::glfw::Viewer::MouseButton::Middle; + + if (action == GLFW_PRESS) + __viewer->mouse_down(mb,modifier); + else + __viewer->mouse_up(mb,modifier); +} + +static void glfw_error_callback(int error, const char* description) +{ + fputs(description, stderr); +} + +static void glfw_char_mods_callback(GLFWwindow* window, unsigned int codepoint, int modifier) +{ + __viewer->key_pressed(codepoint, modifier); +} + +static void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int modifier) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GL_TRUE); + + if (action == GLFW_PRESS) + __viewer->key_down(key, modifier); + else if(action == GLFW_RELEASE) + __viewer->key_up(key, modifier); +} + +static void glfw_window_size(GLFWwindow* window, int width, int height) +{ + int w = width*highdpi; + int h = height*highdpi; + + __viewer->post_resize(w, h); + +} + +static void glfw_mouse_move(GLFWwindow* window, double x, double y) +{ + __viewer->mouse_move(x*highdpi, y*highdpi); +} + +static void glfw_mouse_scroll(GLFWwindow* window, double x, double y) +{ + using namespace std; + scroll_x += x; + scroll_y += y; + + __viewer->mouse_scroll(y); +} + +static void glfw_drop_callback(GLFWwindow *window,int count,const char **filenames) +{ +} + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ + + IGL_INLINE int Viewer::launch(bool resizable,bool fullscreen) + { + // TODO return values are being ignored... + launch_init(resizable,fullscreen); + launch_rendering(true); + launch_shut(); + return EXIT_SUCCESS; + } + + IGL_INLINE int Viewer::launch_init(bool resizable,bool fullscreen) + { + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) + { + return EXIT_FAILURE; + } + glfwWindowHint(GLFW_SAMPLES, 8); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + #ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + #endif + if(fullscreen) + { + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + window = glfwCreateWindow(mode->width,mode->height,"libigl viewer",monitor,nullptr); + } + else + { + if (core.viewport.tail<2>().any()) { + window = glfwCreateWindow(core.viewport(2),core.viewport(3),"libigl viewer",nullptr,nullptr); + } else { + window = glfwCreateWindow(1280,800,"libigl viewer",nullptr,nullptr); + } + } + if (!window) + { + glfwTerminate(); + return EXIT_FAILURE; + } + glfwMakeContextCurrent(window); + // Load OpenGL and its extensions + if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) + { + printf("Failed to load OpenGL and its extensions\n"); + return(-1); + } + #if defined(DEBUG) || defined(_DEBUG) + printf("OpenGL Version %d.%d loaded\n", GLVersion.major, GLVersion.minor); + int major, minor, rev; + major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); + minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); + rev = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); + printf("OpenGL version received: %d.%d.%d\n", major, minor, rev); + printf("Supported OpenGL is %s\n", (const char*)glGetString(GL_VERSION)); + printf("Supported GLSL is %s\n", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); + #endif + glfwSetInputMode(window,GLFW_CURSOR,GLFW_CURSOR_NORMAL); + // Initialize FormScreen + __viewer = this; + // Register callbacks + glfwSetKeyCallback(window, glfw_key_callback); + glfwSetCursorPosCallback(window,glfw_mouse_move); + glfwSetWindowSizeCallback(window,glfw_window_size); + glfwSetMouseButtonCallback(window,glfw_mouse_press); + glfwSetScrollCallback(window,glfw_mouse_scroll); + glfwSetCharModsCallback(window,glfw_char_mods_callback); + glfwSetDropCallback(window,glfw_drop_callback); + // Handle retina displays (windows and mac) + int width, height; + glfwGetFramebufferSize(window, &width, &height); + int width_window, height_window; + glfwGetWindowSize(window, &width_window, &height_window); + highdpi = width/width_window; + glfw_window_size(window,width_window,height_window); + //opengl.init(); + core.align_camera_center(data().V,data().F); + // Initialize IGL viewer + init(); + return EXIT_SUCCESS; + } + + + + IGL_INLINE bool Viewer::launch_rendering(bool loop) + { + // glfwMakeContextCurrent(window); + // Rendering loop + const int num_extra_frames = 5; + int frame_counter = 0; + while (!glfwWindowShouldClose(window)) + { + double tic = get_seconds(); + draw(); + glfwSwapBuffers(window); + if(core.is_animating || frame_counter++ < num_extra_frames) + { + glfwPollEvents(); + // In microseconds + double duration = 1000000.*(get_seconds()-tic); + const double min_duration = 1000000./core.animation_max_fps; + if(duration<min_duration) + { + std::this_thread::sleep_for(std::chrono::microseconds((int)(min_duration-duration))); + } + } + else + { + glfwWaitEvents(); + frame_counter = 0; + } + if (!loop) + return !glfwWindowShouldClose(window); + } + return EXIT_SUCCESS; + } + + IGL_INLINE void Viewer::launch_shut() + { + for(auto & data : data_list) + { + data.meshgl.free(); + } + core.shut(); + shutdown_plugins(); + glfwDestroyWindow(window); + glfwTerminate(); + return; + } + + IGL_INLINE void Viewer::init() + { + core.init(); + + if (callback_init) + if (callback_init(*this)) + return; + + init_plugins(); + } + + IGL_INLINE void Viewer::init_plugins() + { + // Init all plugins + for (unsigned int i = 0; i<plugins.size(); ++i) + { + plugins[i]->init(this); + } + } + + IGL_INLINE void Viewer::shutdown_plugins() + { + for (unsigned int i = 0; i<plugins.size(); ++i) + { + plugins[i]->shutdown(); + } + } + + IGL_INLINE Viewer::Viewer(): + data_list(1), + selected_data_index(0), + next_data_id(1) + { + window = nullptr; + data_list.front().id = 0; + + // Temporary variables initialization + down = false; + hack_never_moved = true; + scroll_position = 0.0f; + + // Per face + data().set_face_based(false); + + // C-style callbacks + callback_init = nullptr; + callback_pre_draw = nullptr; + callback_post_draw = nullptr; + callback_mouse_down = nullptr; + callback_mouse_up = nullptr; + callback_mouse_move = nullptr; + callback_mouse_scroll = nullptr; + callback_key_down = nullptr; + callback_key_up = nullptr; + + callback_init_data = nullptr; + callback_pre_draw_data = nullptr; + callback_post_draw_data = nullptr; + callback_mouse_down_data = nullptr; + callback_mouse_up_data = nullptr; + callback_mouse_move_data = nullptr; + callback_mouse_scroll_data = nullptr; + callback_key_down_data = nullptr; + callback_key_up_data = nullptr; + +#ifndef IGL_VIEWER_VIEWER_QUIET + const std::string usage(R"(igl::opengl::glfw::Viewer usage: + [drag] Rotate scene + A,a Toggle animation (tight draw loop) + F,f Toggle face based + I,i Toggle invert normals + L,l Toggle wireframe + O,o Toggle orthographic/perspective projection + T,t Toggle filled faces + Z Snap to canonical view + [,] Toggle between rotation control types (trackball, two-axis + valuator with fixed up, 2D mode with no rotation)) + <,> Toggle between models + ; Toggle vertex labels + : Toggle face labels)" +); + std::cout<<usage<<std::endl; +#endif + } + + IGL_INLINE Viewer::~Viewer() + { + } + + IGL_INLINE bool Viewer::load_mesh_from_file( + const std::string & mesh_file_name_string) + { + + // first try to load it with a plugin + for (unsigned int i = 0; i<plugins.size(); ++i) + { + if (plugins[i]->load(mesh_file_name_string)) + { + return true; + } + } + + // Create new data slot and set to selected + if(!(data().F.rows() == 0 && data().V.rows() == 0)) + { + append_mesh(); + } + data().clear(); + + size_t last_dot = mesh_file_name_string.rfind('.'); + if (last_dot == std::string::npos) + { + std::cerr<<"Error: No file extension found in "<< + mesh_file_name_string<<std::endl; + return false; + } + + std::string extension = mesh_file_name_string.substr(last_dot+1); + + if (extension == "off" || extension =="OFF") + { + Eigen::MatrixXd V; + Eigen::MatrixXi F; + if (!igl::readOFF(mesh_file_name_string, V, F)) + return false; + data().set_mesh(V,F); + } + else if (extension == "obj" || extension =="OBJ") + { + Eigen::MatrixXd corner_normals; + Eigen::MatrixXi fNormIndices; + + Eigen::MatrixXd UV_V; + Eigen::MatrixXi UV_F; + Eigen::MatrixXd V; + Eigen::MatrixXi F; + + if (!( + igl::readOBJ( + mesh_file_name_string, + V, UV_V, corner_normals, F, UV_F, fNormIndices))) + { + return false; + } + + data().set_mesh(V,F); + data().set_uv(UV_V,UV_F); + + } + else + { + // unrecognized file type + printf("Error: %s is not a recognized file type.\n",extension.c_str()); + return false; + } + + data().compute_normals(); + data().uniform_colors(Eigen::Vector3d(51.0/255.0,43.0/255.0,33.3/255.0), + Eigen::Vector3d(255.0/255.0,228.0/255.0,58.0/255.0), + Eigen::Vector3d(255.0/255.0,235.0/255.0,80.0/255.0)); + + // Alec: why? + if (data().V_uv.rows() == 0) + { + data().grid_texture(); + } + + core.align_camera_center(data().V,data().F); + + for (unsigned int i = 0; i<plugins.size(); ++i) + if (plugins[i]->post_load()) + return true; + + return true; + } + + IGL_INLINE bool Viewer::save_mesh_to_file( + const std::string & mesh_file_name_string) + { + // first try to load it with a plugin + for (unsigned int i = 0; i<plugins.size(); ++i) + if (plugins[i]->save(mesh_file_name_string)) + return true; + + size_t last_dot = mesh_file_name_string.rfind('.'); + if (last_dot == std::string::npos) + { + // No file type determined + std::cerr<<"Error: No file extension found in "<< + mesh_file_name_string<<std::endl; + return false; + } + std::string extension = mesh_file_name_string.substr(last_dot+1); + if (extension == "off" || extension =="OFF") + { + return igl::writeOFF( + mesh_file_name_string,data().V,data().F); + } + else if (extension == "obj" || extension =="OBJ") + { + Eigen::MatrixXd corner_normals; + Eigen::MatrixXi fNormIndices; + + Eigen::MatrixXd UV_V; + Eigen::MatrixXi UV_F; + + return igl::writeOBJ(mesh_file_name_string, + data().V, + data().F, + corner_normals, fNormIndices, UV_V, UV_F); + } + else + { + // unrecognized file type + printf("Error: %s is not a recognized file type.\n",extension.c_str()); + return false; + } + return true; + } + + IGL_INLINE bool Viewer::key_pressed(unsigned int unicode_key,int modifiers) + { + if (callback_key_pressed) + if (callback_key_pressed(*this,unicode_key,modifiers)) + return true; + + for (unsigned int i = 0; i<plugins.size(); ++i) + { + if (plugins[i]->key_pressed(unicode_key, modifiers)) + { + return true; + } + } + + switch(unicode_key) + { + case 'A': + case 'a': + { + core.is_animating = !core.is_animating; + return true; + } + case 'F': + case 'f': + { + data().set_face_based(!data().face_based); + return true; + } + case 'I': + case 'i': + { + data().dirty |= MeshGL::DIRTY_NORMAL; + data().invert_normals = !data().invert_normals; + return true; + } + case 'L': + case 'l': + { + data().show_lines = !data().show_lines; + return true; + } + case 'O': + case 'o': + { + core.orthographic = !core.orthographic; + return true; + } + case 'T': + case 't': + { + data().show_faces = !data().show_faces; + return true; + } + case 'Z': + { + snap_to_canonical_quaternion(); + return true; + } + case '[': + case ']': + { + if(core.rotation_type == ViewerCore::ROTATION_TYPE_TRACKBALL) + core.set_rotation_type(ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP); + else + core.set_rotation_type(ViewerCore::ROTATION_TYPE_TRACKBALL); + + return true; + } + case '<': + case '>': + { + selected_data_index = + (selected_data_index + data_list.size() + (unicode_key=='>'?1:-1))%data_list.size(); + return true; + } + case ';': + data().show_vertid = !data().show_vertid; + return true; + case ':': + data().show_faceid = !data().show_faceid; + return true; + default: break;//do nothing + } + return false; + } + + IGL_INLINE bool Viewer::key_down(int key,int modifiers) + { + if (callback_key_down) + if (callback_key_down(*this,key,modifiers)) + return true; + for (unsigned int i = 0; i<plugins.size(); ++i) + if (plugins[i]->key_down(key, modifiers)) + return true; + return false; + } + + IGL_INLINE bool Viewer::key_up(int key,int modifiers) + { + if (callback_key_up) + if (callback_key_up(*this,key,modifiers)) + return true; + + for (unsigned int i = 0; i<plugins.size(); ++i) + if (plugins[i]->key_up(key, modifiers)) + return true; + + return false; + } + + IGL_INLINE bool Viewer::mouse_down(MouseButton button,int modifier) + { + // Remember mouse location at down even if used by callback/plugin + down_mouse_x = current_mouse_x; + down_mouse_y = current_mouse_y; + + if (callback_mouse_down) + if (callback_mouse_down(*this,static_cast<int>(button),modifier)) + return true; + + for (unsigned int i = 0; i<plugins.size(); ++i) + if(plugins[i]->mouse_down(static_cast<int>(button),modifier)) + return true; + + down = true; + + down_translation = core.camera_translation; + + + // Initialization code for the trackball + Eigen::RowVector3d center; + if (data().V.rows() == 0) + { + center << 0,0,0; + }else + { + center = data().V.colwise().sum()/data().V.rows(); + } + + Eigen::Vector3f coord = + igl::project( + Eigen::Vector3f(center(0),center(1),center(2)), + core.view, + core.proj, + core.viewport); + down_mouse_z = coord[2]; + down_rotation = core.trackball_angle; + + mouse_mode = MouseMode::Rotation; + + switch (button) + { + case MouseButton::Left: + if (core.rotation_type == ViewerCore::ROTATION_TYPE_NO_ROTATION) { + mouse_mode = MouseMode::Translation; + } else { + mouse_mode = MouseMode::Rotation; + } + break; + + case MouseButton::Right: + mouse_mode = MouseMode::Translation; + break; + + default: + mouse_mode = MouseMode::None; + break; + } + + return true; + } + + IGL_INLINE bool Viewer::mouse_up(MouseButton button,int modifier) + { + down = false; + + if (callback_mouse_up) + if (callback_mouse_up(*this,static_cast<int>(button),modifier)) + return true; + + for (unsigned int i = 0; i<plugins.size(); ++i) + if(plugins[i]->mouse_up(static_cast<int>(button),modifier)) + return true; + + mouse_mode = MouseMode::None; + + return true; + } + + IGL_INLINE bool Viewer::mouse_move(int mouse_x,int mouse_y) + { + if(hack_never_moved) + { + down_mouse_x = mouse_x; + down_mouse_y = mouse_y; + hack_never_moved = false; + } + current_mouse_x = mouse_x; + current_mouse_y = mouse_y; + + if (callback_mouse_move) + if (callback_mouse_move(*this,mouse_x,mouse_y)) + return true; + + for (unsigned int i = 0; i<plugins.size(); ++i) + if (plugins[i]->mouse_move(mouse_x, mouse_y)) + return true; + + if (down) + { + switch (mouse_mode) + { + case MouseMode::Rotation: + { + switch(core.rotation_type) + { + default: + assert(false && "Unknown rotation type"); + case ViewerCore::ROTATION_TYPE_NO_ROTATION: + break; + case ViewerCore::ROTATION_TYPE_TRACKBALL: + igl::trackball( + core.viewport(2), + core.viewport(3), + 2.0f, + down_rotation, + down_mouse_x, + down_mouse_y, + mouse_x, + mouse_y, + core.trackball_angle); + break; + case ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP: + igl::two_axis_valuator_fixed_up( + core.viewport(2),core.viewport(3), + 2.0, + down_rotation, + down_mouse_x, down_mouse_y, mouse_x, mouse_y, + core.trackball_angle); + break; + } + //Eigen::Vector4f snapq = core.trackball_angle; + + break; + } + + case MouseMode::Translation: + { + //translation + Eigen::Vector3f pos1 = igl::unproject(Eigen::Vector3f(mouse_x, core.viewport[3] - mouse_y, down_mouse_z), core.view, core.proj, core.viewport); + Eigen::Vector3f pos0 = igl::unproject(Eigen::Vector3f(down_mouse_x, core.viewport[3] - down_mouse_y, down_mouse_z), core.view, core.proj, core.viewport); + + Eigen::Vector3f diff = pos1 - pos0; + core.camera_translation = down_translation + Eigen::Vector3f(diff[0],diff[1],diff[2]); + + break; + } + case MouseMode::Zoom: + { + float delta = 0.001f * (mouse_x - down_mouse_x + mouse_y - down_mouse_y); + core.camera_zoom *= 1 + delta; + down_mouse_x = mouse_x; + down_mouse_y = mouse_y; + break; + } + + default: + break; + } + } + return true; + } + + IGL_INLINE bool Viewer::mouse_scroll(float delta_y) + { + scroll_position += delta_y; + + if (callback_mouse_scroll) + if (callback_mouse_scroll(*this,delta_y)) + return true; + + for (unsigned int i = 0; i<plugins.size(); ++i) + if (plugins[i]->mouse_scroll(delta_y)) + return true; + + // Only zoom if there's actually a change + if(delta_y != 0) + { + float mult = (1.0+((delta_y>0)?1.:-1.)*0.05); + const float min_zoom = 0.1f; + core.camera_zoom = (core.camera_zoom * mult > min_zoom ? core.camera_zoom * mult : min_zoom); + } + return true; + } + + IGL_INLINE bool Viewer::load_scene() + { + std::string fname = igl::file_dialog_open(); + if(fname.length() == 0) + return false; + return load_scene(fname); + } + + IGL_INLINE bool Viewer::load_scene(std::string fname) + { + igl::deserialize(core,"Core",fname.c_str()); + igl::deserialize(data(),"Data",fname.c_str()); + return true; + } + + IGL_INLINE bool Viewer::save_scene() + { + std::string fname = igl::file_dialog_save(); + if (fname.length() == 0) + return false; + return save_scene(fname); + } + + IGL_INLINE bool Viewer::save_scene(std::string fname) + { + igl::serialize(core,"Core",fname.c_str(),true); + igl::serialize(data(),"Data",fname.c_str()); + + return true; + } + + IGL_INLINE void Viewer::draw() + { + using namespace std; + using namespace Eigen; + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + int width_window, height_window; + glfwGetWindowSize(window, &width_window, &height_window); + + auto highdpi_tmp = width/width_window; + + if(fabs(highdpi_tmp-highdpi)>1e-8) + { + post_resize(width, height); + highdpi=highdpi_tmp; + } + + core.clear_framebuffers(); + if (callback_pre_draw) + { + if (callback_pre_draw(*this)) + { + return; + } + } + for (unsigned int i = 0; i<plugins.size(); ++i) + { + if (plugins[i]->pre_draw()) + { + return; + } + } + for(int i = 0;i<data_list.size();i++) + { + core.draw(data_list[i]); + } + if (callback_post_draw) + { + if (callback_post_draw(*this)) + { + return; + } + } + for (unsigned int i = 0; i<plugins.size(); ++i) + { + if (plugins[i]->post_draw()) + { + break; + } + } + } + + IGL_INLINE void Viewer::resize(int w,int h) + { + if (window) { + glfwSetWindowSize(window, w/highdpi, h/highdpi); + } + post_resize(w, h); + } + + IGL_INLINE void Viewer::post_resize(int w,int h) + { + core.viewport = Eigen::Vector4f(0,0,w,h); + for (unsigned int i = 0; i<plugins.size(); ++i) + { + plugins[i]->post_resize(w, h); + } + } + + IGL_INLINE void Viewer::snap_to_canonical_quaternion() + { + Eigen::Quaternionf snapq = this->core.trackball_angle; + igl::snap_to_canonical_view_quat(snapq,1.0f,this->core.trackball_angle); + } + + IGL_INLINE void Viewer::open_dialog_load_mesh() + { + std::string fname = igl::file_dialog_open(); + + if (fname.length() == 0) + return; + + this->load_mesh_from_file(fname.c_str()); + } + + IGL_INLINE void Viewer::open_dialog_save_mesh() + { + std::string fname = igl::file_dialog_save(); + + if(fname.length() == 0) + return; + + this->save_mesh_to_file(fname.c_str()); + } + + IGL_INLINE ViewerData& Viewer::data() + { + assert(!data_list.empty() && "data_list should never be empty"); + assert( + (selected_data_index >= 0 && selected_data_index < data_list.size()) && + "selected_data_index should be in bounds"); + return data_list[selected_data_index]; + } + + IGL_INLINE int Viewer::append_mesh() + { + assert(data_list.size() >= 1); + + data_list.emplace_back(); + selected_data_index = data_list.size()-1; + data_list.back().id = next_data_id++; + return data_list.back().id; + } + + IGL_INLINE bool Viewer::erase_mesh(const size_t index) + { + assert((index >= 0 && index < data_list.size()) && "index should be in bounds"); + assert(data_list.size() >= 1); + if(data_list.size() == 1) + { + // Cannot remove last mesh + return false; + } + data_list[index].meshgl.free(); + data_list.erase(data_list.begin() + index); + if(selected_data_index >= index && selected_data_index>0) + { + selected_data_index--; + } + return true; + } + + IGL_INLINE size_t Viewer::mesh_index(const int id) const { + for (size_t i = 0; i < data_list.size(); ++i) + { + if (data_list[i].id == id) + return i; + } + return 0; + } + + +} // end namespace +} // end namespace +} diff --git a/src/igl/opengl/glfw/Viewer.h b/src/igl/opengl/glfw/Viewer.h new file mode 100644 index 000000000..35aa7dfa2 --- /dev/null +++ b/src/igl/opengl/glfw/Viewer.h @@ -0,0 +1,178 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_VIEWER_H +#define IGL_OPENGL_GLFW_VIEWER_H + +#ifndef IGL_OPENGL_4 +#define IGL_OPENGL_4 +#endif + +#include "../../igl_inline.h" +#include "../MeshGL.h" +#include "../ViewerCore.h" +#include "../ViewerData.h" +#include "ViewerPlugin.h" + +#include <Eigen/Core> +#include <Eigen/Geometry> + +#include <vector> +#include <string> +#include <cstdint> + +#define IGL_MOD_SHIFT 0x0001 +#define IGL_MOD_CONTROL 0x0002 +#define IGL_MOD_ALT 0x0004 +#define IGL_MOD_SUPER 0x0008 + +struct GLFWwindow; + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ + // GLFW-based mesh viewer + class Viewer + { + public: + // UI Enumerations + enum class MouseButton {Left, Middle, Right}; + enum class MouseMode { None, Rotation, Zoom, Pan, Translation} mouse_mode; + IGL_INLINE int launch(bool resizable = true,bool fullscreen = false); + IGL_INLINE int launch_init(bool resizable = true,bool fullscreen = false); + IGL_INLINE bool launch_rendering(bool loop = true); + IGL_INLINE void launch_shut(); + IGL_INLINE void init(); + IGL_INLINE void init_plugins(); + IGL_INLINE void shutdown_plugins(); + Viewer(); + ~Viewer(); + // Mesh IO + IGL_INLINE bool load_mesh_from_file(const std::string & mesh_file_name); + IGL_INLINE bool save_mesh_to_file(const std::string & mesh_file_name); + // Callbacks + IGL_INLINE bool key_pressed(unsigned int unicode_key,int modifier); + IGL_INLINE bool key_down(int key,int modifier); + IGL_INLINE bool key_up(int key,int modifier); + IGL_INLINE bool mouse_down(MouseButton button,int modifier); + IGL_INLINE bool mouse_up(MouseButton button,int modifier); + IGL_INLINE bool mouse_move(int mouse_x,int mouse_y); + IGL_INLINE bool mouse_scroll(float delta_y); + // Scene IO + IGL_INLINE bool load_scene(); + IGL_INLINE bool load_scene(std::string fname); + IGL_INLINE bool save_scene(); + IGL_INLINE bool save_scene(std::string fname); + // Draw everything + IGL_INLINE void draw(); + // OpenGL context resize + IGL_INLINE void resize(int w,int h); // explicitly set window size + IGL_INLINE void post_resize(int w,int h); // external resize due to user interaction + // Helper functions + IGL_INLINE void snap_to_canonical_quaternion(); + IGL_INLINE void open_dialog_load_mesh(); + IGL_INLINE void open_dialog_save_mesh(); + IGL_INLINE ViewerData& data(); + + // Append a new "slot" for a mesh (i.e., create empty entires at the end of + // the data_list and opengl_state_list. + // + // Returns the id of the last appended mesh + // + // Side Effects: + // selected_data_index is set this newly created, last entry (i.e., + // #meshes-1) + IGL_INLINE int append_mesh(); + + // Erase a mesh (i.e., its corresponding data and state entires in data_list + // and opengl_state_list) + // + // Inputs: + // index index of mesh to erase + // Returns whether erasure was successful <=> cannot erase last mesh + // + // Side Effects: + // If selected_data_index is greater than or equal to index then it is + // decremented + // Example: + // // Erase all mesh slots except first and clear remaining mesh + // viewer.selected_data_index = viewer.data_list.size()-1; + // while(viewer.erase_mesh(viewer.selected_data_index)){}; + // viewer.data().clear(); + // + IGL_INLINE bool erase_mesh(const size_t index); + + // Retrieve mesh index from its unique identifier + // Returns 0 if not found + IGL_INLINE size_t mesh_index(const int id) const; + + // Alec: I call this data_list instead of just data to avoid confusion with + // old "data" variable. + // Stores all the data that should be visualized + std::vector<ViewerData> data_list; + + size_t selected_data_index; + int next_data_id; + GLFWwindow* window; + // Stores all the viewing options + ViewerCore core; + // List of registered plugins + std::vector<ViewerPlugin*> plugins; + // Temporary data stored when the mouse button is pressed + Eigen::Quaternionf down_rotation; + int current_mouse_x; + int current_mouse_y; + int down_mouse_x; + int down_mouse_y; + float down_mouse_z; + Eigen::Vector3f down_translation; + bool down; + bool hack_never_moved; + // Keep track of the global position of the scrollwheel + float scroll_position; + // C++-style functions + // + // Returns **true** if action should be cancelled. + std::function<bool(Viewer& viewer)> callback_init; + std::function<bool(Viewer& viewer)> callback_pre_draw; + std::function<bool(Viewer& viewer)> callback_post_draw; + std::function<bool(Viewer& viewer, int button, int modifier)> callback_mouse_down; + std::function<bool(Viewer& viewer, int button, int modifier)> callback_mouse_up; + std::function<bool(Viewer& viewer, int mouse_x, int mouse_y)> callback_mouse_move; + std::function<bool(Viewer& viewer, float delta_y)> callback_mouse_scroll; + std::function<bool(Viewer& viewer, unsigned int key, int modifiers)> callback_key_pressed; + // THESE SHOULD BE DEPRECATED: + std::function<bool(Viewer& viewer, unsigned int key, int modifiers)> callback_key_down; + std::function<bool(Viewer& viewer, unsigned int key, int modifiers)> callback_key_up; + // Pointers to per-callback data + void* callback_init_data; + void* callback_pre_draw_data; + void* callback_post_draw_data; + void* callback_mouse_down_data; + void* callback_mouse_up_data; + void* callback_mouse_move_data; + void* callback_mouse_scroll_data; + void* callback_key_pressed_data; + void* callback_key_down_data; + void* callback_key_up_data; + + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + }; + +} // end namespace +} // end namespace +} // end namespace + +#ifndef IGL_STATIC_LIBRARY +# include "Viewer.cpp" +#endif + +#endif diff --git a/src/igl/opengl/glfw/ViewerPlugin.h b/src/igl/opengl/glfw/ViewerPlugin.h new file mode 100644 index 000000000..9ba7903a8 --- /dev/null +++ b/src/igl/opengl/glfw/ViewerPlugin.h @@ -0,0 +1,182 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_VIEWERPLUGIN_H +#define IGL_OPENGL_GLFW_VIEWERPLUGIN_H + +// TODO: +// * create plugins/skeleton.h +// * pass time in draw function +// * remove Preview3D from comments +// * clean comments +#include <string> +#include <igl/igl_inline.h> +#include <vector> + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ + +// Abstract class for plugins +// All plugins MUST have this class as their parent and may implement any/all +// the callbacks marked `virtual` here. +// +// /////For an example of a basic plugins see plugins/skeleton.h +// +// Return value of callbacks: returning true to any of the callbacks tells +// Viewer that the event has been handled and that it should not be passed to +// other plugins or to other internal functions of Viewer + +// Forward declaration of the viewer +class Viewer; + +class ViewerPlugin +{ +public: + IGL_INLINE ViewerPlugin() + {plugin_name = "dummy";} + + virtual ~ViewerPlugin(){} + + // This function is called when the viewer is initialized (no mesh will be loaded at this stage) + IGL_INLINE virtual void init(Viewer *_viewer) + { + viewer = _viewer; + } + + // This function is called before shutdown + IGL_INLINE virtual void shutdown() + { + } + + // This function is called before a mesh is loaded + IGL_INLINE virtual bool load(std::string filename) + { + return false; + } + + // This function is called before a mesh is saved + IGL_INLINE virtual bool save(std::string filename) + { + return false; + } + + // This function is called when the scene is serialized + IGL_INLINE virtual bool serialize(std::vector<char>& buffer) const + { + return false; + } + + // This function is called when the scene is deserialized + IGL_INLINE virtual bool deserialize(const std::vector<char>& buffer) + { + return false; + } + + // Runs immediately after a new mesh has been loaded. + IGL_INLINE virtual bool post_load() + { + return false; + } + + // This function is called before the draw procedure of Preview3D + IGL_INLINE virtual bool pre_draw() + { + return false; + } + + // This function is called after the draw procedure of Preview3D + IGL_INLINE virtual bool post_draw() + { + return false; + } + + // This function is called after the window has been resized + IGL_INLINE virtual void post_resize(int w, int h) + { + } + + // This function is called when the mouse button is pressed + // - button can be GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON or GLUT_RIGHT_BUTTON + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool mouse_down(int button, int modifier) + { + return false; + } + + // This function is called when the mouse button is released + // - button can be GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON or GLUT_RIGHT_BUTTON + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool mouse_up(int button, int modifier) + { + return false; + } + + // This function is called every time the mouse is moved + // - mouse_x and mouse_y are the new coordinates of the mouse pointer in screen coordinates + IGL_INLINE virtual bool mouse_move(int mouse_x, int mouse_y) + { + return false; + } + + // This function is called every time the scroll wheel is moved + // Note: this callback is not working with every glut implementation + IGL_INLINE virtual bool mouse_scroll(float delta_y) + { + return false; + } + + // This function is called when a keyboard key is pressed. Unlike key_down + // this will reveal the actual character being sent (not just the physical + // key) + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool key_pressed(unsigned int key, int modifiers) + { + return false; + } + + // This function is called when a keyboard key is down + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool key_down(int key, int modifiers) + { + return false; + } + + // This function is called when a keyboard key is release + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool key_up(int key, int modifiers) + { + return false; + } + + std::string plugin_name; +protected: + // Pointer to the main Viewer class + Viewer *viewer; +}; + +namespace serialization +{ + inline void serialize(const ViewerPlugin& obj,std::vector<char>& buffer) + { + obj.serialize(buffer); + } + + inline void deserialize(ViewerPlugin& obj,const std::vector<char>& buffer) + { + obj.deserialize(buffer); + } +} + +} +} +} + +#endif diff --git a/src/igl/opengl/glfw/background_window.cpp b/src/igl/opengl/glfw/background_window.cpp new file mode 100644 index 000000000..964202cc2 --- /dev/null +++ b/src/igl/opengl/glfw/background_window.cpp @@ -0,0 +1,30 @@ +#include "background_window.h" + +#include <iostream> + +IGL_INLINE bool igl::opengl::glfw::background_window(GLFWwindow* & window) +{ + if(!glfwInit()) return false; + glfwSetErrorCallback([](int id,const char* m){std::cerr<<m<<std::endl;}); + glfwWindowHint(GLFW_SAMPLES, 4); + // Use 3.2 core profile + 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); + // Use background window + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + window = glfwCreateWindow(1, 1,"", NULL, NULL); + if(!window) return false; + glfwMakeContextCurrent(window); + if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) + { + printf("Failed to load OpenGL and its extensions"); + } + glGetError(); // pull and safely ignore unhandled errors like GL_INVALID_ENUM + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/opengl/glfw/background_window.h b/src/igl/opengl/glfw/background_window.h new file mode 100644 index 000000000..a37515b89 --- /dev/null +++ b/src/igl/opengl/glfw/background_window.h @@ -0,0 +1,34 @@ +#ifndef IGL_OPENGL_GLFW_BACKGROUND_WINDOW_H +#define IGL_OPENGL_GLFW_BACKGROUND_WINDOW_H +#include "../../igl_inline.h" + +#include "../gl.h" +#include <GLFW/glfw3.h> + +namespace igl +{ + namespace opengl + { + namespace glfw + { + // Create a background window with a valid core profile opengl context + // set to current. + // + // After you're finished with this window you may call + // `glfwDestroyWindow(window)` + // + // After you're finished with glfw you should call `glfwTerminate()` + // + // Outputs: + // window pointer to glfw window + // Returns true iff success + IGL_INLINE bool background_window(GLFWwindow* & window); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "background_window.cpp" +#endif + +#endif diff --git a/src/igl/opengl/glfw/imgui/ImGuiHelpers.h b/src/igl/opengl/glfw/imgui/ImGuiHelpers.h new file mode 100644 index 000000000..892274b0f --- /dev/null +++ b/src/igl/opengl/glfw/imgui/ImGuiHelpers.h @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H +#define IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H + +//////////////////////////////////////////////////////////////////////////////// +#include <imgui/imgui.h> +#include <vector> +#include <string> +#include <algorithm> +#include <functional> +//////////////////////////////////////////////////////////////////////////////// + +// Extend ImGui by populating its namespace directly +// +// Code snippets taken from there: +// https://eliasdaler.github.io/using-imgui-with-sfml-pt2/ +namespace ImGui +{ + +static auto vector_getter = [](void* vec, int idx, const char** out_text) +{ + auto& vector = *static_cast<std::vector<std::string>*>(vec); + if (idx < 0 || idx >= static_cast<int>(vector.size())) { return false; } + *out_text = vector.at(idx).c_str(); + return true; +}; + +inline bool Combo(const char* label, int* idx, std::vector<std::string>& values) +{ + if (values.empty()) { return false; } + return Combo(label, idx, vector_getter, + static_cast<void*>(&values), values.size()); +} + +inline bool Combo(const char* label, int* idx, std::function<const char *(int)> getter, int items_count) +{ + auto func = [](void* data, int i, const char** out_text) { + auto &getter = *reinterpret_cast<std::function<const char *(int)> *>(data); + const char *s = getter(i); + if (s) { *out_text = s; return true; } + else { return false; } + }; + return Combo(label, idx, func, reinterpret_cast<void *>(&getter), items_count); +} + +inline bool ListBox(const char* label, int* idx, std::vector<std::string>& values) +{ + if (values.empty()) { return false; } + return ListBox(label, idx, vector_getter, + static_cast<void*>(&values), values.size()); +} + +inline bool InputText(const char* label, std::string &str, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL) +{ + char buf[1024]; + std::fill_n(buf, 1024, 0); + std::copy_n(str.begin(), std::min(1024, (int) str.size()), buf); + if (ImGui::InputText(label, buf, 1024, flags, callback, user_data)) + { + str = std::string(buf); + return true; + } + return false; +} + +} // namespace ImGui + +#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H diff --git a/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp b/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp new file mode 100644 index 000000000..627e055cb --- /dev/null +++ b/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp @@ -0,0 +1,385 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +//////////////////////////////////////////////////////////////////////////////// +#include "ImGuiMenu.h" +#include <igl/project.h> +#include <imgui/imgui.h> +#include <imgui_impl_glfw_gl3.h> +#include <imgui_fonts_droid_sans.h> +#include <GLFW/glfw3.h> +#include <iostream> +//////////////////////////////////////////////////////////////////////////////// + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ +namespace imgui +{ + +IGL_INLINE void ImGuiMenu::init(igl::opengl::glfw::Viewer *_viewer) +{ + ViewerPlugin::init(_viewer); + // Setup ImGui binding + if (_viewer) + { + if (context_ == nullptr) + { + context_ = ImGui::CreateContext(); + } + ImGui_ImplGlfwGL3_Init(viewer->window, false); + ImGui::GetIO().IniFilename = nullptr; + ImGui::StyleColorsDark(); + ImGuiStyle& style = ImGui::GetStyle(); + style.FrameRounding = 5.0f; + reload_font(); + } +} + +IGL_INLINE void ImGuiMenu::reload_font(int font_size) +{ + hidpi_scaling_ = hidpi_scaling(); + pixel_ratio_ = pixel_ratio(); + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->Clear(); + io.Fonts->AddFontFromMemoryCompressedTTF(droid_sans_compressed_data, + droid_sans_compressed_size, font_size * hidpi_scaling_); + io.FontGlobalScale = 1.0 / pixel_ratio_; +} + +IGL_INLINE void ImGuiMenu::shutdown() +{ + // Cleanup + ImGui_ImplGlfwGL3_Shutdown(); + ImGui::DestroyContext(context_); + context_ = nullptr; +} + +IGL_INLINE bool ImGuiMenu::pre_draw() +{ + glfwPollEvents(); + + // Check whether window dpi has changed + float scaling = hidpi_scaling(); + if (std::abs(scaling - hidpi_scaling_) > 1e-5) + { + reload_font(); + ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); + } + + ImGui_ImplGlfwGL3_NewFrame(); + return false; +} + +IGL_INLINE bool ImGuiMenu::post_draw() +{ + draw_menu(); + ImGui::Render(); + return false; +} + +IGL_INLINE void ImGuiMenu::post_resize(int width, int height) +{ + if (context_) + { + ImGui::GetIO().DisplaySize.x = float(width); + ImGui::GetIO().DisplaySize.y = float(height); + } +} + +// Mouse IO +IGL_INLINE bool ImGuiMenu::mouse_down(int button, int modifier) +{ + ImGui_ImplGlfwGL3_MouseButtonCallback(viewer->window, button, GLFW_PRESS, modifier); + return ImGui::GetIO().WantCaptureMouse; +} + +IGL_INLINE bool ImGuiMenu::mouse_up(int button, int modifier) +{ + return ImGui::GetIO().WantCaptureMouse; +} + +IGL_INLINE bool ImGuiMenu::mouse_move(int mouse_x, int mouse_y) +{ + return ImGui::GetIO().WantCaptureMouse; +} + +IGL_INLINE bool ImGuiMenu::mouse_scroll(float delta_y) +{ + ImGui_ImplGlfwGL3_ScrollCallback(viewer->window, 0.f, delta_y); + return ImGui::GetIO().WantCaptureMouse; +} + +// Keyboard IO +IGL_INLINE bool ImGuiMenu::key_pressed(unsigned int key, int modifiers) +{ + ImGui_ImplGlfwGL3_CharCallback(nullptr, key); + return ImGui::GetIO().WantCaptureKeyboard; +} + +IGL_INLINE bool ImGuiMenu::key_down(int key, int modifiers) +{ + ImGui_ImplGlfwGL3_KeyCallback(viewer->window, key, 0, GLFW_PRESS, modifiers); + return ImGui::GetIO().WantCaptureKeyboard; +} + +IGL_INLINE bool ImGuiMenu::key_up(int key, int modifiers) +{ + ImGui_ImplGlfwGL3_KeyCallback(viewer->window, key, 0, GLFW_RELEASE, modifiers); + return ImGui::GetIO().WantCaptureKeyboard; +} + +// Draw menu +IGL_INLINE void ImGuiMenu::draw_menu() +{ + // Text labels + draw_labels_window(); + + // Viewer settings + if (callback_draw_viewer_window) { callback_draw_viewer_window(); } + else { draw_viewer_window(); } + + // Other windows + if (callback_draw_custom_window) { callback_draw_custom_window(); } + else { draw_custom_window(); } +} + +IGL_INLINE void ImGuiMenu::draw_viewer_window() +{ + float menu_width = 180.f * menu_scaling(); + ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints(ImVec2(menu_width, -1.0f), ImVec2(menu_width, -1.0f)); + bool _viewer_menu_visible = true; + ImGui::Begin( + "Viewer", &_viewer_menu_visible, + ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_AlwaysAutoResize + ); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.4f); + if (callback_draw_viewer_menu) { callback_draw_viewer_menu(); } + else { draw_viewer_menu(); } + ImGui::PopItemWidth(); + ImGui::End(); +} + +IGL_INLINE void ImGuiMenu::draw_viewer_menu() +{ + // Workspace + if (ImGui::CollapsingHeader("Workspace", ImGuiTreeNodeFlags_DefaultOpen)) + { + float w = ImGui::GetContentRegionAvailWidth(); + float p = ImGui::GetStyle().FramePadding.x; + if (ImGui::Button("Load##Workspace", ImVec2((w-p)/2.f, 0))) + { + viewer->load_scene(); + } + ImGui::SameLine(0, p); + if (ImGui::Button("Save##Workspace", ImVec2((w-p)/2.f, 0))) + { + viewer->save_scene(); + } + } + + // Mesh + if (ImGui::CollapsingHeader("Mesh", ImGuiTreeNodeFlags_DefaultOpen)) + { + float w = ImGui::GetContentRegionAvailWidth(); + float p = ImGui::GetStyle().FramePadding.x; + if (ImGui::Button("Load##Mesh", ImVec2((w-p)/2.f, 0))) + { + viewer->open_dialog_load_mesh(); + } + ImGui::SameLine(0, p); + if (ImGui::Button("Save##Mesh", ImVec2((w-p)/2.f, 0))) + { + viewer->open_dialog_save_mesh(); + } + } + + // Viewing options + if (ImGui::CollapsingHeader("Viewing Options", ImGuiTreeNodeFlags_DefaultOpen)) + { + if (ImGui::Button("Center object", ImVec2(-1, 0))) + { + viewer->core.align_camera_center(viewer->data().V, viewer->data().F); + } + if (ImGui::Button("Snap canonical view", ImVec2(-1, 0))) + { + viewer->snap_to_canonical_quaternion(); + } + + // Zoom + ImGui::PushItemWidth(80 * menu_scaling()); + ImGui::DragFloat("Zoom", &(viewer->core.camera_zoom), 0.05f, 0.1f, 20.0f); + + // Select rotation type + int rotation_type = static_cast<int>(viewer->core.rotation_type); + static Eigen::Quaternionf trackball_angle = Eigen::Quaternionf::Identity(); + static bool orthographic = true; + if (ImGui::Combo("Camera Type", &rotation_type, "Trackball\0Two Axes\0002D Mode\0\0")) + { + using RT = igl::opengl::ViewerCore::RotationType; + auto new_type = static_cast<RT>(rotation_type); + if (new_type != viewer->core.rotation_type) + { + if (new_type == RT::ROTATION_TYPE_NO_ROTATION) + { + trackball_angle = viewer->core.trackball_angle; + orthographic = viewer->core.orthographic; + viewer->core.trackball_angle = Eigen::Quaternionf::Identity(); + viewer->core.orthographic = true; + } + else if (viewer->core.rotation_type == RT::ROTATION_TYPE_NO_ROTATION) + { + viewer->core.trackball_angle = trackball_angle; + viewer->core.orthographic = orthographic; + } + viewer->core.set_rotation_type(new_type); + } + } + + // Orthographic view + ImGui::Checkbox("Orthographic view", &(viewer->core.orthographic)); + ImGui::PopItemWidth(); + } + + // Draw options + if (ImGui::CollapsingHeader("Draw Options", ImGuiTreeNodeFlags_DefaultOpen)) + { + if (ImGui::Checkbox("Face-based", &(viewer->data().face_based))) + { + viewer->data().set_face_based(viewer->data().face_based); + } + ImGui::Checkbox("Show texture", &(viewer->data().show_texture)); + if (ImGui::Checkbox("Invert normals", &(viewer->data().invert_normals))) + { + viewer->data().dirty |= igl::opengl::MeshGL::DIRTY_NORMAL; + } + ImGui::Checkbox("Show overlay", &(viewer->data().show_overlay)); + ImGui::Checkbox("Show overlay depth", &(viewer->data().show_overlay_depth)); + ImGui::ColorEdit4("Background", viewer->core.background_color.data(), + ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_PickerHueWheel); + ImGui::ColorEdit4("Line color", viewer->data().line_color.data(), + ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_PickerHueWheel); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.3f); + ImGui::DragFloat("Shininess", &(viewer->data().shininess), 0.05f, 0.0f, 100.0f); + ImGui::PopItemWidth(); + } + + // Overlays + if (ImGui::CollapsingHeader("Overlays", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::Checkbox("Wireframe", &(viewer->data().show_lines)); + ImGui::Checkbox("Fill", &(viewer->data().show_faces)); + ImGui::Checkbox("Show vertex labels", &(viewer->data().show_vertid)); + ImGui::Checkbox("Show faces labels", &(viewer->data().show_faceid)); + } +} + +IGL_INLINE void ImGuiMenu::draw_labels_window() +{ + // Text labels + ImGui::SetNextWindowPos(ImVec2(0,0), ImGuiSetCond_Always); + ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiSetCond_Always); + bool visible = true; + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0,0,0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); + ImGui::Begin("ViewerLabels", &visible, + ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse + | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoInputs); + for (const auto & data : viewer->data_list) + { + draw_labels(data); + } + ImGui::End(); + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); +} + +IGL_INLINE void ImGuiMenu::draw_labels(const igl::opengl::ViewerData &data) +{ + if (data.show_vertid) + { + for (int i = 0; i < data.V.rows(); ++i) + { + draw_text(data.V.row(i), data.V_normals.row(i), std::to_string(i)); + } + } + + if (data.show_faceid) + { + for (int i = 0; i < data.F.rows(); ++i) + { + Eigen::RowVector3d p = Eigen::RowVector3d::Zero(); + for (int j = 0; j < data.F.cols(); ++j) + { + p += data.V.row(data.F(i,j)); + } + p /= (double) data.F.cols(); + + draw_text(p, data.F_normals.row(i), std::to_string(i)); + } + } + + if (data.labels_positions.rows() > 0) + { + for (int i = 0; i < data.labels_positions.rows(); ++i) + { + draw_text(data.labels_positions.row(i), Eigen::Vector3d(0.0,0.0,0.0), + data.labels_strings[i]); + } + } +} + +IGL_INLINE void ImGuiMenu::draw_text(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text) +{ + pos += normal * 0.005f * viewer->core.object_scale; + Eigen::Vector3f coord = igl::project(Eigen::Vector3f(pos.cast<float>()), + viewer->core.view, viewer->core.proj, viewer->core.viewport); + + // Draw text labels slightly bigger than normal text + ImDrawList* drawList = ImGui::GetWindowDrawList(); + drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize() * 1.2, + ImVec2(coord[0]/pixel_ratio_, (viewer->core.viewport[3] - coord[1])/pixel_ratio_), + ImGui::GetColorU32(ImVec4(0, 0, 10, 255)), + &text[0], &text[0] + text.size()); +} + +IGL_INLINE float ImGuiMenu::pixel_ratio() +{ + // Computes pixel ratio for hidpi devices + int buf_size[2]; + int win_size[2]; + GLFWwindow* window = glfwGetCurrentContext(); + glfwGetFramebufferSize(window, &buf_size[0], &buf_size[1]); + glfwGetWindowSize(window, &win_size[0], &win_size[1]); + return (float) buf_size[0] / (float) win_size[0]; +} + +IGL_INLINE float ImGuiMenu::hidpi_scaling() +{ + // Computes scaling factor for hidpi devices + float xscale, yscale; + GLFWwindow* window = glfwGetCurrentContext(); + glfwGetWindowContentScale(window, &xscale, &yscale); + return 0.5 * (xscale + yscale); +} + +} // end namespace +} // end namespace +} // end namespace +} // end namespace diff --git a/src/igl/opengl/glfw/imgui/ImGuiMenu.h b/src/igl/opengl/glfw/imgui/ImGuiMenu.h new file mode 100644 index 000000000..e7144a675 --- /dev/null +++ b/src/igl/opengl/glfw/imgui/ImGuiMenu.h @@ -0,0 +1,110 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H +#define IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H + +//////////////////////////////////////////////////////////////////////////////// +#include <igl/opengl/glfw/Viewer.h> +#include <igl/opengl/glfw/ViewerPlugin.h> +#include <igl/igl_inline.h> +//////////////////////////////////////////////////////////////////////////////// + +// Forward declarations +struct ImGuiContext; + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ +namespace imgui +{ + +class ImGuiMenu : public igl::opengl::glfw::ViewerPlugin +{ +protected: + // Hidpi scaling to be used for text rendering. + float hidpi_scaling_; + + // Ratio between the framebuffer size and the window size. + // May be different from the hipdi scaling! + float pixel_ratio_; + + // ImGui Context + ImGuiContext * context_ = nullptr; + +public: + IGL_INLINE virtual void init(igl::opengl::glfw::Viewer *_viewer) override; + + IGL_INLINE virtual void reload_font(int font_size = 13); + + IGL_INLINE virtual void shutdown() override; + + IGL_INLINE virtual bool pre_draw() override; + + IGL_INLINE virtual bool post_draw() override; + + IGL_INLINE virtual void post_resize(int width, int height) override; + + // Mouse IO + IGL_INLINE virtual bool mouse_down(int button, int modifier) override; + + IGL_INLINE virtual bool mouse_up(int button, int modifier) override; + + IGL_INLINE virtual bool mouse_move(int mouse_x, int mouse_y) override; + + IGL_INLINE virtual bool mouse_scroll(float delta_y) override; + + // Keyboard IO + IGL_INLINE virtual bool key_pressed(unsigned int key, int modifiers) override; + + IGL_INLINE virtual bool key_down(int key, int modifiers) override; + + IGL_INLINE virtual bool key_up(int key, int modifiers) override; + + // Draw menu + IGL_INLINE virtual void draw_menu(); + + // Can be overwritten by `callback_draw_viewer_window` + IGL_INLINE virtual void draw_viewer_window(); + + // Can be overwritten by `callback_draw_viewer_menu` + IGL_INLINE virtual void draw_viewer_menu(); + + // Can be overwritten by `callback_draw_custom_window` + IGL_INLINE virtual void draw_custom_window() { } + + // Easy-to-customize callbacks + std::function<void(void)> callback_draw_viewer_window; + std::function<void(void)> callback_draw_viewer_menu; + std::function<void(void)> callback_draw_custom_window; + + IGL_INLINE void draw_labels_window(); + + IGL_INLINE void draw_labels(const igl::opengl::ViewerData &data); + + IGL_INLINE void draw_text(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text); + + IGL_INLINE float pixel_ratio(); + + IGL_INLINE float hidpi_scaling(); + + float menu_scaling() { return hidpi_scaling_ / pixel_ratio_; } +}; + +} // end namespace +} // end namespace +} // end namespace +} // end namespace + +#ifndef IGL_STATIC_LIBRARY +# include "ImGuiMenu.cpp" +#endif + +#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H diff --git a/src/igl/opengl/glfw/map_texture.cpp b/src/igl/opengl/glfw/map_texture.cpp new file mode 100644 index 000000000..7975d87d4 --- /dev/null +++ b/src/igl/opengl/glfw/map_texture.cpp @@ -0,0 +1,211 @@ +#ifdef IGL_OPENGL_4 + +#include "map_texture.h" +#include "background_window.h" +#include "../create_shader_program.h" + +#include "../gl.h" +#include <GLFW/glfw3.h> + +#include <iostream> +#include <string> + +template <typename DerivedV, typename DerivedF, typename DerivedU> +IGL_INLINE bool igl::opengl::glfw::map_texture( + const Eigen::MatrixBase<DerivedV> & _V, + const Eigen::MatrixBase<DerivedF> & _F, + const Eigen::MatrixBase<DerivedU> & _U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector<unsigned char> & out_data) +{ + int out_w = w; + int out_h = h; + int out_nc = nc; + return map_texture(_V,_F,_U,in_data,w,h,nc,out_data,out_w,out_h,out_nc); +} + + +template <typename DerivedV, typename DerivedF, typename DerivedU> +IGL_INLINE bool igl::opengl::glfw::map_texture( + const Eigen::MatrixBase<DerivedV> & _V, + const Eigen::MatrixBase<DerivedF> & _F, + const Eigen::MatrixBase<DerivedU> & _U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector<unsigned char> & out_data, + int & out_w, + int & out_h, + int & out_nc) +{ + const auto fail = [](const std::string msg) + { + std::cerr<<msg<<std::endl; + glfwTerminate(); + return false; + }; + // Force inputs to be RowMajor at the cost of a copy + Eigen::Matrix< + double, + DerivedV::RowsAtCompileTime, + DerivedV::ColsAtCompileTime, + Eigen::RowMajor> V = _V.template cast<double>(); + Eigen::Matrix< + double, + DerivedU::RowsAtCompileTime, + DerivedU::ColsAtCompileTime, + Eigen::RowMajor> U = _U.template cast<double>(); + Eigen::Matrix< + int, + DerivedF::RowsAtCompileTime, + DerivedF::ColsAtCompileTime, + Eigen::RowMajor> F = _F.template cast<int>(); + const int dim = U.cols(); + GLFWwindow * window; + if(!background_window(window)) + { + fail("Could not initialize glfw window"); + } + + // Compile each shader + std::string vertex_shader = dim == 2 ? + R"( +#version 330 core +layout(location = 0) in vec2 position; +layout(location = 1) in vec2 tex_coord_v; +out vec2 tex_coord_f; +void main() +{ + tex_coord_f = vec2(tex_coord_v.x,1.-tex_coord_v.y); + gl_Position = vec4( 2.*position.x-1., 2.*(1.-position.y)-1., 0.,1.); +} +)" + : + R"( +#version 330 core +layout(location = 0) in vec3 position; +layout(location = 1) in vec2 tex_coord_v; +out vec2 tex_coord_f; +void main() +{ + tex_coord_f = vec2(tex_coord_v.x,1.-tex_coord_v.y); + gl_Position = vec4( 2.*position.x-1., 2.*(1.-position.y)-1., position.z,1.); +} +)" + ; + std::string fragment_shader = R"( +#version 330 core +layout(location = 0) out vec3 color; +uniform sampler2D tex; +in vec2 tex_coord_f; +void main() +{ + color = texture(tex,tex_coord_f).rgb; +} +)"; + GLuint prog_id = + igl::opengl::create_shader_program(vertex_shader,fragment_shader,{}); + glUniform1i(glGetUniformLocation(prog_id, "tex"),0); + // Generate and attach buffers to vertex array + glDisable(GL_CULL_FACE); + GLuint VAO = 0; + glGenVertexArrays(1,&VAO); + glBindVertexArray(VAO); + GLuint ibo,vbo,tbo; + glGenBuffers(1,&ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW); + glGenBuffers(1,&vbo); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER,vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(double)*U.size(), U.data(), GL_STATIC_DRAW); + glVertexAttribLPointer(0, U.cols(), GL_DOUBLE, U.cols() * sizeof(GLdouble), (GLvoid*)0); + glGenBuffers(1,&tbo); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER,tbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(double)*V.size(), V.data(), GL_STATIC_DRAW); + glVertexAttribLPointer(1, V.cols(), GL_DOUBLE, V.cols() * sizeof(GLdouble), (GLvoid*)0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + // Prepare texture + GLuint in_tex; + GLenum format; + { + format = nc==1 ? GL_RED : (nc==3 ? GL_RGB : (nc == 4 ? GL_RGBA : GL_FALSE)); + glGenTextures(1, &in_tex); + glBindTexture(GL_TEXTURE_2D, in_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w, h, 0,format, GL_UNSIGNED_BYTE, in_data); + } + // Prepare framebuffer + GLuint fb = 0; + glGenFramebuffers(1, &fb); + glBindFramebuffer(GL_FRAMEBUFFER, fb); + GLuint out_tex; + glGenTextures(1, &out_tex); + glBindTexture(GL_TEXTURE_2D, out_tex); + // always use float for internal storage + assert(out_nc == 3); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, out_w, out_h, 0,GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, out_tex, 0); + { + GLenum bufs[1] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, bufs); + } + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + fail("framebuffer setup failed."); + } + glBindFramebuffer(GL_FRAMEBUFFER, fb); + // clear screen and set viewport + glClearColor(0.0,1.0,0.0,0.); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0,0,out_w,out_h); + // Attach shader program + glUseProgram(prog_id); + glActiveTexture(GL_TEXTURE0 + 0); + glBindTexture(GL_TEXTURE_2D, in_tex); + // Draw mesh as wireframe + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glBindVertexArray(VAO); + glDrawElements(GL_TRIANGLES, F.size(), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + // Write into memory + assert(out_nc == 3); + out_data.resize(out_nc*out_w*out_h); + glBindTexture(GL_TEXTURE_2D, out_tex); + glGetTexImage(GL_TEXTURE_2D, 0, format, GL_UNSIGNED_BYTE, &out_data[0]); + // OpenGL cleanup + glDeleteBuffers(1,&fb); + glDeleteBuffers(1,&ibo); + glDeleteBuffers(1,&vbo); + glDeleteBuffers(1,&tbo); + glDeleteTextures(1,&in_tex); + glDeleteTextures(1,&out_tex); + glDeleteVertexArrays(1,&VAO); + glUseProgram(0); + glDeleteProgram(prog_id); + // GLFW cleanup + glfwDestroyWindow(window); + glfwTerminate(); + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::opengl::glfw::map_texture<Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, -1, 1, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> > const&, unsigned char const*, int, int, int, std::vector<unsigned char, std::allocator<unsigned char> >&); +#endif + +#endif // IGL_OPENGL_4 diff --git a/src/igl/opengl/glfw/map_texture.h b/src/igl/opengl/glfw/map_texture.h new file mode 100644 index 000000000..ee3b30a45 --- /dev/null +++ b/src/igl/opengl/glfw/map_texture.h @@ -0,0 +1,63 @@ +#ifndef IGL_OPENGL_GLFW_MAP_TEXTURE_H +#define IGL_OPENGL_GLFW_MAP_TEXTURE_H + +#ifdef IGL_OPENGL_4 + +#include "../../igl_inline.h" +#include <Eigen/Core> +#include <vector> + +namespace igl +{ + namespace opengl + { + namespace glfw + { + // Given a mesh (V,F) in [0,1]² and new positions (U) and a texture image + // (in_data), _render_ a new image (out_data) of the same size. + // Inputs: + // V #V by 2 list of undeformed mesh vertex positions (matching texture) + // F #F by 3 list of mesh triangle indices into V + // U #U by 2 list of deformed vertex positions + // in_data w*h*nc array of color values, channels, then columns, then + // rows (e.g., what stbi_image returns and expects) + // w width + // h height + // nc number of channels + // Outputs: + // out_data h*w*nc list of output colors in same order as input + // + template <typename DerivedV, typename DerivedF, typename DerivedU> + IGL_INLINE bool map_texture( + const Eigen::MatrixBase<DerivedV> & V, + const Eigen::MatrixBase<DerivedF> & F, + const Eigen::MatrixBase<DerivedU> & U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector<unsigned char> & out_data); + template <typename DerivedV, typename DerivedF, typename DerivedU> + IGL_INLINE bool map_texture( + const Eigen::MatrixBase<DerivedV> & _V, + const Eigen::MatrixBase<DerivedF> & _F, + const Eigen::MatrixBase<DerivedU> & _U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector<unsigned char> & out_data, + int & out_w, + int & out_h, + int & out_nc); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "map_texture.cpp" +#endif + +#endif // IGL_OPENGL_4 + +#endif diff --git a/src/igl/opengl/init_render_to_texture.cpp b/src/igl/opengl/init_render_to_texture.cpp new file mode 100644 index 000000000..7fbcfea10 --- /dev/null +++ b/src/igl/opengl/init_render_to_texture.cpp @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "init_render_to_texture.h" +#include "gl.h" +#include <cassert> + +IGL_INLINE void igl::opengl::init_render_to_texture( + const size_t width, + const size_t height, + const bool depth_texture, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & d_id) +{ + using namespace std; + // http://www.opengl.org/wiki/Framebuffer_Object_Examples#Quick_example.2C_render_to_texture_.282D.29 + const auto & gen_tex = [](GLuint & tex_id) + { + glGenTextures(1, &tex_id); + glBindTexture(GL_TEXTURE_2D, tex_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + }; + gen_tex(tex_id); + //NULL means reserve texture memory, but texels are undefined + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_BGRA, GL_FLOAT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &fbo_id); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_id); + //Attach 2D texture to this FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id, 0); + if(depth_texture) + { + // Generate a depth texture + gen_tex(d_id); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_DEPTH_COMPONENT32, + width, + height, + 0, + GL_DEPTH_COMPONENT, + GL_FLOAT, + NULL); + glFramebufferTexture2D( + GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,d_id,0); + }else + { + // Attach a depth buffer + glGenRenderbuffers(1, &d_id); + glBindRenderbuffer(GL_RENDERBUFFER, d_id); + glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT24,width,height); + glFramebufferRenderbuffer( + GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,d_id); + } + + //Does the GPU support current FBO configuration? + GLenum status; + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + assert(status == GL_FRAMEBUFFER_COMPLETE); + // Unbind to clean up + if(!depth_texture) + { + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +IGL_INLINE void igl::opengl::init_render_to_texture( + const size_t width, + const size_t height, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & dfbo_id) +{ + return init_render_to_texture(width,height,false,tex_id,fbo_id,dfbo_id); +} diff --git a/src/igl/opengl/init_render_to_texture.h b/src/igl/opengl/init_render_to_texture.h new file mode 100644 index 000000000..ae854e770 --- /dev/null +++ b/src/igl/opengl/init_render_to_texture.h @@ -0,0 +1,77 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_INIT_RENDER_TO_TEXTURE_H +#define IGL_OPENGL_INIT_RENDER_TO_TEXTURE_H +#include "../igl_inline.h" +#include "gl.h" +#include <cstdlib> +namespace igl +{ + namespace opengl + { + // Create a frame buffer that renders color to a RGBA texture a depth to a + // "render buffer". + // + // After calling this, you can use with something like: + // + // glBindFramebuffer(GL_FRAMEBUFFER, fbo_id); + // if(!depth_texture) + // { + // glBindRenderbuffer(GL_RENDERBUFFER, d_id); + // } + // // + // // draw scene ... + // // + // // clean up + // glBindFramebuffer(GL_FRAMEBUFFER,0); + // if(!depth_texture) + // { + // glBindRenderbuffer(GL_RENDERBUFFER, 0); + // } + // // Later ... + // glActiveTexture(GL_TEXTURE0+0); + // glBindTexture(GL_TEXTURE_2D,tex_id); + // if(depth_texture) + // { + // glActiveTexture(GL_TEXTURE0+1); + // glBindTexture(GL_TEXTURE_2D,d_id); + // } + // // draw textures + // + // + // + // Inputs: + // width image width + // height image height + // depth_texture whether to create a texture for depth or to create a + // render buffer for depth + // Outputs: + // tex_id id of the texture + // fbo_id id of the frame buffer object + // d_id id of the depth texture or frame buffer object + // + IGL_INLINE void init_render_to_texture( + const size_t width, + const size_t height, + const bool depth_texture, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & d_id); + // Wrapper with depth_texture = false for legacy reasons + IGL_INLINE void init_render_to_texture( + const size_t width, + const size_t height, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & dfbo_id); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "init_render_to_texture.cpp" +#endif +#endif diff --git a/src/igl/opengl/load_shader.cpp b/src/igl/opengl/load_shader.cpp new file mode 100644 index 000000000..0ee0a978f --- /dev/null +++ b/src/igl/opengl/load_shader.cpp @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "load_shader.h" + +// Copyright Denis Kovacs 4/10/08 +#include "print_shader_info_log.h" +#include <cstdio> +IGL_INLINE GLuint igl::opengl::load_shader( + const std::string & src,const GLenum type) +{ + if(src.empty()) + { + return (GLuint) 0; + } + + GLuint s = glCreateShader(type); + if(s == 0) + { + fprintf(stderr,"Error: load_shader() failed to create shader.\n"); + return 0; + } + // Pass shader source string + const char *c = src.c_str(); + glShaderSource(s, 1, &c, NULL); + glCompileShader(s); + // Print info log (if any) + igl::opengl::print_shader_info_log(s); + return s; +} diff --git a/src/igl/opengl/load_shader.h b/src/igl/opengl/load_shader.h new file mode 100644 index 000000000..6c234d8fc --- /dev/null +++ b/src/igl/opengl/load_shader.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_LOAD_SHADER_H +#define IGL_OPENGL_LOAD_SHADER_H +#include "../igl_inline.h" +#include "gl.h" +#include <string> + +namespace igl +{ + namespace opengl + { + // Creates and compiles a shader from a given string + // + // Inputs: + // src string containing GLSL shader code + // type GLSL type of shader, one of: + // GL_VERTEX_SHADER + // GL_FRAGMENT_SHADER + // GL_GEOMETRY_SHADER + // Returns index id of the newly created shader, 0 on error + // + // Will immediately return 0 if src is empty. + IGL_INLINE GLuint load_shader( + const std::string & src,const GLenum type); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "load_shader.cpp" +#endif + +#endif diff --git a/src/igl/opengl/print_program_info_log.cpp b/src/igl/opengl/print_program_info_log.cpp new file mode 100644 index 000000000..41b37b7fb --- /dev/null +++ b/src/igl/opengl/print_program_info_log.cpp @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "print_program_info_log.h" + +#include <cstdio> +#include <stdlib.h> +// Copyright Denis Kovacs 4/10/08 +IGL_INLINE void igl::opengl::print_program_info_log(const GLuint obj) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + char *infoLog; + + glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength); + + if (infologLength > 0) + { + infoLog = (char *)malloc(infologLength); + glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog); + printf("%s\n",infoLog); + free(infoLog); + } +} diff --git a/src/igl/opengl/print_program_info_log.h b/src/igl/opengl/print_program_info_log.h new file mode 100644 index 000000000..43bcc0974 --- /dev/null +++ b/src/igl/opengl/print_program_info_log.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_PRINT_PROGRAM_INFO_LOG_H +#define IGL_OPENGL_PRINT_PROGRAM_INFO_LOG_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Inputs: + // obj OpenGL index of program to print info log about + IGL_INLINE void print_program_info_log(const GLuint obj); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "print_program_info_log.cpp" +#endif + +#endif diff --git a/src/igl/opengl/print_shader_info_log.cpp b/src/igl/opengl/print_shader_info_log.cpp new file mode 100644 index 000000000..2d3554387 --- /dev/null +++ b/src/igl/opengl/print_shader_info_log.cpp @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "print_shader_info_log.h" + +#include <cstdio> +#include <stdlib.h> +// Copyright Denis Kovacs 4/10/08 +IGL_INLINE void igl::opengl::print_shader_info_log(const GLuint obj) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + char *infoLog; + + // Get shader info log from opengl + glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength); + // Only print if there is something in the log + if (infologLength > 0) + { + infoLog = (char *)malloc(infologLength); + glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog); + printf("%s\n",infoLog); + free(infoLog); + } +} diff --git a/src/igl/opengl/print_shader_info_log.h b/src/igl/opengl/print_shader_info_log.h new file mode 100644 index 000000000..5a571fb11 --- /dev/null +++ b/src/igl/opengl/print_shader_info_log.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_PRINT_SHADER_INFO_LOG_H +#define IGL_OPENGL_PRINT_SHADER_INFO_LOG_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Inputs: + // obj OpenGL index of shader to print info log about + IGL_INLINE void print_shader_info_log(const GLuint obj); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "print_shader_info_log.cpp" +#endif + +#endif diff --git a/src/igl/opengl/report_gl_error.cpp b/src/igl/opengl/report_gl_error.cpp new file mode 100644 index 000000000..2dcc0b632 --- /dev/null +++ b/src/igl/opengl/report_gl_error.cpp @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "report_gl_error.h" +#include "../verbose.h" +#include <cstdio> + +IGL_INLINE GLenum igl::opengl::report_gl_error(const std::string id) +{ + // http://stackoverflow.com/q/28485180/148668 + + // gluErrorString was deprecated + const auto gluErrorString = [](GLenum errorCode)->const char * + { + switch(errorCode) + { + default: + return "unknown error code"; + case GL_NO_ERROR: + return "no error"; + case GL_INVALID_ENUM: + return "invalid enumerant"; + case GL_INVALID_VALUE: + return "invalid value"; + case GL_INVALID_OPERATION: + return "invalid operation"; +#ifndef GL_VERSION_3_0 + case GL_STACK_OVERFLOW: + return "stack overflow"; + case GL_STACK_UNDERFLOW: + return "stack underflow"; + case GL_TABLE_TOO_LARGE: + return "table too large"; +#endif + case GL_OUT_OF_MEMORY: + return "out of memory"; +#ifdef GL_EXT_framebuffer_object + case GL_INVALID_FRAMEBUFFER_OPERATION_EXT: + return "invalid framebuffer operation"; +#endif + } + }; + + GLenum err = glGetError(); + if(GL_NO_ERROR != err) + { + verbose("GL_ERROR: "); + fprintf(stderr,"%s%s\n",id.c_str(),gluErrorString(err)); + } + return err; +} + +IGL_INLINE GLenum igl::opengl::report_gl_error() +{ + return igl::opengl::report_gl_error(std::string("")); +} + diff --git a/src/igl/opengl/report_gl_error.h b/src/igl/opengl/report_gl_error.h new file mode 100644 index 000000000..70e5c7d74 --- /dev/null +++ b/src/igl/opengl/report_gl_error.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_REPORT_GL_ERROR_H +#define IGL_OPENGL_REPORT_GL_ERROR_H +#include "../igl_inline.h" + +// Hack to allow both opengl/ and opengl2 to use this (we shouldn't allow this) +#ifndef __gl_h_ +# include "gl.h" +#endif +#include <string> + +namespace igl +{ + namespace opengl + { + // Print last OpenGL error to stderr prefixed by specified id string + // Inputs: + // id string to appear before any error msgs + // Returns result of glGetError() + IGL_INLINE GLenum report_gl_error(const std::string id); + // No prefix + IGL_INLINE GLenum report_gl_error(); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "report_gl_error.cpp" +#endif + +#endif diff --git a/src/igl/opengl/uniform_type_to_string.cpp b/src/igl/opengl/uniform_type_to_string.cpp new file mode 100644 index 000000000..b2f9a2fdb --- /dev/null +++ b/src/igl/opengl/uniform_type_to_string.cpp @@ -0,0 +1,71 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "uniform_type_to_string.h" + +IGL_INLINE std::string igl::opengl::uniform_type_to_string(const GLenum type) +{ + switch(type) + { + case GL_FLOAT: + return "GL_FLOAT"; + case GL_FLOAT_VEC2: + return "GL_FLOAT_VEC2"; + case GL_FLOAT_VEC3: + return "GL_FLOAT_VEC3"; + case GL_FLOAT_VEC4: + return "GL_FLOAT_VEC4"; + case GL_INT: + return "GL_INT"; + case GL_INT_VEC2: + return "GL_INT_VEC2"; + case GL_INT_VEC3: + return "GL_INT_VEC3"; + case GL_INT_VEC4: + return "GL_INT_VEC4"; + case GL_BOOL: + return "GL_BOOL"; + case GL_BOOL_VEC2: + return "GL_BOOL_VEC2"; + case GL_BOOL_VEC3: + return "GL_BOOL_VEC3"; + case GL_BOOL_VEC4: + return "GL_BOOL_VEC4"; + case GL_FLOAT_MAT2: + return "GL_FLOAT_MAT2"; + case GL_FLOAT_MAT3: + return "GL_FLOAT_MAT3"; + case GL_FLOAT_MAT4: + return "GL_FLOAT_MAT4"; + case GL_FLOAT_MAT2x3: + return "GL_FLOAT_MAT2x3"; + case GL_FLOAT_MAT2x4: + return "GL_FLOAT_MAT2x4"; + case GL_FLOAT_MAT3x2: + return "GL_FLOAT_MAT3x2"; + case GL_FLOAT_MAT3x4: + return "GL_FLOAT_MAT3x4"; + case GL_FLOAT_MAT4x2: + return "GL_FLOAT_MAT4x2"; + case GL_FLOAT_MAT4x3: + return "GL_FLOAT_MAT4x3"; + case GL_SAMPLER_1D: + return "GL_SAMPLER_1D"; + case GL_SAMPLER_2D: + return "GL_SAMPLER_2D"; + case GL_SAMPLER_3D: + return "GL_SAMPLER_3D"; + case GL_SAMPLER_CUBE: + return "GL_SAMPLER_CUBE"; + case GL_SAMPLER_1D_SHADOW: + return "GL_SAMPLER_1D_SHADOW"; + case GL_SAMPLER_2D_SHADOW: + return "GL_SAMPLER_2D_SHADOW"; + default: + return "UNKNOWN_TYPE"; + } +} diff --git a/src/igl/opengl/uniform_type_to_string.h b/src/igl/opengl/uniform_type_to_string.h new file mode 100644 index 000000000..ed0a93e90 --- /dev/null +++ b/src/igl/opengl/uniform_type_to_string.h @@ -0,0 +1,31 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_UNIFORM_TYPE_TO_STRING_H +#define IGL_OPENGL_UNIFORM_TYPE_TO_STRING_H +#include "../igl_inline.h" +#include "gl.h" +#include <string> + +namespace igl +{ + namespace opengl + { + // Convert a GL uniform variable type (say, returned from + // glGetActiveUniform) and output a string naming that type + // Inputs: + // type enum for given type + // Returns string name of that type + IGL_INLINE std::string uniform_type_to_string(const GLenum type); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "uniform_type_to_string.cpp" +#endif + +#endif diff --git a/src/igl/opengl/vertex_array.cpp b/src/igl/opengl/vertex_array.cpp new file mode 100644 index 000000000..c07113ad3 --- /dev/null +++ b/src/igl/opengl/vertex_array.cpp @@ -0,0 +1,61 @@ +#include "vertex_array.h" +#include <igl/opengl/report_gl_error.h> + +template < + typename DerivedV, + typename DerivedF> +IGL_INLINE void igl::opengl::vertex_array( + const Eigen::PlainObjectBase<DerivedV> & V, + const Eigen::PlainObjectBase<DerivedF> & F, + GLuint & va_id, + GLuint & ab_id, + GLuint & eab_id) +{ + // Inputs should be in RowMajor storage. If not, we have no choice but to + // create a copy. + if(!(V.Options & Eigen::RowMajor)) + { + Eigen::Matrix< + typename DerivedV::Scalar, + DerivedV::RowsAtCompileTime, + DerivedV::ColsAtCompileTime, + Eigen::RowMajor> VR = V; + return vertex_array(VR,F,va_id,ab_id,eab_id); + } + if(!(F.Options & Eigen::RowMajor)) + { + Eigen::Matrix< + typename DerivedF::Scalar, + DerivedF::RowsAtCompileTime, + DerivedF::ColsAtCompileTime, + Eigen::RowMajor> FR = F; + return vertex_array(V,FR,va_id,ab_id,eab_id); + } + // Generate and attach buffers to vertex array + glGenVertexArrays(1, &va_id); + glGenBuffers(1, &ab_id); + glGenBuffers(1, &eab_id); + glBindVertexArray(va_id); + glBindBuffer(GL_ARRAY_BUFFER, ab_id); + const auto size_VScalar = sizeof(typename DerivedV::Scalar); + const auto size_FScalar = sizeof(typename DerivedF::Scalar); + glBufferData(GL_ARRAY_BUFFER,size_VScalar*V.size(),V.data(),GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eab_id); + assert(sizeof(GLuint) == size_FScalar && "F type does not match GLuint"); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW); + glVertexAttribPointer( + 0, + V.cols(), + size_VScalar==sizeof(float)?GL_FLOAT:GL_DOUBLE, + GL_FALSE, + V.cols()*size_VScalar, + (GLvoid*)0); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::opengl::vertex_array<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, unsigned int&, unsigned int&, unsigned int&); +#endif diff --git a/src/igl/opengl/vertex_array.h b/src/igl/opengl/vertex_array.h new file mode 100644 index 000000000..1342a9c90 --- /dev/null +++ b/src/igl/opengl/vertex_array.h @@ -0,0 +1,38 @@ +#ifndef IGL_OPENGL_VERTEX_ARRAY_H +#define IGL_OPENGL_VERTEX_ARRAY_H +#include <igl/opengl/../igl_inline.h> +#include <igl/opengl/gl.h> +#include <Eigen/Core> +namespace igl +{ + namespace opengl + { + // Create a GL_VERTEX_ARRAY for a given mesh (V,F) + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of mesh triangle indices into V + // Outputs: + // va_id id of vertex array + // ab_id id of array buffer (vertex buffer object) + // eab_id id of element array buffer (element/face buffer object) + // + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE void vertex_array( + // Note: Unlike most libigl functions, the **input** Eigen matrices must + // be `Eigen::PlainObjectBase` because we want to directly access it's + // underlying storage. It cannot be `Eigen::MatrixBase` (see + // http://stackoverflow.com/questions/25094948/) + const Eigen::PlainObjectBase<DerivedV> & V, + const Eigen::PlainObjectBase<DerivedF> & F, + GLuint & va_id, + GLuint & ab_id, + GLuint & eab_id); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "vertex_array.cpp" +#endif +#endif |