I've successfully written code that takes the (x,y) position of one of my fingers measured by my Leap Motion controller (infrared scanning device that tracks finger and hand movements), and draws a point with similar (x,y) coordinates using OpenGL. Basically, you move your finger, you move the point displayed on the screen.
What I'd like to do now is display the last 100 locations recorded - so, instead of seeing a single point move around, you see a serpentine collection of points that snakes around and follows the movement of your fingertip.
I tried to do this using two vectors - one to hold the x and y position during a given frame, and one to hold a bunch of the previously mentioned vectors. By popping off the first element in the vector after its size goes beyond 100, I figured I could loop through all those points and draw them within my display method.
It compiles fine, but after drawing a few points, I get a runtime error saying "vector iterator not incrementable." Not sure how to deal with that.
Code:
#include <iostream>
#include <fstream>
#include "Leap.h"
#include <math.h>
#include "GL/glut.h"
#include <deque>
using namespace Leap;
using namespace std;
bool one = true;
double x = 10, y = 100;
double screenWidth = 480, screenHeight = 480;
double time, timeDisplayed = 5.0 * (pow(10.0, 9.0));
vector < double > entry, *temp;
vector < vector< double > > collection;
class SampleListener : public Listener {
public:
int firstFrame, firstTime;
bool first;
virtual void onInit(const Controller&);
virtual void onConnect(const Controller&);
virtual void onExit(const Controller&);
virtual void onFrame(const Controller&);
};
void SampleListener::onInit(const Controller& controller){
cout << "Initialized.\n";
first = true;
}
void SampleListener::onConnect(const Controller& controller){
cout << "Connected.\n";
controller.enableGesture(Gesture::TYPE_SCREEN_TAP);
}
void SampleListener::onExit(const Controller& controller){
cout << "Exited.\n";
}
void SampleListener::onFrame(const Controller& controller) {
const Frame frame = controller.frame();
//some initial frame processing/file writing
if (first){
first = !first;
firstFrame = frame.id();
firstTime = frame.timestamp();
}
else {
//record hand information
if (!frame.hands().isEmpty()){
const Hand hand = frame.hands().frontmost();
const Finger track = hand.fingers().frontmost();
x = track.tipPosition().x;
y = track.tipPosition().y;
time = frame.timestamp() - firstTime;
entry.push_back(x);
entry.push_back(y);
entry.push_back(time);
collection.push_back(entry);
entry.clear();
while (collection.size() > 100 && !collection.empty()){
collection.erase(collection.begin());
}
}
}
}
// Create a sample listener and controller
SampleListener listener;
Controller controller;
void display(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_POINTS);
for (auto i = collection.begin(); i != collection.end(); *i++){
cout << collection.size() << endl;
glVertex2f(i->at(0), i->at(1));
}
glEnd();
glFlush();
glutSwapBuffers();
}
void init(){
cout << "in init" << endl;
glClearColor(0.0, 0.0, 0.0, 0.0);
glColor3f(1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-screenWidth/2, screenWidth/2, 0, screenHeight);
glViewport(0, 0, screenWidth, screenHeight);
glPointSize(3.0f);
cout << "leaving init" << endl;
}
void idle(void){
glutPostRedisplay();
}
//<<<<<<<<<<<<<<<<<<<<<<< myKeys >>>>>>>>>>>>>>>>>>>>>>>>
void myKeys(unsigned char key, int x, int y)
{
switch(key)
{
case 'q': // Quit
// Remove the sample listener when done
controller.removeListener(listener);
exit(0);
}
}
int main (int argc, char** argv) {
listener.first = true;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(screenWidth, screenHeight);
glutInitWindowPosition(800, 100);
glutCreateWindow("test");
init();
glutDisplayFunc(display);
glutKeyboardFunc(myKeys);
glutIdleFunc(idle);
controller.addListener(listener);
glutMainLoop();
return 0;
}
If this is threaded, then your collection.erase() in SampleListener::onFrame() would invalidate the iterator in display(). You need a mutex there. Heck, the push_back() could invalidate your iterator. Seems like you want a fixed-length structure with head/tail pointers rather than this dynamic vector.
Related
I'm working with OpenGL/Glut and trying to make a simple "rain" effect. I have a class for raindrops:
class Drop {
private:
int speed;
int posY;
int posX;
public:
Drop() { // constructor
speed = 5;
posY = 15;
posX = (rand() % 500);
}
void move() {
speed += 1;
posY += speed;
}
int getPosY() {
return posY;
}
int getPosX() {
return posX;
}
};
A list of Drops alongside a function to add Drops to the list.
list<Drop> listDrops;
void placeDrop() {
Drop d = Drop();
listDrops.push_back(d);
}
A function to redraw the OpenGL window:
void refreshDisplay() {
if (rand() % 5 == 1) {
placeDrop();
}
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(5);
glBegin(GL_POINTS);
for (Drop a : listDrops) {
glColor3f(255,255,255); // white
glVertex2i(a.getPosX(),a.getPosY());
a.move();
}
glEnd();
glFlush();
glutSwapBuffers();
}
void repeater(int val) {
glutPostRedisplay();
glutTimerFunc(5, repeater, 0);
}
And of course my main and Glut init:
int main(int argc, char** argv) {
srand(time(NULL));
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowSize(500, 500);
glutCreateWindow("Rain");
gluOrtho2D(0.0, 500.0, 500.0, 0.0);
glutDisplayFunc(refreshDisplay);
glutTimerFunc(0, repeater, 0);
glutMainLoop();
return 0;
}
All of the Drops are initialized properly, I can confirm by outputting their co-ordinates after creation. But they don't move at all. It's like Drop::move() is being ignored.
If I instead initialize a global Drop aDrop (just one), and get rid of the iterator over listDrops in refreshDisplay() and instead just have aDrop drawn, it does move as expected.
You are calling move() on a copy of each Drop that is only scoped for one loop iteration
You can use
//---------
// |
// v
for (Drop &a : listDrops) {
glColor3f(255,255,255); // white
glVertex2i(a.getPosX(),a.getPosY());
a.move();
}
To take the Drops by reference
What you did previously was equivalent to
for (int i=0; i<listDrops.size();++i) {
Drop a = listDrops[i]; //a is a copy of the entry
assert(&a != &listDrops[i])
//...
}
I'm trying to left click and drag any 3d Object and if i let go of it, it should stay in its new position, how would i achieve this? The 3d object is loaded from the draw function that i have in a header file.
Someone said i should be using glutMouseFunc or glutMotionFunc.
void MouseClickCallbackFunction(int button, int state, int x, int y)
{
if (state == GLUT_DOWN) {
if (button == GLUT_LEFT)
{
std::cout << "Left " << x << " " << y <<std::endl;
leftClick.x = x;
leftClick.y = y;
}
else if (button == GLUT_RIGHT_BUTTON) {
std::cout << "Right " << x << " " << y << std::endl;
rightClick.x = x;
rightClick.y = y;
}
}
theGame->mouseClicked(button, state, x, y);
glutPostRedisplay();
}
/* function MouseMotionCallbackFunction()
* Description:
* - this is called when the mouse is clicked and moves
*/
void MouseMotionCallbackFunction(int x, int y)
{
theGame->mouseMoved(x, y);
glutPostRedisplay();
}
int main(int argc, char **argv)
{
/* initialize the window and OpenGL properly */
glutInit(&argc, argv);
glutInitContextVersion(4, 2);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("OpenGL Framework");
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
std::cout << "GLEW could not be initialized. \n";
system("pause");
return 0;
}
//glewInit();
std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
/* set up our function callbacks */
glutDisplayFunc(DisplayCallbackFunction);
glutKeyboardFunc(KeyboardCallbackFunction);
glutKeyboardUpFunc(KeyboardUpCallbackFunction);
glutMouseFunc(MouseClickCallbackFunction);
glutMotionFunc(MouseMotionCallbackFunction);
glutTimerFunc(1, TimerCallbackFunction, 0);
/* init the game */
theGame = new Game();
theGame->initializeGame();
/* start the game */
glutMainLoop();
return 0;
}
Here is the Draw fucntion with the object
void Game::draw()
{
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
PassThrough.Bind();
PassThrough.SendUniformMat4("uModel", MonkeyTransform.data, false);
PassThrough.SendUniformMat4("uView", CameraTransform.GetInverse().data, false);
PassThrough.SendUniformMat4("uProj", CameraProjection.data, false);
PassThrough.SendUniform("uTex", 0);
PassThrough.SendUniform("LightPosition", CameraTransform.GetInverse() * vec4(4.0f,0.0f,0.0f,1.0f));
PassThrough.SendUniform("LightAmbient", vec3(0.15f, 0.15f, 0.15f));
PassThrough.SendUniform("LightDiffuse", vec3(0.7f,0.1f,0.2f));
PassThrough.SendUniform("LightSpecular", vec3(0.8f,0.1f,0.1f));
PassThrough.SendUniform("LightSpecularExponent", 50.0f);
PassThrough.SendUniform("Attenuation_Constant", 1.0f);
PassThrough.SendUniform("Attenuation_Linear", 0.1f);
PassThrough.SendUniform("Attenuation_Quadratic", 0.01f);
glBindVertexArray(Monkey.VAO);
glDrawArrays(GL_TRIANGLES, 0, Monkey.GetNumVerticies());
glBindVertexArray(0);
PassThrough.unBind();
glutSwapBuffers();
}
The concept is called mouse picking. A method to achieve this is ray casting. Essentially what you want to do is map the mouse coordinates to a region in your scene. In your case you want to check if the position is within the monkey. Then it is as simple as handling the mouse up, down, and move events.
Mouse down: check if object is being picked. If it is, great- maybe highlight it or something. Store ref to object picked; store position of mouse at beginning of pick (pos)
Mouse move: is mouse down? Is picked object ref? Update object position based on delta between pos and coord in mouse move event
Mouse up: clear picked obj ref, position
This link may be of interest http://antongerdelan.net/opengl/raycasting.html
in this method
void Sierpinski(GLintPoint points) {
glClear(GL_COLOR_BUFFER_BIT);
GLintPoint T[3] = {{10,10},{600,10},{300,600}};
int index=rand()%3;
GLintPoint point=points[index];
drawDot(point.x, point.y);
for (int i = 0;i<55000;i++) {
index=rand()%3;
point.x=(point.x+points[index].x)/2;
point.y=(point.y+points[index].y)/2;
drawDot(point.x,point.y);
}
glFlush();
}
that uses the structure i made
struct GLintPoint {
GLint x,y;
};
it says that there is an error and that "no operator "[]" matches these operands, operator types are GLintPoint[int]" where i try to assign a value from points to point. Well i did use the right brackets and it is an int in there so what is the problem? FYI this code is to draw the Sierpinski gasket with the user making the initial 3 points by clicking the screen with the mouse. Just in case you would like to see it here is the whole program.
#include <windows.h>
#include <gl/Gl.h>
#include "glut.h"
#include <iostream>
using namespace std;
const int screenWidth = 640;
const int screenHeight = 480;
struct GLintPoint {
GLint x,y;
};
void display (void){
glClear(GL_COLOR_BUFFER_BIT);
//glColor3f(1.0,1.0,1.0);
glFlush();
}
void drawDot( GLint x, GLint y)
{
glBegin( GL_POINTS );
glVertex2i( x, y );
glEnd();
}
void Sierpinski(GLintPoint points) {
glClear(GL_COLOR_BUFFER_BIT);
GLintPoint T[3] = {{10,10},{600,10},{300,600}};
int index=rand()%3;
GLintPoint point=points[index];
drawDot(point.x, point.y);
for (int i = 0;i<55000;i++) {
index=rand()%3;
point.x=(point.x+points[index].x)/2;
point.y=(point.y+points[index].y)/2;
drawDot(point.x,point.y);
}
glFlush();
}
void myMouse(int button, int state, int x, int y){
static GLintPoint corner[2];
static int numCorners = 0;
if(state == GLUT_DOWN){
if(button == GLUT_LEFT_BUTTON){
corner[numCorners].x = x;
corner[numCorners].y = screenHeight - y;
if(++numCorners ==2 ){
glRecti(corner[0].x, corner[0].y, corner[1].x, corner[1].y);
numCorners = 0;
glFlush();
}
}
else if(button == GLUT_RIGHT_BUTTON){
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
}
}
}
void myInit() {
glClearColor(1.0,1.0,1.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0f,0.0f,0.0f);
glPointSize(2.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble)screenWidth, 0.0, (GLdouble)screenHeight);
}
void main (int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(screenWidth, screenHeight);
glutInitWindowPosition(100,150);
glutCreateWindow("mouse dots");
glutDisplayFunc(display);
glutPostRedisplay();
glutMouseFunc(myMouse);
myInit();
glutMainLoop();
}
I'm inferring from the name of the function argument, points, that you intend the function to accept an array of points. But it's actually just written to take one.
So when you write points[index] the compiler is looking for operator[] in your GLintPoint struct (and there isn't one).
If you change the function prototype to take an array of GLintPoints I think you'll have better luck.
I have this code:
#include <iostream>
#include <GL/glut.h>
using namespace std;
bool* Keys = new bool[256];
void keyboardDown(unsigned char key, int x, int y)
{
Keys[key] = true;
}
void keyboardUp(unsigned char key, int x, int y)
{
Keys[key] = false;
}
void reshape(int width, int height)
{
GLfloat fieldOfView = 90.0f;
glViewport(0, 0, (GLsizei)width, (GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fieldOfView, (GLfloat)width / (GLfloat)height, 0.1, 500.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void draw()
{
if (Keys['e'])
cout << "e" << endl;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_QUADS);
glColor3f(0.1, 0.1, 0.1); glVertex3f(1.0, 1.0, -1.0);
glColor3f(0.1, 0.9, 0.1); glVertex3f(1.0, -1.0, -1.0);
glColor3f(0.9, 0.1, 0.1); glVertex3f(-1.0, -1.0, -1.0);
glColor3f(0.1, 0.1, 0.9); glVertex3f(-1.0, 1.0, -1.0);
glEnd();
glFlush();
glutSwapBuffers();
}
void initGL(int width, int height)
{
reshape(width, height);
glClearColor(0.1f, 0.5f, 0.7f, 1.0f);
glClearDepth(1.0f);
glOrtho(0, 1, 0, 1, 1, 10);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
}
/* initialize GLUT settings, register callbacks, enter main loop */
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(800, 800);
glutInitWindowPosition(100, 100);
glutCreateWindow("Perspective's GLUT Template");
glutKeyboardFunc(keyboardDown);
glutKeyboardUpFunc(keyboardUp);
glutSpecialFunc(keyboardSpecialDown);
glutSpecialUpFunc(keyboardSpecialUp);
glutReshapeFunc(reshape);
glutDisplayFunc(draw);
glutIgnoreKeyRepeat(true); // ignore keys held down
initGL(800, 600);
glutMainLoop();
return 0;
}
When I start the program, it writes 'e' only when I press on the mouse. I can't find the problem. Why does it only work when the mouse is held down?
You dont have mouse functions in your code, thats why it is not working. Insert from here : http://www.zeuscmd.com/tutorials/glut/03-MouseInput.php
#include <iostream>
#include <gl/glut.h>
using namespace std;
bool lbuttonDown = false;
bool init()
{
return true;
}
void display()
{
}
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_RIGHT_BUTTON)
{
if (state == GLUT_DOWN)
cout << "Right button pressed"
<< endl;
else
cout << "Right button lifted "
<< "at (" << x << "," << y
<< ")" << endl;
}
else if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
lbuttonDown = true;
else
lbuttonDown = false;
}
}
void motion(int x, int y)
{
if (lbuttonDown)
cout << "Mouse dragged with left button at "
<< "(" << x << "," << y << ")" << endl;
}
void motionPassive(int x, int y)
{
cout << "Mouse moved at "
<< "(" << x << "," << y << ")" << endl;
}
void entry(int state)
{
if (state == GLUT_ENTERED)
cout << "Mouse Entered" << endl;
else
cout << "Mouse Left" << endl;
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitWindowPosition(200, 200);
glutInitWindowSize(200, 200);
glutCreateWindow("03 - Mouse Input");
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutPassiveMotionFunc(motionPassive);
glutEntryFunc(entry);
if (!init())
return 1;
glutMainLoop();
return 0;
}
I had the same problem [using freeglut] and calling 'draw()' [or whatever display callback you use] right after glutPostRedisplay() in the key callback function forced it to do the redraw. I actually don't know why it behaves like this. After data is changed by keyboard input and the key callback returned, a redraw is expected but not executed.
I'm trying to make it so that when I press W, A, S, or D, it moves a bunch of lines around on the screen. I read in all the lines from a file and display them, and that works fine.
So I have a keyboard function that has a switch statement that increments an X and Y variable which I use in glTranslate inside of my display function, but my lines don't move. Could anyone help me out with this?
#include <GL/glut.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
int height = 640, width = 640;
int X = 0, Y = 0;
void drawPolyLineFile(char * fileName) {
std::fstream inStream;
inStream.open(fileName, std::ios::in);
if (inStream.fail()) {
std::cerr<< "Error opening file";
return;
}
glClear(GL_COLOR_BUFFER_BIT);
GLint numpolys, numLines, x, y;
inStream >> numpolys;
for ( int j =0; j < numpolys; j++) {
inStream >> numLines;
glBegin(GL_LINE_STRIP);
for (int i = 0; i < numLines; i++) {
inStream >> x >> y;
glVertex2i(x, y);
}
glEnd();
}
//glutSwapBuffers();
inStream.close();
}
void display(void)
{
/* clear all pixels */
glClear (GL_COLOR_BUFFER_BIT);
glTranslatef(X, Y, 0);
drawPolyLineFile("dino.dat");
/* don't wait!
* start processing buffered OpenGL routines
*/
glutSwapBuffers();
}
void init (void)
{
/* select clearing color */
glClearColor (0.0, 0.0, 0.0, 0.0);
/* initialize viewing values */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 640.0, 0.0, 640.0, -1.0, 1.0);
}
void keyboard(unsigned char key, int x, int y) {
float speed = 5.0f;
switch ( key ) {
case 'a':
X -= speed;
std::cerr<< X << std::endl;
break;
case 'd':
X += speed;
std::cerr<< X << std::endl;
break;
case 's':
Y -= speed;
std::cerr<< Y << std::endl;
break;
case 'w':
Y += speed;
std::cerr<< Y << std::endl;
break;
default:
break;
}
}
/*
* Declare initial window size, position, and display mode
* (single buffer and RGBA). Open window with "hello"
* in its title bar. Call initialization routines.
* Register callback function to display graphics.
* Enter main loop and process events.
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize (640, 640);
glutInitWindowPosition (100, 100);
glutCreateWindow ("hello");
init ();
glutKeyboardFunc(keyboard);
glutDisplayFunc(display);
glutMainLoop();
return 0; /* ANSI C requires main to return int. */
}
I haven't read too carefully, but you're most likely missing a
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
before the call to glTranslate. glTranslate composes a translation. From your code it seems like you're expecting it to set a translation.
You'll probably also want to avoid reading from disk at every frame. Load your model into a data structure at startup and render from that instead.
You were missing a glutPostRedisplay in your keyboard handler. Without that no redraw of the window would have been initiated. Settings display as idle func does the trick as well, but works differently: The window is constantly redrawn, almost all CPU time used up for drawing. Your code also had some other fallacies. I fixed that.
#include <GL/glut.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
int X = 0, Y = 0;
void drawPolyLineFile(char * fileName) throw(std::runtime_error)
{
std::fstream inStream;
inStream.open(fileName, std::ios::in);
if (inStream.fail()) {
throw std::runtime_error("Error opening file")
}
GLint numpolys, numLines, x, y;
inStream >> numpolys;
for ( int j =0; j < numpolys; j++) {
inStream >> numLines;
glBegin(GL_LINE_STRIP);
for (int i = 0; i < numLines; i++) {
inStream >> x >> y;
glVertex2i(x, y);
}
glEnd();
}
inStream.close();
}
void display(void)
{
int window_width, window_height;
window_width = glutGet(WINDOW_WIDTH);
window_height = glutGet(WINDOW_HEIGHT);
glViewport(0, 0, window_width, window_height);
/* clear all pixels */
glClearColor (0.0, 0.0, 0.0, 0.0);
glClear (GL_COLOR_BUFFER_BIT);
/* always set all projection parameters a new
* each rendering pass
*/
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, window_width, 0.0, window_height, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(X, Y, 0);
drawPolyLineFile("dino.dat");
/*
* SwapBuffers will flush the OpenGL queue indeed,
* but more importantly it brings the content from
* the back buffer to the front
*/
glutSwapBuffers();
}
void keyboard(unsigned char key, int x, int y) {
float speed = 5.0f;
switch ( key ) {
case 'a':
X -= speed;
std::cerr<< X << std::endl;
break;
case 'd':
X += speed;
std::cerr<< X << std::endl;
break;
case 's':
Y -= speed;
std::cerr<< Y << std::endl;
break;
case 'w':
Y += speed;
std::cerr<< Y << std::endl;
break;
default:
break;
}
glutPostRedisplay();
}
/*
* Declare initial window size, position, and display mode
* (single buffer and RGBA). Open window with "hello"
* in its title bar. Call initialization routines.
* Register callback function to display graphics.
* Enter main loop and process events.
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize (640, 640);
glutInitWindowPosition (100, 100);
glutCreateWindow ("hello");
glutKeyboardFunc(keyboard);
glutDisplayFunc(display);
try {
glutMainLoop();
} catch (std::runtime_error &err) {
std::cerr << err.what();
}
return 0;
}