/* Copyright (c) 2017,2018 Regents of the University of Minnesota. All Rights Reserved. See corresponding header file for details. */ #include "mesh.h" #include "matrix4.h" #include "opengl_headers.h" #include #include namespace mingfx { #define MAX_TEX_ATTRIBS 5 Mesh::Mesh() : gpu_dirty_(true), vertex_buffer_(0), vertex_array_(0), element_buffer_(0), bvh_dirty_(true) { } Mesh::Mesh(const Mesh &other) { verts_ = other.verts_; norms_ = other.norms_; colors_ = other.colors_; tex_coords_ = other.tex_coords_; indices_ = other.indices_; gpu_dirty_ = true; bvh_dirty_ = true; } Mesh::~Mesh() { } int Mesh::AddTriangle(Point3 v1, Point3 v2, Point3 v3) { gpu_dirty_ = true; bvh_dirty_ = true; verts_.push_back(v1[0]); verts_.push_back(v1[1]); verts_.push_back(v1[2]); verts_.push_back(v2[0]); verts_.push_back(v2[1]); verts_.push_back(v2[2]); verts_.push_back(v3[0]); verts_.push_back(v3[1]); verts_.push_back(v3[2]); return num_triangles()-1; } void Mesh::UpdateTriangle(int triangle_id, Point3 v1, Point3 v2, Point3 v3) { gpu_dirty_ = true; bvh_dirty_ = true; int index = triangle_id * 9; verts_[(size_t)index + 0] = v1[0]; verts_[(size_t)index + 1] = v1[1]; verts_[(size_t)index + 2] = v1[2]; verts_[(size_t)index + 3] = v2[0]; verts_[(size_t)index + 4] = v2[1]; verts_[(size_t)index + 5] = v2[2]; verts_[(size_t)index + 6] = v3[0]; verts_[(size_t)index + 7] = v3[1]; verts_[(size_t)index + 8] = v3[2]; } void Mesh::SetNormals(int triangle_id, Vector3 n1, Vector3 n2, Vector3 n3) { gpu_dirty_ = true; if (triangle_id >= num_triangles()) { std::cerr << "Mesh::SetNormals() -- warning: cannot set normals for non-existant triangle with ID=" << triangle_id << ". Make sure the triangle has been added first." << std::endl; return; } int requiredSize = (triangle_id+1)*9; if (norms_.size() < requiredSize) { norms_.resize(requiredSize); } int index = triangle_id * 9; norms_[(size_t)index + 0] = n1[0]; norms_[(size_t)index + 1] = n1[1]; norms_[(size_t)index + 2] = n1[2]; norms_[(size_t)index + 3] = n2[0]; norms_[(size_t)index + 4] = n2[1]; norms_[(size_t)index + 5] = n2[2]; norms_[(size_t)index + 6] = n3[0]; norms_[(size_t)index + 7] = n3[1]; norms_[(size_t)index + 8] = n3[2]; } void Mesh::SetColors(int triangle_id, Color c1, Color c2, Color c3) { gpu_dirty_ = true; if (triangle_id >= num_triangles()) { std::cerr << "Mesh::SetColors() -- warning: cannot set colors for non-existant triangle with ID=" << triangle_id << ". Make sure the triangle has been added first." << std::endl; return; } int requiredSize = (triangle_id+1)*12; if (colors_.size() < requiredSize) { colors_.resize(requiredSize); } int index = triangle_id * 12; colors_[(size_t)index + 0] = c1[0]; colors_[(size_t)index + 1] = c1[1]; colors_[(size_t)index + 2] = c1[2]; colors_[(size_t)index + 3] = c1[3]; colors_[(size_t)index + 4] = c2[0]; colors_[(size_t)index + 5] = c2[1]; colors_[(size_t)index + 6] = c2[2]; colors_[(size_t)index + 7] = c2[3]; colors_[(size_t)index + 8] = c3[0]; colors_[(size_t)index + 9] = c3[1]; colors_[(size_t)index + 10] = c3[2]; colors_[(size_t)index + 11] = c3[3]; } void Mesh::SetTexCoords(int triangle_id, int textureUnit, Point2 uv1, Point2 uv2, Point2 uv3) { gpu_dirty_ = true; if (triangle_id >= num_triangles()) { std::cerr << "Mesh::SetTexCoords() -- warning: cannot set texture coordinates for non-existant triangle with ID=" << triangle_id << ". Make sure the triangle has been added first." << std::endl; return; } // resize as needed based on the number of textureUnits used if (tex_coords_.size() < (size_t)textureUnit+1) { tex_coords_.resize((size_t)textureUnit+1); } // resize the textureUnit-specific array based on the number of triangles int requiredSize = (triangle_id+1)*6; if (tex_coords_[textureUnit].size() < requiredSize) { tex_coords_[textureUnit].resize(requiredSize); } int index = triangle_id * 6; tex_coords_[textureUnit][(size_t)index + 0] = uv1[0]; tex_coords_[textureUnit][(size_t)index + 1] = uv1[1]; tex_coords_[textureUnit][(size_t)index + 2] = uv2[0]; tex_coords_[textureUnit][(size_t)index + 3] = uv2[1]; tex_coords_[textureUnit][(size_t)index + 4] = uv3[0]; tex_coords_[textureUnit][(size_t)index + 5] = uv3[1]; } void Mesh::SetVertices(const std::vector &verts) { gpu_dirty_ = true; bvh_dirty_ = true; verts_.clear(); for (int i=0; i &norms) { gpu_dirty_ = true; norms_.clear(); for (int i=0; i &colors) { gpu_dirty_ = true; colors_.clear(); for (int i=0; i &tex_coords) { gpu_dirty_ = true; // resize as needed based on the number of textureUnits used if (tex_coords_.size() < (size_t)texture_unit+1) { tex_coords_.resize((size_t)texture_unit+1); } tex_coords_[texture_unit].clear(); for (int i=0; i indices) { gpu_dirty_ = true; bvh_dirty_ = true; indices_.clear(); for (int i=0; i &xforms) { gpu_dirty_ = true; instance_xforms_.clear(); for (int i=0; i texCoordsMemSize; std::vector texCoordsMemOffset; for (int i = 0; i < std::min((int)tex_coords_.size(),(int)MAX_TEX_ATTRIBS); i++) { texCoordsMemSize.push_back(tex_coords_[i].size() * sizeof(float)); texCoordsMemOffset.push_back(totalMemSize); totalMemSize += texCoordsMemSize[i]; } GLsizeiptr instanceXformsMemSize = instance_xforms_.size() * sizeof(float); GLsizeiptr instanceXformsMemOffset = totalMemSize; totalMemSize += instanceXformsMemSize; glGenBuffers(1, &vertex_buffer_); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); glBufferData(GL_ARRAY_BUFFER, totalMemSize, NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, vertsMemOffset, vertsMemSize, &verts_[0]); if (norms_.size() > 0) { glBufferSubData(GL_ARRAY_BUFFER, normsMemOffset, normsMemSize, &norms_[0]); } if (colors_.size() > 0) { glBufferSubData(GL_ARRAY_BUFFER, colorsMemOffset, colorsMemSize, &colors_[0]); } for (int i=0; i 0) { glBufferSubData(GL_ARRAY_BUFFER, instanceXformsMemOffset, instanceXformsMemSize, &instance_xforms_[0]); } glGenVertexArrays(1, &vertex_array_); glBindVertexArray(vertex_array_); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); // attribute 0 = vertices (required) int attribID = 0; int nComponents = 3; glEnableVertexAttribArray(attribID); glVertexAttribPointer(attribID, nComponents, GL_FLOAT, GL_FALSE, nComponents*sizeof(GLfloat), (char*)0 + vertsMemOffset); // attribute 1 = normals (optional) attribID = 1; if (norms_.size()) { nComponents = 3; glEnableVertexAttribArray(attribID); glVertexAttribPointer(attribID, nComponents, GL_FLOAT, GL_TRUE, nComponents*sizeof(GLfloat), (char*)0 + normsMemOffset); } else { glDisableVertexAttribArray(attribID); } // attribute 2 = colors (optional) attribID = 2; if (colors_.size()) { nComponents = 4; glEnableVertexAttribArray(attribID); glVertexAttribPointer(attribID, nComponents, GL_FLOAT, GL_TRUE, nComponents*sizeof(GLfloat), (char*)0 + colorsMemOffset); } else { glDisableVertexAttribArray(attribID); } // attribute(s) 3 to 7 = texture coordinates (optional) nComponents = 2; for (int i=0; i vertices; std::vector normals; std::vector texCoords; while (file) { std::string line; do getline(file, line); while (file && (line.length() == 0 || line[0] == '#')); std::stringstream linestream(line); std::string keyword; linestream >> keyword; if (keyword == "v") { Point3 vertex; linestream >> vertex[0] >> vertex[1] >> vertex[2]; vertices.push_back(vertex); } else if (keyword == "vn") { Vector3 normal; linestream >> normal[0] >> normal[1] >> normal[2]; normals.push_back(normal); } else if (keyword == "vt") { Point2 texCoord; linestream >> texCoord[0] >> texCoord[1]; texCoords.push_back(texCoord); } else if (keyword == "f") { std::vector polygon; std::string word; while (linestream >> word) { std::stringstream wstream(word); int v; wstream >> v; polygon.push_back(v-1); // In OBJ files, indices start from 1 } for (int i = 2; i < polygon.size(); i++) { //triangles.push_back(ivec3(polygon[0], polygon[i-1], polygon[i])); int i1 = polygon[0]; int i2 = polygon[(size_t)i-1]; int i3 = polygon[i]; //int t = AddTriangle(vertices[i1], vertices[i2], vertices[i3]); //if (normals.size()) { // SetNormals(t, normals[i1], normals[i2], normals[i3]); //} //if (texCoords.size()) { // SetTexCoords(t, 0, texCoords[i1], texCoords[i2], texCoords[i3]); //} indices_.push_back(i1); indices_.push_back(i2); indices_.push_back(i3); } } } gpu_dirty_ = true; std::vector verts, norms, uvs; for (int i=0;i Mesh::read_triangle_indices_data(int triangle_id) const { std::vector tri; int i = 3*triangle_id; if (indices_.size()) { // indexed faces mode tri.push_back(indices_[(size_t)i+0]); tri.push_back(indices_[(size_t)i+1]); tri.push_back(indices_[(size_t)i+2]); } else { // ordered faces mode tri.push_back(i); tri.push_back(i+1); tri.push_back(i+2); } return tri; } void Mesh::CalcPerFaceNormals() { std::vector norms(num_vertices()); for (int i=0; i indices = read_triangle_indices_data(i); Point3 a = read_vertex_data(indices[0]); Point3 b = read_vertex_data(indices[1]); Point3 c = read_vertex_data(indices[2]); Vector3 n = Vector3::Cross(b-a, c-a).ToUnit(); norms[indices[0]] = n; norms[indices[1]] = n; norms[indices[2]] = n; } SetNormals(norms); } void Mesh::CalcPerVertexNormals() { std::vector norms(num_vertices()); for (int i=0; i indices = read_triangle_indices_data(i); Point3 a = read_vertex_data(indices[0]); Point3 b = read_vertex_data(indices[1]); Point3 c = read_vertex_data(indices[2]); Vector3 n = Vector3::Cross(b-a, c-a); norms[indices[0]] = norms[indices[0]] + n; norms[indices[1]] = norms[indices[1]] + n; norms[indices[2]] = norms[indices[2]] + n; } for (int i=0; i