diff options
| author | Sebastian Park <SebPark03@gmail.com> | 2024-04-10 02:45:04 -0400 |
|---|---|---|
| committer | Sebastian Park <SebPark03@gmail.com> | 2024-04-10 02:45:04 -0400 |
| commit | 47cd8a592ecad52c1b01f27d23476c0a5afeb7f1 (patch) | |
| tree | 36b9abaff4e92a4a6df0d5ecb0e43e05c3aefd48 /wave-sim/src/graphics | |
| parent | fd19124693bb32835ad97802ba1950cd5202dbd2 (diff) | |
initial
Diffstat (limited to 'wave-sim/src/graphics')
| -rw-r--r-- | wave-sim/src/graphics/camera.cpp | 188 | ||||
| -rw-r--r-- | wave-sim/src/graphics/camera.h | 51 | ||||
| -rw-r--r-- | wave-sim/src/graphics/graphicsdebug.cpp | 126 | ||||
| -rw-r--r-- | wave-sim/src/graphics/graphicsdebug.h | 15 | ||||
| -rw-r--r-- | wave-sim/src/graphics/meshloader.cpp | 53 | ||||
| -rw-r--r-- | wave-sim/src/graphics/meshloader.h | 15 | ||||
| -rw-r--r-- | wave-sim/src/graphics/shader.cpp | 234 | ||||
| -rw-r--r-- | wave-sim/src/graphics/shader.h | 71 | ||||
| -rw-r--r-- | wave-sim/src/graphics/shape.cpp | 337 | ||||
| -rw-r--r-- | wave-sim/src/graphics/shape.h | 59 |
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 |
