PDA

View Full Version : Want to move only one of many identical objects. How?

thebigbo
05-11-2014, 10:51 AM
Hi everyone.

I use the below code to create scene with multiple spheres, and with the keyboard method I intend to move only one of them (first one for example). Unfortunately, every time I press a key the whole scene gets redrawn (because of glutPostRedisplay, which recalls the display method). How do I bypass this behavior, so that a single sphere moves and the others keep their old positions? Any help is welcome.

class SolidSphere
{
protected:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;

public:
SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1.0f / (float)(rings - 1);
float const S = 1.0f / (float)(sectors - 1);
unsigned int r, s;

vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const x = sinf(M_PI * r * R) * cosf(2 * M_PI * s * S);
float const y = sinf(-M_PI_2 + M_PI * r * R );
float const z = sinf(2.0f * M_PI * s * S) * sinf(M_PI * r * R );

*t++ = s*S;
*t++ = r*R;

*n++ = x;
*n++ = y;
*n++ = z;
}

indices.resize(rings * sectors * 6);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings - 1; r++) for(s = 0; s < sectors - 1; s++) {
*i++ = r * sectors + s;
*i++ = (r + 1) * sectors + (s + 1);
*i++ = r * sectors + (s + 1);

*i++ = r * sectors + s;
*i++ = (r + 1) * sectors + s;
*i++ = (r + 1) * sectors + (s + 1);
}
}

void draw(GLfloat x, GLfloat y, GLfloat z)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(x,y,z);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
glPopMatrix();

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
};

void init()
{
struct Light {
float ambient[4];
float diffuse[4];
float specular[4];
float position[4];
} light = {
{ .2, .2, .2, 1. },
{ .8, .8, .8, 1. },
{ 1., 1., 1., 1. },
{ 1., 1., 1., .0 }
};

struct Material {
float ambient[4];
float diffuse[4];
float specular[4];
float shininess[1];
} material = {
{ .8, .8, .8, 1. },
{ .8, .8, .8, 1. },
{ 1., 1., 1., 1. },
{ 96 }
};

glClearColor(.5, .5, 1., 1.);

glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, material.shininess);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material.specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material.diffuse);

glLightfv(GL_LIGHT0, GL_POSITION, light.position);
glLightfv(GL_LIGHT0, GL_AMBIENT, light.ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light.diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light.specular);

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
}

SolidSphere **createSpheres()
{
SolidSphere **spheres = new SolidSphere*[numSpheres];
for (int i = 0; i < numSpheres; i++)
spheres[i] = new SolidSphere(1, 12, 24);

return spheres;
}

void display()
{
SolidSphere **spheres = createSpheres();
float const win_aspect = (float)win_width / (float)win_height;

glViewport(0, 0, win_width, win_height);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(.6, 0, 0);
glMatrixMode(GL_PROJECTION);
gluPerspective(45, win_aspect, 1, 10);

glMatrixMode(GL_MODELVIEW);

for (int i = 0; i < numSpheres; i++)
{
posX = ((float)rand())/RAND_MAX * 4 - 2;
posY = ((float)rand())/RAND_MAX * 4 - 2;
posZ = ((float)rand())/RAND_MAX * 5 - 10;
spheres[i]->draw(posX,posY,posZ);
}
for (int i = 0; i < numSpheres; i++)
{
delete spheres[i];
}

delete[] spheres;

glutSwapBuffers();
}

void keyboard(unsigned char key, int x, int y)
{
switch(key)
{
case 27:
exit(0);
case 'a':
posX -= 0.05f;
glutPostRedisplay();
break;
case 'd':
posX += 0.05f;
glutPostRedisplay();
break;
case 's':
posY -= 0.05f;
glutPostRedisplay();
break;
case 'w':
posY += 0.05f;
glutPostRedisplay();
break;
case 'x':
posZ -= 0.05f;
glutPostRedisplay();
break;
case 'z':
posZ += 0.05f;
glutPostRedisplay();
break;
}
}

carsten neumann
05-11-2014, 01:03 PM
You allocate (and destroy) all sphere objects within your display function and assign completely new positions to them (hence they all move). That is not only wasteful performance wise, but also means that you can not easily keep track of the position of each sphere for the duration of your program. Instead create the sphere objects during program initialization and store the spheres position within a SolidSphere object (possibly initialized to a random location). In your keyboard callback you modify the position of the sphere you want to move only and in the display callback render each sphere at its current location.

thebigbo
05-11-2014, 01:32 PM
Done this. It worked. May I ask one more thing? I've created this function:

bool checkCollisions(GLfloat x_1, GLfloat y_1, GLfloat z_1)
{
float distance = sqrt(pow((x_1 - x_2),2) + pow((y_1 - y_2),2) + pow((z_1 - z_2),2));
if(distance <= 2) return true;
return false;
}

... and inside display() I assign its return value to bool collision:

bool collision = checkCollisions(x_1, y_1, z_1);

So far OK, but when I compile I get "error C3861: 'checkCollisions': identifier not found". Any ideas?

EDIT: Solved! I needed to declare prototype of checkCollisions() at the beginning of my code.