summaryrefslogtreecommitdiffstats
path: root/dev/MinGfx/src/mesh.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dev/MinGfx/src/mesh.cc651
1 files changed, 651 insertions, 0 deletions
diff --git a/dev/MinGfx/src/mesh.cc b/dev/MinGfx/src/mesh.cc
new file mode 100644
index 0000000..dd967fa
--- /dev/null
+++ b/dev/MinGfx/src/mesh.cc
@@ -0,0 +1,651 @@
+/*
+ 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 <sstream>
+#include <fstream>
+
+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<Point3> &verts) {
+ gpu_dirty_ = true;
+ bvh_dirty_ = true;
+
+ verts_.clear();
+ for (int i=0; i<verts.size(); i++) {
+ verts_.push_back(verts[i][0]);
+ verts_.push_back(verts[i][1]);
+ verts_.push_back(verts[i][2]);
+ }
+}
+
+void Mesh::SetNormals(const std::vector<Vector3> &norms) {
+ gpu_dirty_ = true;
+ norms_.clear();
+ for (int i=0; i<norms.size(); i++) {
+ norms_.push_back(norms[i][0]);
+ norms_.push_back(norms[i][1]);
+ norms_.push_back(norms[i][2]);
+ }
+}
+
+void Mesh::SetColors(const std::vector<Color> &colors) {
+ gpu_dirty_ = true;
+ colors_.clear();
+ for (int i=0; i<colors.size(); i++) {
+ colors_.push_back(colors[i][0]);
+ colors_.push_back(colors[i][1]);
+ colors_.push_back(colors[i][2]);
+ colors_.push_back(colors[i][3]);
+ }
+}
+
+void Mesh::SetTexCoords(int texture_unit, const std::vector<Point2> &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<tex_coords.size(); i++) {
+ tex_coords_[texture_unit].push_back(tex_coords[i][0]);
+ tex_coords_[texture_unit].push_back(tex_coords[i][1]);
+ }
+}
+
+
+void Mesh::SetIndices(const std::vector<unsigned int> indices) {
+ gpu_dirty_ = true;
+ bvh_dirty_ = true;
+
+ indices_.clear();
+ for (int i=0; i<indices.size(); i++) {
+ indices_.push_back(indices[i]);
+ }
+}
+
+
+void Mesh::SetInstanceTransforms(const std::vector<Matrix4> &xforms) {
+ gpu_dirty_ = true;
+ instance_xforms_.clear();
+ for (int i=0; i<xforms.size(); i++) {
+ for (int j=0; j<16; j++) {
+ std::cout << xforms[i][j] << std::endl;
+ instance_xforms_.push_back(xforms[i][j]);
+ }
+ }
+}
+
+
+void Mesh::SetVertices(float *vertsArray, int numVerts) {
+ gpu_dirty_ = true;
+ bvh_dirty_ = true;
+
+ verts_.clear();
+ int numFloats = numVerts * 3;
+ for (int i=0; i<numFloats; i++) {
+ verts_.push_back(vertsArray[i]);
+ }
+}
+
+void Mesh::SetNormals(float *normsArray, int numNorms) {
+ gpu_dirty_ = true;
+ norms_.clear();
+ int numFloats = numNorms * 3;
+ for (int i=0; i<numFloats; i++) {
+ norms_.push_back(normsArray[i]);
+ }
+}
+
+void Mesh::SetColors(float *colorsArray, int numColors) {
+ gpu_dirty_ = true;
+ colors_.clear();
+ int numFloats = numColors * 4;
+ for (int i=0; i<numFloats; i++) {
+ colors_.push_back(colorsArray[i]);
+ }
+}
+
+void Mesh::SetTexCoords(int textureUnit, float *texCoordsArray, int numTexCoords) {
+ gpu_dirty_ = true;
+ // 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);
+ }
+ tex_coords_[textureUnit].clear();
+ int numFloats = numTexCoords * 2;
+ for (int i=0; i<numFloats; i++) {
+ tex_coords_[textureUnit].push_back(texCoordsArray[i]);
+ }
+}
+
+void Mesh::SetIndices(unsigned int *indexArray, int numIndices) {
+ gpu_dirty_ = true;
+ bvh_dirty_ = true;
+
+ indices_.clear();
+ for (int i=0; i<numIndices; i++) {
+ indices_.push_back(indexArray[i]);
+ }
+}
+
+
+
+
+
+
+void Mesh::UpdateGPUMemory() {
+ if (gpu_dirty_) {
+ // sanity check -- for each attribute that is added (normals, colors, texcoords)
+ // make sure the number of triangles is equal to the number of tris in the verts
+ // array.
+ if ((norms_.size() != 0) && (norms_.size() / 3 != num_vertices())) {
+ std::cerr << "Mesh::UpdateGPUMemory() -- warning: the number of per vertex normals in the mesh is not equal to the number vertices in the mesh. (N = " << norms_.size() / 3 << ", V = " << num_vertices() << ")" << std::endl;
+ }
+ if ((colors_.size() != 0) && (colors_.size() / 4 != num_vertices())) {
+ std::cerr << "Mesh::UpdateGPUMemory() -- warning: the number of per vertex colors in the mesh is not equal to the number vertices in the mesh. (C = " << colors_.size() / 4 << ", V = " << num_vertices() << ")" << std::endl;
+ }
+ for (int i = 0; i < tex_coords_.size(); i++) {
+ if ((tex_coords_[i].size() != 0) && (tex_coords_[i].size() / 2 != num_vertices())) {
+ std::cerr << "Mesh::UpdateGPUMemory() -- warning: the number of per vertex texture coordinates (for texture unit #" << i << ") is not equal to the number vertices in the mesh. (UVs = " << tex_coords_[i].size() / 2 << ", V = " << num_vertices() << ")" << std::endl;
+ }
+ }
+
+ GLsizeiptr totalMemSize = 0;
+
+ GLsizeiptr vertsMemSize = verts_.size() * sizeof(float);
+ GLsizeiptr vertsMemOffset = 0;
+ totalMemSize += vertsMemSize;
+
+ GLsizeiptr normsMemSize = norms_.size() * sizeof(float);
+ GLsizeiptr normsMemOffset = totalMemSize;
+ totalMemSize += normsMemSize;
+
+ GLsizeiptr colorsMemSize = colors_.size() * sizeof(float);
+ GLsizeiptr colorsMemOffset = totalMemSize;
+ totalMemSize += colorsMemSize;
+
+ std::vector<GLsizeiptr> texCoordsMemSize;
+ std::vector<GLsizeiptr> 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<tex_coords_.size(); i++) {
+ glBufferSubData(GL_ARRAY_BUFFER, texCoordsMemOffset[i], texCoordsMemSize[i], &(tex_coords_[i][0]));
+ }
+ if (instance_xforms_.size() > 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<std::min((int)tex_coords_.size(),(int)MAX_TEX_ATTRIBS); i++) {
+ attribID = 3+i;
+ if (tex_coords_[i].size()) {
+ glEnableVertexAttribArray(attribID);
+ glVertexAttribPointer(attribID, nComponents, GL_FLOAT, GL_FALSE, nComponents*sizeof(GLfloat), (char*)0 + texCoordsMemOffset[i]);
+ }
+ else {
+ glDisableVertexAttribArray(attribID);
+ }
+ }
+
+ // attribute 8-11 (takes 4 vec4 attribs to represent a single mat4) = instance transform matrices (optional)
+ attribID = 8;
+ if (instance_xforms_.size()) {
+ glEnableVertexAttribArray(8);
+ glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, 16*sizeof(GLfloat), (char*)0 + instanceXformsMemOffset);
+ glVertexAttribDivisor(8, 1);
+ glEnableVertexAttribArray(9);
+ glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, 16*sizeof(GLfloat), (char*)0 + instanceXformsMemOffset + 4*sizeof(GLfloat));
+ glVertexAttribDivisor(9, 1);
+ glEnableVertexAttribArray(10);
+ glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, 16*sizeof(GLfloat), (char*)0 + instanceXformsMemOffset + 8*sizeof(GLfloat));
+ glVertexAttribDivisor(10, 1);
+ glEnableVertexAttribArray(11);
+ glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, 16*sizeof(GLfloat), (char*)0 + instanceXformsMemOffset + 12*sizeof(GLfloat));
+ glVertexAttribDivisor(11, 1);
+ }
+ else {
+ glDisableVertexAttribArray(8);
+ glDisableVertexAttribArray(9);
+ glDisableVertexAttribArray(10);
+ glDisableVertexAttribArray(11);
+ }
+
+ glBindVertexArray(0);
+
+ if (indices_.size()) {
+ glGenBuffers(1, &element_buffer_);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(unsigned int), &indices_[0], GL_STATIC_DRAW);
+ }
+
+
+ gpu_dirty_ = false;
+ }
+}
+
+
+void Mesh::BuildBVH() {
+ bvh_.CreateFromMesh(*this);
+ bvh_dirty_ = false;
+}
+
+
+BVH* Mesh::bvh_ptr() {
+ if (bvh_dirty_) {
+ BuildBVH();
+ }
+ return &bvh_;
+}
+
+
+void Mesh::Draw() {
+ if (gpu_dirty_) {
+ UpdateGPUMemory();
+ }
+
+ // set defaults to pass to shaders any for optional attribs
+ glVertexAttrib3f(1, 0.0, 0.0, 1.0); // normal = +Z
+ glVertexAttrib4f(2, 1.0, 1.0, 1.0, 1.0); // color = opaque white
+ glVertexAttrib2f(3, 0.0, 0.0); // uv = 0,0 for texture unit 0
+ glVertexAttrib2f(4, 0.0, 0.0); // uv = 0,0 for texture unit 1
+ glVertexAttrib2f(5, 0.0, 0.0); // uv = 0,0 for texture unit 2
+ glVertexAttrib2f(6, 0.0, 0.0); // uv = 0,0 for texture unit 3
+ glVertexAttrib2f(7, 0.0, 0.0); // uv = 0,0 for texture unit 4
+ glVertexAttrib4f(8, 1.0, 0.0, 0.0, 0.0); // instance transform col 1
+ glVertexAttrib4f(9, 0.0, 1.0, 0.0, 0.0); // instance transform col 2
+ glVertexAttrib4f(10, 0.0, 0.0, 1.0, 0.0); // instance transform col 3
+ glVertexAttrib4f(11, 0.0, 0.0, 0.0, 1.0); // instance transform col 4
+
+
+ glBindVertexArray(vertex_array_);
+
+ if (instance_xforms_.size()) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_);
+ glDrawElementsInstanced(GL_TRIANGLES, (GLsizei)indices_.size(), GL_UNSIGNED_INT, (void*)0, (GLsizei)instance_xforms_.size()/16);
+ }
+ else if (indices_.size()) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_);
+ glDrawElements(GL_TRIANGLES, (GLsizei)indices_.size(), GL_UNSIGNED_INT, (void*)0);
+ }
+ else {
+ glDrawArrays(GL_TRIANGLES, 0, num_vertices());
+ }
+
+ glBindVertexArray(0);
+}
+
+
+int Mesh::num_vertices() const {
+ return (int)verts_.size()/3;
+}
+
+int Mesh::num_triangles() const {
+ if (indices_.size()) {
+ return (int)indices_.size()/3;
+ }
+ else {
+ return (int)verts_.size()/9;
+ }
+}
+
+
+
+void Mesh::LoadFromOBJ(const std::string &filename) {
+ std::fstream file(filename.c_str(), std::ios::in);
+ if (!file) {
+ std::cerr << "Failed to load " + filename << std::endl;
+ exit(1);
+ }
+
+ // tmp arrays
+ std::vector<Point3> vertices;
+ std::vector<Vector3> normals;
+ std::vector<Point2> 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<int> 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<float> verts, norms, uvs;
+ for (int i=0;i<vertices.size();i++) {
+ verts_.push_back(vertices[i][0]);
+ verts_.push_back(vertices[i][1]);
+ verts_.push_back(vertices[i][2]);
+ if (normals.size()) {
+ norms_.push_back(normals[i][0]);
+ norms_.push_back(normals[i][1]);
+ norms_.push_back(normals[i][2]);
+ }
+ if (texCoords.size()) {
+ uvs.push_back(texCoords[i][0]);
+ uvs.push_back(texCoords[i][1]);
+ }
+ }
+ if (uvs.size()) {
+ tex_coords_.push_back(uvs);
+ }
+}
+
+
+
+Point3 Mesh::read_vertex_data(int i) const {
+ return Point3(verts_[(size_t)3*i], verts_[(size_t)3*i+1], verts_[(size_t)3*i+2]);
+}
+
+Vector3 Mesh::read_normal_data(int i) const {
+ return Vector3(norms_[(size_t)3*i], norms_[(size_t)3*i+1], norms_[(size_t)3*i+2]);
+}
+
+Color Mesh::read_color_data(int i) const {
+ return Color(colors_[(size_t)4*i], colors_[(size_t)4*i+1], colors_[(size_t)4*i+2], colors_[(size_t)4*i+3]);
+}
+
+Point2 Mesh::read_tex_coords_data(int textureUnit, int i) const {
+ return Point2(tex_coords_[textureUnit][(size_t)2*i], tex_coords_[textureUnit][(size_t)2*i+1]);
+}
+
+std::vector<unsigned int> Mesh::read_triangle_indices_data(int triangle_id) const {
+ std::vector<unsigned int> 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<Vector3> norms(num_vertices());
+ for (int i=0; i<num_triangles(); i++) {
+ std::vector<unsigned int> 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<Vector3> norms(num_vertices());
+ for (int i=0; i<num_triangles(); i++) {
+ std::vector<unsigned int> 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<norms.size(); i++) {
+ norms[i] = norms[i].ToUnit();
+ }
+
+ SetNormals(norms);
+}
+
+
+} // end namespace