Basically, I need to get all the coordinates drawn from a Bezier curve implementation in OpenGL. Specifically, I need the coordinates to move a sphere object (baseball) in my scene along a curved trajectory path. This is what I use to draw my curve:
GL2 gl = drawable.getGL().getGL2();
float ctrlpoints[][] = new float[][]{
{0.0f, 0.0f, 60f},
{0.0f, 3.0f, 45.0f},
{0.0f, 2.0f, 15.0f},
{0.0f, 1.0f, 0f}};
FloatBuffer ctrlpointBuf = FloatBuffer.allocate(ctrlpoints[0].length * ctrlpoints.length);
for (int i = 0; i < ctrlpoints.length; i++) {
for (int j = 0; j < 3; j++) {
ctrlpointBuf.put(ctrlpoints[i][j]);
}
}
ctrlpointBuf.rewind();
gl.glMap1f(GL2.GL_MAP1_VERTEX_3, 0.0f, 1.0f, 3, numControlPoints, ctrlpointBuf);
gl.glEnable(GL2.GL_MAP1_VERTEX_3);
gl.glColor3f(1.0f, 1.0f, 1.0f);
gl.glBegin(GL2.GL_LINE_STRIP);
for (int i = 0; i <= 30; i++) {
gl.glEvalCoord1f((float) i / (float) 30.0);
}
gl.glEnd();
Does any one have any idea how to get the points out of this implementation?
In case anyone's interested, this is how I finally implemented pitching a baseball sphere in my scene using a trajectory drawn with a bezier curve. I used 6502's very illuminating function to compute xyz coordinates for the ball at each frame. The starting value is wherever the ball is on the curve as the frame is being drawn. The ending value and the control points are the exact same for drawing the entire curve. The thing that took me a while to figure out was what arguments to give the parameter t.
I finally figured out that it was the values from 0, the START of the curve, to 1, the END of the curve. So, a baseball pitched from the pitchers mound will be t=0 at 60.5 feet from home plate, and t=1 at 0 feet from home plate. So t might be computed as simply as
I drew the whole curve first as a GL_LINE_STRIP, and then I computed the coordinates for the ball at each frame. When I ran my program, the ball exactly followed the curving line trajectory. Thanks to all who gave answers and made comments.
I took the formula, corrected it, and created a test app on github at git://github.com/rmd6502/BezierLicious.git . I strongly recommend against using anything I did there in a production app - consider it for research purposes only!!
A Bezier curve is quite easy to compute. First of all is separable, that means that you can compute it one coordinate at a time (first x, then y, then z...). For a given coordinate the following is a function that uses the definition:
Note that in the above function the parameter
t
is not the arc-length parameter for the curve but a generic parameter that goes fromt=0
(where the point is at the beginning of the curve) tot=1
(where the point is at the end of the curve).An interactive version of the above picture where you can drag A, B, C, D and AB points is available here. It's implemented with html/js/canvas and tested only on Chrome, Firefox, Safari.
If you need to move your objects at a controlled specific speed in XYZ an easy way is to compute an approximated polyline (for example by sampling the curve for 100 values of
t
) and then walk at constant speed on the resulting polyline.The true arc-length parametrization for a Bezier cubic (i.e. using a parameter that is the length measured along the curve) is rather annoying to compute (IIRC there is no closed form solution for the integral).
You should take a look at De Casteljau's algorithm. It allows you to recursively refine your curve. Just abort after a few steps and use the resulting vertices. You could also evaluate the control points directly along the parametric position and use those. It's not that hard to evaluate Bézier-Splines directly (you could do it in the vertex shader for instance!) The advantage of De Casteljau's algorithm is that you never have to compute (high) powers, though for your case both should be fine (and the direct evaluation is definitely easier to implement/test.)
I think the following line in bezier() should read
double ABC = AB*s + CD*t;
instead of
double ABC = BC*s + CD*t;
A quick test with a .c program gives these results. Note, the curve coordinates start at 10.00 instead of at 20.00 with the above unmodified function.
Test program oglBezier.c: