I am trying to draw text in UI elements with OpenGL but i fail in computing scale
and translation values for different font sizes correctly, such that a text would
fit nicely into a designated area.
This is my first attempt in doing such a thing and it seemed to work until i
noticed that with increasing font size the text will not fit into the calculated area anymore. I am not allowed to attach an image for a visual impression of what i mean, which is very sad.
First i thought this would be caused by rounding the scaled font width in order
to calculate the width that a frame needs but was unable to verify this.
After much trial and error i am not even sure anymore if it's the text that
gets projected incorrectly, or the frames i am trying to fit the text into.
Please have a look and help me find the error, here's an overview of what i do:
I construct a UI element by passing a certain font size in pixels.
For a frame ( e.g. input box ) that should hold at most n characters
the computation would look like this:
- Get the width (in pixels) of a single character:
x_advance and line_height are given by the fonts glyph description.
Since i use mono spaced fonts i treat the x_advance as character width.
font_oo_aspect = 1 / ( x_advance / line_height )
font_width = roundf( font_size / font_oo_aspect )
- Computing the actual frame dimension + some margin:
frame_height = font_size + 2 * vertical pad
frame_width = n * font_width + 2 * horizontal_pad
The x and y location in pixels of the frames top left corner
are set to be relative to its parents top left corner.
When rendering UI elements first all frames are drawn and secondly the text.
- Computing scale and translation for a frame:
ortho_scale_x = 2 / window_width
ortho_scale_y = -2 / window_height
wo2 = frame_width * 0.5
ho2 = frame_height * 0.5
dx = parent_x + location_x + wo2
dy = parent_y + location_y + ho2
scale_x = ortho_scale_x * wo2
scale_y = ortho_scale_y * ho2
translation_x = dx / wo2
translation_y = dy / how
- Computing scale and translation for a text:
view_aspect = window_width / window_height
font_scale = line_height / ( view_aspect * font_size )
scale_x = ortho_scale_x * font_scale
scale_y = -ortho_scale_y * font_scale
translation_x = frame_location_x / font_scale
translation_y = -frame_location_y / font_scale
The respective scale and translation values are sent to the shader.
Here's the vertex shader code which is the same for frames and text:
#version 130
in vec2 vertex;
in vec2 uv;
uniform vec2 scale;
uniform vec3 translation;
out vec2 coords;
void main( void ) {
gl_Position = vec4(
( translation.x + vertex.x ) * scale.x - 1.0,
( translation.y + vertex.y ) * scale.y + 1.0,
translation.z, 1.0 );
coords = uv;
}
I hope i provided everything relevant, if not please let me know.
Regards and thanks,
Alfred
Follows the render function for frames and text:
// The buffer object data that frames use looks like this:
float quad_data[ ] = { // x, y, u, v
1.0f, -1.0f, 1.0f, 0.0f, // bottom right
1.0f, 1.0f, 1.0f, 1.0f, // top right
-1.0f, 1.0f, 0.0f, 1.0f, // top left
-1.0f, -1.0f, 0.0f, 0.0f // bottom left
};
// Each text element is rendered as an individual vertex array object.
void render_frame_batches( void ) {
int i, j;
float f_font_size = 12; // 28;
const float h = core.settings.height;
const float sx = core.perspective.ortho_scale_x;
const float sy = core.perspective.ortho_scale_y;
const float view_aspect = core.perspective.view_aspect;
float dx, dy, layer, wo2, ho2;
Ui_Frame_Renderer *frame_renderer = &core.renderer.queue[ 0 ];
Ui_Render_Batch_Frame *batch = frame_renderer->batch;
Ui_Render_Target_Frame *target;
Ui_Shader *p = &core.programs[ PROGRAM_FRAME ];
const s16 *uni_loc = p->uniform_locations;
glActiveTexture( GL_TEXTURE0 );
glUseProgram( p->program_id );
glUniform1i( ( s32 ) uni_loc[ LOC_U_TEXTURE0 ], 0 );
glBindVertexArray( core.shape_vao[ SHAPE_QUAD ].vao );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, core.shape_vao[ SHAPE_QUAD ].ind );
for( i = 0; i < RENDER_BATCH_FRAME_MAX; i++ ) {
layer = ( float ) batch->layer;
for( j = 0; j < batch->frame_count; j++ ) {
target = &frame_renderer->target[ batch->queue_index + j ];
wo2 = ( float ) target->frame_width * 0.5f;
ho2 = ( float ) target->frame_height * 0.5f;
dx = ( float ) target->tx + wo2;
dy = ( float ) target->ty + ho2;
if( j > 0 ) {
dx += ( float ) batch->parent_x;
dy += ( float ) batch->parent_y;
}
glUniform2f( ( s32 ) uni_loc[ LOC_U_SCALE ], sx * wo2, sy * ho2 );
glUniform3f( ( s32 ) uni_loc[ LOC_U_TRANSLATION ], dx / wo2, dy / ho2, layer );
glUniform2f( ( s32 ) uni_loc[ LOC_U_LOCATION ], dx, h - dy );
s32 ib, ob;
if( target->texture ) { // slider gradients
ib = ob = 0;
glBindTexture( GL_TEXTURE_1D, core.dynamic_textures[ target->texture ] );
} else {
ib = wo2 - target->detail.frame.inner_border;
ob = ho2 - target->detail.frame.outer_border;
glUniform4fv( ( s32 ) uni_loc[ LOC_U_COLOR_BG ],
1, ( float * ) &core.colors[ target->bg ] );
glUniform4fv( ( s32 ) uni_loc[ LOC_U_COLOR_FG ],
1, ( float * ) &core.colors[ target->fg ] );
}
glUniform1i( ( s32 ) uni_loc[ LOC_U_INNER ], ib );
glUniform1i( ( s32 ) uni_loc[ LOC_U_OUTER ], ob );
glDrawElements( GL_TRIANGLES,
core.shape_vao[ SHAPE_QUAD ].len, GL_UNSIGNED_SHORT, 0 );
if( target->texture ) {
glBindTexture( GL_TEXTURE_1D, 0 );
}
}
batch++;
}
frame_renderer++;
batch = frame_renderer->batch;
p = &core.programs[ PROGRAM_FONT ];
uni_loc = p->uniform_locations;
Ui_Font *f = &fonts[ FONT_MENU ];
float font_scale = f->line_height / ( view_aspect * f_font_size );
float ri, ro;
font_stroke_radii( f_font_size, f->line_height, view_aspect, &ri, &ro );
glBindTexture( GL_TEXTURE_2D, f->tex_id );
glUseProgram( p->program_id );
glUniform1i( ( s32 ) uni_loc[ LOC_U_TEXTURE0 ], 0 );
glUniform1i( ( s32 ) uni_loc[ LOC_U_FLAGS ], 1 );
glUniform1f( ( s32 ) uni_loc[ LOC_U_INNER ], ri );
glUniform1f( ( s32 ) uni_loc[ LOC_U_OUTER ], ro );
for( i = 0; i < RENDER_BATCH_FRAME_MAX; i++ ) {
layer = ( float ) batch->layer;
for( j = 0; j < batch->frame_count; j++ ) {
target = &frame_renderer->target[ batch->queue_index + j ];
dx = ( float )( target->tx + batch->parent_x );
dy = ( float )( target->ty + batch->parent_y );
glUniform2f( ( s32 ) uni_loc[ LOC_U_SCALE ],
sx / font_scale, -sy / font_scale );
glUniform3f( ( s32 ) uni_loc[ LOC_U_TRANSLATION ],
dx * font_scale, -dy * font_scale, layer );
glUniform4fv( ( s32 ) uni_loc[ LOC_U_COLOR_FG ], 1,
( float * ) &core.colors[ target->fg ] );
Ui_Text *ui_text = &core.text_vao[ STATIC_STRING_MAX + target->detail.text.id ];
Ui_Vao *obj = &ui_text->vao;
glBindVertexArray( obj->vao );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, obj->ind );
glDrawElements( GL_TRIANGLES, obj->len, GL_UNSIGNED_SHORT, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
glBindVertexArray( 0 );
}
batch++;
}
glBindTexture( GL_TEXTURE_2D, 0 );
glUseProgram( 0 );
}
The calculation for the font scale should look like this:
font_scale = line_height / font_size
Before that i had multiplied the font size with the aspect ratio, which i guess makes no sense as i later divide the two orthographic scale values by the font scale and those already got the width and height "baked" into them. Sorry i can't give a mathematical explanation, it just seems to work nicely with that little change.
Related
i have developed camshift algorithm by watching some tutorials..but unfortunately it didn't track the selected object....here is my code..i am a beginner to opencv and i need help..problem is the rectangle doesn't follow the object to track...
this my code:
bool destroy=false;
CvRect box;
CvRect track_window;
bool drawing_box = false;
cv::Mat imageROI;
IplImage *image = 0, *hsv = 0, *hue = 0, *mask = 0, *backproject = 0, *histimg = 0;
CvHistogram *hist = 0;
int hdims = 16;
float hranges_arr[] = {0,180};
float* hranges = hranges_arr;
int vmin = 10, vmax = 256, smin = 30;
IplImage *image2;
CvScalar hsv2rgb( float hue );
CvConnectedComp track_comp;
CvBox2D track_box;
void draw_box(Mat img, CvRect rect)
{
imageROI= img(cv::Rect(box.x,box.y,box.width,box.height));
cv::rectangle(img, cvPoint(box.x, box.y), cvPoint(box.x+box.width,box.y+box.height),
cvScalar(0,0,255) ,2);
}
CvScalar hsv2rgb( float hue )
{
int rgb[3], p, sector;
static const int sector_data[][3]=
{{0,2,1}, {1,2,0}, {1,0,2}, {2,0,1}, {2,1,0}, {0,1,2}};
hue *= 0.033333333333333333333333333333333f;
sector = cvFloor(hue);
p = cvRound(255*(hue - sector));
p ^= sector & 1 ? 255 : 0;
rgb[sector_data[sector][0]] = 255;
rgb[sector_data[sector][1]] = 0;
rgb[sector_data[sector][2]] = p;
return cvScalar(rgb[2], rgb[1], rgb[0],0);
}
void my_mouse_callback( int event, int x, int y, int flags, void* param )
{
IplImage* frame = (IplImage*) param;
switch( event )
{
case CV_EVENT_MOUSEMOVE:
{
if( drawing_box )
{
box.width = x-box.x;
box.height = y-box.y;
}
}
break;
case CV_EVENT_LBUTTONDOWN:
{
drawing_box = true;
box = cvRect( x, y, 0, 0 );
}
break;
case CV_EVENT_LBUTTONUP:
{
drawing_box = false;
if( box.width < 0 )
{
box.x += box.width;
box.width *= -1;
}
if( box.height < 0 )
{
box.y += box.height;
box.height *= -1;
}
draw_box(frame, box);
}
break;
case CV_EVENT_RBUTTONUP:
{
destroy=true;
}
break;
default:
break;
} }
int _tmain(int argc, _TCHAR* argv[])
{
VideoCapture cap(0);
if(!cap.isOpened())
return -1;
Mat image;
Mat frame;
//cv::Mat image= cv::imread("1.jpg");
cap>>image;
if (!image.data)
return 0;
// Display image
cv::namedWindow("Image");
cv::imshow("Image",image);
IplImage* img = new IplImage(image);
cvSmooth(img,img,CV_GAUSSIAN,3,0,0.0,0.0);
IplImage* temp = cvCloneImage(img);
cvSetMouseCallback("Image", my_mouse_callback, (void*) img);
while( 1 )
{
if (destroy)
{
cvDestroyWindow("Image"); break;
}
cvCopyImage(img, temp);
if (drawing_box)
draw_box(temp, box);
cvShowImage("Image", temp);
if (cvWaitKey(15) == 27)
break;
}
cvReleaseImage(&temp);
cvDestroyWindow("Image");
for(;;)
{
int i, bin_w, c;
cap >> frame;
IplImage* frame_ipl = new IplImage(frame);
hsv = cvCreateImage( cvGetSize(frame_ipl), 8, 3 );
image2 = cvCreateImage( cvGetSize(frame_ipl), 8, 3 );
hue = cvCreateImage( cvGetSize(frame_ipl), 8, 1 );
mask = cvCreateImage( cvGetSize(frame_ipl), 8, 1 );
backproject = cvCreateImage( cvGetSize(frame_ipl), 8, 1 );
hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );
histimg = cvCreateImage( cvSize(320,200), 8, 3 );
cvZero( histimg );
cvCopy( frame_ipl, image2, 0 );
cvCvtColor( image2, hsv, CV_BGR2HSV );
int _vmin = vmin, _vmax = vmax;
cvInRangeS( hsv, cvScalar(0,smin,MIN(_vmin,_vmax),0),
cvScalar(180,256,MAX(_vmin,_vmax),0), mask );
cvSplit( hsv, hue, 0, 0, 0 );
float max_val = 0.f;
cvSetImageROI( hue, box );
cvSetImageROI( mask, box );
cvCalcHist( &hue, hist, 0, mask );
cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 );
cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 );
cvResetImageROI( hue );
cvResetImageROI( mask );
track_window = box;
cvZero( histimg );
bin_w = histimg->width / hdims;
for( i = 0; i < hdims; i++ )
{
int val = cvRound( cvGetReal1D(hist->bins,i)*histimg->height/255 );
CvScalar color = hsv2rgb(i*180.f/hdims);
cvRectangle( histimg, cvPoint(i*bin_w,histimg->height),
cvPoint((i+1)*bin_w,histimg->height - val),
color, -1, 8, 0 );
}
cvCalcBackProject( &hue, backproject, hist );
cvAnd( backproject, mask, backproject, 0 );
cvCamShift( backproject, track_window, cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ), &track_comp, &track_box );
track_window = track_comp.rect;
cv::rectangle(frame, track_window, cv::Scalar(0,255,0));
cv::namedWindow("result");
cv::imshow("result",frame);
if(waitKey(30) >= 0) break;
}
return 0;
}
but I use the c-api
What might this code be changed to IplImage :
void draw_box(Mat img, CvRect rect)
{
imageROI= img(cv::Rect(box.x,box.y,box.width,box.height));
cv::rectangle(img, cvPoint(box.x, box.y), cvPoint(box.x+box.width,box.y+box.height),
cvScalar(0,0,255) ,2);
}
I want to rotate a simple cube in OpenGL using GLUT in C. The rotation will occur when I press a key.
If I use glRotatef(angle, 0.0f, 1.0f, 0.0f) the cube will rotate instantly without an animation. I would like to rotate it slowly so it takes about 2 seconds to complete the rotation.
Create a keyboard callback that toggles a bool and a timer callback that updates an angle:
#include <GL/glut.h>
char spin = 0;
void keyboard( unsigned char key, int x, int y )
{
if( key == ' ' )
{
spin = !spin;
}
}
float angle = 0;
void timer( int value )
{
if( spin )
{
angle += 3;
}
glutTimerFunc( 16, timer, 0 );
glutPostRedisplay();
}
void display()
{
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 45, w / h, 0.1, 10 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( 2, 2, 2, 0, 0, 0, 0, 0, 1 );
glColor3ub( 255, 0, 0 );
glRotatef( angle, 0, 0, 1 );
glutWireCube( 1 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutKeyboardFunc( keyboard );
glutTimerFunc( 0, timer, 0 );
glutMainLoop();
return 0;
}
I am trying to implement a Sierpinsky pyramid, that is like a Sierpinsky triangle but in 3D.
I have this structure to contain all the data about a pyramid:
typedef struct
{
GLfloat xUp;
GLfloat yUp;
GLfloat zUp;
GLfloat base;
GLfloat height;
}pyramid;
Then I've written a function that calculates three sub-pyramids:
void findSubPyramids( pyramid pyr, pyramid subs[3])
{
for(int i=0; i<3; i++)
{
subs[i].height=pyr.height/2.0;
subs[i].base=pyr.base/2.0;
}
memcpy(subs,&pyr,3*sizeof(GLfloat));
subs[1].yUp= pyr.yUp-pyr.height/2.0;
subs[1].xUp= pyr.xUp+pyr.base/4.0;
subs[1].zUp= pyr.zUp-pyr.base/4.0;
subs[2].yUp= subs[1].yUp;
subs[2].xUp= pyr.xUp-pyr.base/4.0;
subs[2].zUp= subs[1].zUp;
}
But this algorithm implementation is wrong: something is wrong with the zUp coordinate of the two sub-pyramids at bottom: indeed the pyramid is not drawn as I want:
But if I use glOrtho instead of gluPerspective the pyramid is drawn ok.I know that the gluPerspective and the functions I use are right, but is the algorithm to be wrong.
This is where I implement the algorithm that calculated all the sub-pyramids:
void drawSierpinskyPyramid (pyramid pyr)
{
assert(EQUAL(pyr.height, pyr.base));
if(pyr.base > 4.0)
{
setRandomColor();
pyramid subs[3];
drawPyramid(pyr);
findSubPyramids(pyr, subs);
for(int i=0; i<3; i++)
{
drawSierpinskyPyramid(subs[i]);
}
}
}
I don't get what's wrong.
Give this a shot:
// gcc -std=c99 main.c -lglut -lGL -lGLU
#include <GL/glut.h>
#include <math.h>
#include <stdlib.h>
typedef struct
{
float x, y, z;
} Vec3f;
void glTriangle( Vec3f* v0, Vec3f* v1, Vec3f* v2 )
{
glColor3ub( rand() % 255, rand() % 255, rand() % 255 );
glVertex3fv( (GLfloat*)v0 );
glVertex3fv( (GLfloat*)v1 );
glVertex3fv( (GLfloat*)v2 );
}
// v0, v1, v2 = base, v3 = top
void glTetrahedron( Vec3f* v0, Vec3f* v1, Vec3f* v2, Vec3f* v3 )
{
glTriangle( v0, v2, v1 );
glTriangle( v0, v1, v3 );
glTriangle( v1, v2, v3 );
glTriangle( v2, v0, v3 );
}
Vec3f Lerp( Vec3f* v0, Vec3f* v1, float u )
{
Vec3f ret = {
v0->x + ( v1->x - v0->x ) * u,
v0->y + ( v1->y - v0->y ) * u,
v0->z + ( v1->z - v0->z ) * u,
};
return ret;
}
void glSierpinskiPyramid( Vec3f* v0, Vec3f* v1, Vec3f* v2, Vec3f* v3, unsigned int level )
{
if( level == 0 )
{
glTetrahedron( v0, v1, v2, v3 );
return;
}
// midpoints
Vec3f m01 = Lerp( v0, v1, 0.5 );
Vec3f m12 = Lerp( v1, v2, 0.5 );
Vec3f m02 = Lerp( v0, v2, 0.5 );
Vec3f m03 = Lerp( v0, v3, 0.5 );
Vec3f m13 = Lerp( v1, v3, 0.5 );
Vec3f m23 = Lerp( v2, v3, 0.5 );
glSierpinskiPyramid( v0, &m01, &m02, &m03, level-1 );
glSierpinskiPyramid( &m01, v1, &m12, &m13, level-1 );
glSierpinskiPyramid( &m02, &m12, v2, &m23, level-1 );
glSierpinskiPyramid( &m03, &m13, &m23, v3, level-1 );
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
gluPerspective( 60, w / h, 0.1, 100 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -9 );
srand(0);
glPushMatrix();
glScalef( 3, 3, 3 );
static float angle = 0;
angle += 1;
glRotatef( angle/3, 0.2, 1, 0 );
Vec3f v0 = { -1, -1 / sqrtf(3), -1 / sqrtf(6) };
Vec3f v1 = { 1, -1 / sqrtf(3), -1 / sqrtf(6) };
Vec3f v2 = { 0, 2 / sqrtf(3), -1 / sqrtf(6) };
Vec3f v3 = { 0, 0, 3 / sqrtf(6) };
glBegin( GL_TRIANGLES );
glSierpinskiPyramid( &v0, &v1, &v2, &v3, 3 );
glEnd();
glPopMatrix();
glutSwapBuffers();
}
void timer(int extra)
{
glutPostRedisplay();
glutTimerFunc(16, timer, 0);
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Sierpinski Pyramid" );
glutDisplayFunc( display );
glutTimerFunc(0, timer, 0);
glEnable( GL_DEPTH_TEST );
glEnable( GL_CULL_FACE );
glutMainLoop();
return 0;
}
I created a 5 face box and put a ball inside of it and allowed it to bounce around inside the box. I created this using glOrtho.
I want to create a 3d Brick breaker style game for my course so I want to get my camera down into the box. I changed from glOrtho to gluPerspective. I had to change some values for my box to render correctly but the ball seems to have gone missing unless i put it on the origin.
This is my value initialization:
void variableInits(){
//Floor Plane
pl1.pos.x = 0; pl1.pos.y = -50; pl1.pos.z = 0;
pl1.norm.x = 0; pl1.norm.y = 1; pl1.norm.z = 0;
//Ceiling Plane
pl2.pos.x = 0; pl2.pos.y = 50; pl2.pos.z = 0;
pl2.norm.x = 0; pl2.norm.y = -1; pl2.norm.z = 0;
//Right Wall Plane
pl3.pos.x = 50; pl3.pos.y = 0; pl3.pos.z = 0;
pl3.norm.x = -1; pl3.norm.y = 0; pl3.norm.z = 0;
//Left Wall Plane
pl4.pos.x = -50; pl4.pos.y = 0; pl4.pos.z = 0;
pl4.norm.x = 1; pl4.norm.y = 0; pl4.norm.z = 0;
//Back Wall Plane
pl5.pos.x = 0; pl5.pos.y = 0; pl5.pos.z = -100;
pl5.norm.x = 0; pl5.norm.y = 0; pl5.norm.z = 1;
//Paddle Plane
paddlePlane.max.x=.25; paddlePlane.max.y=.25; paddlePlane.max.z=1;
paddlePlane.min.x=-.25; paddlePlane.min.y=-.25; paddlePlane.min.z=1;
paddlePlane.normals.x=0; paddlePlane.normals.y=0;paddlePlane.normals.z=-0;
//Ball Init
b1.radius = 10;
b1.pathDirection.x = 0; b1.pathDirection.y = 0; b1.pathDirection.z = 0;
b1.pos.x = 0; b1.pos.y = 0, b1.pos.z = -25;
}
So my ball should draw with a radius of 10 on -25 value of the Z axis. Sadly it does not.
Maybe its an issue with my gluPerspective call?
void reshape(int width, int height)
{
if (height==0)
{
height=1;
}
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.0f,50.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Ill go ahead and post my complete code but it is a bit long.
#include <stdio.h>
#include <gl/glut.h>
#include "pHeader.h"
#define EPSILON 1.0e-8
#define ZERO EPSILON
int g_mainWindow = -1;
float g_lightPos[] = {0, 0, 0, -50};
int angle = 0;
int g_moving;
int g_mouse_x;
int g_mouse_y;
int g_x_angle;
int g_y_angle;
double mX,mY,mZ;
float speed = 1;
plane pl1, pl2, pl3, pl4 ,pl5;
paddlePl paddlePlane;
float collDist = 1000;
ball b1;
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state==GLUT_DOWN)
{
g_moving = 1;
g_mouse_x = x;
g_mouse_y = y;
}
else {
g_moving = 0;
}
}
void mouseDrag(int x, int y)
{
int dx, dy;
if (g_moving){
dx = x - g_mouse_x;
dy = y - g_mouse_y;
g_x_angle += dy;
g_y_angle += dx;
g_mouse_x = x;
g_mouse_y = y;
}
}
void paddle(int x, int y){
GLint viewport[4];
GLdouble modelview[16],projection[16];
glGetIntegerv(GL_VIEWPORT,viewport);
glGetDoublev(GL_MODELVIEW_MATRIX,modelview);
glGetDoublev(GL_PROJECTION_MATRIX,projection);
gluUnProject(x,viewport[3]-y,0,modelview,projection,viewport,&mX,&mY,&mZ);
}
/*************************************************
Vector Math
**************************************************/
float dot(vector XYZ , vector nXYZ){
return (XYZ.x * nXYZ.x) + (XYZ.y * nXYZ.x) + (XYZ.z * nXYZ.z);
}
vector cross(vector v1 , vector v2){
vector result;
result.x = v1.y * v2.z - v1.z * v2.y;
result.y = v1.z * v2.x - v1.x * v2.z;
result.z = v1.x * v2.y - v1.y * v2.x;
return result;
}
vector vectScale(float scale, vector v1){
vector result;
result.x = v1.x * scale;
result.y = v1.y * scale;
result.z = v1.z * scale;
return result;
}
vector vectorSub(vector v1, vector v2){
vector result;
result.x = v1.x - v2.x;
result.y = v1.y - v2.y;
result.z = v1.z - v2.y;
return result;
}
vector flipVect(vector v1){
vector result;
result.x = -v1.x;
result.y = -v1.y;
result.z = -v1.z;
return result;
}
vector vectorAdd(vector v1, vector v2){
vector result;
result.x = v1.x + v2.x;
result.y = v1.y + v2.y;
result.z = v1.z + v2.z;
return result;
}
/****************************************************
End Vecotor Math
****************************************************/
void planeCollision(){
//Check Ceiling
if(b1.pos.y + b1.radius >= 50){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl2.norm) , pl2.norm) , b1.pathDirection);
}
//Check Floor
if(b1.pos.y-b1.radius <= -50){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl1.norm) , pl1.norm) , b1.pathDirection);
}
//Check Right Wall
if(b1.pos.x + b1.radius >= 1){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl3.norm) , pl3.norm) , b1.pathDirection);
}
//Check Left Wall
if(b1.pos.x - b1.radius <= -1){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl4.norm) , pl4.norm) , b1.pathDirection);
}
//Check Back Wall
if(b1.pos.z - b1.radius <= -1){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl5.norm) , pl5.norm) , b1.pathDirection);
}
//Check paddle
if(b1.pos.z + b1.radius >= paddlePlane.max.z && b1.pos.x >= paddlePlane.min.x && b1.pos.x <= paddlePlane.max.x && b1.pos.y >= paddlePlane.min.y && b1.pos.y <= paddlePlane.max.y){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), paddlePlane.normals) , paddlePlane.normals) , b1.pathDirection);
}
}
void drawPlanes(){
glBegin(GL_QUADS);
//Floor
glColor3f(1,0,0);
glNormal3f( 0.0f , 1.0f, 0.0f);
glVertex3f( 050.0f , -050.0f , -100.0f);
glVertex3f(-050.0f , -050.0f , -100.0f);
glVertex3f(-050.0f , -050.0f , 000.0f);
glVertex3f( 050.0f , -050.0f , 000.0f);
//Ceiling
glColor3f(1,0,1);
glNormal3f(0.0f,-1.0f,0.0f);
glVertex3f( 050.0f, 050.0f, -100.0f);
glVertex3f(-050.0f, 050.0f, -100.0f);
glVertex3f(-050.0f, 050.0f, 000.0f);
glVertex3f( 050.0f, 050.0f, 000.0f);
//Right Wall
glColor3f(0,1,0);
glNormal3f( -1.0f , 0.0f, 0.0f);
glVertex3f(050.0f , 050.0f , 000.0f);
glVertex3f(050.0f , 050.0f , -100.0f);
glVertex3f(050.0f ,-050.0f , -100.0f);
glVertex3f(050.0f ,-050.0f, 000.0f);
//LeftWall
glColor3f(0,1,1);
glNormal3f( 1.0f , 0.0f, 0.0f);
glVertex3f(-050.0f , 050.0f , -100.0f);
glVertex3f(-050.0f , 050.0f , 000.0f);
glVertex3f(-050.0f , -050.0f , 000.0f);
glVertex3f(-050.0f , -050.0f , -100.0f);
//Back Wall
glColor3f(0,0,1);
glNormal3f( 0.0f , 0.0f, 1.0f);
glVertex3f( 050.0f , 050.0f , -100.0f);
glVertex3f(-050.0f , 050.0f , -100.0f);
glVertex3f(-050.0f , -050.0f , -100.0f);
glVertex3f( 050.0f , -050.0f , -100.0f);
glEnd();
}
void ballMove(){
glPushMatrix();
glColor3f(1,1,0);
b1.pos.x += (b1.pathDirection.x * speed); b1.pos.y += (b1.pathDirection.y * speed); b1.pos.z += (b1.pathDirection.z * speed);
glTranslatef(b1.pos.x,b1.pos.y,b1.pos.z);
glutSolidSphere(b1.radius,100,100);
printf("%.2f %.2f %.2f\n", b1.pos.x, b1.pos.y, b1.pos.z);
glPopMatrix();
planeCollision();
}
void drawPaddle(){
//printf("x %f y %f\n" , mX , mY);
glPushMatrix();
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_QUADS);
glColor3f(1,1,1);
glVertex3f(mX + 25.0f , mY + 25.0f , 0.0f);
glVertex3f(mX + -25.0f , mY + 25.0f , 0.0f);
glVertex3f(mX + -25.0f , mY + -25.0f , 0.0f);
glVertex3f(mX + 25.0f , mY + -25.0f , 0.0f);
glEnd();
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
glPopMatrix();
paddlePlane.max.x=mX + 0.25f; paddlePlane.max.y=mY + 0.25f; paddlePlane.max.z=1;
paddlePlane.min.x=mX + -0.25f; paddlePlane.min.y=mY + -0.25f; paddlePlane.min.z=1;
}
void display()
{
float red[] = {1,0,0,1};
float blue[] = {0,0,1,1};
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_NORMALIZE);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glPushMatrix();
glRotated(g_y_angle, 0, 1, 0);
glRotated(g_x_angle,1,0,0);
glTranslated(0,0,-100);
drawPaddle();
ballMove();
drawPlanes();
glPopMatrix();
angle += 1;
glFlush();
glutSwapBuffers();
}
void reshape(int width, int height)
{
if (height==0)
{
height=1;
}
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.0f,50.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void idle()
{
glutSetWindow(g_mainWindow);
glutPostRedisplay();
}
void variableInits(){
//Floor Plane
pl1.pos.x = 0; pl1.pos.y = -50; pl1.pos.z = 0;
pl1.norm.x = 0; pl1.norm.y = 1; pl1.norm.z = 0;
//Ceiling Plane
pl2.pos.x = 0; pl2.pos.y = 50; pl2.pos.z = 0;
pl2.norm.x = 0; pl2.norm.y = -1; pl2.norm.z = 0;
//Right Wall Plane
pl3.pos.x = 50; pl3.pos.y = 0; pl3.pos.z = 0;
pl3.norm.x = -1; pl3.norm.y = 0; pl3.norm.z = 0;
//Left Wall Plane
pl4.pos.x = -50; pl4.pos.y = 0; pl4.pos.z = 0;
pl4.norm.x = 1; pl4.norm.y = 0; pl4.norm.z = 0;
//Back Wall Plane
pl5.pos.x = 0; pl5.pos.y = 0; pl5.pos.z = -100;
pl5.norm.x = 0; pl5.norm.y = 0; pl5.norm.z = 1;
//Paddle Plane
paddlePlane.max.x=.25; paddlePlane.max.y=.25; paddlePlane.max.z=1;
paddlePlane.min.x=-.25; paddlePlane.min.y=-.25; paddlePlane.min.z=1;
paddlePlane.normals.x=0; paddlePlane.normals.y=0;paddlePlane.normals.z=-0;
//Ball Init
b1.radius = 10;
b1.pathDirection.x = 0; b1.pathDirection.y = 0; b1.pathDirection.z = 0;
b1.pos.x = 0; b1.pos.y = 0, b1.pos.z = -25;
}
int main(int ac, char* av[])
{
glutInit(&ac, av);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
g_mainWindow = glutCreateWindow("Hello, glut");
variableInits();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutIdleFunc(idle);
glutMouseFunc(mouse);
glutMotionFunc(mouseDrag);
glutPassiveMotionFunc(paddle);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glLightfv(GL_LIGHT0, GL_POSITION, g_lightPos);
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glutMainLoop(); //no return
}
Header
#pragma once
#ifndef pHeader_INCLUDED
#define pHeader_H_INCLUDED
typedef struct vector{
float x,y,z;
}vector;
typedef struct ball{
float radius;
vector pathDirection;
vector pos;
} ball;
typedef struct planeStruc{
vector pos;
vector norm;
} plane;
typedef struct paddlePlane{
vector min;
vector max;
vector normals;
} paddlePl;
#endif
gluPerspective cannot have a value of 0.0 for the near plane, it either generates an error (I'm not sure) or causes some weird infinity math to happen that's certainly not what you want.
You should make the value of the near plane greater than zero, and ideally as large as possible without cutting off things that you want to see. If you make it too small you'll lose a ton of z precision.
Try a value of 0.1 to start with.
this is my draw() function written in C, using vertex arrays:
void draw(float x1, float x2, float y1, float y2)
{
glPushMatrix();
glScalef(1.0 / (x2 - x1), 1.0 / (y2 - y1), 1.0);
glTranslatef(-x1, -y1, 0.0);
glColor3f(1.0, 1.0, 1.0);
if( pts.size > 0 )
{
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, (float*)pts.data );
glDrawArrays( GL_LINE_STRIP, 0, pts.size / 2 );
glDisableClientState( GL_VERTEX_ARRAY );
}
glPopMatrix();
};
before calling draw(), pts get's updated inside the update() function:
void update(double (* func)(double x), float x1, float x2, int N)
{
double x, dx = (double)1.0/(double)N;
vector_cleanup( &pts );
m = 0;
for(x = x1; x < x2; x += dx)
{
vector_resize( &pts, pts.size + 2 );
*(float*)vector_get( &pts, pts.size-2 ) = (float)x;
*(float*)vector_get( &pts, pts.size-1 ) = (float)func3(x);
m++;
}
}
I hope that by converting this code to use VBO, my graphics performance will increase.
EDIT: func3() can be anything, e.g. sin(x) or just some linear mapping. All I'm currently trying to do is, to find out how quickly I can plot a bunch of points.
Using GLEW for extension wrangling:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
typedef struct vector /*dynamic vector of void* pointers. This one is used only by the deflate compressor*/
{
void* data;
size_t size; /*in groups of bytes depending on type*/
size_t allocsize; /*in bytes*/
unsigned typesize; /*sizeof the type you store in data*/
} vector;
static unsigned vector_resize(vector* p, size_t size) /*returns 1 if success, 0 if failure ==> nothing done*/
{
if(size * p->typesize > p->allocsize)
{
size_t newsize = size * p->typesize * 2;
void* data = realloc(p->data, newsize);
if(data)
{
p->allocsize = newsize;
p->data = data;
p->size = size;
}
else return 0;
}
else p->size = size;
return 1;
}
static void vector_cleanup(void* p)
{
((vector*)p)->size = ((vector*)p)->allocsize = 0;
free(((vector*)p)->data);
((vector*)p)->data = NULL;
}
static void vector_init(vector* p, unsigned typesize)
{
p->data = NULL;
p->size = p->allocsize = 0;
p->typesize = typesize;
}
static void* vector_get(vector* p, size_t index)
{
return &((char*)p->data)[index * p->typesize];
}
/* function to calculate each data point */
float func(float x)
{
return (float)sin(x);
}
GLuint vbo = 0;
GLsizei vertcount = 0;
void update(float (* func)(float x), float x1, float x2, int N)
{
float x, dx = 1.0f/N;
vector pts;
vector_init( &pts, sizeof( float ) );
for(x = x1; x < x2; x += dx)
{
vector_resize( &pts, pts.size + 2 );
*(float*)vector_get( &pts, pts.size-2 ) = x;
*(float*)vector_get( &pts, pts.size-1 ) = func(x);
}
vertcount = (GLsizei)( pts.size / 2 );
glBindBuffer( GL_ARRAY_BUFFER, vbo );
glBufferData( GL_ARRAY_BUFFER, pts.size * pts.typesize, pts.data, GL_DYNAMIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
vector_cleanup( &pts );
}
/* plotting function - very slow */
void draw(float x1, float x2, float y1, float y2)
{
glPushMatrix();
glScalef( 1.0f / (x2 - x1), 1.0f / (y2 - y1), 1.0f );
glTranslatef( -x1, -y1, 0.0f );
glColor3f( 1.0f, 1.0f, 1.0f );
glBindBuffer( GL_ARRAY_BUFFER, vbo );
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, 0 );
glDrawArrays( GL_LINE_STRIP, 0, vertcount );
glDisableClientState( GL_VERTEX_ARRAY );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glPopMatrix();
};
/* Redrawing func */
float xmin = -10, xmax = 10, ymin = -5, ymax = 5;
void redraw(void)
{
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// -x, +x, -y, +y, number points
draw(xmin, xmax, ymin, ymax);
glutSwapBuffers();
};
/* Idle proc. Redisplays, if called. */
int nPoints = 3000;
void idle(void)
{
// shift 'xmin' & 'xmax' by one.
xmin++;
xmax++;
update(func, xmin, xmax, nPoints);
glutPostRedisplay();
};
/* Key press processing */
void key(unsigned char c, int x, int y)
{
if(c == 27) exit(0);
};
/* Window reashape */
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
};
/* Main function */
int main(int argc, char **argv)
{
GLenum err;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutCreateWindow("Graph plotter");
glutReshapeWindow(1024, 800);
// init GLEW and output some GL info
err = glewInit();
printf("GL_VERSION : %s\n", glGetString(GL_VERSION) );
printf("GL_VENDOR : %s\n", glGetString(GL_VENDOR) );
printf("GL_RENDERER : %s\n", glGetString(GL_RENDERER) );
if( GLEW_OK != err )
{
printf("glewInit failed: %s", glewGetErrorString(err));
return EXIT_FAILURE;
}
if( !glewIsSupported("GL_VERSION_1_5") )
{
printf("OpenGL version 1.5 or greater required.\n");
return EXIT_FAILURE;
}
glGenBuffers( 1, &vbo );
/* Register GLUT callbacks. */
glutDisplayFunc(redraw);
glutKeyboardFunc(key);
glutReshapeFunc(reshape);
glutIdleFunc(idle);
/* Init the GL state */
glLineWidth(2.0);
/* Main loop */
glutMainLoop();
return 0;
}