asteroidgen/src/gen.cpp

1027 lines
28 KiB
C++

#include <vector>
#include <iostream>
#include <algorithm>
#include <string>
#include <fstream>
#include <stack>
#include <set>
#include <map>
#include <unistd.h>
#include <glm/vec3.hpp>
#include <glm/gtc/epsilon.hpp>
#include <glm/gtc/random.hpp>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <OpenMesh/Tools/Decimater/DecimaterT.hh>
#include <OpenMesh/Tools/Decimater/ModProgMeshT.hh>
#include <OpenMesh/Tools/Decimater/ModQuadricT.hh>
#include <OpenMesh/Tools/Decimater/ModAspectRatioT.hh>
#include <OpenMesh/Tools/Decimater/ModEdgeLengthT.hh>
#include "PerlinNoise.hpp"
#include "cola.hpp"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include "RTree.h"
// #include "MarchingCube.h"
#include "mersenne-twister.h"
#include "LSCM.h"
using namespace std;
using namespace glm;
using namespace siv;
using namespace OpenMesh;
struct MyTraits: public OpenMesh::DefaultTraits {
VertexAttributes( OpenMesh::Attributes::Normal |
OpenMesh::Attributes::TexCoord2D );
};
typedef TriMesh_ArrayKernelT<MyTraits> TriMesh;
typedef Decimater::DecimaterT<TriMesh> TriDecimater;
typedef Decimater::ModProgMeshT<TriMesh>::Handle HModProgMesh;
typedef Decimater::ModQuadricT<TriMesh>::Handle HModQuadric;
typedef Decimater::ModAspectRatioT<TriMesh>::Handle HModAspectRatio;
typedef Decimater::ModEdgeLengthT<TriMesh>::Handle HModEdgeLength;
class progress_callback
{
void begin(vector<string> steps);
void beginStep(int iStep, vector<string> substeps);
void progress(int iSubStep, float percent);
void endStep();
void end();
};
class vertices {
public:
vector<vec3> pos;
vector<vec3> norm;
vector<vec3> uv;
void reserve(size_t n) {
pos.reserve(n);
norm.reserve(n);
uv.reserve(n);
}
int append(const vertices &m) {
size_t idx = pos.size();
pos.insert(pos.end(), m.pos.begin(), m.pos.end());
norm.insert(norm.end(), m.norm.begin(), m.norm.end());
uv.insert(uv.end(), m.uv.begin(), m.uv.end());
return idx;
}
int append(const vec3 &p, const vec3 &n, const vec3 &u) {
size_t idx = pos.size();
pos.push_back(p);
norm.push_back(n);
uv.push_back(u);
return idx;
}
};
struct edge {
int v[2];
int t[2];
edge(int v1, int v2, int t1) {
v[0] = v1;
v[1] = v2;
if (v1 > v2)
swap(v[0], v[1]);
t[0] = t1;
t[1] = -1;
}
edge(int v1, int v2) {
v[0] = v1;
v[1] = v2;
if (v1 > v2)
swap(v[0], v[1]);
t[0] = -1;
t[1] = -1;
}
};
struct triangle {
int v[3];
//int e[3];
};
bool operator<(const triangle &lhs, const triangle &rhs) {
if (lhs.v[0] < rhs.v[0])
return true;
else if (lhs.v[0] > rhs.v[0])
return false;
else {
if (lhs.v[1] < rhs.v[1])
return true;
else if (lhs.v[1] > rhs.v[1])
return false;
else {
if (lhs.v[2] < rhs.v[2])
return true;
else if (lhs.v[2] > rhs.v[2])
return false;
else {
return false;
}
}
}
}
struct quant {
// int v[3];
//
// quant(float a, float b, float c) {
// v[0] = 1e4 * a;
// v[1] = 1e4 * b;
// v[2] = 1e4 * c;
// }
float v[3];
quant(float a, float b, float c) {
v[0] = a;
v[1] = b;
v[2] = c;
}
};
bool operator<(const quant &lhs, const quant &rhs) {
if (lhs.v[0] < rhs.v[0])
return true;
else if (lhs.v[0] > rhs.v[0])
return false;
else {
if (lhs.v[1] < rhs.v[1])
return true;
else if (lhs.v[1] > rhs.v[1])
return false;
else {
if (lhs.v[2] < rhs.v[2])
return true;
else if (lhs.v[2] > rhs.v[2])
return false;
else {
return false;
}
}
}
}
bool operator<(const edge &lhs, const edge &rhs) {
if (lhs.v[0] < rhs.v[0])
return true;
else if (lhs.v[0] > rhs.v[0])
return false;
else {
if (lhs.v[1] < rhs.v[1])
return true;
else if (lhs.v[1] > rhs.v[1])
return false;
else {
return false;
}
}
}
void save(ostream &out, const vertices &verts) {
for (size_t i = 0; i < verts.pos.size(); i++) {
out << "v " << verts.pos[i].x << " " << verts.pos[i].y << " "
<< verts.pos[i].z << "\n";
}
for (size_t i = 0; i < verts.norm.size(); i++) {
out << "vn " << verts.norm[i].x << " " << verts.norm[i].y << " "
<< verts.norm[i].z << "\n";
}
for (size_t i = 0; i < verts.uv.size(); i++) {
out << "vt " << verts.uv[i].x << " " << verts.uv[i].y << " "
<< verts.uv[i].z << "\n";
}
}
void save_ascii(ostream &out, const vertices &verts) {
for (size_t i = 0; i < verts.pos.size(); i++) {
out << verts.pos[i].x << " " << verts.pos[i].y << " " << verts.pos[i].z
<< " ";
out << verts.norm[i].x << " " << verts.norm[i].y << " "
<< verts.norm[i].z << "\n";
}
}
void save(ostream &out, const vector<triangle> &tris) {
for (size_t i = 0; i < tris.size(); i++) {
int v0 = tris[i].v[0] + 1;
int v1 = tris[i].v[1] + 1;
int v2 = tris[i].v[2] + 1;
out << "f " << v0 << "/" << v0 << "/" << v0 << " ";
out << v1 << "/" << v1 << "/" << v1 << " ";
out << v2 << "/" << v2 << "/" << v2 << "\n";
}
}
struct closest_vertex_context {
int ignore[3];
int v;
float d;
vec3 p;
const vector<vec3> &pos;
closest_vertex_context(const vector<vec3> &pos, const vec3 &p) :
p(p), pos(pos) {
v = -1;
d = 1;
}
};
// find closest vertex not part of first triangle
bool closest_vertex(int v, void *data) {
closest_vertex_context *context = (closest_vertex_context *) data;
for (size_t i = 0; i < 3; i++) {
if (context->ignore[i] == v) {
return true;
}
}
// found closest vertex, add edges and triangle
// triangle tr;
// tr.v[0] = context->v0;
// tr.v[1] = context->v1;
float d = distance(context->p, context->pos[v]);
if (d < context->d) {
// tr.v[2] = v;
// if (context->triset.find(tr) == context->triset.end()) {
context->d = d;
context->v = v;
// }
}
return true;
}
// http://blackpawn.com/texts/pointinpoly/
bool SameSide(const vec3 &p1, const vec3 &p2, const vec3 &a, const vec3 &b) {
vec2 cp1 = cross(b - a, p1 - a);
vec2 cp2 = cross(b - a, p2 - a);
if (dot(cp1, cp2) >= 0)
return true;
else
return false;
}
bool PointInTriangle(const vec3 &p, const vec3 &a, const vec3 &b,
const vec3 &c) {
if (SameSide(p, a, b, c) && SameSide(p, b, a, c) && SameSide(p, c, a, b))
return true;
else
return false;
}
int Main(int argc, char* argv[]) {
cola::parser parser;
parser.define("help", "Show this help").alias("h");
parser.define("reduce", "Reduce mesh").alias("r").with_arg<int>(50000);
parser.define("control", "Print control image").alias("c");
parser.define("frequency", "Noise frequency").alias("f").with_arg<cola::real>(
2);
parser.define("octave", "Noise octaves").alias("o").with_arg<int>(2);
parser.define("meteroids", "Meteroid craters").alias("m").with_arg<int>(
1000);
parser.define("seed", "Seed for noise").alias("s").with_arg<int>(time(0));
parser.define("threshold", "Threshold for borders").alias("t").with_arg<
cola::real>(0.2);
parser.define("mingrad", "Minimum gradient").alias("g").with_arg<cola::real>(
4);
parser.define("pixels", "Number of pixels").alias("p").with_arg<int>(128);
parser.define("texture-size", "Number of pixels").with_arg<int>(4096);
parser.define("save-points", "Save points");
parser.define("scale-x", "Scale in x").with_arg<cola::real>(-1);
parser.define("scale-y", "Scale in y").with_arg<cola::real>(-1);
parser.define("scale-z", "Scale in z").with_arg<cola::real>(-1);
parser.parse(argc, argv);
if (parser.is_passed("help")) {
parser.easy_usage("generate random astroids");
return 0;
}
bool show_progress = !isatty(fileno(stdin));
// 3d noise, exp falloff
const size_t n = parser.get<int>("pixels");
const double frequency = parser.get<cola::real>("frequency");
const int octave = parser.get<int>("octave");
const double t = parser.get<cola::real>("threshold");
const double mingrad = parser.get<cola::real>("mingrad");
const int seed = parser.get<int>("seed");
const int meteroids = parser.get<int>("meteroids");
const int texture_size = parser.get<int>("texture-size");
srand(seed);
const float scaleX =
parser.is_passed("scale-x") ?
parser.get<cola::real>("scale-x") : 1. + randf_cc() * 4;
const float scaleY =
parser.is_passed("scale-y") ?
parser.get<cola::real>("scale-y") : 1. + randf_cc() * 4;
const float scaleZ =
parser.is_passed("scale-z") ?
parser.get<cola::real>("scale-z") : 1. + randf_cc() * 4;
const bool reduce = parser.is_passed("reduce");
const int reduceTo = parser.get<int>("reduce");
cout << "Generate noise field" << endl;
cout << " frequency: " << frequency << endl;
cout << " octave: " << octave << endl;
cout << " threshold: " << t << endl;
cout << " pixels: " << n << endl;
cout << " seed: " << seed << endl;
cout << " scale: " << scaleX << " " << scaleY << " " << scaleZ << endl;
const size_t nx = n * n;
const size_t ny = n;
const size_t nz = 1;
PerlinNoise noise(seed);
const double s = (1.0d / n);
const double o = 0.5;
const double minScale = std::min(scaleZ, std::min(scaleX, scaleY));
vector<float> voxels(n * n * n);
for (size_t ix = 0; ix < n; ix++) {
if (show_progress)
(cout << "\r " << int(100. * float(ix) / n) << "%").flush();
double x = ix * s - o;
size_t ox = ix * n * n;
for (size_t iy = 0; iy < n; iy++) {
double y = iy * s - o;
size_t oy = ox + iy * n;
for (size_t iz = 0; iz < n; iz++) {
double z = iz * s - o;
float v = (float) noise.octaveNoise0_1(x * frequency,
y * frequency, z * frequency, octave);
v *= std::max(
1.
- 4. / minScale
* (scaleX * x * x + scaleY * y * y
+ scaleZ * z * z), 0.);
// if (v > t)
// v = 1;
// else
// v = 0;
voxels[oy + iz] = v;
}
}
}
// https://en.wikipedia.org/wiki/File:Vesta_Cratered_terrain_with_hills_and_ridges.jpg
// shoot from outside, find first threshold and create hole
for (int i = 0; i < meteroids; i++) {
vec3 dir = sphericalRand(1.0f);
vec3 pos = vec3(-0.7) * dir;
vec3 idx = (pos + vec3(0.5)) * vec3(n);
bool hit = false;
for (size_t j = 0; !hit && j < (2 * n); j++) {
pos += dir * vec3(s);
idx = (pos + vec3(0.5)) * vec3(n);
if (any(lessThan(idx, vec3(0)))) {
continue;
}
if (any(greaterThan(idx, vec3(n)))) {
continue;
}
float v =
voxels[nx * int(idx.x) + ny * int(idx.y) + nz * int(idx.z)];
if (v > 0.2f) {
hit = true;
}
}
if (hit) {
float r = 0.005 + pow(randf_cc(), 4096) * 0.2;
pos -= dir * vec3(r / 1.5f);
idx = (pos + vec3(0.5)) * vec3(n);
size_t nr = r / s;
vec3 idx_min = max(vec3(0), idx - vec3(nr));
vec3 idx_max = min(vec3(n), idx + vec3(nr));
for (size_t ix = idx_min.x; ix < idx_max.x; ix++) {
for (size_t iy = idx_min.y; iy < idx_max.y; iy++) {
for (size_t iz = idx_min.z; iz < idx_max.z; iz++) {
float d = length(vec3(ix, iy, iz) - idx) * s;
if (d > r)
continue;
voxels[nx * ix + ny * iy + nz * iz] = 0;
}
}
}
} else {
cout << "no hit!" << endl;
}
}
if (parser.is_passed("control")) {
cout << "Write control image" << endl;
vector<uint8> img(n * n);
for (size_t ix = 0; ix < n; ix++) {
for (size_t iy = 0; iy < n; iy++) {
img[ix * n + iy] = voxels[ix * nx + iy * ny + n / 2] * 255;
}
}
stbi_write_bmp("control.bmp", n, n, 1, img.data());
}
if (parser.is_passed("save-points")) {
cout << "Create points from voxels" << endl;
cout << " Minimum gradient: " << mingrad << endl;
vec3 vecs[3 * 3 * 3];
for (int ix = 0; ix < 3; ix++) {
for (int iy = 0; iy < 3; iy++) {
for (int iz = 0; iz < 3; iz++) {
vecs[ix * 9 + iy * 3 + iz] = normalize(
vec3(ix - 1, iy - 1, iz - 1));
}
}
}
// center to zero
vecs[9 + 3 + 1] = vec3(0, 0, 0);
const size_t nx = n * n;
const size_t ny = n;
const size_t nz = 1;
// create vertices with normal for each border pixel
vertices verts;
for (size_t ix = 1; ix < (n - 1); ix++) {
double x = ix * s - o;
size_t ox = ix * n * n;
for (size_t iy = 1; iy < (n - 1); iy++) {
double y = iy * s - o;
size_t oy = ox + iy * n;
for (size_t iz = 1; iz < (n - 1); iz++) {
size_t io = oy + iz;
if (voxels[io] == 0)
continue;
vec3 gradient = vec3(0, 0, 0);
for (int jx = 0; jx < 3; jx++) {
for (int jy = 0; jy < 3; jy++) {
for (int jz = 0; jz < 3; jz++) {
gradient +=
vecs[jx * 9 + jy * 3 + jz]
* (float) voxels[io
+ nx * (jx - 1)
+ ny * (jy - 1)
+ nz * (jz - 1)];
}
}
}
// no border = no gradient
if (length(gradient) < mingrad + 0.1)
continue;
double z = iz * s - o;
verts.append(vec3(x, y, z), normalize(gradient) * -1.f,
vec3(0, 0, 0));
}
}
}
cout << "Save points" << endl;
ofstream ofile("points.obj");
save(ofile, verts);
ofstream oafile("points.ascii");
save_ascii(oafile, verts);
}
cout << "Marching cubes" << endl;
TriMesh mesh;
vertices verts;
vector<triangle> tris;
map<quant, int> vertexLookup;
RTree<int, float, 3, float> tree;
size_t nT = 0;
for (size_t ix = 0; ix < (n - 1); ix++) {
if (show_progress)
(cout << "\r " << int(100. * float(ix) / n) << "%").flush();
double x = ix * s - o;
size_t ox = ix * n * n;
for (size_t iy = 0; iy < (n - 1); iy++) {
double y = iy * s - o;
size_t oy = ox + iy * n;
for (size_t iz = 0; iz < (n - 1); iz++) {
size_t io = oy + iz;
double z = iz * s - o;
//
// GRIDCELL gridCell;
// gridCell.p[0] = vec3(x, y, z);
// gridCell.p[1] = vec3(x + s, y, z);
// gridCell.p[2] = vec3(x + s, y + s, z);
// gridCell.p[3] = vec3(x, y + s, z);
// gridCell.p[4] = vec3(x, y, z + s);
// gridCell.p[5] = vec3(x + s, y, z + s);
// gridCell.p[6] = vec3(x + s, y + s, z + s);
// gridCell.p[7] = vec3(x, y + s, z + s);
//
// gridCell.val[0] = voxels[io];
// gridCell.val[1] = voxels[io + nx];
// gridCell.val[2] = voxels[io + nx + ny];
// gridCell.val[3] = voxels[io + ny];
// gridCell.val[4] = voxels[io + nz];
// gridCell.val[5] = voxels[io + nx + nz];
// gridCell.val[6] = voxels[io + nx + ny + nz];
// gridCell.val[7] = voxels[io + ny + nz];
// TRIANGLE triangles[6];
// int nTris = Polygonise(gridCell, t, triangles);
// nT += nTris;
// for (int iTri = 0; iTri < nTris; iTri++) {
// vec3 *ps = triangles[iTri].p;
// if (all(epsilonEqual(ps[0], ps[1], 1e-8f))
// || all(epsilonEqual(ps[0], ps[2], 1e-8f))
// || all(epsilonEqual(ps[2], ps[1], 1e-8f)))
// continue;
// TriMesh::VertexHandle vhandle[3];
// for (size_t k = 0; k < 3; k++) {
// vec3 &p = ps[k];
// quant q(p.x, p.y, p.z);
// if (reduce) {
// map<quant, int>::iterator vit = vertexLookup.find(
// q);
// if (vit != vertexLookup.end()) {
// TriMesh::VertexHandle h = mesh.vertex_handle(
// vit->second);
// vhandle[k] = h;
// }
// }
// if (!vhandle[k].is_valid()) {
// TriMesh::Point pnt(p.x, p.y, p.z);
// vhandle[k] = mesh.add_vertex(pnt);
// if (reduce)
// vertexLookup[q] = vhandle[k].idx();
//
// }
// }
//
// mesh.add_face(vhandle, 3);
// }
}
//(cout << ".").flush();
}
//(cout << " " << nT << " " << vertexLookup.size() << "\n").flush();
}
#if 0
// create triangles
RTree<int, float, 3, float> tree;
for (size_t i = 0; i < verts.pos.size(); i++) {
float p[3] = {verts.pos[i].x, verts.pos[i].y, verts.pos[i].z};
tree.Insert(p, p, i);
}
// find closest vertex to first
int second_vertex = -1;
float second_dist = 1.0;
for (size_t j = 1; j < verts.pos.size(); j++) {
float d = distance(verts.pos[j], verts.pos[0]);
if (d < second_dist) {
second_dist = d;
second_vertex = j;
}
}
cout << "Create triangles from vertices" << endl;
vector<triangle> tris;
vector<edge> edges;
stack<int> uedges;
edges.push_back(edge(0, second_vertex));
uedges.push(edges.size() - 1);
while (uedges.size() > 0) {
int ei = uedges.top();
int v0 = edges[ei].v[0];
int v1 = edges[ei].v[1];
int t0 = edges[ei].t[0];
int t1 = edges[ei].t[1];
vec3 center = (verts.pos[v0] + verts.pos[v1]) / 2.f;
closest_vertex_context context(verts.pos, center);
// first triangle set?
if (t0 >= 0) {
// remove from unfinished
uedges.pop();
// first triangle
for (size_t i = 0; i < 3; i++)
context.ignore[i] = tris[t0].v[i];
} else {
// change first triangle
context.ignore[0] = v0;
context.ignore[1] = v1;
context.ignore[2] = v1;
}
// find closest vertex not in edge of first triangle
float range = second_dist;
while (context.v < 0) {
range *= 2;
float smin[3] = {center.x - range, center.y - range, center.z
- range};
float smax[3] = {center.x + range, center.y + range, center.z
+ range};
if (tree.Search(smin, smax, closest_vertex, &context) == 0)
cout << "no results!" << endl;
}
// found closest vertex, add edges and triangle
triangle tr;
tr.v[0] = v0;
tr.v[1] = v1;
tr.v[2] = context.v;
int e1 = edges.size();
int e2 = e1 + 1;
int t = tris.size();
if (t0 < 0) {
edges[ei].t[0] = t;
} else {
edges[ei].t[1] = t;
}
edges.push_back(edge(v0, context.v, t));
edges.push_back(edge(v1, context.v, t));
uedges.push(e1);
uedges.push(e2);
// tr.e[0] = ei;
// tr.e[1] = e1;
// tr.e[2] = e2;
tris.push_back(tr);
triset.insert(tr);
if (tris.size() % 1000 == 0) {
cout << tris.size() << " ";
cout << uedges.size() << endl;
}
}
#endif
cout << "Stage1 Vertices: " << mesh.n_vertices() << endl;
cout << "Stage1 Faces: " << mesh.n_faces() << endl;
cout << "Stage1 Save file" << endl;
OpenMesh::IO::write_mesh(mesh, "stage1.obj");
cout << "Stage 2: Smooth mesh" << endl;
{
// this vector stores the computed centers of gravity
vector<TriMesh::Point> cogs;
vector<TriMesh::Point>::iterator cog_it;
cogs.reserve(mesh.n_vertices());
// smoothing mesh argv[1] times
TriMesh::VertexIter v_it, v_end(mesh.vertices_end());
TriMesh::VertexVertexIter vv_it;
TriMesh::Point cog;
TriMesh::Scalar valence;
for (int i = 0; i < 2; ++i) {
cogs.clear();
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);
}
}
cout << "Stage2: Vertices: " << mesh.n_vertices() << endl;
cout << "Stage2: Faces: " << mesh.n_faces() << endl;
cout << "Stage2: Save file" << endl;
OpenMesh::IO::write_mesh(mesh, "stage2.obj");
cout << "Stage3: Optimize mesh" << endl;
#if 1
mesh.request_face_status();
mesh.request_edge_status();
mesh.request_vertex_status();
TriDecimater decimater(mesh); // a decimater object, connected to a mesh
#if 1
HModQuadric hModQuadric; // use a quadric module
decimater.add(hModQuadric); // register module at the decimater
cout << decimater.module(hModQuadric).name() << endl; // module access
/*
* since we need exactly one priority module (non-binary)
* we have to call set_binary(false) for our priority module
* in the case of HModQuadric, unset_max_err() calls set_binary(false) internally
*/
decimater.module(hModQuadric).unset_max_err();
#endif
#if 0
HModAspectRatio hModAspectRatio; // use a quadric module
decimater.add(hModAspectRatio);// register module at the decimater
//decimater.module(hModAspectRatio).set_aspect_ratio(5);
decimater.module(hModAspectRatio).set_binary(false);
cout << decimater.module(hModAspectRatio).name() << endl;// module access
#endif
#if 0
HModEdgeLength hModEdgeLength; // use a quadric module
decimater.add(hModEdgeLength);// register module at the decimater
//decimater.module(hModAspectRatio).set_aspect_ratio(5);
decimater.module(hModEdgeLength).set_binary(false);
cout << decimater.module(hModEdgeLength).name() << endl;// module access
#endif
// HModProgMesh hModProgMesh; // use a quadric module
// decimater.add(hModProgMesh); // register module at the decimater
// cout << decimater.module(hModProgMesh).name() << endl; // module access
if (!decimater.initialize())
cerr << "Init failed!" << endl;
cout << "Stage3: Decimated " << decimater.decimate_to(reduceTo) << endl;
// after decimation: remove decimated elements from the mesh
mesh.garbage_collection();
cout << "Stage3: Vertices: " << mesh.n_vertices() << endl;
cout << "Stage3: Faces: " << mesh.n_faces() << endl;
cout << "Stage3: Save file" << endl;
OpenMesh::IO::write_mesh(mesh, "stage3.obj");
#endif // OPT
cout << "Stage4: UV map" << endl;
TriMesh mappedMesh;
float texf = 1. / texture_size;
vector<uint8[3]> diff(texture_size * texture_size * 3);
mesh.request_vertex_normals();
mesh.request_face_normals();
mesh.update_face_normals();
mesh.update_vertex_normals();
int rects = ceil(sqrt(mesh.n_faces())), rx = 0, ry = 0;
float rectSize = floor(float(texture_size) / rects) / texture_size;
size_t nfaces = mesh.n_faces(), iface = 0;
for (auto i = mesh.faces_begin(); i != mesh.faces_end(); i++) {
if (show_progress)
(cout << "\r " << int(100. * float(iface++) / nfaces) << "%").flush();
vec3 p[3], n[3];
int k = 0;
TriMesh::VertexHandle h[3];
for (auto j = mesh.fv_begin(*i); j != mesh.fv_end(*i); j++) {
if (!j->is_valid())
cout << "invalid vertex" << endl;
TriMesh::Point pt = mesh.point(*j);
p[k] = vec3(pt[0], pt[1], pt[2]);
TriMesh::Normal nm = mesh.calc_vertex_normal(*j);
n[k] = vec3(nm[0], nm[1], nm[2]);
h[k] = mappedMesh.add_vertex(pt);
mappedMesh.set_normal(h[k], nm);
k++;
if (k > 3)
cout << "not a triangle!" << endl;
}
mappedMesh.add_face(h, 3);
vec3 ex = normalize(p[1] - p[0]);
vec3 ey = normalize(cross(ex, p[2] - p[0]));
vec3 ez = normalize(cross(ex, ey));
mat3 base = mat3(ex, ey, ez);
vec3 t[3] = { p[0] * base, p[1] * base, p[2] * base };
vec3 lower = min(min(t[0], t[1]), t[2]);
vec3 upper = max(max(t[0], t[1]), t[2]);
t[0] -= lower;
t[1] -= lower;
t[2] -= lower;
vec3 extent = upper - lower;
float s = std::max(std::max(extent.x, extent.y), extent.z);
t[0] *= 0.8 * rectSize / s;
t[1] *= 0.8 * rectSize / s;
t[2] *= 0.8 * rectSize / s;
vec3 off(rx * rectSize, 0, ry * rectSize);
t[0] += off;
t[1] += off;
t[2] += off;
mappedMesh.set_texcoord2D(h[0], TriMesh::TexCoord2D(t[0].x, t[0].z));
mappedMesh.set_texcoord2D(h[1], TriMesh::TexCoord2D(t[1].x, t[1].z));
mappedMesh.set_texcoord2D(h[2], TriMesh::TexCoord2D(t[2].x, t[2].z));
//if (rx == 0 && ry == 0) {
t[0].y = 0;
t[1].y = 0;
t[2].y = 0;
// cout << "ftex " << texf << endl;
// cout << "t[0] " << t[0].x << " " << t[0].z << endl;
// cout << "t[1] " << t[1].x << " " << t[1].z << endl;
// cout << "t[2] " << t[2].x << " " << t[2].z << endl;
// cout << "t[0]p " << t[0].x * ntex << " " << (1 - t[0].z) * ntex << endl;
// cout << "t[1]p " << t[1].x * ntex << " " << (1 - t[1].z) * ntex << endl;
// cout << "t[2]p " << t[2].x * ntex << " " << (1 - t[2].z) * ntex << endl;
// fill texture
// fill whole rect for now
int rpix = floor(rectSize * texture_size);
vec3 eb = t[1] - t[0];
float leb = length(eb);
vec3 ec = t[2] - t[0];
float lec = length(ec);
// mat2 textureBase = mat2(vec2(eb.x, eb.z), vec2(ec.x, ec.z));
// mat2 itextureBase = inverse(textureBase);
mat3 textureBase = mat3(eb, cross(eb, ec), ec);
mat3 itextureBase = inverse(textureBase);
// eb = normalize(eb);
// ec = normalize(ec);
vec3 db = p[1] - p[0];
vec3 dc = p[2] - p[0];
vec3 mb = normalize(p[1] - p[0]);
vec3 mc = normalize(p[2] - p[0]);
// mat2 faceBase = mat2(vec2(mb.x, mb.z), vec2(mc.x, mc.z));
// mat2 ifaceBase = inverse(faceBase);
mat3 faceBase = mat3(db, cross(db, dc), dc);
mat3 ifaceBase = inverse(faceBase);
// mat2 trafo = ifaceBase * textureBase * faceBase;
for (int ix = rx * rpix; ix < (rx + 1) * rpix; ix++) {
for (int iy = (texture_size - (ry + 1) * rpix);
iy < (texture_size - ry * rpix); iy++) {
// cout << "pix " << ix << " " << iy << endl;
// cout << "uv " << float(ix) * texf << " " << 1.f - float(iy) * texf << endl;
// pixel to uv space
vec3 res = vec3(float(ix) * texf, 0.f, 1.f - float(iy) * texf)
- t[0];
// cout << "r = " << r.x << " " << r.y << endl;
res = faceBase * itextureBase * res;
// cout << "res = " << res.x << " " << res.y << endl;
// vec2 res = ifaceBase * textureBase * r * faceBase;
//
// cout << "pix " << ix << " " << res.x << " " << res.y << " " << res.z << endl;
// float fb = dot(r, eb) / leb;
// float fc = dot(r, ec) / lec;
vec3 sp = p[0] + res; //res.x * mb + res.y * mc;
float v = (float) noise.octaveNoise0_1(sp.x * frequency * 4,
sp.y * frequency * 4, sp.z * frequency * 4, 8);
//
v = pow(v, 3);
// default color
float r = 0.12 + v * 0.72, g = 0.1 + 0.7 * v, b = 0.1 + 0.7 * v;
diff[ix + iy * texture_size][0] = r * 255;
diff[ix + iy * texture_size][1] = g * 255;
diff[ix + iy * texture_size][2] = b * 255;
// diff[ix + iy * ntex] = v * 255;
// diff[ix + iy * ntex][0] = (32 * rx) % 255;
// diff[ix + iy * ntex][1] = (32 * ry) % 255;
// diff[ix + iy * ntex][2] = 0; //(0.5 + sp.z) * 255;
// float rd = 1.f; //0.7 + 0.3 * length(r) * rects;
// diff[ix + iy * ntex][0] = rd * (0.5 + sp.x) * 255;
// diff[ix + iy * ntex][1] = rd * (0.5 + sp.y) * 255;
// diff[ix + iy * ntex][2] = rd * (0.5 + sp.z) * 255;
// diff[ix + iy * ntex][0] = length(r) * rects * 255;
// diff[ix + iy * ntex][1] = length(r) * rects * 255;
// diff[ix + iy * ntex][2] = length(r) * rects * 255;
// diff[ix + iy * ntex][0] = r.x * rects * 200;
// diff[ix + iy * ntex][1] = -r.z * rects * 200;
// diff[ix + iy * ntex][2] = 0;
// diff[ix + iy * ntex][0] = 128 + (res.x - 0.5) * 200;
// diff[ix + iy * ntex][1] = 128 + (res.y - 0.5) * 200;
// diff[ix + iy * ntex][2] = 0; //res.z * 255;
}
//}
}
rx++;
if (rx >= rects) {
rx = 0;
ry++;
}
}
//
// for (int ix = 0; ix < ntex; ix++) {
// for (int iy = 0; iy < ntex; iy++) {
//
// float v = (float) noise.octaveNoise0_1(ix * texf * frequency,
// iy * texf * frequency, 0.5 * frequency, 4);
// diff[ix + (ntex - iy) * ntex] = 5 + v * 250;
// }
// }
stbi_write_bmp("diffuse.bmp", texture_size, texture_size, 3, diff.data());
cout << "Stage4: Vertices: " << mesh.n_vertices() << endl;
cout << "Stage4: Faces: " << mesh.n_faces() << endl;
cout << "Stage4: Save file" << endl;
OpenMesh::IO::write_mesh(mappedMesh, "stage4.obj",
IO::Options::VertexTexCoord | IO::Options::VertexNormal);
/*
for (size_t ix = 0; ix < n; ix++) {
for (size_t iy = 0; iy < n; iy++) {
img[ix * n + iy] = voxels[ix * nx + iy * ny + n / 2] * 255;
}
}
*/
#if LSCM
nlInitialize(argc, argv);
// nlInitialize(argc, argv);
LSCM::IndexedMesh lscmMesh;
// copy data
const TriMesh::Point *points = mesh.points();
for (size_t i = 0; i < mesh.n_vertices(); i++) {
lscmMesh.add_vertex(
LSCM::vec3(points[i].data()[0], points[i].data()[1],
points[i].data()[2]), LSCM::vec2(0.0, 0.0));
}
for (auto i = mesh.faces_begin(); i != mesh.faces_end(); i++) {
lscmMesh.begin_facet();
for (auto j = mesh.fv_begin(*i); j != mesh.fv_end(*i); j++) {
lscmMesh.add_vertex_to_facet(j->idx());
}
lscmMesh.end_facet();
}
LSCM::LSCM lscm(lscmMesh);
lscm.set_spectral(false);
lscm.apply();
lscmMesh.save("stage3.obj");
#endif
// // copy uv back
// mesh.request_vertex_texcoords2D();
//
// if (lscmMesh.nb_vertices() != mesh.n_vertices())
// cout << "invali" << endl;
// for (unsigned int v = 0; v < lscmMesh.nb_vertices(); ++v) {
// TriMesh::TexCoord2D tex(lscmMesh.vertex[v].tex_coord.x, lscmMesh.vertex[v].tex_coord.y);
// mesh.set_texcoord2D(mesh.vertex_handle(v), tex);
// }
//
// cout << "Write File" << endl;
// OpenMesh::IO::write_mesh(mesh, "stage3.obj", IO::Options::VertexTexCoord);
return 0;
}