4

I'm working on generating a planet made out of a hexagon grid. The poles aren't needed - making this a bit easier. Is there a better way to turn the cylinder into a sphere that would have uniform hexagons/triangles?

Here's the desired steps:

  1. Generate a 2D plane of hexagons (ok)
  2. Turn the plane into a Cylinder (ok)
  3. Turn the Cylinder into a Sphere / Geosphere (kind of works)

For step 2, I'm just using Sin and Cos to move the vertices into a circular shape. For step 3, right now I'm just using: vertices[i] = vertices[i].normalized * radius;

Image to visualize the problem as it is currently.

Note that the poles are cut off on purpose. The red parts show what one hexagon mesh looks like. I would have to keep them roughly the same size and orientation since they are used for gameplay and visual elements. Each hex has a list of neighbors and works like a graph basically.

Fattie
  • 27,874
  • 70
  • 431
  • 719
tsiiffi
  • 41
  • 1
  • 2
  • 2
    http://en.wikipedia.org/wiki/Map_projection#Metric_properties_of_maps might be useful to you. It illustrates that you can only preserve some aspects of the planar mesh when you project it onto the sphere, and gives some ideas of what quantities you might want to preserve. For many reasonable combinations there exist suitable map projections. Perhaps you could not go via the cylinder, but start with the net of an icosahedron and end up with a geodesic sphere as [discussed on Math SE](http://stackoverflow.com/q/3031875/1468366). – MvG Mar 18 '15 at 16:29
  • Just to be clear, are you JUST TRYING TO BUILD MESH? In that case, hexagons haven't been used for 40 years - you build mesh with triangles. (Some [notes](http://answers.unity3d.com/answers/305522/view.html).) But for what possible reason would you worry about the mesh - you just click a button to get a perfect mesh sphere in any resolution. – Fattie Feb 04 '16 at 01:47
  • Conversely, do you mean you want ON THE TEXTURE OF THE SPHERE, YOU WANT TO SEE A LOT OF HEXAGONS? Such as you would use in a hexagon-tile game. If this is this case, the *mesh* is utterly irrelevant, you needn't even know what type of mesh is being used. Pls let us know which it is! – Fattie Feb 04 '16 at 01:48
  • all-time classic answer linking to the all-time classic blog post on the matter ... http://stackoverflow.com/a/8427705/294884 AndreasKahler's code has a couple minor errors (well it did years ago) so take care. Also http://answers.unity3d.com/questions/311412/generating-a-grid-on-a-sphere.html and http://answers.unity3d.com/questions/35313/texturing-a-icosphere.html – Fattie Feb 09 '16 at 14:00
  • are you there @tsiiffi – Fattie Feb 15 '16 at 16:30
  • @tsiiffi take a look at this: [Hexagonal tilling of hemi-sphere](http://stackoverflow.com/a/42093868/2521214) with some tweaking it could be may be converted to your map purposes. – Spektre Feb 07 '17 at 15:40

1 Answers1

8

Instead of cylinder to sphere mapping I would do a sphere triangulation...

  1. I would first start with 2 hexagons

    each start at pole and end on the equator or do the half only and mirror the other when all done...

  2. then recursively subdivide the triangles

    so divide lines to half and change the mid point coordinate to align with sphere surface. This will create triangulated sphere. Subdivide to valid number of points to form hexagons and have enough grid points.

  3. change the hexagon mid-point coordinates back to plane of hexagon

    So take the other 6 point and compute average coordinates which gives you the point for middle ...

Something like this:

hexagonation

for more ideas look here:

[edit1] triangulation (no hexagon corrections)

//---------------------------------------------------------------------------
#include <math.h>
#include "list.h"
class mesh
    {
public:
    class _pnt { public: double p[3]; _pnt(){}; _pnt(_pnt& a){ *this=a; }; ~_pnt(){}; _pnt* operator = (const _pnt *a) { *this=*a; return this; }; /*_pnt* operator = (const _pnt &a) { ...copy... return this; };*/ };
    class _fac { public: int i0,i1,i2; _fac(){}; _fac(_fac& a){ *this=a; }; ~_fac(){}; _fac* operator = (const _fac *a) { *this=*a; return this; }; /*_fac* operator = (const _fac &a) { ...copy... return this; };*/ };
    List<_pnt> pnt; // mesh points
    List<_fac> fac; // mesh triangles

    mesh()      {}
    mesh(mesh& a)   { *this=a; }
    ~mesh() {}
    mesh* operator = (const mesh *a) { *this=*a; return this; }
    //mesh* operator = (const mesh &a) { ...copy... return this; }

    void draw();            // draws the mesh with OpenGL
    void sphere(int n);     // init mesh with unit sphere from triangles (n recursion layers)
    };
//---------------------------------------------------------------------------
void mesh::draw()
    {
    int i;
    _fac *f;
    // fill
    glColor3f(0.7,0.7,0.7);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glDepthFunc(GL_LEQUAL);
    glBegin(GL_TRIANGLES);
    for (i=0,f=fac.dat;i<fac.num;i++,f++)
        {
        glVertex3dv(pnt.dat[f->i0].p);
        glVertex3dv(pnt.dat[f->i1].p);
        glVertex3dv(pnt.dat[f->i2].p);
        }
    glEnd();
    // wireframe
    glColor3f(0.1,0.3,0.7);
    glLineWidth(2.0);
    for (i=0,f=fac.dat;i<fac.num;i++,f++)
        {
        glBegin(GL_LINE_LOOP);
        glVertex3dv(pnt.dat[f->i0].p);
        glVertex3dv(pnt.dat[f->i1].p);
        glVertex3dv(pnt.dat[f->i2].p);
        glEnd();
        }
    glLineWidth(1.0);
    }
//---------------------------------------------------------------------------
void mesh::sphere(int n)
    {
    // init 2 hexagons
    int i,j,m,i0,i1,i2,j0,j1,j2;
    double a,da=M_PI/3.0;
    double *p0,*p1;
    _pnt p;
    _fac f,*g;
    p.p[0]= 0.0;
    p.p[1]= 0.0;
    p.p[2]=+1.0;
    pnt.add(p);
    p.p[2]=-1.0;
    pnt.add(p);
    for (i=0,a=0.0;i<6;i++,a+=da)
        {
        p.p[0]=cos(a);
        p.p[1]=sin(a);
        p.p[2]= 0.0;
        pnt.add(p);
        }
    // top half
    f.i0=0; f.i1=2; f.i2=3; fac.add(f);
    f.i0=0; f.i1=3; f.i2=4; fac.add(f);
    f.i0=0; f.i1=4; f.i2=5; fac.add(f);
    f.i0=0; f.i1=5; f.i2=6; fac.add(f);
    f.i0=0; f.i1=6; f.i2=7; fac.add(f);
    f.i0=0; f.i1=7; f.i2=2; fac.add(f);
    // botom half
    f.i0=1; f.i1=3; f.i2=2; fac.add(f);
    f.i0=1; f.i1=4; f.i2=3; fac.add(f);
    f.i0=1; f.i1=5; f.i2=4; fac.add(f);
    f.i0=1; f.i1=6; f.i2=5; fac.add(f);
    f.i0=1; f.i1=7; f.i2=6; fac.add(f);
    f.i0=1; f.i1=2; f.i2=7; fac.add(f);
    // subdivide triangles
    for (;n>0;n--)              // recursion layers
     for (m=fac.num,i=0;i<m;i++)// scan through all original faces
        {
        g=&fac[i];
        // point indexes
        i0=g->i0; j0=pnt.num;   //     i0
        i1=g->i1; j1=j0+1;      //   j0  j2
        i2=g->i2; j2=j0+2;      // i1  j1  i2
        // genere mid points + sphere surface correction distance from (0,0,0) must be 1.0 (radius)
        for (j=0;j<3;j++) p.p[j]=0.5*(pnt[i0].p[j]+pnt[i1].p[j]); a=1.0/sqrt((p.p[0]*p.p[0])+(p.p[1]*p.p[1])+(p.p[2]*p.p[2])); for (j=0;j<3;j++) p.p[j]*=a; pnt.add(p);
        for (j=0;j<3;j++) p.p[j]=0.5*(pnt[i1].p[j]+pnt[i2].p[j]); a=1.0/sqrt((p.p[0]*p.p[0])+(p.p[1]*p.p[1])+(p.p[2]*p.p[2])); for (j=0;j<3;j++) p.p[j]*=a; pnt.add(p);
        for (j=0;j<3;j++) p.p[j]=0.5*(pnt[i2].p[j]+pnt[i0].p[j]); a=1.0/sqrt((p.p[0]*p.p[0])+(p.p[1]*p.p[1])+(p.p[2]*p.p[2])); for (j=0;j<3;j++) p.p[j]*=a; pnt.add(p);
        // change original fac
        g->i0=j0; g->i1=j1; g->i2=j2;
        //  add 3 x fac
        f.i0=i0; f.i1=j0; f.i2=j2; fac.add(f);
        f.i0=j0; f.i1=i1; f.i2=j1; fac.add(f);
        f.i0=j2; f.i1=j1; f.i2=i2; fac.add(f);
        }
    }
//---------------------------------------------------------------------------

Was a bit curious so I tried to encode this usage is simple:

mesh obj;       // somewhere global...
obj.sphere(3); // init (call once or on change of n...) 
obj.draw();    // inside your gl draw scene routine/event...

So here is the result overview

result

top and bottom poles looks good enough, some distortion is present along the equators but partially it can be caused also by fish-eye. If the initial shape is feed with closer to wanted result start geometry then it may have much better results

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 1
    Great work! It looks like this still causes some pentagons (I guess it's a mathematical fact and can't be avoided). Looks like triangulation is a better starting point than mapping the cylinder. – tsiiffi Mar 19 '15 at 15:07
  • Dumb question; would it be possible to shift all the pentagons to the poles somehow? Then I could cut the poles off and make that are unaccessible. – tsiiffi Mar 19 '15 at 15:24
  • @tsiiffi I don't think so because you need different hexagon count per layers (least on poles, most on equator) and there is no way to do that with hexagons only. so you need at least one pentagon per single hexagon count change per layer and that you can move only along longitude angle ... – Spektre Mar 19 '15 at 16:34
  • But this is for Unity3D, Spektre – Fattie Feb 04 '16 at 01:50
  • Great answer @Spektre - Why isn't this answer accepted? – Reblochon Masque Dec 05 '20 at 03:30
  • 1
    @ReblochonMasque thx, well the user is offline since ... but I am used to not accepted (even HQ) answers its not that uncommon in here... Also if you want this for better sphere use icosahedron as start instead (this was developed to better suite OP case). I got a number of different related Answers to this topic (2 are linked in this answer) but over the years there are few new ones like [cube/sphere mapping](https://stackoverflow.com/a/65050436/2521214) , [Hyper spiral a](https://stackoverflow.com/a/57240140/2521214) , [Hyper spiral b](https://stackoverflow.com/a/62754601/2521214) – Spektre Dec 05 '20 at 07:08