305 lines
9.7 KiB
C++
305 lines
9.7 KiB
C++
#include "mesh.h"
|
|
#include <glm/ext.hpp>
|
|
#include <set>
|
|
#include "glassert.h"
|
|
#include "sparrowrenderer.h"
|
|
|
|
Mesh::Mesh() :
|
|
material(NULL),
|
|
vao(0),
|
|
nb_buffers(0),
|
|
vbo(NULL)
|
|
{}
|
|
|
|
Mesh::~Mesh()
|
|
{
|
|
destroyGL();
|
|
}
|
|
|
|
void Mesh::initGL(bool isDynamic)
|
|
{
|
|
if(vbo != NULL)
|
|
destroyGL();
|
|
|
|
GLenum buffer_type = isDynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
|
|
|
|
// create VAO
|
|
glAssert(glGenVertexArrays(1, &vao));
|
|
glAssert(glBindVertexArray(vao));
|
|
|
|
nb_buffers = 2; // positions and indices buffers
|
|
if(mesh->hasNormals())
|
|
++nb_buffers;
|
|
if(mesh->hasTexCoords())
|
|
++nb_buffers;
|
|
if(mesh->hasTangents())
|
|
++nb_buffers;
|
|
|
|
// create VBOs
|
|
vbo = new GLuint[nb_buffers]();
|
|
glAssert(glGenBuffers(nb_buffers, vbo));
|
|
|
|
// init indices vbos
|
|
glAssert(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[INDICES_BUFFER]));
|
|
glAssert(glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), buffer_type));
|
|
|
|
// init positions vbo
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[POSITION_BUFFER]));
|
|
glAssert(glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(glm::vec3), positions.data(), buffer_type));
|
|
|
|
if(hasNormals())
|
|
{
|
|
// init normals vbo
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[NORMAL_BUFFER]));
|
|
glAssert(glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), buffer_type));
|
|
}
|
|
|
|
if(hasNormals())
|
|
{
|
|
// init texCoords vbo
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[TEXCOORD_BUFFER]));
|
|
glAssert(glBufferData(GL_ARRAY_BUFFER, texCoords.size() * sizeof(glm::vec2), texCoords.data(), buffer_type));
|
|
}
|
|
|
|
if(hasTangents())
|
|
{
|
|
// init tangents vbo
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[TANGENT_BUFFER]));
|
|
glAssert(glBufferData(GL_ARRAY_BUFFER, tangents.size() * sizeof(glm::vec3)*2, tangents.data(), buffer_type));
|
|
}
|
|
|
|
// unbind vao
|
|
glAssert(glBindVertexArray(0));
|
|
}
|
|
|
|
void Mesh::draw(Shader* shader, bool drawNormals, bool drawTexCoord, bool drawTangents)
|
|
{
|
|
bool crappy = (shader == NULL);
|
|
material.bindAttributes(shader);
|
|
glAssert(glBindVertexArray(vao));
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[POSITION_BUFFER]));
|
|
if(crappy)
|
|
{
|
|
glAssert(glEnableClientState(GL_VERTEX_ARRAY));
|
|
glAssert(glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0)));
|
|
}
|
|
else
|
|
{
|
|
glAssert(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), BUFFER_OFFSET(0)));
|
|
glAssert(glEnableVertexAttribArray(0));
|
|
}
|
|
if(hasNormals() && drawNormals)
|
|
{
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[NORMAL_BUFFER]));
|
|
if(crappy)
|
|
{
|
|
glAssert(glEnableClientState(GL_NORMAL_ARRAY));
|
|
glAssert(glNormalPointer(GL_FLOAT, 0, BUFFER_OFFSET(0)));
|
|
}
|
|
else
|
|
{
|
|
glAssert(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), BUFFER_OFFSET(0)));
|
|
glAssert(glEnableVertexAttribArray(1));
|
|
}
|
|
}
|
|
if(hasTexCoords() && drawTexCoord)
|
|
{
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[TEXCOORD_BUFFER]));
|
|
if(crappy)
|
|
{
|
|
glAssert(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
|
glAssert(glTexCoordPointer(2, GL_FLOAT, 0, BUFFER_OFFSET(0)));
|
|
}
|
|
else
|
|
{
|
|
glAssert(glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec2), BUFFER_OFFSET(0)));
|
|
glAssert(glEnableVertexAttribArray(2));
|
|
}
|
|
}
|
|
if(hasTangents() && drawTangents && !crappy)
|
|
{
|
|
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo[TANGENT_BUFFER]));
|
|
glAssert(glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Mesh::Tangents), BUFFER_OFFSET(0)));
|
|
glAssert(glEnableVertexAttribArray(3));
|
|
glAssert(glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Mesh::Tangents), BUFFER_OFFSET(sizeof(glm::vec3))));
|
|
glAssert(glEnableVertexAttribArray(4));
|
|
}
|
|
glAssert(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[INDICES_BUFFER]));
|
|
glAssert(glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, NULL));
|
|
glAssert(glBindVertexArray(0));
|
|
if(crappy)
|
|
{
|
|
glAssert(glDisableClientState(GL_VERTEX_ARRAY));
|
|
if(hasNormals() && drawNormals)
|
|
glAssert(glDisableClientState(GL_NORMAL_ARRAY));
|
|
if(hasTexCoords() && drawTexCoord)
|
|
glAssert(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
|
|
}
|
|
}
|
|
|
|
void Mesh::destroyGL()
|
|
{
|
|
if(vbo != NULL)
|
|
{
|
|
glAssert(glDeleteVertexArrays(1, &vao));
|
|
glAssert(glDeleteBuffers(nb_buffers, vbo));
|
|
delete[] vbo;
|
|
vao = 0;
|
|
nb_buffers = 0;
|
|
vbo = NULL;
|
|
}
|
|
}
|
|
|
|
struct VertexComparator
|
|
{
|
|
// c'est plutot crade mais j'ai pas trouve d'autre moyen pour le moment
|
|
static Mesh* mesh;
|
|
static void setMesh(MeshBuilder* m) {VertexComparator::mesh = m;}
|
|
|
|
bool operator() (const int& vertId1, const int& vertId2) const
|
|
{
|
|
if(mesh->positions[vertId1].x != mesh->positions[vertId2].x)
|
|
return (mesh->positions[vertId1].x < mesh->positions[vertId2].x);
|
|
if(mesh->positions[vertId1].y != mesh->positions[vertId2].y)
|
|
return (mesh->positions[vertId1].y < mesh->positions[vertId2].y);
|
|
if(mesh->positions[vertId1].z != mesh->positions[vertId2].z)
|
|
return (mesh->positions[vertId1].z < mesh->positions[vertId2].z);
|
|
if(mesh->hasTexCoords())
|
|
{
|
|
if(mesh->texCoords[vertId1].x != mesh->texCoords[vertId2].x)
|
|
return (mesh->texCoords[vertId1].x < mesh->texCoords[vertId2].x);
|
|
if(mesh->texCoords[vertId1].y != mesh->texCoords[vertId2].y)
|
|
return (mesh->texCoords[vertId1].y < mesh->texCoords[vertId2].y);
|
|
}
|
|
if(mesh->hasNormals())
|
|
{
|
|
if(mesh->normals[vertId1].x != mesh->normals[vertId2].x)
|
|
return (mesh->normals[vertId1].x < mesh->normals[vertId2].x);
|
|
if(mesh->normals[vertId1].y != mesh->normals[vertId2].y)
|
|
return (mesh->normals[vertId1].y < mesh->normals[vertId2].y);
|
|
if(mesh->normals[vertId1].z != mesh->normals[vertId2].z)
|
|
return (mesh->normals[vertId1].z < mesh->normals[vertId2].z);
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
Mesh* VertexComparator::mesh = NULL;
|
|
|
|
void Mesh::mergeVertices()
|
|
{
|
|
std::vector<int> swapped;
|
|
std::set<int, VertexComparator> vertexSet;
|
|
VertexComparator::setMesh(this);
|
|
|
|
int swappedOffset = positions.size() - 1;
|
|
|
|
for(int i=0; i<indices.size(); ++i)
|
|
{
|
|
if(indices[i] >= positions.size())
|
|
indices[i] = swapped[swappedOffset - indices[i]];
|
|
std::pair<std::set<int,VertexComparator>::iterator,bool> ret = vertexSet.insert(g.indices[i]);
|
|
if(!ret.second) // duplicate found
|
|
{
|
|
// updating indices references
|
|
int toDelete = indices[i];
|
|
swapped.push_back(toDelete);
|
|
indices[i] = *(ret.first);
|
|
// deleting the duplicate
|
|
positions[toDelete] = positions.back();
|
|
positions.pop_back();
|
|
if(hasTexCoords())
|
|
{
|
|
texCoords[toDelete] = texCoords.back();
|
|
texCoords.pop_back();
|
|
}
|
|
if(hasNormals())
|
|
{
|
|
normals[toDelete] = normals.back();
|
|
normals.pop_back();
|
|
}
|
|
if(hasTangents())
|
|
{
|
|
tangents[indices[i]].tangent += tangents[toDelete].tangent;
|
|
tangents[indices[i]].binormal += tangents[toDelete].binormal;
|
|
tangents[toDelete] = tangents.back();
|
|
tangents.pop_back();
|
|
}
|
|
}
|
|
}
|
|
for(Tangents &t : tangents)
|
|
{
|
|
t.tangent = glm::normalize(t.tangent);
|
|
t.binormal = glm::normalize(t.binormal);
|
|
}
|
|
}
|
|
|
|
void Mesh::computeNormals()
|
|
{
|
|
normals.resize(positions.size());
|
|
for (int i=0; i < indices.size(); i += 3)
|
|
{
|
|
int v0 = indices[i];
|
|
int v1 = indices[i+1];
|
|
int v2 = indices[i+2];
|
|
glm::vec3 n = glm::cross(positions[v1] - positions[v0], positions[v2] - positions[v0]);
|
|
normals[v0] += n;
|
|
normals[v1] += n;
|
|
normals[v2] += n;
|
|
}
|
|
for(glm::vec3 &n : normals)
|
|
n = glm::normalize(n);
|
|
}
|
|
|
|
void Mesh::computeTangents()
|
|
{
|
|
if(!hasTexCoords())
|
|
return;
|
|
tangents = std::vector<Tangents>(positions.size());
|
|
|
|
for (int j=0; j < indices.size(); j += 3)
|
|
{
|
|
int vertexId0 = indices[j];
|
|
int vertexId1 = indices[j+1];
|
|
int vertexId2 = indices[j+2];
|
|
|
|
const glm::vec3 &v1 = positions[vertexId0];
|
|
const glm::vec3 &v2 = positions[vertexId1];
|
|
const glm::vec3 &v3 = positions[vertexId2];
|
|
|
|
const glm::vec2& w1 = texCoords[vertexId0];
|
|
const glm::vec2& w2 = texCoords[vertexId1];
|
|
const glm::vec2& w3 = texCoords[vertexId2];
|
|
|
|
float x1 = v2.x - v1.x;
|
|
float x2 = v3.x - v1.x;
|
|
float y1 = v2.y - v1.y;
|
|
float y2 = v3.y - v1.y;
|
|
float z1 = v2.z - v1.z;
|
|
float z2 = v3.z - v1.z;
|
|
|
|
float s1 = w2.x - w1.x;
|
|
float s2 = w3.x - w1.x;
|
|
float t1 = w2.y - w1.y;
|
|
float t2 = w3.y - w1.y;
|
|
|
|
float r = 1.0f / (s1 * t2 - s2 * t1);
|
|
glm::vec3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
|
|
(t2 * z1 - t1 * z2) * r);
|
|
glm::vec3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
|
|
(s1 * z2 - s2 * z1) * r);
|
|
|
|
Tangents& tan1 = tangents[vertexId0];
|
|
Tangents& tan2 = tangents[vertexId1];
|
|
Tangents& tan3 = tangents[vertexId2];
|
|
|
|
tan1.tangent += sdir;
|
|
tan2.tangent += sdir;
|
|
tan3.tangent += sdir;
|
|
|
|
tan1.binormal += tdir;
|
|
tan2.binormal += tdir;
|
|
tan3.binormal += tdir;
|
|
}
|
|
}
|