Original Post
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... 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: I prefer not to have to use something too complicated and would lean more to a simpler approach if at all possible...
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;
#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();
}