asteroidgen/src/Mesh.cpp

336 lines
8.0 KiB
C++

#include "Mesh.h"
#include "gl.h"
#include <glm/geometric.hpp>
#include <map>
#include <limits>
#include <stack>
#include <fstream>
using namespace std;
using namespace glm;
static const uvec3::value_type InvalidIndex =
numeric_limits<uvec3::value_type>::max();
Mesh::Mesh() {
}
Mesh::Mesh(Mesh &other) {
positions = other.positions;
normals = other.normals;
texcoords = other.texcoords;
faceNormals = other.faceNormals;
indices = other.indices;
patches = other.patches;
edges = other.edges;
adjacents = other.adjacents;
tangents = other.tangents;
bitangents = other.bitangents;
}
Mesh::Mesh(Mesh &&other) {
positions.swap(other.positions);
normals.swap(other.normals);
texcoords.swap(other.texcoords);
faceNormals.swap(other.faceNormals);
indices.swap(other.indices);
patches.swap(other.patches);
edges.swap(other.edges);
adjacents.swap(other.adjacents);
tangents.swap(other.tangents);
bitangents.swap(other.bitangents);
}
Mesh& Mesh::operator=(Mesh &&other) {
positions.swap(other.positions);
normals.swap(other.normals);
texcoords.swap(other.texcoords);
faceNormals.swap(other.faceNormals);
indices.swap(other.indices);
patches.swap(other.patches);
edges.swap(other.edges);
adjacents.swap(other.adjacents);
tangents.swap(other.tangents);
bitangents.swap(other.bitangents);
return *this;
}
void Mesh::generateNormals() {
normals.clear();
normals.resize(positions.size(), vec3(0));
#pragma omp parallel for
for (size_t i = 0; i < indices.size(); i++) {
int a = indices[i].x, b = indices[i].y, c = indices[i].z;
vec3 faceNormal = normalize(
cross(positions[b] - positions[a],
positions[c] - positions[a]));
normals[a] += faceNormal;
normals[b] += faceNormal;
normals[c] += faceNormal;
}
for (size_t i = 0; i < normals.size(); i++)
normals[i] = normalize(normals[i]);
}
void Mesh::generateFaceNormals() {
faceNormals.clear();
faceNormals.resize(indices.size(), vec3(uninitialize));
#pragma omp parallel for
for (size_t i = 0; i < indices.size(); i++) {
int a = indices[i].x, b = indices[i].y, c = indices[i].z;
faceNormals[i] = normalize(
cross(positions[b] - positions[a],
positions[c] - positions[a]));
}
}
void Mesh::generateEdges() {
edges.clear();
map<pair<uint32_t, uint32_t>, uint32_t> edgeMap;
for (size_t iTri = 0; iTri < indices.size(); iTri++) {
uvec3 idx = indices[iTri];
for (size_t k = 0; k < 3; k++) {
int a = idx[k];
int b = idx[(k + 1) % 3];
uvec4 edge1(std::min(a, b), std::max(a, b), iTri, 0);
auto key = std::make_pair(edge1.x, edge1.y);
auto it1 = edgeMap.find(key);
if (it1 != edgeMap.end()) {
edges[it1->second].w = iTri;
} else {
size_t idx = edges.size();
edges.push_back(edge1);
edgeMap[key] = idx;
}
}
}
}
void Mesh::generateTangentBasis() {
for (size_t i = 0; i < positions.size(); i += 3) {
// Shortcuts for vertices
vec3 & v0 = positions[i + 0];
vec3 & v1 = positions[i + 1];
vec3 & v2 = positions[i + 2];
// Shortcuts for UVs
vec2 & uv0 = texcoords[i + 0];
vec2 & uv1 = texcoords[i + 1];
vec2 & uv2 = texcoords[i + 2];
// Edges of the triangle : postion delta
vec3 deltaPos1 = v1 - v0;
vec3 deltaPos2 = v2 - v0;
// UV delta
vec2 deltaUV1 = uv1 - uv0;
vec2 deltaUV2 = uv2 - uv0;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r;
vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r;
// Set the same tangent for all three vertices of the triangle.
// They will be merged later, in vboindexer.cpp
tangents.push_back(tangent);
tangents.push_back(tangent);
tangents.push_back(tangent);
// Same thing for binormals
bitangents.push_back(bitangent);
bitangents.push_back(bitangent);
bitangents.push_back(bitangent);
}
}
void Mesh::generateAdjacent() {
adjacents.resize(indices.size(), uvec3(InvalidIndex));
for (size_t i = 0; i < edges.size(); i++) {
int a = edges[i].z;
int b = edges[i].w;
uvec3 &aa = adjacents[a];
for (size_t j = 0; j < 3; j++) {
if (aa[j] == InvalidIndex) {
aa[j] = b;
break;
}
}
uvec3 &ab = adjacents[b];
for (size_t j = 0; j < 3; j++) {
if (ab[j] == InvalidIndex) {
ab[j] = a;
break;
}
}
}
}
void Mesh::moveToMean() {
vec3 mean(0);
for (size_t i = 0; i < positions.size(); i++) {
mean += positions[i];
}
mean *= vec3(1.f / positions.size());
for (size_t i = 0; i < positions.size(); i++) {
positions[i] -= mean;
}
}
void Mesh::smooth() {
if (positions.size() == 0 || indices.size() == 0)
return;
vec3v_t cogs(positions.size(), vec3(0.f));
vector<int> valence(positions.size(), 0);
for (size_t iTri = 0; iTri < indices.size(); iTri++) {
const uvec3 &idx = indices[iTri];
for (size_t iE = 0; iE < 3; iE++) {
valence[idx[iE]] += 2;
cogs[idx[iE]] += positions[idx[(iE + 1) % 3]];
cogs[idx[iE]] += positions[idx[(iE + 2) % 3]];
}
}
/*
for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it) {
cog[0] = cog[1] = cog[2] = valence = 0.0;
for (vv_it = mesh.vv_iter(*v_it); vv_it.is_valid(); ++vv_it) {
cog += mesh.point(*vv_it);
++valence;
}
cogs.push_back(cog / valence);
}
for (v_it = mesh.vertices_begin(), cog_it = cogs.begin(); v_it != v_end;
++v_it, ++cog_it)
if (!mesh.is_boundary(*v_it))
mesh.set_point(*v_it, *cog_it);
*/
#pragma omp parallel for
for (size_t i = 0; i < positions.size(); i++) {
// vtx[i] = vtx[i] * vec3(0.8)
// + cogs[i] * vec3(0.2f / valence[i]);
positions[i] = cogs[i] * vec3(1.f / valence[i]);
}
}
void Mesh::saveAttrib(std::ostream &out, const char *prefix,
const vec3v_t &elements) const {
for (size_t i = 0; i < elements.size(); i++) {
out << prefix << elements[i].x << " " << elements[i].y << " "
<< elements[i].z << "\n";
}
}
void Mesh::saveAttrib(std::ostream &out, const char *prefix,
const vec2v_t &elements) const {
for (size_t i = 0; i < elements.size(); i++) {
out << prefix << elements[i].x << " " << elements[i].y << "\n";
}
}
void Mesh::saveFaces(std::ostream &out, const uvec3v_t &incdices,
size_t attribs) const {
for (size_t i = 0; i < incdices.size(); i++) {
out << "f";
for (size_t j = 0; j < 3; j++) {
int v = incdices[i][j] + 1;
out << " " << v;
if (attribs > 1)
out << "/" << v;
if (attribs > 2)
out << "/" << v;
out << " ";
}
out << "\n";
}
}
Mesh Mesh::createPatched(float threshold) const {
assert(adjacents.size() == indices.size());
Mesh mesh;
vector<bool> processed(indices.size(), false);
for (size_t i = 0; i < indices.size(); i++) {
if (processed[i])
continue;
vector<uint32_t> faces;
map<uint32_t, uint32_t> vertexMap;
vec3 normal = faceNormals[i];
// check all adjacent faces
stack<uint32_t> facesToCheck;
facesToCheck.push(i);
while (facesToCheck.size()) {
uint32_t face = facesToCheck.top();
facesToCheck.pop();
if (face == InvalidIndex || processed[face])
continue;
if (dot(normal, faceNormals[face]) > threshold) {
faces.push_back(face);
//normal = normalize(normal + 0.5f * faceNormals[face]);
processed[face] = true;
facesToCheck.push(adjacents[face].x);
facesToCheck.push(adjacents[face].y);
facesToCheck.push(adjacents[face].z);
}
}
// fill new arrays
for (size_t j = 0; j < faces.size(); j++) {
uvec3 idx = indices[faces[j]];
for (size_t k = 0; k < 3; k++) {
auto it = vertexMap.find(idx[k]);
if (it == vertexMap.end()) {
uint32_t newIdx = mesh.positions.size();
mesh.positions.push_back(positions[idx[k]]);
if (normals.size())
mesh.normals.push_back(normals[idx[k]]);
vertexMap[idx[k]] = newIdx;
idx[k] = newIdx;
} else {
idx[k] = it->second;
}
}
mesh.indices.push_back(idx);
}
}
return mesh;
}
void Mesh::save(std::ostream &out) const {
size_t nAttribs = 1;
saveAttrib(out, "v ", positions);
if (normals.size()) {
nAttribs++;
saveAttrib(out, "vn ", normals);
}
if (texcoords.size()) {
nAttribs++;
saveAttrib(out, "vt ", texcoords);
}
saveFaces(out, indices, nAttribs);
}
void Mesh::save(std::string filename) const {
std::ofstream out(filename.c_str());
save(out);
}