celestia/src/tools/cmod/cmodsphere/cmodsphere.cpp

307 lines
8.9 KiB
C++

#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include <Eigen/Core>
using namespace std;
// TODO: these shouldn't be hardcoded
static int latSamples = 1440;
static int longSamples = 2880;
constexpr float pi = 3.14159265f;
static float* samples = nullptr;
// Read a big-endian 32-bit unsigned integer
static uint32_t readUint(istream& in)
{
uint32_t ret;
in.read((char*) &ret, sizeof(uint32_t));
return (uint32_t) ret;
}
static float readFloat(istream& in)
{
uint32_t i = readUint(in);
uint32_t n = ((i & 0xff) << 24) | ((i & 0xff00) << 8) | ((i & 0xff0000) >> 8) | ((i & 0xff000000) >> 24);
return *((float*) &n);
}
bool readLongLatAscii(istream& in)
{
return false;
}
bool readBinary(istream& in,
unsigned int latSampleCount,
unsigned int longSampleCount)
{
for (unsigned int i = 0; i < latSampleCount; i++)
{
for (unsigned int j = 0; j < longSampleCount; j++)
{
float r = readFloat(in) / 1000.0f;
samples[i * longSampleCount + j] = r;
}
}
return true;
}
inline float sample(float samples[],
unsigned int width,
unsigned int height,
float s,
float t)
{
float ssamp = (float) (width - 1) + 0.99f;
float tsamp = (float) (height - 1) + 0.99f;
return samples[(unsigned int) (t * tsamp) * width +
(unsigned int) (s * ssamp)];
}
inline float sampleBilinear(const float samples[],
unsigned int width,
unsigned int height,
float s,
float t)
{
unsigned int x0 = (unsigned int) (s * width) % width;
unsigned int y0 = (unsigned int) (t * height) % height;
unsigned int x1 = (unsigned int) (x0 + 1) % width;
unsigned int y1 = (unsigned int) (y0 + 1) % height;
float tx = s * width - (float) (unsigned int) (s * width);
float ty = t * height - (float) (unsigned int) (t * height);
float s00 = samples[y0 * width + x0];
float s01 = samples[y0 * width + x1];
float s10 = samples[y1 * width + x0];
float s11 = samples[y1 * width + x1];
float s0 = (1.0f - tx) * s00 + tx * s01;
float s1 = (1.0f - tx) * s10 + tx * s11;
return (1.0f - ty) * s0 + ty * s1;
}
// subdiv is the number of rows in the triangle
void triangleSection(unsigned int subdiv,
Eigen::Vector3f v0, Eigen::Vector3f v1, Eigen::Vector3f v2,
Eigen::Vector2f tex0, Eigen::Vector2f tex1, Eigen::Vector2f tex2)
{
float ssamp = (float) (longSamples - 1) + 0.99f;
float tsamp = (float) (latSamples - 1) + 0.99f;
for (unsigned int i = 0; i <= subdiv; i++)
{
for (unsigned int j = 0; j <= i; j++)
{
float u = (i == 0) ? 0.0f : (float) j / (float) i;
float v = (float) i / (float) subdiv;
Eigen::Vector3f w0 = (1.0f - v) * v0 + v * v1;
Eigen::Vector3f w1 = (1.0f - v) * v0 + v * v2;
Eigen::Vector3f w = (1.0f - u) * w0 + u * w1;
Eigen::Vector2f t((1.0f - u) * tex1.x() + u * tex2.x(),
(1.0f - v) * tex0.y() + v * tex1.y());
w.normalize();
if (samples != nullptr)
{
float theta = (float) acos(w.y());
float phi = (float) atan2(-w.z(), w.x());
float s = phi / (2.0f * pi) + 0.5f;
float t = theta / pi;
float r = sampleBilinear(samples, longSamples, latSamples, s, t);
w = w * r;
}
cout << w.x() << " " << w.y() << " " << w.z() << " "
<< t.x() << " " << t.y() << "\n";
}
}
}
// return the nth triangular number
inline unsigned int trinum(unsigned int n)
{
return (n * (n + 1)) / 2;
}
void triangleMesh(unsigned int subdiv,
unsigned int baseIndex)
{
for (unsigned int i = 0; i < subdiv; i++)
{
for (unsigned int j = 0; j <= i; j++)
{
unsigned int t0 = baseIndex + trinum(i) + j;
unsigned int t1 = baseIndex + trinum(i + 1) + j;
cout << t0 << " " << t1 << " " << t1 + 1 << "\n";
if (j != i)
cout << t0 << " " << t1 + 1 << " " << t0 + 1 << "\n";
}
}
}
int main(int argc, char* argv[])
{
// Get the command line arguments
if (argc != 4)
{
cerr << "Usage: cmodsphere <width> <height> <tessellation>\n";
return 1;
}
if (sscanf(argv[1], "%u", &longSamples) != 1)
{
cerr << "Invalid width\n";
return 1;
}
if (sscanf(argv[2], "%u", &latSamples) != 1)
{
cerr << "Invalid height\n";
return 1;
}
unsigned int subdiv = 0;
if (sscanf(argv[3], "%u", &subdiv) != 1)
{
cerr << "Invalid tessellation level\n";
return 1;
}
samples = new float[latSamples * longSamples];
#ifdef _WIN32
// Enable binary reads for stdin on Windows
_setmode(_fileno(stdin), _O_BINARY);
#endif
// Read the height map
readBinary(cin, latSamples, longSamples);
// Output the mesh header
cout << "#celmodel__ascii\n";
cout << "\n";
cout << "material\n";
cout << "diffuse 0.8 0.8 0.8\n";
cout << "end_material\n";
cout << "\n";
cout << "mesh\n";
cout << "vertexdesc\n";
cout << "position f3\n";
cout << "texcoord0 f2\n";
cout << "end_vertexdesc\n";
cout << "\n";
// Octahedral subdivision; the subdivison level for an a face
// is one fourth the overall tessellation level.
unsigned int primitiveFaces = 8;
subdiv = subdiv / 4;
unsigned int s1 = subdiv + 1;
unsigned int verticesPerPrimFace = (s1 * s1 + s1) / 2;
unsigned int vertexCount = primitiveFaces * verticesPerPrimFace;
unsigned int trianglesPerPrimFace = s1 * s1 - 2 * s1 + 1;
unsigned int triangleCount = primitiveFaces * trianglesPerPrimFace;
cout << "vertices " << vertexCount << "\n";
triangleSection(subdiv,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
Eigen::Vector3f(1.0f, 0.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, -1.0f),
Eigen::Vector2f(0.0f, 0.0f),
Eigen::Vector2f(0.00f, 0.5f),
Eigen::Vector2f(0.25f, 0.5f));
triangleSection(subdiv,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, 1.0f),
Eigen::Vector3f(1.0f, 0.0f, 0.0f),
Eigen::Vector2f(0.0f, 0.0f),
Eigen::Vector2f(0.75f, 0.5f),
Eigen::Vector2f(1.00f, 0.5f));;
triangleSection(subdiv,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
Eigen::Vector3f(-1.0f, 0.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, 1.0f),
Eigen::Vector2f(0.0f, 0.0f),
Eigen::Vector2f(0.50f, 0.5f),
Eigen::Vector2f(0.75f, 0.5f));
triangleSection(subdiv,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, -1.0f),
Eigen::Vector3f(-1.0f, 0.0f, 0.0f),
Eigen::Vector2f(0.0f, 0.0f),
Eigen::Vector2f(0.25f, 0.5f),
Eigen::Vector2f(0.50f, 0.5f));
triangleSection(subdiv,
Eigen::Vector3f(0.0f, -1.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, -1.0f),
Eigen::Vector3f(1.0f, 0.0f, 0.0f),
Eigen::Vector2f(0.0f, 1.0f),
Eigen::Vector2f(0.25f, 0.5f),
Eigen::Vector2f(0.00f, 0.5f));
triangleSection(subdiv,
Eigen::Vector3f(0.0f, -1.0f, 0.0f),
Eigen::Vector3f(1.0f, 0.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, 1.0f),
Eigen::Vector2f(0.0f, 1.0f),
Eigen::Vector2f(1.00f, 0.5f),
Eigen::Vector2f(0.75f, 0.5f));
triangleSection(subdiv,
Eigen::Vector3f(0.0f, -1.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, 1.0f),
Eigen::Vector3f(-1.0f, 0.0f, 0.0f),
Eigen::Vector2f(0.0f, 1.0f),
Eigen::Vector2f(0.75f, 0.5f),
Eigen::Vector2f(0.50f, 0.5f));
triangleSection(subdiv,
Eigen::Vector3f(0.0f, -1.0f, 0.0f),
Eigen::Vector3f(-1.0f, 0.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, -1.0f),
Eigen::Vector2f(0.0f, 1.0f),
Eigen::Vector2f(0.50f, 0.5f),
Eigen::Vector2f(0.25f, 0.5f));
cout << "trilist 0 " << triangleCount * 3 << "\n";
for (unsigned int f = 0; f < primitiveFaces; f++)
{
triangleMesh(subdiv, f * verticesPerPrimFace);
}
cout << "end_mesh\n";
}