This post will let me store some useful code and possibly share it with others. I had originally found this wonderful explanation on stack overflow on how to generate triangles in a super simple manner for a nice sphere mesh.

The idea is to take an octohedron, or icosahedron, and subdivide the triangles on the surface of the mesh, such that each triangle creates 4 new triangles (each triangle creates a Triforce symbol).

One thing the stack overflow page didn’t describe is intermittent normalization between each subdivision. If you imagine subdividing an octohedron over and over without any normalization, the final resulting sphere will have triangles that vary in size quite a bit. However, if after each subdivision every single vertex is normalized then the vertices will snap to the unit sphere more often. This results in a final mesh that has triangles of closer to uniform geodesic area.

The final mesh isn’t purely geodesic and there will be variation in the size of the triangles, but it will be hardly noticeable. Sphere meshes will look super nice and also behave well when simulating soft bodies with Matyka’s pressure volume.

Here’s an example program you can use to perform some subdivisions upon an octohedron (click here to view the program’s output):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
#include <cstdio> #include <cmath> #include <cassert> struct Vec3 { Vec3( ) { } Vec3( float x0, float y0, float z0 ) { x = x0; y = y0; z = z0; } float x; float y; float z; const Vec3 operator+( const Vec3& a ) { Vec3 v; v.x = x + a.x; v.y = y + a.y; v.z = z + a.z; return v; } const Vec3 operator-( const Vec3& a ) { Vec3 v; v.x = x - a.x; v.y = y - a.y; v.z = z - a.z; return v; } const Vec3 operator*( float a ) const { Vec3 v; v.x = x * a; v.y = y * a; v.z = z * a; return v; } }; float Dot( Vec3 a, Vec3 b ) { return a.x * b.x + a.y * b.y + a.z * b.z; } inline const Vec3 Cross( const Vec3& a, const Vec3& b ) { return Vec3( (a.y * b.z) - (b.y * a.z), (b.x * a.z) - (a.x * b.z), (a.x * b.y) - (b.x * a.y) ); } const Vec3 Normalize( Vec3 v ) { return v * (1.0f / std::sqrt( Dot( v, v ) )); } const int stackSize = 1024 * 8; Vec3 in[ stackSize ]; int ip = 0; Vec3 out[ stackSize ]; int op = 0; Vec3 octohedron[ 6 ]; void Subdivide( void ) { op = 0; for ( int i = 0; i < ip; i += 3 ) { Vec3 a = in[ i ]; Vec3 b = in[ i + 1 ]; Vec3 c = in[ i + 2 ]; Vec3 ab = (a + b) * 0.5f; Vec3 bc = (b + c) * 0.5f; Vec3 ca = (c + a) * 0.5f; out[ op++ ] = b; out[ op++ ] = bc; out[ op++ ] = ab; out[ op++ ] = c; out[ op++ ] = ca; out[ op++ ] = bc; out[ op++ ] = a; out[ op++ ] = ab; out[ op++ ] = ca; out[ op++ ] = ab; out[ op++ ] = bc; out[ op++ ] = ca; assert( op <= stackSize ); } for ( int i = 0; i < op; ++i ) in[ i ] = Normalize( out[ i ] ); ip = op; } int main( ) { octohedron[ 0 ] = Vec3( 1.0f, 0.0f, 0.0f ); octohedron[ 1 ] = Vec3( 0.0f,-1.0f, 0.0f ); octohedron[ 2 ] = Vec3(-1.0f, 0.0f, 0.0f ); octohedron[ 3 ] = Vec3( 0.0f, 1.0f, 0.0f ); octohedron[ 4 ] = Vec3( 0.0f, 0.0f, 1.0f ); octohedron[ 5 ] = Vec3( 0.0f, 0.0f,-1.0f ); in[ ip++ ] = octohedron[ 2 - 1 ]; in[ ip++ ] = octohedron[ 1 - 1 ]; in[ ip++ ] = octohedron[ 5 - 1 ]; in[ ip++ ] = octohedron[ 3 - 1 ]; in[ ip++ ] = octohedron[ 2 - 1 ]; in[ ip++ ] = octohedron[ 5 - 1 ]; in[ ip++ ] = octohedron[ 4 - 1 ]; in[ ip++ ] = octohedron[ 3 - 1 ]; in[ ip++ ] = octohedron[ 5 - 1 ]; in[ ip++ ] = octohedron[ 1 - 1 ]; in[ ip++ ] = octohedron[ 4 - 1 ]; in[ ip++ ] = octohedron[ 5 - 1 ]; in[ ip++ ] = octohedron[ 1 - 1 ]; in[ ip++ ] = octohedron[ 2 - 1 ]; in[ ip++ ] = octohedron[ 6 - 1 ]; in[ ip++ ] = octohedron[ 2 - 1 ]; in[ ip++ ] = octohedron[ 3 - 1 ]; in[ ip++ ] = octohedron[ 6 - 1 ]; in[ ip++ ] = octohedron[ 3 - 1 ]; in[ ip++ ] = octohedron[ 4 - 1 ]; in[ ip++ ] = octohedron[ 6 - 1 ]; in[ ip++ ] = octohedron[ 4 - 1 ]; in[ ip++ ] = octohedron[ 1 - 1 ]; in[ ip++ ] = octohedron[ 6 - 1 ]; Subdivide( ); Subdivide( ); FILE* fp = fopen( "out.txt", "w" ); for ( int i = 0; i < ip; i += 3 ) { Vec3 a = in[ i ]; Vec3 b = in[ i + 1 ]; Vec3 c = in[ i + 2 ]; fprintf( fp, "%7.4f, %7.4f, %7.4f,\n%7.4f, %7.4f, %7.4f,\n%7.4f, %7.4f, %7.4f,\n\n", a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z ); } fprintf( fp, "%d\n", op / 3 ); for ( int i = 0; i < ip; i += 3 ) { Vec3 a = in[ i ]; Vec3 b = in[ i + 1 ]; Vec3 c = in[ i + 2 ]; Vec3 n = Normalize( Cross( b - a, c - a ) ); fprintf( fp, "%7.4f, %7.4f, %7.4f,\n", n.x, n.y, n.z ); } fclose( fp ); } |