410 lines
11 KiB
C++
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;
|
|
}
|
|
}
|