Drawing Multiple Lines with Mouse in OpenGL - c++

I'm trying to draw multiple line segments with my mouse in my OpenGL and C++ program. Right now I can draw one, and once I start drawing another one, the previous one disappears.
Below is my code that is relevant to the mouse drawing. Any suggestions on how I can draw multiple lines?
LineSegment seg;
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { // if left button is clicked, that is the start point
seg.x1 = seg.x2 = x;
seg.y1 = seg.y2 = y;
}
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { // once they lift the mouse, thats the finish point
seg.x2 = x;
seg.y2 = y;
}
}
void motion(int x, int y) {
seg.x2 = x;
seg.y2 = y;
glutPostRedisplay(); // refresh the screen showing the motion of the line
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT); // clear the screen
glBegin(GL_LINES); // draw lines
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
glEnd();
glutSwapBuffers();
}

void display(void) {
glClear(GL_COLOR_BUFFER_BIT); // clear the screen
glBegin(GL_LINES); // draw lines
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
glEnd();
glutSwapBuffers();
}
You need to save in a data structure the previous line segments and add to it whenever you click with the mouse. Then the drawloop needs to iterate through that data structure and draw each of the saved line segments.
std::vector<LineSegment> segmentvector;
//Each time you release the mouse button, add the current line segment to this vector
/*...*/
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_LINES);
for(const LineSegment & seg : segmentvector) {
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
}
glVertex2f(currseg.x1, currseg.y1);
glVertex2f(currseg.x2, currseg.y2);
glEnd();
I would also STRONGLY ADVISE that you not use Fixed-Function-Pipeline functions when learning OpenGL. There are lots of tutorials online for learning modern OpenGL.

You need to set up some logic to save the state of the lines you have already drawn. Currently, you never start drawing another line, you just reset the start position of the current line.
Here is a possible solution for what you're looking for:
std::vector<LineSegment> segs;
LineSegment currentSeg;
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
LineSegment newSeg; // Make a new segment
newSeg.x1 = newSeg.x2 = x;
newSeg.y1 = newSeg.y2 = y;
segs.insert(newSeg); // Insert segment into segment vector
currentSeg = newSeg;
}
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
currentSeg.x2 = x;
currentSeg.y2 = y;
}
}
void motion(int x, int y) {
currentSeg.x2 = x;
currentSeg.y2 = y;
glutPostRedisplay();
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_LINES);
// Iterate through segments in your vector and draw each
for(const auto& seg : segs) {
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
}
glEnd();
glutSwapBuffers();
}

Related

Using GLFW to capture mouse dragging? [C++]

So i'm trying to capture mouse dragging in my OpenGL application. I've done the following so far:
glfwSetMouseButtonCallback(window, mouse_callback);
static void mouse_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button == GLFW_MOUSE_BUTTON_LEFT) {
double x;
double y;
glfwGetCursorPos(window, &x, &y);
if (previous_y_position - y > 0)
{
camera_translation.y -= 1.0f;
previous_y_position = y;
}
else
{
camera_translation.y += 1.0f;
previous_y_position = y;
}
}
}
The problem with this though is if I would like to zoom in I need to move my mouse upwards and then click repeatedly. For some reason if I press down on the left mouse button and drag upwards it does nothing.
In cursor_pos_callback, just confirm if the button is pressed, and that works.
void mouse_cursor_callback( GLFWwindow * window, double xpos, double ypos)
{
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_RELEASE)
{
return;
}
// `write your drag code here`
}
mouse_callback is stateless. It receives events, momentary "actions".
You need to make your program to "remember" that mouse button is pressed. So that when button is pressed in a frame 1, you can refer to this information in all the frames after that and before mouse button is released.
The simple way is to flip a boolean flag on press/release:
static void mouse_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if(GLFW_PRESS == action)
lbutton_down = true;
else if(GLFW_RELEASE == action)
lbutton_down = false;
}
if(lbutton_down) {
// do your drag here
}
}
Schematically:
state released pressed released
timeline -------------|------------------------------|---------------
^ ^
mouse_callback calls GLFW_PRESS GLFW_RELEASE
The hard way is to use a state machine (especially if you need more complicated combinations of states of input controllers).

Processing Draw Program

I was making a basic paint/draw program in processing where the Left click would draw a point and a Right click would draw a background colored rectangle to "erase". The thing is that when I start erasing it wont let me go back to drawing. Also i would like to make the dots draw faster so it looks more like a line rather than a dotted line. Thanks!
Here is the code:
void setup() {
size(600, 600);
background(#C4C4C4);
}
void draw() {
frameRate(60);
if (mouseButton == LEFT) {
fill(#030303);
point(mouseX, mouseY);
}
else if(mouseButton == RIGHT){
fill(#C4C4C4);
noStroke();
rect(mouseX-15, mouseY-15, 30, 30);
}
}
Your main problem is that you call noStroke() when the user presses the right button, but you never set the stroke back when the user presses the left button. The point() function uses the stroke color, not the fill color, so you have to reset it. This works:
void setup() {
size(400, 400);
background(#C4C4C4);
}
void draw() {
if (mouseButton == LEFT) {
stroke(#030303);
point(mouseX, mouseY);
}
else if(mouseButton == RIGHT){
fill(#C4C4C4);
noStroke();
rect(mouseX-15, mouseY-15, 30, 30);
}
}
As for drawing lines instead of a point, you can use the pmouseX and pmouseY variables (which hold the previous position of the mouse) to draw a line. Specifically, change this:
point(mouseX, mouseY);
To this:
line(pmouseX, pmouseY, mouseX, mouseY);

SDL2 double buffer not working, still tearing

I need a double buffer because i'm starting to notice tearing when I want to move my texture made tile map around the screen via mouse click.
I'm using SDL2 and this is a SDL2 specific question, check out my code that produces tearing, whats wrong?
//set up buffer and display
bufferTexture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_STREAMING,800,600);
displayTexture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,800,600);
while(running)
{
handleEvents();
SDL_SetRenderTarget(renderer,bufferTexture); // everything goes into this buffer
SDL_RenderClear(renderer); // clear buffer before draw
gTileMovement.updateMapCoordinates();
for(int i = 0; i < MAP_ROWS; i++)//rows
{
for(int j = 0; j < MAP_COLUMNS; j++)//columns
{
x = (j * 100) - (i * 100);
y = ((i * 100) + (j * 100)) / 2;
drawTiles(i,j,x,y);
}
}
//move from buffer to display texture
memcpy(&displayTexture,&bufferTexture,sizeof((&bufferTexture)+1));
//change render target back to display texture
SDL_SetRenderTarget(renderer,displayTexture);
//show it all on screen
SDL_RenderPresent(renderer);
}
for all it matters here is my drawTiles function too, is this not conventional? :
void drawTiles(int i,int j,int x,int y)
{
//updates based on a mouse clicks xy coords
gTileMovement.updateMapCoordinates();
if(tileMap[i][j] == 1) // grass?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[1]);
}
if(tileMap[i][j] == 0) // wall?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[0]);
}
if(tileMap[i][j] == 2) // tree?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[2]);
}
}
Which followes into how I SDL_RenderCopy the tiles through a class. This copies the textures onto the current targeted renderer does it not? Which is the buffer texture if i'm not mistaken.
void LTexture::render(int x, int y, SDL_Rect * clip)
{
SDL_Rect renderQuad = {x, y, mWidth, mHeight};
if(clip != NULL)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
SDL_RenderCopy(renderer, mTexture, clip, &renderQuad);
}

3D / FPS Camera Issues - SDL2

I got a problem with my code, I'm trying to make a First Person 3D Camera.
I use SDL_GetKeyboardState(NULL) to get the pressed keys.
When I press one of the defined keys nothing happens, why?
Camera (Controll):
void Control(float movevel, float mousevel, bool mi, SDL_Window* window)
{
if (mi) //if the mouse is in the screen
{
int MidX = 640 / 2; //middle of the screen
int MidY = 480 / 2;
SDL_ShowCursor(SDL_DISABLE); //we don't show the cursor
int tmpx, tmpy;
SDL_GetMouseState(&tmpx, &tmpy); //get the current position of the cursor
camYaw += mousevel*(MidX - tmpx); //get the rotation, for example, if the mouse current position is 315, than 5*0.2, this is for Y
camPitch += mousevel*(MidY - tmpy); //this is for X
lockCamera();
//SDL_WarpMouse(MidX, MidY); //move back the cursor to the center of the screen
SDL_WarpMouseInWindow(window, MidX, MidY);
const Uint8* kstate = SDL_GetKeyboardState(NULL);
if (kstate[SDLK_w])
{
if (camPitch != 90 && camPitch != -90)
{ //if we are facing directly up or down, we don't go forward, it will be commented out, when there will be gravity
moveCamera(movevel, 0.0); //move forward
}
moveCameraUp(movevel, 0.0); //move up/down
}
if (kstate[SDLK_s])
{
//same, just we use 180 degrees, so we move at the different direction (move back)
if (camPitch != 90 && camPitch != -90)
{
moveCamera(movevel, 180.0);
}
moveCameraUp(movevel, 180.0);
}
if (kstate[SDLK_a])
{ //move left
moveCamera(movevel, 90.0);
}
if (kstate[SDLK_d])
{ //move right
moveCamera(movevel, 270);
}
}
glRotatef(-camPitch, 1.0, 0.0, 0.0);
glRotatef(-camYaw, 0.0, 1.0, 0.0);
}
Main (Loop)
while (running)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
SDL_Event ev;
while (SDL_PollEvent(&ev))
{
switch (ev.type)
{
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
int x, y;
SDL_GetMouseState(&x, &y);
Main::handleKeys(ev.key.keysym.scancode, x, y);
break;
}
}
Main::update(window);
Main::render(window);
SDL_GL_SwapWindow(window);
}
Main (Update):
void Main::update(SDL_Window* window)
{
Control(0.2, 0.2, mousein, window);
UpdateCamera(0.2); //move the camera to the new location
}
you should call the glRotatef functions before you translate the camera, otherwise it will be rotated about the origin and not its position
Use SDL_PumpEvents() to update the state array. (From the SDL_GetKeyboardState() wiki).
I believe that if you do:
SDL_PumpEvents();
SDL_GetKeyboardState(NULL);
you should get the results you are looking for. (You need to SDL_PumpEvents every loop as well as SDL_GetKeyboardState obviously)
(EDIT)
Another option:
case SDL_KEYDOWN:
switch(event.key.keysym.sym){
case SDLK_w
(... code)
break;
case SDLK_s:
(... code)
break;
case SDLK_a:
(... code)
break;
case SDLK_d:
(... code)
break;
You can also send the event.key.keysym.sym to a function and use it their ofcourse.

Displaying a moving object in sdl

I have a problem with my SDL program. My goal is to make a dot move along a line. I have all the coordinates saved in a data file. So I just wanted to read them from the file and display the dot at the right position.
The dot class (which is named linefollower) looks like this.
class Linefollower
{
private:
int x, y;
char orientation;
public:
//Initializes the variables
Linefollower();
void set(int m_x, int m_y, char m_orietnation);
void show();
char get_orientation();
};
Linefollower::Linefollower()
{
x = 0;
y = 0;
orientation = 'E';
}
void Linefollower::set(int m_x, int m_y, char m_orientation)
{
x = m_x;
y = m_y;
orientation = m_orientation;
}
void Linefollower::show()
{
//Show the linefollower
apply_surface(x, y, linefollower, screen );
}
char Linefollower::get_orientation()
{
return orientation;
}
The apply_surface function.
void apply_surface( int x, int y, SDL_Surface * source, SDL_Surface* destination)
{
//Temporary rectangle to hold the offsets
SDL_Rect offset;
//Get the offsets
offset.x = x;
offset.y = y;
//Blit the surface
SDL_BlitSurface( source, NULL, destination, &offset);
}
The loop which ought to display the animation looks like this.
//While the user hasn't quit
while( quit == false )
{
//Apply the surface to the screen
apply_surface( 0, 0, image, screen );
fin.read((char*) &my_linefollower, sizeof my_linefollower);
if(my_linefollower.get_orientation() == 'Q')
break;
my_linefollower.show();
//Upadate the screen
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
SDL_Delay(200);
}
Now I was expecting, that I get a moving dot on the screen, but the only thing which I got is the background (image) for a few seconds untill the if(my_linefollower.get_orientation() == 'Q')
break; was true. What do I do wrong?
PS: I guess it is worth noticing that I am a beginner in SDL and I took most of the code from a tutorial. Learning it exactly would be a waste of time for me, since it is unlikely that I am going to use it again any time soon.
First, you should change your offset in apply_surface to something like this:
SDL_Rect offset = { x, y, 0, 0 };
SDL_Rect doesn't have a constructor to set your members to 0 by default, so you get uninitialized memory for your width and height.
Also, you should check what linefollower contains, if it's a valid SDL_Surface. Removing your file reading code and manually controlling a Linefollower will allow you to easily find where the error comes from.
Use a debugger to validate your x and y coordinates.
Other than that, you code should work, although your window will be unresponsive because you're not pumping events through SDL_PollEvent.