# Generating a Gsphere

This topic is 2782 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey guys. I've just started one of my assignments for in my final year of university and I'm in need of some help with creating a gsphere. Part of my project I'm working on is creating some procedural planet generation. I've been looking at the way this great tutorial shows a simple way to make Spherical Landscapes - http://freespace.virgin.net/hugo.elias/models/m_landsp.htm In a paragraph he mentions that gspheres are a much better better choice for this problem. I've created what I think is called an lsphere with the help of some tutorials, but I've no clue on how to even create a gsphere. I've tried looking for any example of some with no luck. Another problem that lies is I'll need to create UV and normal coordinates for the gsphere as well. Heres how I've created the current sphere I'm using which works quite well but the textures pack up at the poles and it looks a little messy.
void Sphere::BuildSimpleSphere()
{
float phiStep = PI / m_NumStacks;

// Don't count the poles as rings.
int numRings = m_NumStacks - 1;

// Compute Vertices for each stack ring.
for( int i = 1; i <= numRings; ++i )
{
float phi = i * phiStep;

// Vertices of ring
float thetaStep = 2.0f * PI / m_NumSlices;
for( int j = 0; j <= m_NumSlices; ++j )
{
float theta = j * thetaStep;

// Need to possibly fix up the normal and texture co-ords because of  - (m_Radius/2) due to centering the sphere
NiPoint3 pos;
pos.z = (m_Radius * sinf(phi) * sinf(theta));

NiPoint3 normal = pos;
normal.UnitizeVector(normal);

NiPoint2 uv;
uv.x = theta / (2.0f * PI);
uv.y = phi / PI;

m_Vertices.push_back(pos);
m_Normals.push_back(normal);
m_UVs.push_back(uv);
}
}

// Poles: note that there will be texture coordinate distortion

m_Normals.push_back( NiPoint3(0.0f, -1.0f, 0.0f) );
m_Normals.push_back( NiPoint3(0.0f, 1.0f, 0.0f) );

m_UVs.push_back( NiPoint2(0.0f, 1.0f) );
m_UVs.push_back( NiPoint2(0.0f, 0.0f) );

int northPoleIndex = (int)m_Vertices.size() - 1;
int southPoleIndex = (int)m_Vertices.size() - 2;

int numRingVertices = m_NumSlices + 1;

// Compute indices for inner stacks (not connected to poles).
for(int i = 0; i < m_NumStacks - 2; ++i)
{
for(int j = 0; j < m_NumSlices; ++j)
{
m_Indices.push_back(i * numRingVertices + j);
m_Indices.push_back(i * numRingVertices + j + 1);
m_Indices.push_back((i + 1) * numRingVertices + j);

m_Indices.push_back((i + 1)  *numRingVertices + j);
m_Indices.push_back(i * numRingVertices + j + 1);
m_Indices.push_back((i + 1)  *numRingVertices + j + 1);
}
}

// Compute indices for top stack.  The top stack was written
// first to the vertex buffer.
for(int i = 0; i < m_NumSlices; ++i)
{
m_Indices.push_back(northPoleIndex);
m_Indices.push_back(i  +1);
m_Indices.push_back(i);
}

// Compute indices for bottom stack.  The bottom stack was written
// last to the vertex buffer, so we need to offset to the index
// of first vertex in the last ring.
int baseIndex = (numRings - 1) * numRingVertices;
for(UINT i = 0; i < m_NumSlices; ++i)
{
m_Indices.push_back(southPoleIndex);
m_Indices.push_back(baseIndex + i);
m_Indices.push_back(baseIndex + i + 1);
}
}

If anyone can show me how to create one for my situation it would be greatly appreciated as it's driving me a little stir crazy. Thanks a heap, Scott.

##### Share on other sites
Tbh, I never heard of the terms gsphere and lsphere (just g-strings), but you can get away without slices and without the singularities at the poles relatively trivial by creating 6 rectangles (or simply: a cube), and normalizing each vertex.

This gave me awesome results once I experimented with procedural planets.

##### Share on other sites
gsphere is an abbreviation for geosphere, which is short for a geodesic sphere.

##### Share on other sites
I have no idea what an G-Sphere is, but it could be a geodesic sphere? If so, here is a good tutorial on how to construct them: Make a geodesic sphere.

##### Share on other sites
Thanks for the reply guys. It looks like the geodesic sphere is what I'm looking for. Only problem is I'm not perfect with some 3D stuff yet. So if anyone has some tutorials on how to generate one of these it would be greatly appreciated. I always get stuck on setting up things like the indices.

The VB one seems a little funky? Thanks for that great blog on the person who is building this crazy planet generator. I'm reading that at the moment.

##### Share on other sites
Quote:
 Original post by ScottehyThanks for the reply guys. It looks like the geodesic sphere is what I'm looking for. Only problem is I'm not perfect with some 3D stuff yet. So if anyone has some tutorials on how to generate one of these it would be greatly appreciated. I always get stuck on setting up things like the indices. The VB one seems a little funky? Thanks for that great blog on the person who is building this crazy planet generator. I'm reading that at the moment.
Indexing is always the hardest part. I say dig yourself into it, so that if you face an other indexing nightmare again, it will be much easier to solve. But a piece of paper and a pen can do wonders.

##### Share on other sites
The usual method for a geosphere is starting off with an icosahedron, and doing recursive subdivisions on each triangles by joining the midpoint of the edges. That's the closest you can get to a perfectly trianglulated sphere (which is actually mathematically impossible).

##### Share on other sites
Thanks oliii, trouble is I'm having an issue wrapping it around my head how to generate one. The tutorials I've been looking at for them are just confusing and seem a little over complex. Would you possibly have any tutorial sites on them that aren't to bad?

##### Share on other sites
I'll see if I can dig up an old, old demo, and clean it up!

##### Share on other sites
Awesome, thank you :)

##### Share on other sites
I've lost my gdnet+, so I can't access my web storage, but here goes...

#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <math.h>#include <vector>#include <gl/glut.h>float width  = 640;float height = 480;float elevation = 0;float rotation	= 0;int level = 0;int zoom = 5;bool keyLeft=false;bool keyRight=false;bool keyUp=false;bool keyDown=false;void glutKeyboard(unsigned char key, int x, int y){	if (key == 27)		exit(0);	if(key == '-')	{		if(level > 0) level--;	}	if(key == '+' || key == '=')	{		if(level < 10) level++;	}}void glutKeyboardUp(unsigned char key, int x, int y){}void glutSpecial(int key, int x, int y){	if(key == GLUT_KEY_DOWN)		keyDown = true;	if(key == GLUT_KEY_UP)		keyUp = true;	if(key == GLUT_KEY_LEFT)		keyLeft = true;	if(key == GLUT_KEY_RIGHT)		keyRight = true;}void glutMouse(int button, int state, int x, int y){	if(state == GLUT_DOWN && button == GLUT_LEFT_BUTTON && level > 0)		level--;		if(state == GLUT_DOWN && button == GLUT_RIGHT_BUTTON && level < 6)		level++;	if(state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON)		zoom = (zoom + 1) % 10;}void glutSpecialUp(int key, int x, int y){	if(key == GLUT_KEY_DOWN)		keyDown = false;	if(key == GLUT_KEY_UP)		keyUp = false;	if(key == GLUT_KEY_LEFT)		keyLeft = false;	if(key == GLUT_KEY_RIGHT)		keyRight = false;}void glutPassiveMotion(int x, int y){	rotation  += (x - width / 2) / 100.0f;	elevation += (y - height / 2) / 100.0f;}void glutDisplay(){	glutWarpPointer(width / 2, height / 2);	if(keyLeft)		rotation  += 0.03f;	if(keyRight)	rotation  -= 0.03f;	if(keyDown)		elevation += 0.03f;	if(keyUp)		elevation -= 0.03f;	float cos_rot = cos(rotation);	float sin_rot = sin(rotation);	float cos_elv = cos(elevation);	float sin_elv = sin(elevation);	float eye[3];	float dir[3];	float side[3];	float up[3];	side[0] = -sin_rot; 	side[1] = 0.0f;	side[2] = cos_rot;	dir[0] = cos_rot * cos_elv; 	dir[1] = sin_elv;	dir[2] = sin_rot * cos_elv;	up[0] = (side[1] * dir[2]) - (side[2] * dir[1]);	up[1] = (side[2] * dir[0]) - (side[0] * dir[2]);	up[2] = (side[0] * dir[1]) - (side[1] * dir[0]);		float distance = 6.25f - (zoom * 0.5f);	eye[0] = dir[0] * distance;	eye[1] = dir[1] * distance;	eye[2] = dir[2] * distance;		glClearColor(0.2f, 0.2f, 0.2f, 0.2f);	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	glMatrixMode(GL_PROJECTION);	glLoadIdentity();	gluPerspective(75.0f, 4 / 3.0f, 0.1f, 1000.0f);	glMatrixMode(GL_MODELVIEW);	glLoadIdentity();	gluLookAt(eye[0], eye[1], eye[2], 0, 0, 0, up[0], up[1], up[2]);	glPushMatrix();	extern void renderGeosphere(unsigned int level);	renderGeosphere(level);	glPopMatrix();	glutSwapBuffers();}void glutTimer(int t){	glutDisplay();	glutTimerFunc(t, glutTimer, (int) 500.0f / 60.0f);}	void glutReshape(int w, int h){	width  = w;	height = h;	glViewport(	0, 0, w, h);}void main(int argc, char** argv){    glutInit( &argc, argv );	glutInitDisplayMode		(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);		glutInitWindowSize		(width, height);	glutInitWindowPosition	(0, 0);	glutCreateWindow		("geosphere");		glPointSize				(3.0f);	glEnable				(GL_BLEND);	glBlendFunc				(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	glEnable				(GL_CULL_FACE);		glutPassiveMotionFunc	(glutPassiveMotion);	glutKeyboardFunc		(glutKeyboard);	glutKeyboardUpFunc		(glutKeyboardUp);	glutSpecialFunc			(glutSpecial);	glutSpecialUpFunc		(glutSpecialUp);	glutMouseFunc			(glutMouse);	glutDisplayFunc			(glutDisplay);	glutReshapeFunc			(glutReshape);	glutTimerFunc			(0, glutTimer, (int) 100.0f / 60.0f);	glutIgnoreKeyRepeat		(true);	// light init	GLfloat mat_ambient[] = { 1.0, 1.0, 1.0, 1.0 };	GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };	GLfloat light_position[] = { 0.0, -10.0, -10.0, 1.0 };	GLfloat lm_ambient[] = { 0.3, 0.3, 0.3, 1.0 };	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);	glMaterialf(GL_FRONT, GL_SHININESS, 50.0);	glLightfv(GL_LIGHT0, GL_POSITION, light_position);	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lm_ambient);	glEnable(GL_LIGHTING);	glEnable(GL_LIGHT0);	glEnable(GL_DEPTH_TEST);	glShadeModel(GL_SMOOTH);	glutMainLoop			();}struct Vector{	Vector(){}	Vector(float ix, float iy, float iz) : x(ix), y(iy), z(iz){}	Vector operator+(const Vector& other) const { return Vector(x + other.x, y + other.y, z + other.z); }	Vector operator-(const Vector& other) const { return Vector(x - other.x, y - other.y, z - other.z); }	Vector operator*(const float& scalar) const { return Vector(x * scalar, y * scalar, z * scalar); }	Vector operator^(const Vector& other) const { return Vector((y * other.z) - (z * other.y), (z * other.x) - (x * other.z), (x * other.y) - (y * other.x)); }	void normalise() { float l = sqrt(x*x+y*y+z*z); x /= l; y /= l; z /= l; }	float x, y, z;};struct Mesh{	Mesh()	{		m_displayList = GL_INVALID_VALUE;	}	~Mesh()	{		if(glIsList(m_displayList))			glDeleteLists(m_displayList, 1);	}	void render() const	{		if(glIsList(m_displayList))			glCallList(m_displayList);	}	bool buildDisplayList()	{		if(glIsList(m_displayList))			glDeleteLists(m_displayList, 1);				m_displayList = glGenLists(1);				if(m_displayList == GL_INVALID_VALUE)			return false;		glNewList(m_displayList, GL_COMPILE);		const Vector* vertices = &(m_vertices[0]);		unsigned int triangleCount = m_vertices.size() / 3;		glColor4f(1.0f, 0.0f, 0.0f, 0.5f);		glBegin(GL_TRIANGLES);		for(unsigned int i = 0; i < triangleCount; i ++)		{			const Vector& v0 =  vertices[i * 3 + 0];			const Vector& v1 =  vertices[i * 3 + 1];			const Vector& v2 =  vertices[i * 3 + 2];			Vector n = (v2 - v0) ^ (v1 - v0);			n.normalise();			glNormal3f(n.x, n.y, n.z);			glVertex3f(v0.x, v0.y, v0.z);			glVertex3f(v1.x, v1.y, v1.z);			glVertex3f(v2.x, v2.y, v2.z);		}		glEnd();		glEndList();		return true;	}	GLuint m_displayList;	std::vector<Vector> m_vertices;};Mesh* icosahedron(){	static const float a = (float) sqrt(2.0f/(5.0f + sqrt(5.0f)));	static const float b = (float) sqrt(2.0f/(5.0f - sqrt(5.0f)));	static Vector vertices[12] = 	{		Vector(-a, 0.0f, b), Vector(a, 0.0f, b), Vector(-a, 0.0f, -b), Vector(a, 0.0f, -b),		Vector(0.0f, b, a), Vector(0.0f, b, -a), Vector(0.0f, -b, a), Vector(0.0f, -b, -a),		Vector(b, a, 0.0f), Vector(-b, a, 0.0f), Vector(b, -a, 0.0f), Vector(-b, -a, 0.0f)	};	static unsigned short triangles[20][3] = 	{		{ 1,  4, 0 }, {  4, 9, 0 }, { 4, 5,  9 }, { 8, 5,  4 }, { 1, 8,  4 },		{ 1, 10, 8 }, { 10, 3, 8 }, { 8, 3,  5 }, { 3, 2,  5 }, { 3, 7,  2 },		{ 3, 10, 7 }, { 10, 6, 7 }, { 6, 11, 7 }, { 6, 0, 11 }, { 6, 1,  0 },		{ 10, 1, 6 }, { 11, 0, 9 }, { 2, 11, 9 }, { 5, 2,  9 }, { 11, 2, 7 }	};	Mesh* mesh = new Mesh;	for(int i = 0; i < 20; i ++)	{		int v0 = triangles[i][0];		int v1 = triangles[i][1];		int v2 = triangles[i][2];		mesh->m_vertices.push_back(vertices[v0]);		mesh->m_vertices.push_back(vertices[v1]);		mesh->m_vertices.push_back(vertices[v2]);	}	mesh->buildDisplayList();	return mesh;}Mesh* lod(const Mesh* geosphere){	Mesh* mesh = new Mesh;	for(unsigned int i = 0; i < geosphere->m_vertices.size() / 3; i ++)	{		const Vector& v0 = geosphere->m_vertices[i*3 + 0];		const Vector& v1 = geosphere->m_vertices[i*3 + 1];		const Vector& v2 = geosphere->m_vertices[i*3 + 2];		Vector m0 = (v0 + v1) * 0.5f;		Vector m1 = (v1 + v2) * 0.5f;		Vector m2 = (v2 + v0) * 0.5f;				m0.normalise();		m1.normalise();		m2.normalise();		mesh->m_vertices.push_back(v0);		mesh->m_vertices.push_back(m0);		mesh->m_vertices.push_back(m2);		mesh->m_vertices.push_back(m0);		mesh->m_vertices.push_back(v1);		mesh->m_vertices.push_back(m1);		mesh->m_vertices.push_back(m2);		mesh->m_vertices.push_back(m1);		mesh->m_vertices.push_back(v2);		mesh->m_vertices.push_back(m0);		mesh->m_vertices.push_back(m1);		mesh->m_vertices.push_back(m2);	}	mesh->buildDisplayList();	return mesh;}std::vector<const Mesh*> m_lods;void renderGeosphere(unsigned int level){	while(level >= m_lods.size())	{		if(m_lods.size() == 0)		{			m_lods.push_back(icosahedron());		}		else		{			m_lods.push_back(lod(m_lods.back()));		}			}	GLfloat torus_diffuse[] = { 0.7, 0.7, 0.0, 1.0 };	GLfloat cube_diffuse[] = { 0.0, 0.7, 0.7, 1.0 };	GLfloat sphere_diffuse[] = { 0.7, 0.0, 0.7, 1.0 };	GLfloat octa_diffuse[] = { 0.7, 0.4, 0.4, 1.0 };	//glMaterialfv(GL_FRONT, GL_DIFFUSE, torus_diffuse);	glMaterialfv(GL_FRONT, GL_DIFFUSE, cube_diffuse);	//glMaterialfv(GL_FRONT, GL_DIFFUSE, sphere_diffuse);	//glMaterialfv(GL_FRONT, GL_DIFFUSE, octa_diffuse);	m_lods[level]->render();}

in particular, this is the icosahedron generation

Mesh* icosahedron(){	static const float a = (float) sqrt(2.0f/(5.0f + sqrt(5.0f)));	static const float b = (float) sqrt(2.0f/(5.0f - sqrt(5.0f)));	static Vector vertices[12] = 	{		Vector(-a, 0.0f, b), Vector(a, 0.0f, b), Vector(-a, 0.0f, -b), Vector(a, 0.0f, -b),		Vector(0.0f, b, a), Vector(0.0f, b, -a), Vector(0.0f, -b, a), Vector(0.0f, -b, -a),		Vector(b, a, 0.0f), Vector(-b, a, 0.0f), Vector(b, -a, 0.0f), Vector(-b, -a, 0.0f)	};	static unsigned short triangles[20][3] = 	{		{ 1,  4, 0 }, {  4, 9, 0 }, { 4, 5,  9 }, { 8, 5,  4 }, { 1, 8,  4 },		{ 1, 10, 8 }, { 10, 3, 8 }, { 8, 3,  5 }, { 3, 2,  5 }, { 3, 7,  2 },		{ 3, 10, 7 }, { 10, 6, 7 }, { 6, 11, 7 }, { 6, 0, 11 }, { 6, 1,  0 },		{ 10, 1, 6 }, { 11, 0, 9 }, { 2, 11, 9 }, { 5, 2,  9 }, { 11, 2, 7 }	};	Mesh* mesh = new Mesh;	for(int i = 0; i < 20; i ++)	{		int v0 = triangles[i][0];		int v1 = triangles[i][1];		int v2 = triangles[i][2];		mesh->m_vertices.push_back(vertices[v0]);		mesh->m_vertices.push_back(vertices[v1]);		mesh->m_vertices.push_back(vertices[v2]);	}	mesh->buildDisplayList();	return mesh;}

and this is the tesselation
Mesh* lod(const Mesh* geosphere){	Mesh* mesh = new Mesh;	for(unsigned int i = 0; i < geosphere->m_vertices.size() / 3; i ++)	{		const Vector& v0 = geosphere->m_vertices[i*3 + 0];		const Vector& v1 = geosphere->m_vertices[i*3 + 1];		const Vector& v2 = geosphere->m_vertices[i*3 + 2];		Vector m0 = (v0 + v1) * 0.5f;		Vector m1 = (v1 + v2) * 0.5f;		Vector m2 = (v2 + v0) * 0.5f;				m0.normalise();		m1.normalise();		m2.normalise();		mesh->m_vertices.push_back(v0);		mesh->m_vertices.push_back(m0);		mesh->m_vertices.push_back(m2);		mesh->m_vertices.push_back(m0);		mesh->m_vertices.push_back(v1);		mesh->m_vertices.push_back(m1);		mesh->m_vertices.push_back(m2);		mesh->m_vertices.push_back(m1);		mesh->m_vertices.push_back(v2);		mesh->m_vertices.push_back(m0);		mesh->m_vertices.push_back(m1);		mesh->m_vertices.push_back(m2);	}	mesh->buildDisplayList();	return mesh;}

##### Share on other sites
So basically, each level of detail is a tesselation of the previous level, where each triangle is divided into 4 smaller triangles, by taking the midpoint of the edges.

This means that each level of detail has 4 times the triangle (and vertex) count as the previous level of detail. The lowest level of detail is just the icosahedron.

It can't be any simpler! :)

##### Share on other sites
I'd have suggested oliii's solution if he hadn't beaten me to it. ;-) Let me just throw out that, although it's probably easiest to just hard-code the icosahedron vertices as oliii did, they can also be computed as the intersection of a sphere with a system of equiangular lines (which you can generate iteratively). This fact probably isn't very useful in three dimensions where it's easy to hard-code the answer, but it might be useful if you were ever in need of general-purpose n-dimensional geosphere-generation code.

##### Share on other sites
Thanks a heap oliii, I'll be having a play around with this to see how it works, Thank you so much for digging that up. Thanks for the tip as well Emergent, with a username like that would you happen to use gamebryo?

##### Share on other sites
One fun thing with geospheres is texturing. In this particular case, your texture needs to be split into triangles to be mapped on each icosahedron faces.

##### Share on other sites
Quote:
 Original post by ScottehyEmergent, with a username like that would you happen to use gamebryo?

Nah, no connection; I chose the name before gamebryo came out, at a time when I was interested in emergent behaviors; the idea was that I, this being, was himself "just" the emergent behavior of a collection of cells/molecules/atoms/... (it's turtles all the way down?)

Basically, it was much pop-academic philosophical silliness. ;-)

But the name remains. Still, it beats my old one...

(I'm sure that's way more of an answer than you were looking for, so I'll call this an ending.)

##### Share on other sites
Thanks a heap oliii, you've done more than I could ask for. I didn't realize the textures had to be put into triangles like that though. Thats really weird haha. Thanks a heap :) - that article as well looks a great read. So i'm going to read through that now.

##### Share on other sites
Hmm yes, I haven;t tested the texture mapping though, so I could be wrong. Maybe a simple spherical mapping would do the trick as well.

##### Share on other sites
Noooooo, I want the bear back!!

##### Share on other sites
Yeah I'm not to sure. When I get around to that part of my assignment I'm sure I'll post on here letting someone know :)

##### Share on other sites

This topic is 2782 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628645
• Total Posts
2984023

• 9
• 9
• 9
• 10
• 21