SparrowRenderer/src/mesh.cpp

410 lines
11 KiB
C++

#include "mesh.h"
#include <glm/ext.hpp>
#include <set>
#include "sparrowrenderer.h"
#include "material.h"
#include "buffer.h"
const char* const Mesh::flagStr[Mesh::NB_FLAGS] =
{
"INDEXED",
"TEXTURABLE",
"INSTANCED",
"MESH_3D",
"MESH_2D",
"TANGENT_SPACE",
"BILLBOARD",
"COLOR_TEXTURE",
"ALPHA_MASK",
"PHONG",
"DIFFUSE_TEXTURE",
"AMBIENT_TEXTURE",
"SPECULAR_TEXTURE",
"NORMAL_MAP",
"BUMP_MAP"
};
Mesh::Mesh() :
material(NULL),
isDoubleSided(false),
isBillboard(false),
depth(0),
vao(0),
primitive_type(GL_TRIANGLES)
{
clearBuffers();
}
Mesh::~Mesh()
{
destroyGL();
}
void Mesh::addBuffer(Buffer *b, int bufferType)
{
buffersId[bufferType] = buffers.size();
buffers.push_back(b);
}
void Mesh::clearBuffers()
{
for(int i=0; i<NB_BUFFERS; ++i)
buffersId[i] = -1;
for(Buffer *b : buffers)
delete(b);
buffers.clear();
}
void Mesh::initGL()
{
destroyGL();
// create VAO
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
Buffer *b;
// init positions VBO
if(!positions3D.empty())
{
b = new Buffer(positions3D, Buffer::VBO);
b->setVertexAttrib(0, 3);
addBuffer(b, POSITION_BUFFER);
// init normals vbo
if(!normals.empty())
{
b = new Buffer(normals, Buffer::VBO);
b->setVertexAttrib(1, 3);
addBuffer(b, NORMAL_BUFFER);
}
// init tangents vbo
if(!tangents.empty())
{
b = new Buffer(tangents, Buffer::VBO);
b->setVertexAttrib(3, 3);
b->setVertexAttrib(4, 3);
addBuffer(b, TANGENT_BUFFER);
}
}
else if(!positions2D.empty())
{
b = new Buffer(positions2D, Buffer::VBO);
b->setVertexAttrib(0, 2);
addBuffer(b, POSITION_BUFFER);
}
else
{
fprintf(stderr, "ERROR : Mesh can't be initialized without position data");
return;
}
// init texCoords vbo
if(!texCoords.empty())
{
b = new Buffer(texCoords, Buffer::VBO);
b->setVertexAttrib(2, 2);
addBuffer(b, TEXCOORD_BUFFER);
}
// init instances vbo
if(!instances_offsets.empty())
{
b = new Buffer(instances_offsets, Buffer::VBO);
b->setVertexAttrib(5, 3, 0, 1);
addBuffer(b, INSTANCE_BUFFER);
}
// init EBO
if(!indices.empty())
{
b = new Buffer(indices, Buffer::EBO);
addBuffer(b, INDICES_BUFFER);
}
// unbind vao
glBindVertexArray(0);
}
void Mesh::draw(Shader* shader, bool drawNormals, bool drawTexCoord, bool drawTangents)
{
if(isDoubleSided)
glDisable(GL_CULL_FACE);
bool crappy = (shader == NULL);
material->bindAttributes(shader);
glBindVertexArray(vao);
if(crappy)
{
Buffer *b = buffers[buffersId[POSITION_BUFFER]];
b->bind();
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0)); // TODO : check 2D positions
if(!texCoords.empty() && drawTexCoord)
{
b = buffers[buffersId[TEXCOORD_BUFFER]];
b->bind();
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, BUFFER_OFFSET(0));
}
if(!normals.empty() && drawNormals)
{
b = buffers[buffersId[NORMAL_BUFFER]];
b->bind();
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, BUFFER_OFFSET(0));
}
b->unbind();
}
if(indices.empty())
{
int size = positions3D.empty() ? positions2D.size() : positions3D.size();
if(!instances_offsets.empty() && !crappy)
glDrawArraysInstanced(primitive_type, 0, size, instances_offsets.size());
else
glDrawArrays(primitive_type, 0, size);
}
else
{
Buffer *b = buffers[buffersId[INDICES_BUFFER]];
b->bind();
if(!instances_offsets.empty() && !crappy)
glDrawElementsInstanced(primitive_type, indices.size(), GL_UNSIGNED_INT, NULL, instances_offsets.size());
else
glDrawElements(primitive_type, indices.size(), GL_UNSIGNED_INT, NULL);
b->unbind();
}
if(crappy)
{
glDisableClientState(GL_VERTEX_ARRAY);
if(!normals.empty() && drawNormals)
glDisableClientState(GL_NORMAL_ARRAY);
if(!texCoords.empty() && drawTexCoord)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
glBindVertexArray(0);
if(isDoubleSided)
glEnable(GL_CULL_FACE);
}
void Mesh::destroyGL()
{
if(vao != 0)
{
glDeleteVertexArrays(1, &vao);
vao = 0;
}
clearBuffers();
}
unsigned int Mesh::getFlags()
{
unsigned int flags = material->getFlags();
if(!indices.empty())
flags |= 1 << MESH_INDEXED;
if(!texCoords.empty())
flags |= 1 << MESH_TEXTURABLE;
if(!instances_offsets.empty())
flags |= 1 << MESH_INSTANCED;
if(!positions3D.empty())
{
flags |= 1 << MESH_3D;
if(!tangents.empty())
flags |= 1 << MESH_TANGENT_SPACE;
if(isBillboard)
flags |= 1 << MESH_BILLBOARD;
}
else
flags |= 1 << MESH_2D;
return flags;
}
struct VertexComparator
{
// c'est plutot crade mais j'ai pas trouve d'autre moyen pour le moment
static Mesh* mesh;
static void setMesh(Mesh* m) {VertexComparator::mesh = m;}
bool operator() (const int& vertId1, const int& vertId2) const
{
if(mesh->positions3D[vertId1].x != mesh->positions3D[vertId2].x)
return (mesh->positions3D[vertId1].x < mesh->positions3D[vertId2].x);
if(mesh->positions3D[vertId1].y != mesh->positions3D[vertId2].y)
return (mesh->positions3D[vertId1].y < mesh->positions3D[vertId2].y);
if(mesh->positions3D[vertId1].z != mesh->positions3D[vertId2].z)
return (mesh->positions3D[vertId1].z < mesh->positions3D[vertId2].z);
if(!mesh->texCoords.empty())
{
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->normals.empty())
{
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()
{
if(positions3D.empty())
return;
bool *deleted = new bool[positions3D.size()];
int *offsets = new int[positions3D.size()];
std::set<int, VertexComparator> vertexSet;
VertexComparator::setMesh(this);
for(std::size_t i=0; i<indices.size(); ++i)
{
std::pair<std::set<int,VertexComparator>::iterator,bool> ret = vertexSet.insert(indices[i]);
deleted[indices[i]] = !ret.second && *(ret.first) != indices[i];
if(deleted[indices[i]])
{
if(!tangents.empty())
{
tangents[*(ret.first)].tangent += tangents[indices[i]].tangent;
tangents[*(ret.first)].binormal += tangents[indices[i]].binormal;
}
indices[i] = *(ret.first);
}
}
int offset = 0;
int pos = 0;
for(std::size_t i=0; i<positions3D.size(); ++i)
{
if(deleted[i])
++offset;
else
{
offsets[i] = offset;
if(offset != 0)
{
positions3D[pos] = positions3D[i];
if(!texCoords.empty())
texCoords[pos] = texCoords[i];
if(!normals.empty())
normals[pos] = normals[i];
if(!tangents.empty())
tangents[pos] = tangents[i];
}
++pos;
}
}
for(std::size_t i=0; i<indices.size(); ++i)
indices[i] -= offsets[indices[i]];
positions3D.resize(positions3D.size()-offset);
if(!texCoords.empty())
texCoords.resize(texCoords.size()-offset);
if(!normals.empty())
normals.resize(normals.size()-offset);
if(!tangents.empty())
tangents.resize(tangents.size()-offset);
for(Tangents &t : tangents)
{
t.tangent = glm::normalize(t.tangent);
t.binormal = glm::normalize(t.binormal);
}
}
void Mesh::computeNeighbors()
{
if(positions3D.empty())
return;
// TODO : compute adjacency and change primitivetype
}
void Mesh::computeNormals()
{
if(positions3D.empty())
return;
normals.resize(positions3D.size());
std::memset(normals.data(), 0, normals.size());
for (std::size_t 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(positions3D[v1] - positions3D[v0], positions3D[v2] - positions3D[v0]);
normals[v0] += n;
normals[v1] += n;
normals[v2] += n;
}
for(glm::vec3 &n : normals)
n = glm::normalize(n);
}
void Mesh::computeTangents()
{
if(texCoords.empty())
return;
tangents = std::vector<Tangents>(positions3D.size());
for (std::size_t 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 = positions3D[vertexId0];
const glm::vec3 &v2 = positions3D[vertexId1];
const glm::vec3 &v3 = positions3D[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;
}
}