Index: new-game.cpp
===================================================================
--- new-game.cpp	(revision fd6f4653858e63f2de67d87f3a8ac9a5ca787745)
+++ new-game.cpp	(revision 3effd814070c6d05ee43290be8ebe13ba007c267)
@@ -33,31 +33,4 @@
 using namespace std;
 using namespace glm;
-
-/* LASERS TODO:
- * -Allow lasers that face any direction
- * -Make the lasers rotate to always face the camera
- * -Use textures to draw lasers
- *    -The textures should be grayscale and have transparency
- *    -The laser shader should take an input color to blend with the texture to give the lasers color
- *    -DONE
- * -The lasers should be SceneObjects and drawn like all other objects
- *    -DONE
- * -Make lasers shoot from the ends of the ship's wings when the user presses a button and disappear after a second or so
- *
- * NOTE: Asteroid movement currently depends on framerate, fix this in a generic/reusable way
- */
-
-/* LASER POSITIONING ALGORITHM (to be implemented)
- * -Determine the length of the laser based on the start and end points
- * -Create a laser (long thin rectangle) of that length and the appropriate width
- *  along the z axis, facing up, and with its start point at the origin (to make sure rotations happen around that point) 
- * -Determine the line from the camera that intersects the line the laser should lie on at a 90 degree angle
- * -The angle between that line and the laser's normal should be the angle needed to rotate the laser so it faces the camera
- * -Now, create the transformation matrix for the laser to correctly position it
- *   -First, rotate along the z axis by the angle calculated above
- *   -Then, determine the correct angles and rotate around the y and x axes to make the laser point in the right direction
- *   -Finally, translate the laser to its correct position by getting the difference between the camera position
- *    and the laser start point
- */
 
 enum State {
@@ -229,4 +202,9 @@
 
 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+
+/*
+* TODO: Make lasers shoot from the ends of the ship's wings when the user presses a button and disappear after a second or so
+* TODO: Asteroid movement currently depends on framerate, fix this in a generic/reusable way
+*/
 
 int main(int argc, char* argv[]) {
@@ -408,4 +386,8 @@
    shaderBufferInfo[texture_sp] = BufferInfo();
    shaderBufferInfo[laser_sp] = BufferInfo();
+
+   cam_pos = vec3(0.0f, 0.0f, 2.0f);
+   float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
+   float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
 
    SceneObject obj;
@@ -958,9 +940,4 @@
    // glm::perspective can create the projection matrix
 
-   cam_pos = vec3(0.0f, 0.0f, 2.0f);
-   //cam_pos = vec3(-2.1f, -1.5f, -1.5f); // Good position for checking ship faces
-   float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
-   float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
-
    mat4 T = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
    mat4 yaw_mat = rotate(mat4(1.0f), -cam_yaw, vec3(0.0f, 1.0f, 0.0f));
@@ -969,4 +946,6 @@
    view_mat = R*T;
 
+   // TODO: Create a function to construct the projection matrix
+   // (Maybe I should just use glm::perspective, after making sure it matches what I have now)
    float fov = 67.0f * ONE_DEG_IN_RAD;
    float aspect = (float)width / (float)height;
@@ -1275,6 +1254,4 @@
       vec4 ray_world = inverse(view_mat) * ray_eye;
 
-      vec4 cam_pos_temp = vec4(cam_pos, 1.0f);
-
       vec4 click_point;
       vec3 closest_point = vec3(0.0f, 0.0f, -FAR_CLIP); // Any valid point will be closer than the far clipping plane, so initial value to that
@@ -1290,5 +1267,5 @@
                   vec3(it->points[p_idx + 6], it->points[p_idx + 7], it->points[p_idx + 8]),
                },
-               &*it, ray_world, cam_pos_temp, click_point
+               &*it, ray_world, vec4(cam_pos, 1.0f), click_point
             )) {
                click_point = view_mat * click_point;
@@ -1582,4 +1559,16 @@
 }
 
+/* LASER RENDERING/POSITIONING ALGORITHM
+ * -Draw a thin rectangle for the laser beam, using the specified width and endpoints
+ * -Texture the beam with a grayscale partially transparent image
+ * -In the shader, blend the image with a color to support lasers of different colors
+ *
+ * The flat part of the textured rectangle needs to always face the camera, so the laser's width is constant
+ * This is done as follows:
+* -Determine the length of the laser based on the start and end points
+* -Draw a rectangle along the z-axis and rotated upwards along the y-axis, with the correct final length and width
+* -Rotate the beam around the z-axis by the correct angle, sot that in its final position, the flat part faces the camera
+* -Rotate the beam along the x-axis and then along the y-axis and then translate it to put it into its final position
+*/
 void addLaserToScene(vec3 start, vec3 end, vec3 color, GLfloat width, GLuint laser_sp) {
    SceneObject obj = SceneObject();
@@ -1589,5 +1578,6 @@
    obj.deleted = false;
 
-   float length = (end-start).length();
+   vec3 ray = end - start;
+   float length = glm::length(ray);
 
    obj.points = {
@@ -1634,5 +1624,24 @@
 
    obj.num_points = obj.points.size() / 3;
-   obj.model_base = translate(mat4(1.0f), start);
+
+   float xAxisRotation = asin(ray.y / length);
+   float yAxisRotation = atan2(-ray.x, -ray.z);
+
+   vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
+               rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
+               vec4(0.0f, 1.0f, 0.0f, 1.0f));
+
+   // To project point P onto line AB:
+   // projection = A + dot(AP,AB) / dot(AB,AB) * AB
+   vec3 projOnLaser = start + glm::dot(cam_pos-start, ray) / (length*length) * ray;
+   vec3 laserToCam = cam_pos - projOnLaser;
+
+   float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
+
+   obj.model_base = mat4(1.0f);
+   obj.model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f)) * obj.model_base;
+   obj.model_base = rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) * obj.model_base;
+   obj.model_base = rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) * obj.model_base;
+   obj.model_base = translate(mat4(1.0f), start) * obj.model_base;
    obj.model_transform = mat4(1.0f);
 
@@ -1737,4 +1746,5 @@
       shaderBufferInfo[shaderIt->first].ubo_base = lastShaderUboCount * 2;
 
+      /*
       cout << "shader: " << shaderIt->first << endl;
       cout << "point counts: " << shaderCounts[shaderIt->first] << endl;
@@ -1742,4 +1752,5 @@
       cout << "vbo_base: " << shaderBufferInfo[shaderIt->first].vbo_base << endl;
       cout << "ubo_base: " << shaderBufferInfo[shaderIt->first].ubo_base << endl;
+      */
 
       shaderBufferInfo[shaderIt->first].vbo_offset = 0;
