terrain-gen/src/mesh.cpp

239 lines
9.0 KiB
C++

#include "mesh.h"
Mesh::Mesh(float blockWidth): m_blockWidth(blockWidth)
{
}
Mesh::~Mesh()
{
glDeleteVertexArrays(1, &m_vao);
glDeleteBuffers(1, &m_vbo);
glDeleteBuffers(1, &m_ebo);
}
GLuint Mesh::addVertex(const TypedVertex &v)
{
m_vertices.push_back(v);
return GLuint(m_vertices.size() - 1);
}
GLuint Mesh::addTriangle(GLuint v1, GLuint v2, GLuint v3)
{
GLuint index = GLuint(m_indexes.size());
m_indexes.push_back(v1);
m_indexes.push_back(v2);
m_indexes.push_back(v3);
return index;
}
void Mesh::normalizeNormals()
{
for (uint i = 0; i < m_vertices.size(); i ++) {
float length = std::sqrt(m_vertices[i].normalX * m_vertices[i].normalX +
m_vertices[i].normalY * m_vertices[i].normalY +
m_vertices[i].normalZ * m_vertices[i].normalZ);
m_vertices[i].normalX /= length;
m_vertices[i].normalY /= length;
m_vertices[i].normalZ /= length;
}
}
void Mesh::removeDoubles()
{
if (!m_vertices.size()) {
return;
}
bool* done = new bool[m_vertices.size()]{false};
std::vector<uint> indexMap;
indexMap.resize(m_vertices.size());
std::vector<TypedVertex> newVertices;
std::vector<uint> newIndexes;
std::vector<uint> mergePosition;
std::vector<uint> mergeNormal;
uint sameVerticesCount = 0;
TypedVertex reference;
bool computeAverage;
for (uint vertexIndex = 0; vertexIndex < m_vertices.size(); vertexIndex++) {
if (done[vertexIndex]) {
continue;
}
sameVerticesCount = 1;
mergePosition.clear();
mergeNormal.clear();
computeAverage = true;
reference = m_vertices[vertexIndex];
for (uint otherVertexIndex = vertexIndex + 1; otherVertexIndex < m_vertices.size(); otherVertexIndex++) {
bool samePosition = std::abs(reference.x - m_vertices[otherVertexIndex].x) < 0.05f &&
std::abs(reference.y - m_vertices[otherVertexIndex].y) < 0.05f &&
std::abs(reference.z - m_vertices[otherVertexIndex].z) < 0.05f;
if (samePosition) {
float distanceBorderX = std::fmod(m_vertices[otherVertexIndex].x, m_blockWidth);
float distanceBorderY = std::fmod(m_vertices[otherVertexIndex].y, m_blockWidth);
float distanceBorderZ = std::fmod(m_vertices[otherVertexIndex].z, m_blockWidth);
bool isOnBorder = std::abs(distanceBorderX) < 0.05f || std::abs(distanceBorderY) < 0.05f || std::abs(distanceBorderZ) < 0.05f;
if (isOnBorder) {
m_vertices[vertexIndex].x = m_vertices[otherVertexIndex].x - (std::abs(distanceBorderX) < 0.05f ? distanceBorderX : 0);
m_vertices[vertexIndex].y = m_vertices[otherVertexIndex].y - (std::abs(distanceBorderY) < 0.05f ? distanceBorderY : 0);
m_vertices[vertexIndex].z = m_vertices[otherVertexIndex].z - (std::abs(distanceBorderZ) < 0.05f ? distanceBorderZ : 0);
computeAverage = false;
} else if (computeAverage) {
m_vertices[vertexIndex].x += m_vertices[otherVertexIndex].x;
m_vertices[vertexIndex].y += m_vertices[otherVertexIndex].y;
m_vertices[vertexIndex].z += m_vertices[otherVertexIndex].z;
}
mergePosition.push_back(otherVertexIndex);
float dotProduct = reference.normalX * m_vertices[otherVertexIndex].normalX +
reference.normalY * m_vertices[otherVertexIndex].normalY +
reference.normalZ * m_vertices[otherVertexIndex].normalZ;
if (m_vertices[otherVertexIndex].biome == reference.biome && dotProduct > float(std::cos(45 * M_PI / 180))) {
// TODO: Pondérer la moyenne des normals
m_vertices[vertexIndex].normalX += m_vertices[otherVertexIndex].normalX;
m_vertices[vertexIndex].normalY += m_vertices[otherVertexIndex].normalY;
m_vertices[vertexIndex].normalZ += m_vertices[otherVertexIndex].normalZ;
sameVerticesCount++;
mergeNormal.push_back(otherVertexIndex);
done[otherVertexIndex] = true;
}
}
}
if (computeAverage) {
m_vertices[vertexIndex].x /= (mergePosition.size() + 1);
m_vertices[vertexIndex].y /= (mergePosition.size() + 1);
m_vertices[vertexIndex].z /= (mergePosition.size() + 1);
}
m_vertices[vertexIndex].normalX /= (mergeNormal.size() + 1);
m_vertices[vertexIndex].normalY /= (mergeNormal.size() + 1);
m_vertices[vertexIndex].normalZ /= (mergeNormal.size() + 1);
indexMap[vertexIndex] = uint(newVertices.size());
newVertices.push_back(m_vertices[vertexIndex]);
for (uint mergePositionIndex: mergePosition) {
m_vertices[mergePositionIndex].x = m_vertices[vertexIndex].x;
m_vertices[mergePositionIndex].y = m_vertices[vertexIndex].y;
m_vertices[mergePositionIndex].z = m_vertices[vertexIndex].z;
}
for (uint mergedIndex: mergeNormal) {
m_vertices[mergedIndex].normalX = m_vertices[vertexIndex].normalX;
m_vertices[mergedIndex].normalY = m_vertices[vertexIndex].normalY;
m_vertices[mergedIndex].normalZ = m_vertices[vertexIndex].normalZ;
indexMap[mergedIndex] = uint(newVertices.size()) - 1;
}
}
for (uint i = 0; i < m_indexes.size(); i += 3) {
uint i1 = indexMap[m_indexes[i]];
uint i2 = indexMap[m_indexes[i + 1]];
uint i3 = indexMap[m_indexes[i + 2]];
if (i1 != i2 && i2 != i3 && i3 != i1) {
newIndexes.push_back(i1);
newIndexes.push_back(i2);
newIndexes.push_back(i3);
}
}
m_vertices = newVertices;
m_indexes = newIndexes;
}
std::vector<GLuint>& Mesh::indexes()
{
return m_indexes;
}
std::vector<TypedVertex>& Mesh::vertices()
{
return m_vertices;
}
void Mesh::initGl(Shader* meshProgram, Shader* normalProgram)
{
initializeOpenGLFunctions();
glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo);
glGenBuffers(1, &m_ebo);
m_meshProgram = meshProgram;
m_normalProgram = normalProgram;
int locationPos = m_meshProgram->attributeLocation("in_position");
int locationNormal = m_meshProgram->attributeLocation("in_normal");
int locationBiome = m_meshProgram->attributeLocation("in_biome");
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glVertexAttribPointer(GLuint(locationPos), 3, GL_FLOAT, GL_FALSE, sizeof(TypedVertex), reinterpret_cast<void *>(offsetof(TypedVertex, x)));
glEnableVertexAttribArray(GLuint(locationPos));
glVertexAttribPointer(GLuint(locationNormal), 3, GL_FLOAT, GL_FALSE, sizeof(TypedVertex), reinterpret_cast<void *>(offsetof(TypedVertex, normalX)));
glEnableVertexAttribArray(GLuint(locationNormal));
glVertexAttribIPointer(GLuint(locationBiome), 1, GL_UNSIGNED_INT, sizeof(TypedVertex), reinterpret_cast<void *>(offsetof(TypedVertex, biome)));
glEnableVertexAttribArray(GLuint(locationBiome));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER,
GLsizei(m_vertices.size() * sizeof(TypedVertex)),
m_vertices.data(),
GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
GLsizei(m_indexes.size() * sizeof(GLuint)),
m_indexes.data(),
GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Mesh::render(QMatrix4x4 &model)
{
QMatrix3x3 normalMatrix = model.normalMatrix();
m_meshProgram->bind();
glUniformMatrix3fv(m_meshProgram->uniformLocation("u_normalMatrix"), 1, GL_FALSE, normalMatrix.data());
glUniformMatrix4fv(m_meshProgram->uniformLocation("u_model"), 1, GL_FALSE, model.data());
glBindVertexArray(m_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glDrawElements(GL_TRIANGLES, GLsizei(m_indexes.size()), GL_UNSIGNED_INT, reinterpret_cast<void *>(0));
glBindVertexArray(0);
m_meshProgram->release();
}
void Mesh::renderNormals(QMatrix4x4 &model)
{
QMatrix3x3 normalMatrix = model.normalMatrix();
m_normalProgram->bind();
glUniformMatrix3fv(m_meshProgram->uniformLocation("u_normalMatrix"), 1, GL_FALSE, normalMatrix.data());
glUniformMatrix4fv(m_meshProgram->uniformLocation("u_model"), 1, GL_FALSE, model.data());
glBindVertexArray(m_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glDrawElements(GL_POINTS, GLsizei(m_indexes.size()), GL_UNSIGNED_INT, reinterpret_cast<void *>(0));
glBindVertexArray(0);
m_normalProgram->release();
}