# Ray based Picking

## Screen Click Point to 3D Ray

Uses the inverse projection matrix to unproject the mouse click position in the Normalised Device Space into the camera space. Then use the inversion view matrix to transform it to the world space.

This is the most important part for picking.

```cpp
glm::vec3 screenPosToRay(int mouseX, int mouseY, int w, int h,
                         const glm::mat4 &proj, const glm::mat4 &view)
{
    float x = (2.0f * mouseX) / w - 1.0f;
    float y = 1.0f - (2.0f * mouseY) / h;

    // clicked point on the near plane in NDC space
    // if we assume the eye space is scaled so that z_{near} = -1.0
    // then this NDC coordinate is the same as its clip coordinate
    glm::vec4 ray_clip(x, y, -1.0f, 1.0f);

    // set one point with (x, y) and z = -1.0 in eye/camera space
    // the camera is located at (0, 0, 0)
    glm::vec4 ray_eye = glm::inverse(proj) * ray_clip;

    // ray direction in scaled eye space: z_{near} = -1.0
    ray_eye = glm::vec4(ray_eye.x, ray_eye.y, -1.0f, 0.0f);

    // convert the vector to the word space
    glm::vec3 ray_world = glm::normalize(glm::vec3(glm::inverse(view) * ray_eye));
    return ray_world;
}
```

## Mouse button event callback (main.cpp)

```cpp
void mouse_button_callback(GLFWwindow *win, int button, int action, int mods)
{
    
    if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
    {
        double mx, my;
        glfwGetCursorPos(win, &mx, &my);

        std::cout << "Mouse click at: (" << mx <<", " << my << ")" << std::endl;

        int w, h;
        glfwGetWindowSize(win, &w, &h);

        glm::vec3 rayOrig = viewPos;
        glm::vec3 rayDir = screenPosToRay((int)mx, (int)my, w, h, matProj, matView);

        Ray ray{rayOrig, rayDir};

        
        for (std::shared_ptr<Mesh> pMesh : meshList)
        {
            HitInfo hit;
            if (pMesh->pSpatial->Raycast(ray, hit)) {
                std::cout << "Picked triangle index: " << hit.triIndex << ", t=" << hit.t << std::endl;
                pMesh->setPicked(true);
            } else {
                pMesh->setPicked(false);
                std::cout << "No objects picked" << std::endl;
            }
        }
    }
}
```

## Picked Model Highlighting

added a bPicked uniform in the shader and uses colour blending.

```glsl
// LabA11
uniform bool bPicked = false;

out vec4 colour_out;

void main()
{
    vec3 colour = texture(textureMap, texCoord).rgb;

    if (bPicked)
        colour = 0.6 * colour + 0.4 * vec3(1.0, 1.0, 0.0);
    
    ...
}
```
