[comp.sys.sgi] question: lookat

mmachlis@athena.mit.edu (Matthew A Machlis) (01/17/91)

I am developing a 3-D realtime simulation on a Personal IRIS which has
head-tracking capability to display what the user is actually looking at.
I am using the lookat() command to actually do the transformation involved
in head-tracking.  I have run into an unfortunate aspect of the lookat
command: everything is fine when I am looking straight ahead (along the
positive z-axis) with zero twist.  But as the "look vector" is rotated
downward (towards the negative y axis), still with zero twist, the 
direction which is "up" in terms of what is displayed on the screen
rotates 180 degrees as the head rotates through straight down.
How can I compensate for this twisting of the up direction with lookat?

PLEASE... if you have any information or think you might be able to help
but don't understand exactly what I am talking about, email me.  Thanks
in advance.
--
----------------------------------------------------------------------------
Matt Machlis
MIT Space Systems Laboratory
(617)253-2272

Dan Karron@UCBVAX.BERKELEY.EDU (01/17/91)

The twisting artifact in lookat() can be avoided by using a negative reverse
sequence of modeling transformations to specify your eye position and
vector.

This is the same as specifying your eye in worldspace and inverting
the matrix to give your the eye position viewing transform for that
eye position.

If this is unclear, I can post a long explanation. The short version
is to use modeling transforms for your eye position.


+-----------------------------------------------------------------------------+
| karron@nyu.edu (E-mail alias that will always find me)                      |
| Fax: 212 340 7190           *           Dan Karron, Research Associate      |
| . . . . . . . . . . . . . . *           New York University Medical Center  |
| 560 First Avenue           \*\    Pager <1> (212) 397 9330                  |
| New York, New York 10016    \**\        <2> 10896   <3> <your-number-here>  |
| (212) 340 5210               \***\_________________________________________ |
| Main machine: karron.med.nyu.edu (128.122.135.3) IRIS 85GT                  |
+-----------------------------------------------------------------------------+

farestam@ORION.CERFACS.FR (Stefan Farestam) (01/17/91)

In article <1991Jan16.181821.20724@athena.mit.edu>, Matthew A Machlis 
<bloom-beacon.mit.edu!bloom-picayune.mit.edu!news> writes

  > I am developing a 3-D realtime simulation on a Personal IRIS which has
  > head-tracking capability to display what the user is actually looking at.
  > I am using the lookat() command to actually do the transformation involved
  > in head-tracking.  I have run into an unfortunate aspect of the lookat
  > command: everything is fine when I am looking straight ahead (along the
  > positive z-axis) with zero twist.  But as the "look vector" is rotated
  > downward (towards the negative y axis), still with zero twist, the 
  > direction which is "up" in terms of what is displayed on the screen
  > rotates 180 degrees as the head rotates through straight down.
  > How can I compensate for this twisting of the up direction with lookat?

I'm not sure I understand your problem, but the following might prove useful
for you:

Suppose that :

* view_point is the point you are looking from

* ref_point is the point you are looking towards

* up_vec is a vector that represents the direction up (i.e. if you would
  plot up_vec after lookat was applied it would be a vertical vector
  pointing upwards on the screen)

* v = view_point - ref_point

* w = (up_vec) x (v)   where 'x' signifies the vector product

* v and w are normalized

then the twist angle used in lookat can be calculated by:

   swy = (int) copysign(1.0, wy) ;

   twist = swy * acos((wx*vz - wz*vx) / sqrt(vx*vx         + vz*vz)
                                      / sqrt(wx*wx + wy*wy + wz*wz)) ;

and the call to lookat would look like:

   lookat(view_point->x, view_point->y, view_point->z,
           ref_point->x,  ref_point->y,  ref_point->z, twist * 180/PI * 10) ;

/Stefan Farestam


--

   .................................................................   
 .             Stefan Farestam <farestam@cerfacs.fr>                 .
.   __ __  __ _  _ _                                                  .
.  /  |_ )|_ /_\/ (          European Centre for Research and         .
 . \_ |__\| /   \__)    Advanced Training in Scientific Computation  .
   .................................................................  

thant@horus.esd.sgi.com (Thant Tessman) (01/18/91)

This is a shar file with a command I wrote called 'upat'.   It works
like lookat except you can specify what up is.

After unpacking it, compile it with:

	cc -o example example.c upat.c -lgl_s -lm


--------------------------cut here and save in a file------------------
#!/bin/sh -v
# this is a shell archive (once) named upat.mail
# created by thant@horus at Thu Jan 17 08:59:48 PST 1991
# 
# The rest of this file is a shell script which will extract: 
#	example.c
#	upat.c
#
#	to use, type sh upat.mail
#
echo x -  example.c
cat >  example.c  << 'EndOfRecord'
#include <gl.h>
#include <device.h>

extern void upat(float vx, float vy, float vz,
		 float px, float py, float pz,
		 float ux, float uy, float uz);

main() {

    int dev;
    short val;
    float i, j, k;

    initialize();

    /* first flyby */

    printf("Y is up.  (Just like lookat.)\n");
    j = 2.0;
    for (i = -8.0, k=12.0; i<12.0; i+=0.05, k-=0.05) {
	color(BLACK); clear();
	perspective(400, 5.0/4.0, 1.0, 50.0);
	upat(i, j, k,
	     0.0, 0.0, 0.0,
	     0.0, 1.0, 0.0);
	draw_axes();
	swapbuffers();
    }


    /* second flyby */

    printf("Z is up, but same path.\n");
    j = 2.0;
    for (i = -8.0, k=12.0; i<12.0; i+=0.05, k-=0.05) {
	color(BLACK); clear();
	perspective(400, 5.0/4.0, 1.0, 50.0);
	upat(i, j, k,
	     0.0, 0.0, 0.0,
	     0.0, 0.0, 1.0);
	draw_axes();
	swapbuffers();
    }


    /* third flyby */

    printf("(1, 1, 1) is up.  Different path.\n");
    for (i = -8.0, j=10.0, k=12.0; i<12.0; i+=0.05, k-=0.05, j-=0.05) {
	color(BLACK); clear();
	perspective(400, 5.0/4.0, 1.0, 50.0);
	upat(i, j, k,
	     0.0, 0.0, 0.0,
	     1.0, 1.0, 1.0);
	draw_axes();
	swapbuffers();
    }
}


initialize() {

    keepaspect(5, 4);
    winopen("");

    doublebuffer();
    gconfig();

    qdevice(ESCKEY);
}


draw_axes() {

    color(RED);
    move(0.0, 0.0, 0.0);
    draw(1.0, 0.0, 0.0);

    cmov(1.2, 0.0, 0.0);
    charstr("x");

    color(GREEN);
    move(0.0, 0.0, 0.0);
    draw(0.0, 1.0, 0.0);

    cmov(0.0, 1.2, 0.0);
    charstr("y");

    color(BLUE);
    move(0.0, 0.0, 0.0);
    draw(0.0, 0.0, 1.0);

    cmov(0.0, 0.0, 1.2);
    charstr("z");

    color(WHITE);
    move(0.5, 0.5, 0.5);
    draw(0.5, 0.5, 0.0);

    move(0.5, 0.0, 0.0);
    draw(0.5, 0.5, 0.0);
    draw(0.0, 0.5, 0.0);

    move(0.5, 0.5, 0.5);
    draw(0.0, 0.5, 0.5);

    move(0.0, 0.5, 0.0);
    draw(0.0, 0.5, 0.5);
    draw(0.0, 0.0, 0.5);

    move(0.5, 0.5, 0.5);
    draw(0.5, 0.0, 0.5);

    move(0.5, 0.0, 0.0);
    draw(0.5, 0.0, 0.5);
    draw(0.0, 0.0, 0.5);
}
EndOfRecord
len=`wc -c <  example.c `
if [ $len !=  1993  ] ; then
echo error:  example.c  was $len bytes long, should have been  1993
fi
echo x -  upat.c
cat >  upat.c  << 'EndOfRecord'
#include "math.h"
#include "gl.h"
#include "stdio.h"

#define X 0
#define Y 1
#define Z 2

void normalize(float *);
void cross(float *result, float *v1, float *v2);

void upat(float vx, float vy, float vz,
          float px, float py, float pz,
          float ux, float uy, float uz) {

    int i;
    float forward[3], side[3], up[3];
    float m[4][4];

    forward[X] = px - vx;
    forward[Y] = py - vy;
    forward[Z] = pz - vz;

    up[X] = ux;	    /* temporarily use view-up to hold world-up */
    up[Y] = uy;
    up[Z] = uz;

    normalize(forward);

    /* make side from view forward and world up */
    cross(side, forward, up);
    normalize(side);

    /* make view up from view forward and view side */
    cross(up, side, forward);

    m[0][0] = side[X];
    m[1][0] = side[Y];
    m[2][0] = side[Z];
    m[3][0] = 0.0;

    m[0][1] = up[X];
    m[1][1] = up[Y];
    m[2][1] = up[Z];
    m[3][1] = 0.0;

    m[0][2] = -forward[X];
    m[1][2] = -forward[Y];
    m[2][2] = -forward[Z];
    m[3][2] = 0.0;

    m[0][3] = 0.0;
    m[1][3] = 0.0;
    m[2][3] = 0.0;
    m[3][3] = 1.0;

    multmatrix(m);
    translate(-vx, -vy, -vz);

}


void normalize(float *v) {

    float r;

    r = sqrt( v[X]*v[X] + v[Y]*v[Y] + v[Z]*v[Z] );

    v[X] /= r;
    v[Y] /= r;
    v[Z] /= r;
}


void cross(float *result, float *v1, float *v2) {

    result[X] = v1[Y]*v2[Z] - v1[Z]*v2[Y];
    result[Y] = v1[Z]*v2[X] - v1[X]*v2[Z];
    result[Z] = v1[X]*v2[Y] - v1[Y]*v2[X];
}
EndOfRecord
len=`wc -c <  upat.c `
if [ $len !=  1480  ] ; then
echo error:  upat.c  was $len bytes long, should have been  1480
fi