summaryrefslogtreecommitdiff
path: root/wave-sim/src/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'wave-sim/src/graphics')
-rw-r--r--wave-sim/src/graphics/camera.cpp188
-rw-r--r--wave-sim/src/graphics/camera.h51
-rw-r--r--wave-sim/src/graphics/graphicsdebug.cpp126
-rw-r--r--wave-sim/src/graphics/graphicsdebug.h15
-rw-r--r--wave-sim/src/graphics/meshloader.cpp53
-rw-r--r--wave-sim/src/graphics/meshloader.h15
-rw-r--r--wave-sim/src/graphics/shader.cpp234
-rw-r--r--wave-sim/src/graphics/shader.h71
-rw-r--r--wave-sim/src/graphics/shape.cpp337
-rw-r--r--wave-sim/src/graphics/shape.h59
10 files changed, 1149 insertions, 0 deletions
diff --git a/wave-sim/src/graphics/camera.cpp b/wave-sim/src/graphics/camera.cpp
new file mode 100644
index 0000000..85fc7d9
--- /dev/null
+++ b/wave-sim/src/graphics/camera.cpp
@@ -0,0 +1,188 @@
+#include "graphics/camera.h"
+
+#include <iostream>
+
+Camera::Camera()
+ : m_position(0,0,0),
+ m_pitch(0), m_yaw(0),
+ m_look(0, 0, 1),
+ m_orbitPoint(0, 0, 0),
+ m_isOrbiting(false),
+ m_view(Eigen::Matrix4f::Identity()),
+ m_proj(Eigen::Matrix4f::Identity()),
+ m_viewDirty(true),
+ m_projDirty(true),
+ m_fovY(90), m_aspect(1), m_near(0.1f), m_far(50.f),
+ m_zoom(1)
+{}
+
+// ================== Position
+
+void Camera::setPosition(const Eigen::Vector3f &position)
+{
+ m_position = position;
+ m_viewDirty = true;
+}
+
+void Camera::move(const Eigen::Vector3f &deltaPosition)
+{
+ if (deltaPosition.squaredNorm() == 0) return;
+
+ m_position += deltaPosition;
+
+ if (m_isOrbiting) {
+ m_orbitPoint += deltaPosition;
+ }
+
+ m_viewDirty = true;
+
+}
+
+// ================== Rotation
+
+void Camera::setRotation(float pitch, float yaw)
+{
+ m_pitch = pitch;
+ m_yaw = yaw;
+ m_viewDirty = true;
+ updateLook();
+}
+
+void Camera::rotate(float deltaPitch, float deltaYaw)
+{
+ m_pitch += deltaPitch;
+ m_yaw += deltaYaw;
+ m_pitch = std::clamp(m_pitch, (float) -M_PI_2 + 0.01f, (float) M_PI_2 - 0.01f);
+ m_viewDirty = true;
+ updateLook();
+
+ if (m_isOrbiting) {
+ m_position = m_orbitPoint - m_look * m_zoom;
+ }
+}
+
+// ================== Position and Rotation
+
+void Camera::lookAt(const Eigen::Vector3f &eye, const Eigen::Vector3f &target)
+{
+ m_position = eye;
+ m_look = (target - eye).normalized();
+ m_viewDirty = true;
+ updatePitchAndYaw();
+}
+
+// ================== Orbiting
+
+void Camera::setOrbitPoint(const Eigen::Vector3f &orbitPoint)
+{
+ m_orbitPoint = orbitPoint;
+ m_viewDirty = true;
+}
+
+bool Camera::getIsOrbiting()
+{
+ return m_isOrbiting;
+}
+
+void Camera::setIsOrbiting(bool isOrbiting)
+{
+ m_isOrbiting = isOrbiting;
+ m_viewDirty = true;
+}
+
+void Camera::toggleIsOrbiting()
+{
+ m_isOrbiting = !m_isOrbiting;
+ m_viewDirty = true;
+
+ if (m_isOrbiting) {
+ m_zoom = (m_orbitPoint - m_position).norm();
+ m_look = (m_orbitPoint - m_position).normalized();
+ updatePitchAndYaw();
+ }
+}
+
+void Camera::zoom(float zoomMultiplier)
+{
+ if (!m_isOrbiting) return;
+
+ m_zoom *= zoomMultiplier;
+ m_position = m_orbitPoint - m_look * m_zoom;
+ m_viewDirty = true;
+}
+
+// ================== Intrinsics
+
+void Camera::setPerspective(float fovY, float aspect, float near, float far)
+{
+ m_fovY = fovY;
+ m_aspect = aspect;
+ m_near = near;
+ m_far = far;
+ m_projDirty = true;
+}
+
+void Camera::setAspect(float aspect)
+{
+ m_aspect = aspect;
+ m_projDirty = true;
+}
+
+// ================== Important Getters
+
+const Eigen::Matrix4f &Camera::getView()
+{
+ if (m_viewDirty) {
+ Eigen::Matrix3f R;
+ Eigen::Vector3f f = m_look.normalized();
+ Eigen::Vector3f u = Eigen::Vector3f::UnitY();
+ Eigen::Vector3f s = f.cross(u).normalized();
+ u = s.cross(f);
+ R.col(0) = s;
+ R.col(1) = u;
+ R.col(2) = -f;
+ m_view.topLeftCorner<3, 3>() = R.transpose();
+ m_view.topRightCorner<3, 1>() = -R.transpose() * m_position;
+ m_view(3, 3) = 1.f;
+ m_viewDirty = false;
+ }
+ return m_view;
+}
+
+const Eigen::Matrix4f &Camera::getProjection()
+{
+ if(m_projDirty) {
+ float theta = m_fovY * 0.5f;
+ float invRange = 1.f / (m_far - m_near);
+ float invtan = 1.f / tanf(theta);
+ m_proj(0, 0) = invtan / m_aspect;
+ m_proj(1, 1) = invtan;
+ m_proj(2, 2) = -(m_near + m_far) * invRange;
+ m_proj(3, 2) = -1;
+ m_proj(2, 3) = -2 * m_near * m_far * invRange;
+ m_proj(3, 3) = 0;
+ m_projDirty = false;
+ }
+ return m_proj;
+}
+
+const Eigen::Vector3f &Camera::getLook()
+{
+ return m_look;
+}
+
+// ================== Private Helpers
+
+void Camera::updateLook()
+{
+ m_look = Eigen::Vector3f(0, 0, 1);
+ m_look = Eigen::AngleAxis<float>(m_pitch, Eigen::Vector3f::UnitX()) * m_look;
+ m_look = Eigen::AngleAxis<float>(m_yaw, Eigen::Vector3f::UnitY()) * m_look;
+ m_look = m_look.normalized();
+}
+
+void Camera::updatePitchAndYaw()
+{
+ m_pitch = asinf(-m_look.y());
+ m_yaw = atan2f(m_look.x(), m_look.z());
+}
diff --git a/wave-sim/src/graphics/camera.h b/wave-sim/src/graphics/camera.h
new file mode 100644
index 0000000..04586af
--- /dev/null
+++ b/wave-sim/src/graphics/camera.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "Eigen/Dense"
+
+class Camera
+{
+public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+ Camera();
+
+ void setPosition(const Eigen::Vector3f &position);
+ void move (const Eigen::Vector3f &deltaPosition);
+
+ void setRotation(float pitch, float yaw);
+ void rotate (float deltaPitch, float deltaYaw);
+
+ void lookAt(const Eigen::Vector3f &eye, const Eigen::Vector3f &target);
+
+ void setOrbitPoint(const Eigen::Vector3f &target);
+ bool getIsOrbiting();
+ void setIsOrbiting(bool orbit);
+ void toggleIsOrbiting();
+ void zoom(float zoomMultiplier);
+
+ const Eigen::Matrix4f &getView();
+ const Eigen::Matrix4f &getProjection();
+ const Eigen::Vector3f &getLook();
+
+ void setPerspective(float fovY, float aspect, float near, float far);
+ void setAspect(float aspect);
+
+private:
+ void updateLook();
+ void updatePitchAndYaw();
+
+ // Do not mess with the order of these variables. Some Eigen voodoo will cause an inexplicable crash.
+
+ Eigen::Vector3f m_position;
+
+ float m_pitch, m_yaw;
+ Eigen::Vector3f m_look;
+
+ Eigen::Vector3f m_orbitPoint;
+ bool m_isOrbiting;
+
+ Eigen::Matrix4f m_view, m_proj;
+ bool m_viewDirty, m_projDirty;
+
+ float m_fovY, m_aspect, m_near, m_far;
+ float m_zoom;
+};
diff --git a/wave-sim/src/graphics/graphicsdebug.cpp b/wave-sim/src/graphics/graphicsdebug.cpp
new file mode 100644
index 0000000..b9d831c
--- /dev/null
+++ b/wave-sim/src/graphics/graphicsdebug.cpp
@@ -0,0 +1,126 @@
+#include <GL/glew.h>
+#include "graphics/graphicsdebug.h"
+
+#include <iostream>
+#include <vector>
+
+
+void checkError(std::string prefix) {
+ GLenum err = glGetError();
+ if (err != GL_NO_ERROR) {
+ std::cerr << prefix << (prefix == std::string("") ? "" : ": ") << "GL is in an error state before painting." << std::endl;
+ printGLErrorCodeInEnglish(err);
+ }
+}
+
+void printGLErrorCodeInEnglish(GLenum err) {
+ std::cerr << "GL error code " << err << ":" << std::endl;
+ switch(err) {
+ case GL_INVALID_ENUM:
+ std::cerr << "GL_INVALID_ENUM" << std::endl;
+ std::cerr << "An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag." << std::endl;
+ break;
+ case GL_INVALID_VALUE:
+ std::cerr << "GL_INVALID_VALUE" << std::endl;
+ std::cerr << "A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag." << std::endl;
+ break;
+ case GL_INVALID_OPERATION:
+ std::cerr << "GL_INVALID_OPERATION" << std::endl;
+ std::cerr << "The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag." << std::endl;
+ break;
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION" << std::endl;
+ std::cerr << "The framebuffer object is not complete. The offending command is ignored and has no other side effect than to set the error flag." << std::endl;
+ break;
+ case GL_OUT_OF_MEMORY:
+ std::cerr << "GL_OUT_OF_MEMORY" << std::endl;
+ std::cerr << "There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded." << std::endl;
+ break;
+ case GL_STACK_UNDERFLOW:
+ std::cerr << "GL_STACK_UNDERFLOW" << std::endl;
+ std::cerr << "An attempt has been made to perform an operation that would cause an internal stack to underflow." << std::endl;
+ break;
+ case GL_STACK_OVERFLOW:
+ std::cerr << "GL_STACK_OVERFLOW" << std::endl;
+ std::cerr << "An attempt has been made to perform an operation that would cause an internal stack to overflow." << std::endl;
+ break;
+ default:
+ std::cerr << "Unknown GL error code" << std::endl;
+ }
+}
+
+void checkFramebufferStatus() {
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ std::cerr << "Framebuffer is incomplete." << std::endl;
+ printFramebufferErrorCodeInEnglish(status);
+ }
+}
+
+void printFramebufferErrorCodeInEnglish(GLenum err) {
+ switch(err) {
+ case GL_FRAMEBUFFER_UNDEFINED:
+ std:: cerr << "GL_FRAMEBUFFER_UNDEFINED is returned if the specified framebuffer is the default read or draw framebuffer, but the default framebuffer does not exist." << std::endl;
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT is returned if any of the framebuffer attachment points are framebuffer incomplete." << std::endl;
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT is returned if the framebuffer does not have at least one image attached to it." << std::endl;
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
+ std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER is returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) named by GL_DRAW_BUFFERi." << std::endl;
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
+ std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER is returned if GL_READ_BUFFER is not GL_NONE and the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point named by GL_READ_BUFFER." << std::endl;
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ std::cerr << "GL_FRAMEBUFFER_UNSUPPORTED is returned if the combination of internal formats of the attached images violates an implementation-dependent set of restrictions." << std::endl;
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
+ std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES." << std::endl;
+ std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is also returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not the same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached textures." << std::endl;
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
+ std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS is returned if any framebuffer attachment is layered, and any populated attachment is not layered, or if all populated color attachments are not from textures of the same target." << std::endl;
+ break;
+ }
+}
+
+void checkShaderCompilationStatus(GLuint shaderID) {
+ GLint status;
+ glGetShaderiv(shaderID, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE) {
+ std::cerr << "Error: Could not compile shader." << std::endl;
+
+ GLint maxLength = 0;
+ glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxLength);
+
+ // The maxLength includes the null character
+ std::vector<GLchar> errorLog(maxLength);
+ glGetShaderInfoLog(shaderID, maxLength, &maxLength, &errorLog[0]);
+
+ std::cerr << &errorLog[0] << std::endl;
+ } else {
+ std::cerr << "Shader compiled." << std::endl;
+ }
+}
+
+void checkShaderLinkStatus(GLuint shaderProgramID) {
+ GLint linked;
+ glGetProgramiv(shaderProgramID, GL_LINK_STATUS, &linked);
+ if (linked == GL_FALSE) {
+ std::cerr << "Shader failed to link" << std::endl;
+
+ GLint maxLength = 0;
+ glGetProgramiv(shaderProgramID, GL_INFO_LOG_LENGTH, &maxLength);
+
+ // The maxLength includes the null character
+ std::vector<GLchar> errorLog(maxLength);
+ glGetProgramInfoLog(shaderProgramID, maxLength, &maxLength, &errorLog[0]);
+
+ std::cerr << &errorLog[0] << std::endl;
+ } else {
+ std::cerr << "Shader linked successfully." << std::endl;
+ }
+}
diff --git a/wave-sim/src/graphics/graphicsdebug.h b/wave-sim/src/graphics/graphicsdebug.h
new file mode 100644
index 0000000..9be33b4
--- /dev/null
+++ b/wave-sim/src/graphics/graphicsdebug.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <GL/glew.h>
+#include <string>
+
+#define GRAPHICS_DEBUG_LEVEL 0
+
+void checkError(std::string prefix = "");
+void printGLErrorCodeInEnglish(GLenum err);
+
+void checkFramebufferStatus();
+void printFramebufferErrorCodeInEnglish(GLenum err);
+
+void checkShaderCompilationStatus(GLuint shaderID);
+void checkShaderLinkStatus(GLuint shaderProgramID);
diff --git a/wave-sim/src/graphics/meshloader.cpp b/wave-sim/src/graphics/meshloader.cpp
new file mode 100644
index 0000000..84e78c7
--- /dev/null
+++ b/wave-sim/src/graphics/meshloader.cpp
@@ -0,0 +1,53 @@
+#include "graphics/meshloader.h"
+
+#define TINYOBJLOADER_IMPLEMENTATION
+#include "util/tiny_obj_loader.h"
+
+#include <iostream>
+
+#include <QString>
+#include <QFile>
+#include <QTextStream>
+#include <QRegularExpression>
+
+using namespace Eigen;
+
+bool MeshLoader::loadTetMesh(const std::string &filepath, std::vector<Eigen::Vector3d> &vertices, std::vector<Eigen::Vector4i> &tets)
+{
+ QString qpath = QString::fromStdString(filepath);
+ QFile file(qpath);
+
+ if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ std::cout << "Error opening file: " << filepath << std::endl;
+ return false;
+ }
+ QTextStream in(&file);
+
+ QRegularExpression vrxp("v (-?\\d*\\.?\\d+) +(-?\\d*\\.?\\d+) +(-?\\d*\\.?\\d+)");
+ QRegularExpression trxp("t (\\d+) +(\\d+) +(\\d+) +(\\d+)");
+
+ while(!in.atEnd()) {
+ QString line = in.readLine();
+ auto match = vrxp.match(line);
+ if(match.hasMatch()) {
+ vertices.emplace_back(match.captured(1).toDouble(),
+ match.captured(2).toDouble(),
+ match.captured(3).toDouble());
+ continue;
+ }
+ match = trxp.match(line);
+ if(match.hasMatch()) {
+ tets.emplace_back(match.captured(1).toInt(),
+ match.captured(2).toInt(),
+ match.captured(3).toInt(),
+ match.captured(4).toInt());
+ }
+ }
+ file.close();
+ return true;
+}
+
+MeshLoader::MeshLoader()
+{
+
+}
diff --git a/wave-sim/src/graphics/meshloader.h b/wave-sim/src/graphics/meshloader.h
new file mode 100644
index 0000000..e6b87fd
--- /dev/null
+++ b/wave-sim/src/graphics/meshloader.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <vector>
+#include "Eigen/Dense"
+#include "Eigen/StdVector"
+
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Matrix4i)
+
+class MeshLoader
+{
+public:
+ static bool loadTetMesh(const std::string &filepath, std::vector<Eigen::Vector3d> &vertices, std::vector<Eigen::Vector4i> &tets);
+private:
+ MeshLoader();
+};
diff --git a/wave-sim/src/graphics/shader.cpp b/wave-sim/src/graphics/shader.cpp
new file mode 100644
index 0000000..3789be0
--- /dev/null
+++ b/wave-sim/src/graphics/shader.cpp
@@ -0,0 +1,234 @@
+#include "graphics/shader.h"
+#include "graphics/graphicsdebug.h"
+
+#include <QFile>
+#include <QString>
+#include <QTextStream>
+#include <iostream>
+
+Shader::Shader(const std::string &vertexPath,
+ const std::string &fragmentPath)
+{
+ createProgramID();
+
+ std::vector<GLuint> shaders = {
+ createShaderFromString(getFileContents(vertexPath), GL_VERTEX_SHADER),
+ createShaderFromString(getFileContents(fragmentPath), GL_FRAGMENT_SHADER)
+ };
+
+ buildShaderProgramFromShaders(shaders);
+ discoverShaderData();
+}
+
+Shader::~Shader()
+{
+ glDeleteProgram(m_programID);
+}
+
+Shader::Shader(Shader &&that)
+ : m_programID(that.m_programID),
+ m_attributes(std::move(that.m_attributes)),
+ m_uniforms(std::move(that.m_uniforms))
+{
+ that.m_programID = 0;
+}
+
+Shader& Shader::operator=(Shader &&that)
+{
+ this->~Shader();
+
+ m_programID = that.m_programID;
+ m_attributes = std::move(that.m_attributes);
+ m_uniforms = std::move(that.m_uniforms);
+
+ that.m_programID = 0;
+
+ return *this;
+}
+
+// ================== Regular Use
+
+void Shader::bind() const { glUseProgram(m_programID); }
+
+void Shader::unbind() const { glUseProgram(0); }
+
+GLuint Shader::getUniformLocation(std::string name)
+{
+ return glGetUniformLocation(m_programID, name.c_str());
+}
+
+GLuint Shader::getEnumeratedUniformLocation(std::string name, int index)
+{
+ std::string n = name + "[" + std::to_string(index) + "]";
+ return glGetUniformLocation(m_programID, n.c_str());
+}
+
+// ================== Setting Uniforms
+
+// Note: the overload to set matrix uniforms is in the .h file
+
+void Shader::setUniform(const std::string &name, float f)
+{
+ glUniform1f(m_uniforms[name], f);
+}
+
+void Shader::setUniform(const std::string &name, int i)
+{
+ glUniform1i(m_uniforms[name], i);
+}
+
+void Shader::setUniform(const std::string &name, bool b)
+{
+ glUniform1i(m_uniforms[name], static_cast<GLint>(b));
+}
+
+// ================== Creating the Program
+
+void Shader::createProgramID()
+{
+ m_programID = glCreateProgram();
+}
+
+void Shader::buildShaderProgramFromShaders(const std::vector<GLuint> &shaderHandles)
+{
+ // Attach shaders
+ for (const GLuint &shaderHandle : shaderHandles) {
+ glAttachShader(m_programID, shaderHandle);
+ }
+
+ // Link program
+ glLinkProgram(m_programID);
+ checkShaderLinkStatus(m_programID);
+
+ // Detach and delete shaders
+ for (const GLuint &shaderHandle : shaderHandles) {
+ glDetachShader(m_programID, shaderHandle);
+ glDeleteShader(shaderHandle);
+ }
+}
+
+// ================== Creating Shaders From Filepaths
+
+std::string Shader::getFileContents(std::string filepath)
+{
+ QString filepathStr = QString::fromStdString(filepath);
+ QFile file(filepathStr);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ throw std::runtime_error(std::string("Failed to open shader: ") + filepath);
+ }
+
+ QTextStream stream(&file);
+ QString contents = stream.readAll();
+ file.close();
+
+ return contents.toStdString();
+}
+
+GLuint Shader::createShaderFromString(const std::string &str, GLenum shaderType)
+{
+ GLuint shaderHandle = glCreateShader(shaderType);
+
+ // Compile shader code
+ const char *codePtr = str.c_str();
+ glShaderSource(shaderHandle, 1, &codePtr, nullptr); // Assumes code is null terminated
+ glCompileShader(shaderHandle);
+
+ checkShaderCompilationStatus(shaderHandle);
+
+ return shaderHandle;
+}
+
+// ================== Discovering Attributes/Uniforms/Textures
+
+void Shader::discoverShaderData() {
+ discoverAttributes();
+ discoverUniforms();
+}
+
+void Shader::discoverAttributes() {
+ bind();
+ GLint attribCount;
+ glGetProgramiv(m_programID, GL_ACTIVE_ATTRIBUTES, &attribCount);
+ for (int i = 0; i < attribCount; i++) {
+ const GLsizei bufSize = 256;
+ GLsizei nameLength = 0;
+ GLint arraySize = 0;
+ GLenum type;
+ GLchar name[bufSize];
+ glGetActiveAttrib(m_programID, i, bufSize, &nameLength, &arraySize, &type, name);
+ name[std::min(nameLength, bufSize - 1)] = 0;
+ m_attributes[std::string(name)] = glGetAttribLocation(m_programID, name);
+ }
+ unbind();
+}
+
+void Shader::discoverUniforms() {
+ bind();
+ GLint uniformCount;
+ glGetProgramiv(m_programID, GL_ACTIVE_UNIFORMS, &uniformCount);
+ for (int i = 0; i < uniformCount; i++) {
+ const GLsizei bufSize = 256;
+ GLsizei nameLength = 0;
+ GLint arraySize = 0;
+ GLenum type;
+ GLchar name[bufSize];
+ glGetActiveUniform(m_programID, i, bufSize, &nameLength, &arraySize, &type, name);
+ name[std::min(nameLength, bufSize - 1)] = 0;
+
+ std::string strname(name);
+ if (isUniformArray(name, nameLength)) {
+ addUniformArray(strname, arraySize);
+ } else if (isTexture(type)) {
+ addTexture(strname);
+ } else {
+ addUniform(strname);
+ }
+ }
+ unbind();
+}
+
+bool Shader::isUniformArray(const GLchar *name, GLsizei nameLength) {
+ // Check if the last 3 characters are '[0]'
+ return (name[nameLength - 3] == '[') &&
+ (name[nameLength - 2] == '0') &&
+ (name[nameLength - 1] == ']');
+}
+
+bool Shader::isTexture(GLenum type) {
+ return (type == GL_SAMPLER_2D) ||
+ (type == GL_SAMPLER_CUBE);
+}
+
+void Shader::addUniformArray(const std::string &name, size_t size) {
+ std::string cleanName = name.substr(0, name.length() - 3);
+ for (auto i = static_cast<size_t>(0); i < size; i++) {
+ std::string enumeratedName = name;
+ enumeratedName[enumeratedName.length() - 2] = static_cast<char>('0' + i);
+ std::tuple< std::string, size_t > nameIndexTuple = std::make_tuple(cleanName, i);
+ m_uniformArrays[nameIndexTuple] = glGetUniformLocation(m_programID, enumeratedName.c_str());
+ }
+
+#if GRAPHICS_DEBUG_LEVEL > 0
+ m_trackedUniformArrays[name] = false;
+#endif
+}
+
+void Shader::addTexture(const std::string &name) {
+ GLint location = glGetUniformLocation(m_programID, name.c_str());
+ m_textureLocations[name] = location;
+ GLint slot = m_textureSlots.size();
+ m_textureSlots[location] = slot; // Assign slots in increasing order.
+
+#if GRAPHICS_DEBUG_LEVEL > 0
+ m_trackedTextures[name] = false;
+#endif
+}
+
+void Shader::addUniform(const std::string &name) {
+ m_uniforms[name] = glGetUniformLocation(m_programID, name.c_str());
+
+#if GRAPHICS_DEBUG_LEVEL > 0
+ m_trackedUniforms[name] = false;
+#endif
+}
diff --git a/wave-sim/src/graphics/shader.h b/wave-sim/src/graphics/shader.h
new file mode 100644
index 0000000..08f6654
--- /dev/null
+++ b/wave-sim/src/graphics/shader.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <map>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <GL/glew.h>
+#include "Eigen/Dense"
+#include "util/unsupportedeigenthing/OpenGLSupport"
+
+
+class Shader {
+public:
+ Shader(const std::string &vertexPath,
+ const std::string &fragmentPath);
+
+ virtual ~Shader();
+
+ Shader(Shader &that) = delete;
+ Shader& operator=(Shader &that) = delete;
+ Shader(Shader &&that);
+ Shader& operator=(Shader &&that);
+
+ // Basic Usage
+ GLuint getProgramID() const { return m_programID; }
+ void bind() const;
+ void unbind() const;
+ GLuint getUniformLocation(std::string name);
+ GLuint getEnumeratedUniformLocation(std::string name, int index);
+
+ // Setting Uniforms
+ void setUniform(const std::string &name, float f);
+ void setUniform(const std::string &name, int i);
+ void setUniform(const std::string &name, bool b);
+ template<typename type, int n, int m>
+ void setUniform(const std::string &name, const Eigen::Matrix<type, n, m> &mat)
+ {
+ glUniform(m_uniforms[name], mat);
+ }
+
+
+private:
+ // Creating the Program
+ void createProgramID();
+ void buildShaderProgramFromShaders(const std::vector<GLuint> &shaders);
+
+ // Creating Shaders From Filepaths
+ std::string getFileContents(std::string path);
+ GLuint createShaderFromString(const std::string &str, GLenum shaderType);
+
+ // Discovering attributes/uniforms/textures
+ void discoverShaderData();
+ void discoverAttributes();
+ void discoverUniforms();
+ bool isUniformArray(const GLchar *name , GLsizei nameLength);
+ bool isTexture(GLenum type);
+ void addUniform(const std::string &name);
+ void addUniformArray(const std::string &name, size_t size);
+ void addTexture(const std::string &name);
+
+ // Identifies the shader program associated with this shader
+ GLuint m_programID;
+
+ // Collections of known attributes/uniforms/textures
+ std::map<std::string, GLuint> m_attributes;
+ std::map<std::string, GLuint> m_uniforms;
+ std::map<std::tuple<std::string, size_t>, GLuint> m_uniformArrays;
+ std::map<std::string, GLuint> m_textureLocations; // name to uniform location
+ std::map<GLuint, GLuint> m_textureSlots; // uniform location to texture slot
+};
diff --git a/wave-sim/src/graphics/shape.cpp b/wave-sim/src/graphics/shape.cpp
new file mode 100644
index 0000000..e59e009
--- /dev/null
+++ b/wave-sim/src/graphics/shape.cpp
@@ -0,0 +1,337 @@
+#include "shape.h"
+
+#include <iostream>
+
+#include "graphics/shader.h"
+
+using namespace Eigen;
+
+Shape::Shape()
+ : m_tetVao(-1),
+ m_numSurfaceVertices(),
+ m_verticesSize(),
+ m_modelMatrix(Eigen::Matrix4f::Identity()),
+ m_wireframe(false)
+{
+}
+
+//void Shape::init(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3d> &normals, const std::vector<Eigen::Vector3i> &triangles)
+//{
+// if(vertices.size() != normals.size()) {
+// std::cerr << "Vertices and normals are not the same size" << std::endl;
+// return;
+// }
+// glGenBuffers(1, &m_surfaceVbo);
+// glGenBuffers(1, &m_surfaceIbo);
+// glGenVertexArrays(1, &m_surfaceVao);
+
+// glBindBuffer(GL_ARRAY_BUFFER, m_surfaceVbo);
+// glBufferData(GL_ARRAY_BUFFER, sizeof(double) * vertices.size() * 3 * 2, nullptr, GL_DYNAMIC_DRAW);
+// glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * vertices.size() * 3, static_cast<const void *>(vertices.data()));
+// glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * vertices.size() * 3, sizeof(double) * vertices.size() * 3, static_cast<const void *>(normals.data()));
+// glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_surfaceIbo);
+// glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * 3 * triangles.size(), static_cast<const void *>(triangles.data()), GL_STATIC_DRAW);
+// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+// glBindVertexArray(m_surfaceVao);
+// glBindBuffer(GL_ARRAY_BUFFER, m_surfaceVbo);
+// glEnableVertexAttribArray(0);
+// glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, 0, static_cast<GLvoid *>(0));
+// glEnableVertexAttribArray(1);
+// glVertexAttribPointer(1, 3, GL_DOUBLE, GL_FALSE, 0, reinterpret_cast<GLvoid *>(sizeof(double) * vertices.size() * 3));
+// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_surfaceIbo);
+// glBindVertexArray(0);
+// glBindBuffer(GL_ARRAY_BUFFER, 0);
+// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+// m_numSurfaceVertices = triangles.size() * 3;
+// m_verticesSize = vertices.size();
+// m_faces = triangles;
+//}
+
+void Shape::init(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3i> &triangles)
+{
+ std::vector<Eigen::Vector3d> verts;
+ std::vector<Eigen::Vector3d> normals;
+ std::vector<Eigen::Vector3i> faces;
+ std::vector<Eigen::Vector3d> forces;
+ verts.reserve(triangles.size() * 3);
+ normals.reserve(triangles.size() * 3);
+ for(auto& f : triangles) {
+ auto& v1 = vertices[f[0]];
+ auto& v2 = vertices[f[1]];
+ auto& v3 = vertices[f[2]];
+ auto& e1 = v2 - v1;
+ auto& e2 = v3 - v1;
+ auto n = e1.cross(e2);
+ int s = verts.size();
+ faces.push_back(Eigen::Vector3i(s, s + 1, s + 2));
+ normals.push_back(n);
+ normals.push_back(n);
+ normals.push_back(n);
+ verts.push_back(v1);
+ verts.push_back(v2);
+ verts.push_back(v3);
+ forces.push_back(Eigen::Vector3d(1.0, 1.0, 0.0));
+ forces.push_back(Eigen::Vector3d(1.0, 1.0, 0.0));
+ forces.push_back(Eigen::Vector3d(1.0, 1.0, 0.0));
+ }
+ glGenBuffers(1, &m_surfaceVbo);
+ glGenBuffers(1, &m_surfaceIbo);
+ glGenVertexArrays(1, &m_surfaceVao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_surfaceVbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(double) * verts.size() * 3 * 3, nullptr, GL_DYNAMIC_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * verts.size() * 3, static_cast<const void *>(verts.data()));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * verts.size() * 3, sizeof(double) * verts.size() * 3, static_cast<const void *>(normals.data()));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * verts.size() * 3 * 2, sizeof(double) * verts.size() * 3, static_cast<const void *>(forces.data()));
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_surfaceIbo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * 3 * faces.size(), static_cast<const void *>(faces.data()), GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ glBindVertexArray(m_surfaceVao);
+ glBindBuffer(GL_ARRAY_BUFFER, m_surfaceVbo);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, 0, static_cast<GLvoid *>(0));
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 3, GL_DOUBLE, GL_FALSE, 0, reinterpret_cast<GLvoid *>(sizeof(double) * verts.size() * 3));
+ glEnableVertexAttribArray(2);
+ glVertexAttribPointer(2, 3, GL_DOUBLE, GL_FALSE, 0, reinterpret_cast<GLvoid *>(sizeof(double) * verts.size() * 3 * 2));
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_surfaceIbo);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ m_numSurfaceVertices = faces.size() * 3;
+ m_verticesSize = vertices.size();
+ m_faces = triangles;
+
+ if (vertices.size() > 4) { //shape
+ m_red = 0.93;
+ m_green = 0.8;
+ m_blue = 1.f;
+ m_alpha = 1.f;
+ } else { //ground
+ m_red = 1;
+ m_green = 1;
+ m_blue = 1;
+ m_alpha = 1.f;
+ }
+ m_force = 0;
+// m_red = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
+// m_blue = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
+// m_green = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
+// m_alpha = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
+}
+
+void Shape::setColor(float r, float g, float b) {
+ m_red = r;
+ m_green = g;
+ m_blue = b;
+}
+
+void Shape::init(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3i> &triangles, const std::vector<Eigen::Vector4i> &tetIndices)
+{
+ init(vertices, triangles);
+
+ std::vector<Eigen::Vector2i> lines;
+ for(Vector4i tet : tetIndices) {
+ lines.emplace_back(tet[0], tet[1]);
+ lines.emplace_back(tet[0], tet[2]);
+ lines.emplace_back(tet[0], tet[3]);
+ lines.emplace_back(tet[1], tet[2]);
+ lines.emplace_back(tet[1], tet[3]);
+ lines.emplace_back(tet[2], tet[3]);
+ }
+ glGenBuffers(1, &m_tetVbo);
+ glGenBuffers(1, &m_tetIbo);
+ glGenVertexArrays(1, &m_tetVao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_tetVbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(double) * vertices.size() * 3, vertices.data(), GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_tetIbo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * 2 * lines.size(), static_cast<const void *>(lines.data()), GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ glBindVertexArray(m_tetVao);
+ glBindBuffer(GL_ARRAY_BUFFER, m_tetVbo);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, 0, static_cast<GLvoid *>(0));
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_tetIbo);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ m_numTetVertices = lines.size() * 2;
+}
+
+void Shape::setVertices(const std::vector<Eigen::Vector3d> &vertices)
+{
+ if(vertices.size() != m_verticesSize) {
+ std::cerr << "You can't set vertices to a vector that is a different length that what shape was inited with" << std::endl;
+ return;
+ }
+ std::vector<Eigen::Vector3d> verts;
+ std::vector<Eigen::Vector3d> normals;
+ std::vector<Eigen::Vector3d> forces;
+ verts.reserve(m_faces.size() * 3);
+ normals.reserve(m_faces.size() * 3);
+ for(auto& f : m_faces) {
+ auto& v1 = vertices[f[0]];
+ auto& v2 = vertices[f[1]];
+ auto& v3 = vertices[f[2]];
+ auto& e1 = v2 - v1;
+ auto& e2 = v3 - v1;
+ auto n = e1.cross(e2);
+ normals.push_back(n);
+ normals.push_back(n);
+ normals.push_back(n);
+ verts.push_back(v1);
+ verts.push_back(v2);
+ verts.push_back(v3);
+ forces.push_back(Eigen::Vector3d(1.0, 1.0, 0.0));
+ forces.push_back(Eigen::Vector3d(1.0, 1.0, 0.0));
+ forces.push_back(Eigen::Vector3d(1.0, 1.0, 0.0));
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, m_surfaceVbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * verts.size() * 3, static_cast<const void *>(verts.data()));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * verts.size() * 3, sizeof(double) * verts.size() * 3, static_cast<const void *>(normals.data()));
+ if(m_tetVao != static_cast<GLuint>(-1)) {
+ glBindBuffer(GL_ARRAY_BUFFER, m_tetVbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * vertices.size() * 3, static_cast<const void *>(vertices.data()));
+ }
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * vertices.size() * 3 * 2, sizeof(double) * vertices.size() * 3 * 2, static_cast<const void *>(forces.data()));
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Shape::setVerticesF(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3d> &forces) {
+ if(vertices.size() != m_verticesSize) {
+ std::cerr << "You can't set vertices to a vector that is a different length that what shape was inited with" << std::endl;
+ return;
+ }
+ if(vertices.size() != forces.size()) {
+ std::cerr << "Vertices and forces are not the same size" << std::endl;
+ return;
+ }
+ std::vector<Eigen::Vector3d> verts;
+ std::vector<Eigen::Vector3d> normals;
+ std::vector<Eigen::Vector3d> glForces;
+ verts.reserve(m_faces.size() * 3);
+ normals.reserve(m_faces.size() * 3);
+ glForces.reserve(m_faces.size() * 3);
+
+ double maxForceNorm = 500;
+ for(auto& f : m_faces) {
+ auto& v1 = vertices[f[0]];
+ auto& v2 = vertices[f[1]];
+ auto& v3 = vertices[f[2]];
+// auto& f1 = forces[f[0]].normalized();
+// auto& f2 = forces[f[1]].normalized();
+// auto& f3 = forces[f[2]].normalized();
+ auto& f1 = forces[f[0]];
+ auto& f2 = forces[f[1]];
+ auto& f3 = forces[f[2]];
+ maxForceNorm = std::max(f1.norm(), maxForceNorm);
+ maxForceNorm = std::max(f2.norm(), maxForceNorm);
+ maxForceNorm = std::max(f3.norm(), maxForceNorm);
+ auto& e1 = v2 - v1;
+ auto& e2 = v3 - v1;
+ auto n = e1.cross(e2);
+ normals.push_back(n);
+ normals.push_back(n);
+ normals.push_back(n);
+ verts.push_back(v1);
+ verts.push_back(v2);
+ verts.push_back(v3);
+ glForces.push_back(f1);
+ glForces.push_back(f2);
+ glForces.push_back(f3); // Cool effect if it's v1, v2, v3 instead
+ }
+// std::cout << maxForceNorm << std::endl;
+ for(Eigen::Vector3d &f : glForces) {
+ f /= maxForceNorm;
+ f = f.cwiseAbs();
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, m_surfaceVbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * verts.size() * 3, static_cast<const void *>(verts.data()));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * verts.size() * 3, sizeof(double) * verts.size() * 3, static_cast<const void *>(normals.data()));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * verts.size() * 3 * 2, sizeof(double) * verts.size() * 3, static_cast<const void *>(glForces.data()));
+ if(m_tetVao != static_cast<GLuint>(-1)) {
+ glBindBuffer(GL_ARRAY_BUFFER, m_tetVbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * vertices.size() * 3, static_cast<const void *>(vertices.data()));
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Shape::setModelMatrix(const Eigen::Affine3f &model)
+{
+ m_modelMatrix = model.matrix();
+}
+
+void Shape::toggleWireframe()
+{
+ m_wireframe = !m_wireframe;
+}
+
+void Shape::toggleForce() {
+ m_force = std::abs(m_force - 1);
+}
+
+void Shape::setVertices(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3d> &normals)
+{
+ std::vector<Eigen::Vector3d> forces;
+ if(vertices.size() != normals.size()) {
+ std::cerr << "Vertices and normals are not the same size" << std::endl;
+ return;
+ }
+ if(vertices.size() != m_verticesSize) {
+ std::cerr << "You can't set vertices to a vector that is a different length that what shape was inited with" << std::endl;
+ return;
+ }
+ for(Eigen::Vector3d v : vertices) {
+ forces.push_back(Eigen::Vector3d(1.0, 1.0, 0.0));
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, m_surfaceVbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * vertices.size() * 3, static_cast<const void *>(vertices.data()));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * vertices.size() * 3, sizeof(double) * vertices.size() * 3, static_cast<const void *>(normals.data()));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(double) * vertices.size() * 3 * 2, sizeof(double) * vertices.size() * 3, static_cast<const void *>(forces.data()));
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Shape::draw(Shader *shader)
+{
+ Eigen::Matrix3f m3 = m_modelMatrix.topLeftCorner(3, 3);
+ Eigen::Matrix3f inverseTransposeModel = m3.inverse().transpose();
+
+ if(m_wireframe && m_tetVao != static_cast<GLuint>(-1)) {
+ shader->setUniform("wire", 1);
+ shader->setUniform("model", m_modelMatrix);
+ shader->setUniform("inverseTransposeModel", inverseTransposeModel);
+ shader->setUniform("red", 1);
+ shader->setUniform("green", 1);
+ shader->setUniform("blue", 1);
+ shader->setUniform("alpha", 1);
+ shader->setUniform("displayForce", m_force);
+ glBindVertexArray(m_tetVao);
+ glDrawElements(GL_LINES, m_numTetVertices, GL_UNSIGNED_INT, reinterpret_cast<GLvoid *>(0));
+ glBindVertexArray(0);
+ } else {
+ shader->setUniform("wire", 0);
+ shader->setUniform("model", m_modelMatrix);
+ shader->setUniform("inverseTransposeModel", inverseTransposeModel);
+ shader->setUniform("red", m_red);
+ shader->setUniform("green", m_green);
+ shader->setUniform("blue", m_blue);
+ shader->setUniform("alpha", m_alpha);
+ shader->setUniform("displayForce", m_force);
+ glBindVertexArray(m_surfaceVao);
+ glDrawElements(GL_TRIANGLES, m_numSurfaceVertices, GL_UNSIGNED_INT, reinterpret_cast<GLvoid *>(0));
+ glBindVertexArray(0);
+ }
+}
diff --git a/wave-sim/src/graphics/shape.h b/wave-sim/src/graphics/shape.h
new file mode 100644
index 0000000..6540ef6
--- /dev/null
+++ b/wave-sim/src/graphics/shape.h
@@ -0,0 +1,59 @@
+#ifndef SHAPE_H
+#define SHAPE_H
+
+#include <GL/glew.h>
+#include <vector>
+
+#include <Eigen/Dense>
+
+class Shader;
+
+class Shape
+{
+public:
+ Shape();
+
+// void init(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3d> &normals, const std::vector<Eigen::Vector3i> &triangles);
+ void init(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3i> &triangles);
+ void init(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3i> &triangles, const std::vector<Eigen::Vector4i> &tetIndices);
+
+ void setVertices(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3d> &normals);
+ void setVertices(const std::vector<Eigen::Vector3d> &vertices);
+ void setVerticesF(const std::vector<Eigen::Vector3d> &vertices, const std::vector<Eigen::Vector3d> &forces);
+
+ void setModelMatrix(const Eigen::Affine3f &model);
+
+ void toggleWireframe();
+
+ void draw(Shader *shader);
+
+ void setColor(float r, float g, float b);
+
+ void toggleForce();
+
+private:
+ GLuint m_surfaceVao;
+ GLuint m_tetVao;
+ GLuint m_surfaceVbo;
+ GLuint m_tetVbo;
+ GLuint m_surfaceIbo;
+ GLuint m_tetIbo;
+
+ unsigned int m_numSurfaceVertices;
+ unsigned int m_numTetVertices;
+ unsigned int m_verticesSize;
+ float m_red;
+ float m_blue;
+ float m_green;
+ float m_alpha;
+
+ std::vector<Eigen::Vector3i> m_faces;
+
+ Eigen::Matrix4f m_modelMatrix;
+
+ bool m_wireframe;
+
+ int m_force;
+};
+
+#endif // SHAPE_H