/* icosmesh Tool to generate subdivided icosahedron mesh data. */ #import #import "icosmesh.h" #import "JAIcosTriangle.h" #import "JAVertexSet.h" #import "JAIcosMesh.h" #define kFileName "OOPlanetData" #define kLevels 6 /* Coordinates of icosahedron with poles on the Y axis. Based on http://www.csee.umbc.edu/~squire/reference/polyhedra.shtml#icosahedron */ static const Vector kBaseVertices[12] = { { +0.000000000000, +1.000000000000, +0.000000000000 }, { +0.894427200187, +0.447213577125, +0.000000000000 }, { +0.276393205089, +0.447213577125, +0.850650817090 }, { -0.723606805183, +0.447213577125, +0.525731117519 }, { -0.723606805183, +0.447213577125, -0.525731117519 }, { +0.276393205089, +0.447213577125, -0.850650817090 }, { +0.723606805183, -0.447213577125, +0.525731117519 }, { -0.276393205089, -0.447213577125, +0.850650817090 }, { -0.894427200187, -0.447213577125, +0.000000000000 }, { -0.276393205089, -0.447213577125, -0.850650817090 }, { +0.723606805183, -0.447213577125, -0.525731117519 }, { +0.000000000000, -1.000000000000, +0.000000000000 } }; #define kBaseFaceCount 20 static const struct { unsigned a, b, c; } kBaseTriangles[kBaseFaceCount] = { { 0, 1, 2 }, { 0, 2, 3 }, { 0, 3, 4 }, { 0, 4, 5 }, { 0, 5, 1 }, { 11, 6, 7 }, { 11, 7, 8 }, { 11, 8, 9 }, { 11, 9, 10 }, { 11, 10, 6 }, { 1, 2, 6 }, { 2, 3, 7 }, { 3, 4, 8 }, { 4, 5, 9 }, { 5, 1, 10 }, { 6, 7, 2 }, { 7, 8, 3 }, { 8, 9, 4 }, { 9, 10, 5 }, { 10, 6, 1 } }; static NSArray *SubdivideMesh(NSArray *triangles); static void WritePrelude(FILE *header, FILE *source); static void WriteVertices(FILE *header, FILE *source, JAVertexSet *vertices); static void WriteMeshForTriangles(FILE *source, unsigned level, NSArray *triangles, JAVertexSet *vertices, unsigned *faceCount, unsigned *maxVertex); static void WriteMesh(FILE *source, unsigned level, JAIcosMesh *mesh); static const char *SizeEnumeratorForMaximum(unsigned maxValue); static const char *SizeTypeForMaximum(unsigned maxValue); int main (int argc, const char * argv[]) { /* Apparently, someone wants to run this under Linux, so we make an autorelease pool to suppress warnings when running without gc. */ (void)[NSAutoreleasePool new]; FILE *header = fopen(kFileName ".h", "w"); FILE *source = fopen(kFileName ".c", "w"); if (header == NULL || source == NULL) { fprintf(stderr, "Failed to open output files.\n"); return EXIT_FAILURE; } WritePrelude(header, source); // Load up the base triangles. unsigned i; NSMutableArray *baseTriangles = [NSMutableArray arrayWithCapacity:kBaseFaceCount]; for (i = 0; i < kBaseFaceCount; i++) { Vector a = kBaseVertices[kBaseTriangles[i].a]; Vector b = kBaseVertices[kBaseTriangles[i].b]; Vector c = kBaseVertices[kBaseTriangles[i].c]; // printf("%g %g %g %g %g %g %g %g %g\n", a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z); JAIcosTriangle *tri = [JAIcosTriangle triangleWithVectorA:a b:b c:c]; [baseTriangles addObject:tri]; } unsigned faceCount[kLevels]; unsigned maxIndex[kLevels]; JAVertexSet *vertices = [[JAVertexSet alloc] init]; WriteMeshForTriangles(source, 0, baseTriangles, vertices, &faceCount[0], &maxIndex[0]); NSArray *triangles = baseTriangles; for (i = 1; i < kLevels; i++) { triangles = (NSMutableArray *)SubdivideMesh(triangles); WriteMeshForTriangles(source, i, triangles, vertices, &faceCount[i], &maxIndex[i]); } WriteVertices(header, source, vertices); fprintf(source, "\n\nconst OOPlanetDataLevel kPlanetData[kOOPlanetDataLevels] =\n{\n"); for (i = 0; i < kLevels; i++) { if (i != 0) fprintf(source, ",\n"); fprintf(source, "\t{ %u, %u, %s, kFaceIndicesLevel%u }", maxIndex[i], faceCount[i], SizeEnumeratorForMaximum(maxIndex[i]), i); } fprintf(source, "\n};\n"); fclose(header); fclose(source); return EXIT_SUCCESS; } static NSArray *SubdivideMesh(NSArray *triangles) { NSMutableArray *result = [NSMutableArray arrayWithCapacity:triangles.count * 4]; for (JAIcosTriangle *triangle in triangles) { [result addObjectsFromArray:[triangle subdivide]]; } return result; } static void WritePrelude(FILE *header, FILE *source) { fprintf(header, "/*\n" "\t%s.h\n" "\tFor Oolite\n" "\t\n" "\tThis file was automatically generated by tools/icosmesh. Do not modify.\n" "\t\n" "\tThis data may be used freely.\n" "*/\n" "\n" "#include \"OOOpenGLOnly.h\"\n" "\n" "\n" "#define kOOPlanetDataLevels %u\n" "\n" "\n" "typedef struct\n" "{\n" "\tunsigned vertexCount;\n" "\tunsigned faceCount;\n" "\tGLenum type;\n" "\tconst void *indices; // faceCount * 3\n" "} OOPlanetDataLevel;\n" "\n" "\n" "extern const OOPlanetDataLevel kPlanetData[kOOPlanetDataLevels];\n", kFileName, kLevels); fprintf(source, "/*\n" "\t%s.c\n" "\tFor Oolite\n" "\t\n" "\tThis file was automatically generated by tools/icosmesh. Do not modify.\n" "\t\n" "\tThis data may be used freely.\n" "*/\n" "\n" "#include \"OOPlanetData.h\"\n", kFileName); } static void WriteVertices(FILE *header, FILE *source, JAVertexSet *vertices) { unsigned i, count = vertices.count; fprintf(header, "\n\n#define kOOPlanetDataVertexCount %u\n\n", count); fprintf(header, "extern const GLfloat kOOPlanetVertices[kOOPlanetDataVertexCount * 3];\n"); #if OUTPUT_BINORMALS fprintf(header, "extern const GLfloat kOOPlanetBinormals[kOOPlanetDataVertexCount * 3];\n"); #endif fprintf(header, "extern const GLfloat kOOPlanetTexCoords[kOOPlanetDataVertexCount * 2];\n"); fprintf(source, "\n\n/* Shared vertex array\n %u vertices\n*/\nconst GLfloat kOOPlanetVertices[kOOPlanetDataVertexCount * 3] =\n{\n", count); NSArray *data = [vertices positionArray]; for (i = 0; i < count; i++) { if (i != 0) fprintf(source, ",\n"); fprintf(source, "\t%+.8ff, %+.8ff, %+.8ff", [[data objectAtIndex:i * 3] doubleValue], [[data objectAtIndex:i * 3 + 1] doubleValue], [[data objectAtIndex:i * 3 + 2] doubleValue]); } #if OUTPUT_BINORMALS fprintf(source, "\n};\n\n/* Shared binormal array\n %u vectors\n*/\nconst GLfloat kOOPlanetBinormals[kOOPlanetDataVertexCount * 3] =\n{\n", count); data = [vertices binormalArray]; for (i = 0; i < count; i++) { if (i != 0) fprintf(source, ",\n"); fprintf(source, "\t%+.8ff, %+.8ff, %+.8ff", [[data objectAtIndex:i * 3] doubleValue], [[data objectAtIndex:i * 3 + 1] doubleValue], [[data objectAtIndex:i * 3 + 2] doubleValue]); } #endif fprintf(source, "\n};\n\n/* Shared texture coordinate array\n %u pairs\n*/\nconst GLfloat kOOPlanetTexCoords[kOOPlanetDataVertexCount * 2] =\n{\n", count); data = [vertices texCoordArray]; for (i = 0; i < count; i++) { if (i != 0) fprintf(source, ",\n"); fprintf(source, "\t%+.8ff, %+.8ff", [[data objectAtIndex:i * 2] doubleValue], [[data objectAtIndex:i * 2 + 1] doubleValue]); } fprintf(source, "\n};\n"); } static void WriteMeshForTriangles(FILE *source, unsigned level, NSArray *triangles, JAVertexSet *vertices, unsigned *faceCount, unsigned *maxVertex) { JAIcosMesh *mesh = [JAIcosMesh meshWithVertexSet:vertices]; [mesh addTriangles:triangles]; WriteMesh(source, level, mesh); *faceCount = mesh.faceCount; *maxVertex = mesh.maxIndex + 1; } static void WriteMesh(FILE *source, unsigned level, JAIcosMesh *mesh) { unsigned i, count = mesh.faceCount; NSArray *indices = [mesh indexArray]; fprintf(source, "\n\n/* Level %u index array\n %u faces\n*/\nstatic const %s kFaceIndicesLevel%u[%u] =\n{\n", level, count, SizeTypeForMaximum(mesh.maxIndex), level, count * 3); for (i = 0; i < count; i++) { if (i != 0) fprintf(source, ",\n"); fprintf(source, "\t%5u, %5u, %5u", [[indices objectAtIndex:i * 3] unsignedIntValue], [[indices objectAtIndex:i * 3 + 1] unsignedIntValue], [[indices objectAtIndex:i * 3 + 2] unsignedIntValue]); } fprintf(source, "\n};\n"); } // Convert vector to latitude and longitude (or θ and φ). void VectorToCoordsRad(Vector v, double *latitude, double *longitude) { v = VectorNormal(v); double las = v.y; if (las != 1.0f) { double lat = asin(las); double rlac = 1.0 / sqrt(1.0 - las * las); // Equivalent to abs(1/cos(lat)) if (latitude != NULL) *latitude = lat; if (longitude != NULL) { double los = v.x * rlac; double lon = asin(fmin(1.0, fmax(-1.0, los))); // Quadrant rectification. if (v.z < 0.0f) { // We're beyond 90 degrees of longitude in some direction. if (v.x < 0.0f) { // ...specifically, west. lon = -M_PI - lon; } else { // ...specifically, east. lon = M_PI - lon; } } *longitude = lon; } } else { // Straight up, avoid divide-by-zero if (latitude != NULL) *latitude = M_PI / 2.0f; if (longitude != NULL) *longitude = 0.0f; // arbitrary } } void VectorToCoords0_1(Vector v, double *latitude, double *longitude) { VectorToCoordsRad(v, latitude, longitude); if (latitude != NULL) *latitude = 1.0 - (*latitude / M_PI + 0.5); if (longitude != NULL) *longitude = 1.0 - (*longitude / (M_PI * 2.0) + 0.5); } static const char *SizeEnumeratorForMaximum(unsigned maxValue) { if (maxValue <= UCHAR_MAX) return "GL_UNSIGNED_BYTE"; if (maxValue <= USHRT_MAX) return "GL_UNSIGNED_SHORT"; return "GL_UNSIGNED_INT"; } static const char *SizeTypeForMaximum(unsigned maxValue) { if (maxValue <= UCHAR_MAX) return "GLubyte"; if (maxValue <= USHRT_MAX) return "GLushort"; return "GLuint"; }