PDA

View Full Version : Making a Bezier curve oscillate



vindy
07-07-2011, 06:25 PM
How might one go about making a Bezier curve oscillate? I've been trying to do this all day today by changing the control points back and forth on each pass through the renderScene function but to no avail. Any help would be appreciated.

carsten neumann
07-08-2011, 07:56 AM
To me it sounds like you are doing the right thing.
You could give a more detailed description of what you are doing (perhaps posting relevant code pieces), perhaps then it's possible to find the problem.

vindy
07-08-2011, 09:01 AM
Ok. Sorry for not posting the code right away. I've been burned on this board by a couple of people when posting my code for it being either too much code or for lack of comments and good variable names. In each case the other board users had a point but they were rather insensitive about it. Anyhow I'll explain as best I can what I'm doing first then post the relevant code with comments.
Alright, here we go. I've got a 4x3 array. This array holds the four control points for the curve. The first and last points are the end points of course. The second point starts out on the left and above the curve while the third point starts out on the right and below the curve. First off I call glMap1f with GL_MAP1_VERTEX_3, a "u range" from 0 to 100, a distance of 3 between points in the data, along with an argument of 4 indicating the number of control points and a pointer to the beginning of the array holding the control points. I then enable the evaluator and begin connecting points produced by glEvalCoord1f with a line strip by looping from 0 to 100 and passing the values as arguments to that function. I then draw the curve by swapping buffers. Now, the next part of my code flips a flag from 0 to 1 and back to 0 and so on. When the flag is 0 and the y coordinate of the second control point is greater than -4.0 it decrements the y coordinate and increments the y coordinate of the third control point. When the flag is 1 it does the opposite. That's about it. Sorry for the tediousness of this description. Now here's the code:

<div class="ubbcode-block"><div class="ubbcode-header">Click to reveal.. <input type="button" class="form-button" value="Show me!" onclick="toggle_spoiler(this, 'Yikes, my eyes!', 'Show me!')" />]<div style="display: none;">

//The control points I told about
GLfloat ctrlPoints[4][3] = {{-4.0, 0.0f, 0.0f}, {-6.0f, 4.0f, 0.0f},
{6.0f, -4.0f, 0.0f}, {4.0f, 0.0f, 0.0f}};

//The flag for oscillating the points
int flag = 0;

//Your standard resize function, make note of the call to gluOrtho2D
void handleResize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-10.0f, 10.0f, -10.0f, 10.0f);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void drawScene()
{
//Clear the screen and load the identity matrix
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//Lets get this party started by setting up the Bezier
glMap1f(GL_MAP1_VERTEX_3, 0.0f, 100.0f, 3, 4, &amp;ctrlPoints[0][0]);

//Enable the evaluator
glEnable(GL_MAP1_VERTEX_3);

//Make the curve red
glColor3f(1.0f, 0.0f, 0.0f);

//Begin drawing the curve with a line strip connecting the points
glBegin(GL_LINE_STRIP);

//Loop through the u range and produce the points
for(int i = 0;i <= 100; i++)
glEvalCoord1f((GLfloat) i);

//End the drawing
glEnd();

//And disable the evaluator
glDisable(GL_MAP1_VERTEX_3);

//Now swap buffers
glutSwapBuffers();

//If the flag is 0 and the second control y-point is greater than the y-point
//at which it bottoms out then keep decrementing the second y-point and keep
//incrementing the third y-point
if(flag == 0 &amp;&amp; ctrlPoints[1][1] >= -4.0f)
{
ctrlPoints[1][1] = ctrlPoints[1][1] - (GLfloat)0.1f;
ctrlPoints[2][1] = ctrlPoints[2][1] + (GLfloat)0.1f;
}
else if(flag == 0 &amp;&amp; ctrlPoints[1][1] < -4.0f)//Otherwise flip the flag
flag = 1;

//If flag is 1 and the second y-point is less than its top y-point
//then do the opposite as when the flag was 0
if(flag == 1 &amp;&amp; ctrlPoints[1][1] <= 4.0f)
{
ctrlPoints[1][1] = ctrlPoints[1][1] + (GLfloat)0.1f;
ctrlPoints[2][1] = ctrlPoints[2][1] - (GLfloat)0.1f;
}
else if(flag == 1 &amp;&amp; ctrlPoints[1][1] > 4.0f)
flag = 0;
}

[/QUOTE]</div>

Thanks in advance for your help! :)

carsten neumann
07-08-2011, 10:46 AM
Hmm, I don't see where it goes wrong, sorry.

Try this to isolate the problem:
- create a second ctrlPoints array that has the control points that correspond to the end state of your oscillation and draw that together with the current control points (also disable all the code related to updating control points).
- if that works and you see two different curves the problem is in how you update the control points.
- if you still only see one curve (I assume you do see something?) there is something wrong with how you use the control points or mapping/evaluation calls - maybe a mixup in coordinate axis? Try varying other than the y axis and see if that makes a difference.

vindy
07-08-2011, 02:13 PM
I see one curve which corresponds to the control points as they initially are. When I try drawing more than one curve in one pass through the drawScene function it works. You know what I'm thinking? It could have something to do with the beginning of the drawScene function where glClear is called and the Identity Matrix is loaded. Let me try clearing only the color buffer bit and load the identity matrix only once during the program.

vindy
07-08-2011, 02:26 PM
Ok, that didn't do the trick. I also tried your suggestion of varying points for the x axis and it didn't work either. I'm at a loss. :(

carsten neumann
07-08-2011, 03:31 PM
I see one curve which corresponds to the control points as they initially are. When I try drawing more than one curve in one pass through the drawScene function it works.


Ok, that suggests the problem is with updating the values.

Try some printf debugging like below, then study the output carefully if it matches what you expect - perhaps increase the amount by which you increment/decrement from 0.1f to 1.f so that you hit the interesting cases (changes in direction) quicker.



unsigned int frameCount = 0;

void drawScene()
{
printf(">>> drawScene [%u] >>>\n", frameCount);

// everything up to glutSwapBuffers();

printf("ctrlPoints[1] = %f %f %f\n",
ctrlPoints[1][0], ctrlPoints[1][1], ctrlPoints[1][2]);
printf("ctrlPoints[2] = %f %f %f\n",
ctrlPoints[2][0], ctrlPoints[2][1], ctrlPoints[2][2]);

if(flag == 0 &amp;&amp; ctrlPoints[1][1] >= -4.0f)
{
printf("dec ctrlPoint[1] - inc ctrlPoint[2]\n");

ctrlPoints[1][1] = ctrlPoints[1][1] - (GLfloat)0.1f;
ctrlPoints[2][1] = ctrlPoints[2][1] + (GLfloat)0.1f;
}
else if(flag == 0 &amp;&amp; ctrlPoints[1][1] < -4.0f)
{
printf("switching direction to inc - dec\n");
flag = 1;
}

if(flag == 1 &amp;&amp; ctrlPoints[1][1] <= 4.0f)
{
printf("inc ctrlPoint[1] - dec ctrlPoint[2]\n");

ctrlPoints[1][1] = ctrlPoints[1][1] + (GLfloat)0.1f;
ctrlPoints[2][1] = ctrlPoints[2][1] - (GLfloat)0.1f;
}
else if(flag == 1 &amp;&amp; ctrlPoints[1][1] > 4.0f)
{
printf("switching direction to dec - inc\n");
flag = 0;
}

printf("ctrlPoints[1] = %f %f %f\n",
ctrlPoints[1][0], ctrlPoints[1][1], ctrlPoints[1][2]);
printf("ctrlPoints[2] = %f %f %f\n",
ctrlPoints[2][0], ctrlPoints[2][1], ctrlPoints[2][2]);

printf("<<< drawScene [%u] <<<\n", frameCount);

++frameCount;
}

vindy
07-08-2011, 04:12 PM
I agree the problem must lie with the changing of the control points, but I've studied how they change during the course of the program with Visual Studio's debugger by watching the array data and Visual Studio show's the values to be changing the way they should. I tried changing the "step size" from 0.1 to 1.0 and still no progress. I've hit a brick wall. Let me try the printf suggestion of yours.

vindy
07-08-2011, 04:17 PM
Hmmm, the values are changing the way they should by the printf method of debugging. Grrrr....somebody put me in a padded room now!

carsten neumann
07-11-2011, 07:43 AM
Uhm, I must have misunderstood you at some point or the information you are giving is contradictory.
As far as I understood it changing the control points by hand gave you a differently looking curve, but if you change them from code it does not change?
Are you redrawing your scene after changing the control points?
Are you drawing so fast that you can't see the changing curve?

vindy
07-11-2011, 12:02 PM
Sorry for being confusing. As far as I can tell this is what is going on: The control points in the array are in fact changing on each pass through the drawScene function. However, the shape of the curve remains the same. Remember how I'm doing it with the code?

1.) Draw the curve
2.) Change the control points
3.) glutMainLoop

(repeat)

Now if I put numbers 1 and 2 of the process in a while loop so the control points are changed numerous times in one pass through the drawScene function, say the y-points oscillate once during one pass, what I see is the curve going halfway through the oscillation, and then a black screen, and then I see the curve again as it was before beginning to oscillate, and then that's it; no more movement. I hope this explanation hasn't confused you further. If you need more clarification I'd be happy to provide it. Again, thanks for taking the time to offer your help. I really appreciate it.

carsten neumann
07-11-2011, 04:08 PM
Remember how I'm doing it with the code?

1.) Draw the curve
2.) Change the control points
3.) glutMainLoop

(repeat)


Wait, are you saying you make multiple calls to glutMainLoop? That's not how glut works. It implements a (albeit quite simple) event driven application where in main you do some setup work and register a couple of callbacks for certain events (e.g. redraw the scene, change the window size, or key presses) and then you let the whole thing run with a single call to glutMainLoop().

I have the impression the problem is in the way you are using glut, can you post your entire program? Please use [ code]/[ /code] tags around it so that it get's formatted in fixed width font.

vindy
07-11-2011, 05:16 PM
No, I just put that as number 3 to make it clear how things are flowing. glutMainLoop is called once in my program at the bottom of my main function. Sorry for the mix up with how I explained things. Anyhow, here's the entire program per your request:

<div class="ubbcode-block"><div class="ubbcode-header">Click to reveal.. <input type="button" class="form-button" value="Show me!" onclick="toggle_spoiler(this, 'Yikes, my eyes!', 'Show me!')" />]<div style="display: none;">



#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/GLee.h>
#include <GL/GLU.h>
#include <GL/glut.h>

#endif

GLfloat ctrlPoints[4][3] = {{-4.0, 0.0f, 0.0f}, {-6.0f, 4.0f, 0.0f},
{6.0f, -4.0f, 0.0f}, {4.0f, 0.0f, 0.0f}};

int flag = 0;

void handleResize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-10.0f, 10.0f, -10.0f, 10.0f);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void initRendering() {
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING); //Enable lighting
glEnable(GL_LIGHT0); //Enable light #0


glEnable(GL_NORMALIZE); //Automatically normalize normals
}

void drawScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glMap1f(GL_MAP1_VERTEX_3, 0.0f, 100.0f, 3, 4, &amp;ctrlPoints[0][0]);

glEnable(GL_MAP1_VERTEX_3);

glColor3f(1.0f, 0.0f, 0.0f);

glBegin(GL_LINE_STRIP);

for(int i = 0;i <= 100; i++)
glEvalCoord1f((GLfloat) i);

glEnd();

glDisable(GL_MAP1_VERTEX_3);

glutSwapBuffers();

if(flag == 0 &amp;&amp; ctrlPoints[1][1] >= -4.0f)
{
ctrlPoints[1][1] = ctrlPoints[1][1] - (GLfloat)0.1f;
ctrlPoints[2][1] = ctrlPoints[2][1] + (GLfloat)0.1f;
}
else if(flag == 0 &amp;&amp; ctrlPoints[1][1] < -4.0f)
flag = 1;

if(flag == 1 &amp;&amp; ctrlPoints[1][1] <= 4.0f)
{
ctrlPoints[1][1] = ctrlPoints[1][1] + (GLfloat)0.1f;
ctrlPoints[2][1] = ctrlPoints[2][1] - (GLfloat)0.1f;
}
else if(flag == 1 &amp;&amp; ctrlPoints[1][1] > 4.0f)
flag = 0;
}

void handleKeypress(unsigned char key, int x, int y) {


switch (key) {
case 27: //Escape key
glutLeaveGameMode();
exit(0);
break;
}
}

int main(int argc, char** argv) {

//Initialize GLUT
glutInit(&amp;argc, argv);

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

glutGameModeString("1024x768:32@85");

glutEnterGameMode();

initRendering();

//Set handler functions
glutDisplayFunc(drawScene);
glutKeyboardFunc(handleKeypress);
glutReshapeFunc(handleResize);
glutSetCursor(GLUT_CURSOR_NONE);

glutMainLoop();
return 0;
}


[/QUOTE]</div>

You know I was experimenting with NURBS earlier today and was running into the same problem. I'm beginning to think Beziers and NURBS and such can't be animated like I'm trying to do. I could be wrong though.

carsten neumann
07-12-2011, 07:49 AM
Ok, I've tried your program. The problem is simply that you are only drawing frames when needed, not continuously, so your program starts, draws the first frame and then never again (side question: that should have become apparent while printf debugging, it would have only printed one frame of information).

Create a glutIdleFunc:



void idle(void)
{
glutPostRedisplay();
}


and register it in main with the other callbacks:

glutIdleFunc(&amp;idle);

vindy
07-12-2011, 08:07 AM
I think I made a mistake when writting the printf debugging version because even with that I couldn't tell where I was going wrong. I've got to admit though I'm confused as to why the idle function is needed. I'm a newbie when it comes to OpenGL and this is the first time I've heard of or needed an idle function. Why is it that glutSwapBuffers doesn't draw the new curve at every pass through the drawScene function? Sorry if my ignorance is annoying. I'm still very much trying to learn how all this stuff works. In any case, thank you very much for all your help! :) Its cool to see it actually working.

carsten neumann
07-12-2011, 09:20 AM
glutSwapBuffers does not draw anything, it makes visible what you have drawn into the backbuffer by exchanging the front and backbuffer.

In order for animations to work you have to continuously draw new frames each one with a slight change compared to the previous one to trick the human eye into seeing a fluid motion. Normally glut programs only redraw the scene if there is a change that requires it (e.g. if the window changes size, or is covered/uncovered by another window, etc.) or if you explicitly request a redraw by calling glutPostRedisplay() - this is usually used in a callback handling input that changes the view or toggles a light source or something else that changes what you see.

You can do the following experiment:
- add to drawScene a line like: printf("drawScene\n");
now everytime the scene is drawn you get a printout
- comment the glutIdleFunc(&amp;idle); line
- rebuild and run your program

you will only see one (or perhaps a small number of) lines printed and then nothing - that's because nothing tells glut that the scene needs redrawing.
If you add the glutIdleFunc line again it will continually print lines.

vindy
07-12-2011, 12:43 PM
Hmm, I think I'm starting to understand. But doesn't the code between the glBegin(GL_LINE_STRIP) and glEnd() make changes to the backbuffer since changes are made to the curve via the glMap1f(...) function by changing the control points on each pass? I mean on each pass glEvalCoord1f spits out vertices that are a little different than the last time right?
Well, I'm grateful you've kept up with my thread here on the board and thanks again! :)