Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎲 GameMaker

Plotting objects on a sphere....

Started by keyofrassilon Aug 22, 2005 at 10:14 PM 0 replies 650+ views
Original Post
keyofrassilon
keyofrassilon
Could someone that has access to the search capabilities of this forum conjure up all the relevant posts on the subject or point me in the right direction here.... I am currently using glRotate *shudders* to try and place an object (more a billboard) of a tree on the surface of a sphere...I tried using the normals that I calculated and those didnt give accurate results....or I was going about it all wrong....Currently I am using something along these lines...

angleX1 = RADTODEG(double(j) * PI / (double(n)/2));
angleX2 = RADTODEG(double(j+1) * PI / (double(n)/2));
angleY1 = RADTODEG(double(i) * TWOPI / double(n))-90;
angleY2 = RADTODEG(double(i+1) * TWOPI / double(n))-90;

I split my array build up with two for loops i and j in the update and in the render output it obviously...The actual terrain, texturing and normals are all correct but the objects when placed offset one of the x, y or z planes start to lean dramatically the further away from each of these planes...I suppose glRotate is the problem and I should possibly build a vertex buffer of billboards instead of a single display list or static vertex buffer rotated each time? The actual class in its entirety is as listed below:

#include "sphere.h"
#include "stellartrader.h"
#include "gl_graph.h"
#include "perlin.h"

char debugText[255];

float obj_billboard_norm[12][3] = {0.7071, 0.0000, 0.7071,
		0.7071, 0.0000, 0.7071,0.7071, 0.0000, 0.7071,
		0.7071, 0.0000, 0.7071,0.7071, 0.0000, 0.7071,
		0.7071, 0.0000, 0.7071,-0.7071, 0.0000, 0.7071,
		-0.7071, 0.0000, 0.7071,-0.7071, 0.0000, 0.7071,
		-0.7071, 0.0000, 0.7071,-0.7071, 0.0000, 0.7071,
		-0.7071, 0.0000, 0.7071};

float obj_billboard_vert[12][3] = {-0.3536, 1.0000, 0.3536,
		-0.3536, 0.0000, 0.3536,0.3536, 1.0000, -0.3536,
		0.3536, 0.0000, -0.3536,0.3536, 1.0000, -0.3536,
		-0.3536, 0.0000, 0.3536,-0.3536, 1.0000, -0.3536,
		-0.3536, 0.0000, -0.3536,0.3536, 1.0000, 0.3536,
		0.3536, 0.0000, 0.3536,0.3536, 1.0000, 0.3536,
		-0.3536, 0.0000, -0.3536};

float obj_billboard_tex[12][3] = {0.9995, 0.9995,
		0.9995, 0.0005,0.0005, 0.9995,
		0.0005, 0.0005,0.0005, 0.9995,
		0.9995, 0.0005,0.0000, 1.0000,
		0.0000, 0.0000,1.0000, 1.0000,
		1.0000, 0.0000,1.0000, 1.0000,
		0.0000, 0.0000};


Sphere::Sphere(int n, int bwidth, int bheight) {
	normals.reserve(n * n * 4);
	texes.reserve(n * n * 4);
	verts.reserve(n * n * 4);
	colors.reserve(n * n * 4);
	objnormals.reserve(n * n * 4);
	objverts.reserve(n * n * 4);
	buffer.reserve((bwidth+1) * (bheight+1) * 4);
	l_buffer.reserve((bwidth+1) * (bheight+1) * 4);
	bufferWidth = bwidth;
	bufferHeight = bheight;
	maxSlices = n;
	numVerts = 0;
	vertCount = 0;
	hi = 0;
	lo = 10000;
}

Sphere::~Sphere() {
}

void Sphere::assignBuffer(unsigned char *b, unsigned char *l, int bwidth, int bheight, double bump) {
	for(int i=0;i<bufferWidth*bufferHeight*3;i++) {
		buffer = b;
		l_buffer = l;
	}

	bufferWidth = bwidth;
	bufferHeight = bheight;
	bumpHeight = bump;
}

Vec3d Sphere::CalculateNormals(Vec3d v1, Vec3d v2, Vec3d v3)
{
	// Reduce each vert's normal to unit
	float length;
	float u[3], v[3];
	Vec3d unit;

	// V2 - V3;
	u[0] = v2.x - v3.x;
	u[1] = v2.y - v3.y;
	u[2] = v2.z - v3.z;

	// V2 - V1;
	v[0] = v2.x - v1.x;
	v[1] = v2.y - v1.y;
	v[2] = v2.z - v1.z;

	unit.x = (u[1]*v[2] - u[2]*v[1]);
	unit.y = (u[2]*v[0] - u[0]*v[2]);
	unit.z = (u[0]*v[1] - u[1]*v[0]);

	length = (double)sqrt((unit.x*unit.x) + (unit.y*unit.y) + (unit.z*unit.z));

	if (length == 0.0f)
		length = 1.0f;

	unit.x /= length;
	unit.y /= length;
	unit.z /= length;

	return unit;
}

void Sphere::setScale(double scale) {
	scaleSet = scale;
}

double Sphere::getHi() {
	return hi;
}

double Sphere::getLo() {
	return lo;
}

Vec2f Sphere::getSphereEyeCoord(Vec3d pos, Vec3d cam) {
	Vec2f coord;
	double x,y,z;

	coord.x = PI + (atan2(pos.z - cam.z,pos.x - cam.x));
	x = pos.x - cam.x;
	y = pos.y - cam.y;
	z = pos.z - cam.z;
	coord.y = -(PI * 0.5f - atan2(sqrt(x*x+z*z),y));
	return coord;
}

void Sphere::Update(Vec3d pos, Vec3d cam, double r,int n, bool useClip, bool useArea, bool useHeight)
{
   int i,j,index = 0;
   double theta1,theta2,theta3;
   double angleX1, angleX2, angleY1, angleY2;
   double u, v, height;
   double hu, hv;
   double per;
   double part;
   float slice, restslice;
   int objindex = 0, objinc = 0;
   Vec3d e,p;
   Vec2d lalpha;

   // take horizontal camera - 90 degrees = center on closest vert;

   if(n > maxSlices) n = maxSlices;
   slices = n;
   radius = r;

   if (r < 0)
      r = -r;
   if (n < 0)
      n = -n;
	
	eyeSphere = getSphereEyeCoord(pos,cam);

	for (j=0;j<n/2;j++) { // rotate X for objects
		if(useArea)
			part = FABS((j / (n/2)) - 0.5) * 0.1;
		else
			part = 0.1;

		theta1 = double(j) * TWOPI / double(n) - PIDIV2; // vertical shift
		theta2 = (double(j) + 1) * TWOPI / double(n) - PIDIV2;
		slice = CLAMP(FABS(eyeSphere.y) * 0.25f * part * 10,0.1,1);
		if(theta1 > eyeSphere.y - PI * part && theta1 < eyeSphere.y + PI * part || n < 64 || !useArea && !useClip) {
			for (i=0;i<n;i++) {
				 // rotate Y for objects
				theta3 = double(i) * TWOPI / double(n); // horizontal shift
				angleX1 = RADTODEG(double(j) * PI / (double(n)/2));
				angleX2 = RADTODEG(double(j+1) * PI / (double(n)/2));
				angleY1 = RADTODEG(double(i) * TWOPI / double(n))-90;
				angleY2 = RADTODEG(double(i+1) * TWOPI / double(n))-90;
				restslice = TWOPI - (TWOPI - PIDIV2 * slice);
				if(theta3 > eyeSphere.x - PI * slice && theta3 < eyeSphere.x + PI * slice ||
					eyeSphere.x > TWOPI - PI * slice && theta3 > eyeSphere.x - PI * slice || eyeSphere.x > TWOPI - PI * slice && theta3 < restslice ||
					eyeSphere.x < PI * slice && theta3 < eyeSphere.x + PI * slice || eyeSphere.x < PI * slice && theta3 > TWOPI - restslice || slice > 0.25f || n < 64 || !useArea) {
					u = (double(i)+0.08)/(double)n;
					v = 2*(double(j)+1.08)/(double)n;
					hu = i/(double)n * bufferWidth;
					hv = 2*(double(j)+1)/(double)n * bufferHeight;
					
					per = ((PerlinNoise2D(u * n,v * n,1,1,1.5)+1)/2);
					if(per > hi)
						hi = per;
					if(per < lo)
						lo = per;
					
					if(useHeight) {
						height = double(buffer[(hv * 3) * bufferWidth + hu * 3]) / 255 * bumpHeight;
						height = height * 2 - height;
						height = height * per;
					}
					else
						height = 0;

					e.x = cos(theta2) * cos(theta3);
					e.y = sin(theta2);
					e.z = cos(theta2) * sin(theta3);
					p.x = (r + height)  * e.x;
					p.y = (r + height)  * e.y;
					p.z = (r + height)  * e.z;
					
					colors[index] = float(l_buffer[(hv * 3) * bufferWidth + hu * 3]) / 255.0f * per * 1.25;
					normals[index] = e;
					texes[index] = Vec2f(u,v);
					verts[index] = p;
					index++;
					objnormals[objindex] = Vec3d(angleX2,0,angleY1);
					objverts[objindex] = verts[index];
					objindex++;

					u = (double(i)+0.08)/(double)n;
					v = 2*(double(j)+0.08)/(double)n;
					hu = i/(double)n * bufferWidth-0;
					hv = 2*(double(j)-0)/(double)n * bufferHeight;
					if(hv < 0) hv = 0;

					per = ((PerlinNoise2D(u * n,v * n,1,1,1.5)+1)/2);
					if(per > hi)
						hi = per;
					if(per < lo)
						lo = per;
					
					if(useHeight) {
						height = double(buffer[(hv * 3) * bufferWidth + hu * 3]) / 255 * bumpHeight;
						height = height * 2 - height;
						height = height * per;
					}
					else
						height = 0;

					e.x = cos(theta1) * cos(theta3);
					e.y = sin(theta1);
					e.z = cos(theta1) * sin(theta3);
					p.x = (r + height)  * e.x;
					p.y = (r + height)  * e.y;
					p.z = (r + height)  * e.z;
					
					colors[index] = float(l_buffer[(hv * 3) * bufferWidth + hu * 3]) / 255.0f * per * 1.25;
					normals[index] = e;
					texes[index] = Vec2f(u,v);
					verts[index] = p;
					index++;

					theta3 = (double(i)+1) * TWOPI / double(n);

					u = (double(i)+1.08)/(double)n;
					v = 2*(double(j)+0.08)/(double)n;
					hu = (i+1)/(double)n * bufferWidth-0;
					hv = 2*(double(j)-0)/(double)n * bufferHeight;
					if(hv < 0) hv = 0;

					per = ((PerlinNoise2D(u * n,v * n,1,1,1.5)+1)/2);
					if(per > hi)
						hi = per;
					if(per < lo)
						lo = per;
					
					if(useHeight) {
						height = double(buffer[(hv * 3) * bufferWidth + hu * 3]) / 255 * bumpHeight;
						height = height * 2 - height;
						height = height * per;
					}
					else
						height = 0;

					e.x = cos(theta1) * cos(theta3);
					e.y = sin(theta1);
					e.z = cos(theta1) * sin(theta3);
					p.x = (r + height)  * e.x;
					p.y = (r + height)  * e.y;
					p.z = (r + height)  * e.z;
					
					colors[index] = float(l_buffer[(hv * 3) * bufferWidth + hu * 3]) / 255.0f * per * 1.25;
					normals[index] = e;
					texes[index] = Vec2f(u,v);
					verts[index] = p;
					index++;
					objnormals[objindex] = Vec3d(angleX1,0,angleY2);
					objverts[objindex] = verts[index];
					objindex++;

					u = (double(i)+1.08)/(double)n;
					v = 2*(double(j)+1.08)/(double)n;
					hu = (i+1)/(double)n * bufferWidth-0;
					hv = 2*(double(j)+1)/(double)n * bufferHeight;

					per = ((PerlinNoise2D(u * n,v * n,1,1,1.5)+1)/2);
					if(per > hi)
						hi = per;
					if(per < lo)
						lo = per;
					
					if(useHeight) {
						height = double(buffer[(hv * 3) * bufferWidth + hu * 3]) / 255 * bumpHeight;
						height = height * 2 - height;
						height = height * per;
					}
					else
						height = 0;

					e.x = cos(theta2) * cos(theta3);
					e.y = sin(theta2);
					e.z = cos(theta2) * sin(theta3);
					p.x = (r + height)  * e.x;
					p.y = (r + height)  * e.y;
					p.z = (r + height)  * e.z;
					
					colors[index] = float(l_buffer[(hv * 3) * bufferWidth + hu * 3]) / 255.0f * per * 1.25;
					normals[index] = e;
					texes[index] = Vec2f(u,v);
					verts[index] = p;
					index++;
				}
			}
		}
	}
	numVerts = index;
	numObjects = objindex;
}

int Sphere::getCount() {
	return vertCount;
}

int Sphere::getVert() {
	return numVerts;
}

double Sphere::getClosestVecDistance(double scale, Vec3d cam) {
	return fabs(objverts[closestVec].distanceTo(cam)) - scale;
}

Vec3d Sphere::getClosestVecNormal() {
	return objnormals[closestVec];
}

void Sphere::setBump(double scale, double bump) {
	bumpHeight = bump / scale;
}

void Sphere::setLightPos(Vec3d light) {
	lightPos = light;
}

void Sphere::Draw(double scale, int lod, Vec3d pos, Vec3d cam, bool useClip, bool useArea, bool useHeight, bool useMulti, float alpha)
{
	double dist = 0, closest = 1e20, farthest = 0;
	double cdist = fabs(pos.distanceTo(cam));
	double area = cdist / scale * (1 - (0.9 / scale));
	double distCenter = fabs(cdist - radius);
	double clip = cdist / scale * (1 - (0.025 / scale));
	Vec3d p;
	Vec2f eye = getSphereEyeCoord(pos,cam);

	if(eye.distanceTo(eyeSphere) > PI * 0.005f && useArea ||
		eye.distanceTo(eyeSphere) > PI * 0.005f && useClip)
		Update(pos,cam,1,lod,useClip, useArea,useHeight);	

	closestVec = 0;
	farthestVec = 0;

	glPushMatrix();
	glTranslated(pos.x, pos.y, pos.z);
	glEnable(GL_NORMALIZE);
	glScaled(scale,scale,scale);
	glBegin(GL_QUADS);

	vertCount = 0;
	if(numVerts > 0) {
		for (int i=0;i<numVerts;i+=4) {
			p = pos + verts;
			dist = fabs(p.distanceTo(cam)) / scale;

			if(dist < closest) {
				closest = dist;
				closestVec = i;
			}
			if(dist > farthest) {
				farthest = dist;
				farthestVec = i;
			}
			
			if(useArea && dist < area || useClip && dist < clip || slices < 160) {
				if(useArea)
					glColor4f(colors,colors,colors,alpha);
				glNormal3dv(&normals.x);
				if(useMulti == true) {
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texes.x, texes.y); 
					glMultiTexCoord2fARB(GL_TEXTURE1_ARB, texes.x, texes.y); 
				}
				else
					glTexCoord2fv(&texes.x);
				glVertex3dv(&verts.x);
				
				if(useArea)
					glColor4f(colors[i+1],colors[i+1],colors[i+1],alpha);
				glNormal3dv(&normals[i+1].x);
				if(useMulti == true) {
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texes[i+1].x, texes[i+1].y); 
					glMultiTexCoord2fARB(GL_TEXTURE1_ARB, texes[i+1].x, texes[i+1].y); 
				}
				else
					glTexCoord2fv(&texes[i+1].x);
				glVertex3dv(&verts[i+1].x);
				
				if(useArea)
					glColor4f(colors[i+2],colors[i+2],colors[i+2],alpha);
				glNormal3dv(&normals[i+2].x);
				if(useMulti == true) {
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texes[i+2].x, texes[i+2].y); 
					glMultiTexCoord2fARB(GL_TEXTURE1_ARB, texes[i+2].x, texes[i+2].y); 
				}
				else
					glTexCoord2fv(&texes[i+2].x);
				glVertex3dv(&verts[i+2].x);
				
				if(useArea)
					glColor4f(colors[i+3],colors[i+3],colors[i+3],alpha);
				glNormal3dv(&normals[i+3].x);
				if(useMulti == true) {
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texes[i+3].x, texes[i+3].y); 
					glMultiTexCoord2fARB(GL_TEXTURE1_ARB, texes[i+3].x, texes[i+3].y); 
				}
				else
					glTexCoord2fv(&texes[i+3].x);
				glVertex3dv(&verts[i+3].x);
				vertCount+=4;
			}
		}
		p = pos + verts[farthestVec];
		dist = fabs(p.distanceTo(cam));
	}

	glEnd();
	glDisable(GL_NORMALIZE);
	glPopMatrix();
}

void Sphere::DrawObjects(double scale, Vec3d pos, Vec3d cam, float alpha)
{
	double dist = 0, closest = 1e20, farthest = 0;
	double cdist = fabs(pos.distanceTo(cam));
	double area = cdist * 0.05;
	double distCenter;
	double clip = cdist / scale * (1 - (0.025 / scale));
	Vec3d p, n, v;
	
	glDisable(GL_CULL_FACE);
	glDisable(GL_LIGHTING);
	if(numObjects > 0) {
		for (int i=0;i<numObjects;i++) {
			p = pos + objverts * scale;
			dist = fabs(p.distanceTo(cam));
			distCenter = fabs(p.distanceTo(pos));
			if(dist < closest) {
				closest = dist;
				closestVec = i;
			}
			n.x = objnormals.x;
			n.y = objnormals.y;
			n.z = objnormals.z;
			v.x = pos.x + objverts.x * scale;
			v.y = pos.y + objverts.y * scale;
			v.z = pos.z + objverts.z * scale;
			
			if(dist < area && distCenter > bumpHeight * scale * 150.25 && distCenter < bumpHeight * scale * 150.35) {
				glColor4f(1,1,1,alpha);
				drawTree(0.1,v,n);
			}
		}
	}
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHTING);
}

void Sphere::drawTree(float radius, Vec3d pos, Vec3d rot)
{
	glPushMatrix();
	glTranslated(pos.x,pos.y,pos.z);
	glRotated(rot.x, 1.0, 0.0, 0.0);
	glRotated(rot.y, 0.0, 1.0, 0.0);
	glRotated(rot.z, 0.0, 0.0, 1.0);
	glEnable(GL_NORMALIZE);
	glScaled(radius, radius, radius);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState (GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer (2, GL_FLOAT, 0, obj_billboard_tex);
	glNormalPointer (GL_FLOAT, 0, obj_billboard_norm);
	glVertexPointer (3, GL_FLOAT, 0, obj_billboard_vert);
	glDrawArrays(GL_TRIANGLES, 0, 12);
	glDisableClientState (GL_VERTEX_ARRAY);
	glDisableClientState (GL_NORMAL_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisable(GL_NORMALIZE);
	glPopMatrix();
}

I prefer not to have to use something too complicated and would lean more to a simpler approach if at all possible...

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.