Notes for Wednesday April 7 -- mouse and keyboard

Mouse events

In class we showed how we added to the library so that we can get and respond to mouse events. To do that, we override the definitions of these methods:

   canvas.onDrag       = (x, y) => { };
   canvas.onMove       = (x, y) => { };
   canvas.onPress      = (x, y) => { };
   canvas.onRelease    = (x, y) => { };
The arguments x and y represent the position of the cursor on the canvase, where x ranges between -1.0 (left) and +1.0 (right), and y ranges between -1.0 (bottom) and +1.0 (top).

Keyboard events

In class we also showed how we added to the library so that we can get and respond to keyboard events. To do that, we override the definitions of these methods:

   canvas.onKeyPress   = key => { };
   canvas.onKeyRelease = key => { };
The argument key is the numerical keycode for the key on the keyboard being pressed or released.

In class we showed how to use mouse and keyboard events to let the user interactively change various aspects of the animation.

Ray tracing to a triangle

We also showed how to trace a ray into the scene to see which objects are at the cursor.

We implemented just the simple case of the object being a sphere, but we also talked about how to ray trace to a triangle. That could be used to allow us to ray trace to an entire triangle mesh.

To ray trace to a triangle, the basic idea is that given three triangle vertices A,B,C, we first ray trace to the plane P that contains points A,B and C. Then we need to check whether the point S where the ray intersects P (where P • S == 0) is inside the triangle. In other words, find S = V + tW for some t such that P • S == 0.

Now consider just the edge from vertex A to vertex B. We can compute the equation of the plane Q which is perpendicular to P and which contains A and B. Point S is "inside" this edge if S is on the same side of Q as vertex C. This will be true when (Q • S) * (Q • C) > 0.

We do this for all three edges. If S is inside all three edges, then it is inside the triangle, and we know that the ray has hit the triangle.

Ray tracing to a triangle mesh shape

To do hit testing on an entire triangle mesh, we ray trace to all of its triangles. If the ray hits any of those triangles, then the ray has hit the mesh. If the mesh is very complex, we can do hit testing on a simplified version of the mesh, which is roughly the same shape as the rendered mesh, but contains fewer triangles.

To do this hit testing, it would work to transform all of the vertices of the mesh by our transform matrix M. But that can get expensive. So in practice, it is better instead to transform the ray (V,W) by the inverse matrix M-1. This provides the same result at much lower computational expense.

Videos we watched

Homework -- due before class on Wednesday April 14

Starting with the code we implemented in class, which is in hw8.zip, implement interactive buttons and sliders in the 3D scene, and possibly other kinds of widgets.

  1. A button should have two parts: the frame (which does not move) and the button itself, which depresses when the user presses down on it. The frame, for example, can be made out of a cube or a cylinder. The button itself can be made, for example, out of a cube, a cylinder or a sphere.

    When the user presses a button, you should make something happen in the 3D scene. For example, some object could change color, or something could start spinning.

    Extra credit: Try to implement two different types of buttons: (a) A button that makes something happen only while the button is pressed, and (b) a toggle button that turns something on when it is pressed and released, and then turns something off when it is again pressed and released.

  2. A slider should have at least two parts: the frame (which does not move) and the slide itself, which moves along the frame when the user drags the cursor, but cannot slide off the end of the frame. The frame, for example, can be made out of a cube. The slide can be made, for example, out of a cube or a cylinder. Optionally your slider could also have other parts, such as a visible rail that that slider moves along.

    When the user adjusts a slider, you should make something change in the 3D scene. For example, some object could vary continuously in color, or something could spin at a faster or slower rate.

    For example, you can use three sliders side by side to adjust some object's red, green and blue color components, respectively. For this use, it is good design practice to tint the sliders themselves red, green and blue, respectively.

Extra credit: Try creating your own kind of interactive widget, such as a rotating dial, or a choice panel (a row of buttons such that when one button is pressed, the others all lift up). Feel free to be creative.

Hint: Tracking the active part of a button, or the moving slide of a slider, is easier than you might think. No matter what shape you are tracking with your cursor, whether it is a cube or a cylinder or a sphere or something else, you can use an invisible sphere as the target object.

You don't need an actual sphere mesh for this, only the S = [centerx,centery,centerz,radius] data to pass into the third argument of your rayHitsSphere(V,W,S) function.

Just make sure that the center of sphere S is the same as the center of your cube or cylinder or whatever shape, and that the radius of sphere S is about the same as half the width of your shape. You might want to tweak the radius of S a bit to see what works best in practice.