1

I'm trying to create an openGL program that allows the user to click anywhere on the window, creating a point in that exact cursor coordinate for every click, and then draw a curve through those points. I did manage to implement that part, and draw a straight line through every point, but now I have to make that line curved using catmull-rom. I'm new to this concept, and I did find examples on how the function glm:catmullRom() works, but I can't find an example on how it works with the rest of the program to draw the curve. I attempted to use it, but now I get a straight line that's cut off from the window. I need to know how to approach this setup correctly. Basically, this is what I've done so far:

double* cursorX = new double;
double* cursorY = new double;
vector<GLfloat>controlPoints = {0.0, 0.0, 0.0}; // Default value (can't pass in an empty vector to VBO)
glm::vec3 point1;
glm::vec3 point2;
glm::vec3 point3;
glm::vec3 point4;
vector<glm::vec3>interpolatedPoints;

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
    if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
    {
        glfwGetCursorPos(window, cursorX, cursorY);
        controlPoints.push_back(*cursorX); // X-coordinate
        controlPoints.push_back(*cursorY); // Y-coordinate
        controlPoints.push_back(0);        // Z-coordinate (always fixed at 0)
    }
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{

    if (key == GLFW_KEY_ENTER && action == GLFW_PRESS)
    {
        // Test with 8 points, each point having X,Y, and Z, meaning 24 indices in total!
        point1 = { controlPoints[1], controlPoints[2], controlPoints[3] };
        point2 = { controlPoints[4], controlPoints[5], controlPoints[6] };
        point3 = { controlPoints[7], controlPoints[8], controlPoints[9] };
        point4 = { controlPoints[10], controlPoints[11], controlPoints[12] };
        interpolatedPoints.push_back(glm::catmullRom(point1, point2, point3, point4, 0));

        point1 = { controlPoints[13], controlPoints[14], controlPoints[15] };
        point2 = { controlPoints[16], controlPoints[17], controlPoints[18] };
        point3 = { controlPoints[19], controlPoints[20], controlPoints[21] };
        point4 = { controlPoints[22], controlPoints[23], controlPoints[24] };
        interpolatedPoints.push_back(glm::catmullRom(point1, point2, point3, point4, 0.5));
    }
}

// Initialize VAO and VBO
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

// Bind VAO
glBindVertexArray(VAO);

// Bind and implement VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferData(GL_ARRAY_BUFFER, controlPoints.size() * sizeof(GLfloat), &controlPoints.front(), GL_STATIC_DRAW);

// Connecting coordinates to shader
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);

    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glBindVertexArray(VAO);

        // Update VBO
        glBufferData(GL_ARRAY_BUFFER, interpolatedPoints.size() * sizeof(glm::vec3), &interpolatedPoints.front(), GL_STATIC_DRAW);

        glDrawArrays(GL_LINE_STRIP, 0, interpolatedPoints.size() * sizeof(glm::vec3));

    // ...
    }
    // ...
}
Jonathan
  • 55
  • 8

1 Answers1

1

std::vector indexes start with 0, but your code starts indexing from 1. So fix it accordingly. Also for some reason you push a dummy point into controlPoints which means that it actually starts at 3:

    point1 = { controlPoints[3], controlPoints[4], controlPoints[5] };
    point2 = { controlPoints[6], controlPoints[7], controlPoints[8] };
    // ...

A better solution is to change it to a vector of glm::vec3 and remove that dummy element. You do it as follows:

vector<glm::vec3> controlPoints;

and

controlPoints.push_back(glm::vec3(*cursorX, *cursorY, 0));

and

interpolatedPoints.push_back(glm::catmullRom(controlPoints[0], 
    controlPoints[1], controlPoints[2], controlPoints[3], 0));

and

glBufferData(GL_ARRAY_BUFFER, controlPoints.size() * sizeof(glm::vec3), controlPoints.data(), GL_STATIC_DRAW);

Also don't allocate cursorX and cursorY on the heap. There is simply no reason to do this:

double cursorX, cursorY;
glfwGetCursorPos(window, &cursorX, &cursorY);
controlPoints.push_back(glm::vec3(cursorX, cursorY, 0));
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • Your suggestions make sense, except for your glBufferData call. Shouldn't it take the data from interpolatedPoints, not controlPoints? Keep in mind that I have two glBufferData calls; one that binds the VBO at the start, and one that constantly gets updated in the game loop. Also, is my glDrawArrays call correct? I wasn't too sure if I take the size of interpolatedPoints or controlPoints. Edit: When I use glBufferBata with interpolatedPoints, I get a straight line (regardless of points). If with controlPoints, the line connects through the points, but its not curved. – Jonathan Oct 09 '16 at 15:51